Re-enable pygments and add copy-code button

This commit is contained in:
Dave Gallant
2023-12-17 18:53:46 -05:00
parent e7fc123bfe
commit bbf6a152e0
89 changed files with 1449 additions and 940 deletions

View File

@@ -7,7 +7,9 @@
font-weight: 600;
}
h2:hover a, h3:hover a, h4:hover a {
h2:hover a,
h3:hover a,
h4:hover a {
visibility: visible;
text-decoration: none;
}

View File

@@ -4,8 +4,8 @@ languageCode: en-us
googleAnalytics: G-V8WJDERTX9
copyright: Dave Gallant
preserveTaxonomyNames: true
pygmentsstyle: "monokai"
pygmentscodefences: false
pygmentsstyle: nord
pygmentscodefences: true
pygmentscodefencesguesssyntax: true
theme:
- archie

View File

@@ -29,7 +29,6 @@ flowchartDiagrams:
sequenceDiagrams:
enable: false
options: ""
---
<!--more-->
@@ -48,9 +47,9 @@ This [gist](https://gist.github.com/triangletodd/02f595cd4c0dc9aac5f7763ca226418
There is an issue on Kubernetes regarding swap [here](https://github.com/kubernetes/kubernetes/issues/53533). There claims to be support for swap in 1.22, but for now let's disable it:
```
sysctl vm.swappiness=0
swapoff -a
```shell
sudo sysctl vm.swappiness=0
sudo swapoff -a
```
It might be worth experimenting with swap enabled in the future to see how that might affect performance.
@@ -59,7 +58,7 @@ It might be worth experimenting with swap enabled in the future to see how that
To avoid IP Forwarding issues with Traefik, run the following on the host:
```sh
```shell
sudo sysctl net.ipv4.ip_forward=1
sudo sysctl net.ipv6.conf.all.forwarding=1
sudo sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/g' /etc/sysctl.conf
@@ -81,7 +80,7 @@ Now back on the host run `pct list` to determine what VMID it was given.
Open `/etc/pve/lxc/$VMID.conf` and append:
```sh
```
lxc.apparmor.profile: unconfined
lxc.cap.drop:
lxc.mount.auto: "proc:rw sys:rw"
@@ -92,6 +91,7 @@ All of the above configurations are described in the [manpages](https://linuxcon
Notice that `cgroup2` is used since Proxmox VE 7.0 has switched to a [pure cgroupv2 environment](https://pve.proxmox.com/pve-docs/chapter-pct.html#pct_cgroup).
Thankfully cgroup v2 support has been supported in k3s with these contributions:
- https://github.com/k3s-io/k3s/pull/2584
- https://github.com/k3s-io/k3s/pull/2844
@@ -99,7 +99,7 @@ Thankfully cgroup v2 support has been supported in k3s with these contributions:
From within the container, run:
```sh
```shell
echo '#!/bin/sh -e
ln -s /dev/console /dev/kmsg
mount --make-rshared /' > /etc/rc.local
@@ -113,7 +113,7 @@ One of the simplest ways to install K3s on a remote host is to use [k3sup](https
Ensure that you supply a valid `CONTAINER_IP` and choose the `k3s-version` you prefer.
As of 2021/11, it is still defaulting to the 1.19 channel, so I overrode it to 1.22 for cgroup v2 support. See the published releases [here](https://github.com/k3s-io/k3s/releases).
```sh
```shell
ssh-copy-id root@$CONTAINER_IP
k3sup install --ip $CONTAINER_IP --user root --k3s-version v1.22.3+k3s1
```
@@ -124,7 +124,6 @@ If all goes well, you should see a path to the `kubeconfig` generated. I moved t
Installing K3s in LXC on Proxmox works with a few tweaks to the default configuration. I later followed the Tekton's [Getting Started](https://tekton.dev/docs/getting-started/) guide and was able to deploy it in a few commands.
```console
$ kubectl get all --namespace tekton-pipelines
NAME READY STATUS RESTARTS AGE

View File

@@ -56,13 +56,13 @@ A few days ago, yewtu.be went down briefly, and that motivated me enough to self
The quickest way to get invidious up is with docker-compose as mentioned in the [docs](https://docs.invidious.io/installation/).
I made a few modifications (such as pinning the container's tag), and ended up with:
I made a few modifications, and ended up with:
```yaml
version: "3"
services:
invidious:
image: quay.io/invidious/invidious:5160d8bae39dc5cc5d51abee90571a03c08d0f2b
image: quay.io/invidious/invidious
restart: unless-stopped
ports:
- "0.0.0.0:3000:3000"

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
@@ -92,6 +92,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -121,6 +123,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -23,8 +23,8 @@ Feel free to reach out at me@davegallant.ca."/>
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -71,7 +71,7 @@ Feel free to reach out at me@davegallant.ca."/>
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -94,6 +94,8 @@ Feel free to reach out at me@davegallant.ca."/>
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
<main>
@@ -145,6 +147,7 @@ Feel free to reach out at me@davegallant.ca."/>
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -20,8 +20,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -68,7 +68,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -91,6 +91,12 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
<link href="/css/copy-code-button.min.css" rel="stylesheet">
</header>
<main>
@@ -109,93 +115,81 @@ As of right now, the latest AUR is <code>4.2.2-1</code>.</p>
<p>These steps highlight how to get it working with <code>Python3.8</code> by making a 1 line modification to AppGate source code.</p>
<h1 id="packaging">Packaging</h1>
<p>We already know the community package is currently out of date, so let&rsquo;s clone it:</p>
<pre><code class="language-shell">git clone https://aur.archlinux.org/appgate-sdp.git
cd appgate-sdp
</code></pre>
<p>You&rsquo;ll likely notice that the version is not what we want, so let&rsquo;s modify the <code>PKGBUILD</code> to the following:</p>
<pre><code class="language-shell"># Maintainer: Pawel Mosakowski &lt;pawel at mosakowski dot net&gt;
pkgname=appgate-sdp
conflicts=('appgate-sdp-headless')
pkgver=4.3.2
_download_pkgver=4.3
pkgrel=1
epoch=
pkgdesc=&quot;Software Defined Perimeter - GUI client&quot;
arch=('x86_64')
url=&quot;https://www.cyxtera.com/essential-defense/appgate-sdp/support&quot;
license=('custom')
# dependecies calculated by namcap
depends=('gconf' 'libsecret' 'gtk3' 'python' 'nss' 'libxss' 'nodejs' 'dnsmasq')
source=(&quot;https://sdpdownloads.cyxtera.com/AppGate-SDP-${_download_pkgver}/clients/${pkgname}_${pkgver}_amd64.deb&quot;
&quot;appgatedriver.service&quot;)
options=(staticlibs)
prepare() {
tar -xf data.tar.xz
}
package() {
cp -dpr &quot;${srcdir}&quot;/{etc,lib,opt,usr} &quot;${pkgdir}&quot;
mv -v &quot;$pkgdir/lib/systemd/system&quot; &quot;$pkgdir/usr/lib/systemd/&quot;
rm -vrf &quot;$pkgdir/lib&quot;
cp -v &quot;$srcdir/appgatedriver.service&quot; &quot;$pkgdir/usr/lib/systemd/system/appgatedriver.service&quot;
mkdir -vp &quot;$pkgdir/usr/share/licenses/appgate-sdp&quot;
cp -v &quot;$pkgdir/usr/share/doc/appgate/copyright&quot; &quot;$pkgdir/usr/share/licenses/appgate-sdp&quot;
cp -v &quot;$pkgdir/usr/share/doc/appgate/LICENSE.github&quot; &quot;$pkgdir/usr/share/licenses/appgate-sdp&quot;
cp -v &quot;$pkgdir/usr/share/doc/appgate/LICENSES.chromium.html.bz2&quot; &quot;$pkgdir/usr/share/licenses/appgate-sdp&quot;
}
md5sums=('17101aac7623c06d5fbb95f50cf3dbdc'
'002644116e20b2d79fdb36b7677ab4cf')
</code></pre>
<p>Let&rsquo;s first make sure we have some dependencies. If you do not have <a href="https://github.com/Jguer/yay">yay</a>, check it out.</p>
<pre><code class="language-shell">yay -S dnsmasq gconf
</code></pre>
<p>Now, let&rsquo;s install it:</p>
<pre><code class="language-shell">makepkg -si
</code></pre>
<h1 id="running-the-client">Running the client</h1>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>git clone https://aur.archlinux.org/appgate-sdp.git
</span></span><span style="display:flex;"><span><span style="color:#81a1c1">cd</span> appgate-sdp
</span></span></code></pre></div><p>You&rsquo;ll likely notice that the version is not what we want, so let&rsquo;s modify the <code>PKGBUILD</code> to the following:</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#616e87;font-style:italic"># Maintainer: Pawel Mosakowski &lt;pawel at mosakowski dot net&gt;</span>
</span></span><span style="display:flex;"><span>pkgname<span style="color:#81a1c1">=</span>appgate-sdp
</span></span><span style="display:flex;"><span>conflicts<span style="color:#81a1c1">=(</span><span style="color:#a3be8c">&#39;appgate-sdp-headless&#39;</span><span style="color:#81a1c1">)</span>
</span></span><span style="display:flex;"><span>pkgver<span style="color:#81a1c1">=</span>4.3.2
</span></span><span style="display:flex;"><span>_download_pkgver<span style="color:#81a1c1">=</span>4.3
</span></span><span style="display:flex;"><span>pkgrel<span style="color:#81a1c1">=</span><span style="color:#b48ead">1</span>
</span></span><span style="display:flex;"><span>epoch<span style="color:#81a1c1">=</span>
</span></span><span style="display:flex;"><span>pkgdesc<span style="color:#81a1c1">=</span><span style="color:#a3be8c">&#34;Software Defined Perimeter - GUI client&#34;</span>
</span></span><span style="display:flex;"><span>arch<span style="color:#81a1c1">=(</span><span style="color:#a3be8c">&#39;x86_64&#39;</span><span style="color:#81a1c1">)</span>
</span></span><span style="display:flex;"><span>url<span style="color:#81a1c1">=</span><span style="color:#a3be8c">&#34;https://www.cyxtera.com/essential-defense/appgate-sdp/support&#34;</span>
</span></span><span style="display:flex;"><span>license<span style="color:#81a1c1">=(</span><span style="color:#a3be8c">&#39;custom&#39;</span><span style="color:#81a1c1">)</span>
</span></span><span style="display:flex;"><span><span style="color:#616e87;font-style:italic"># dependecies calculated by namcap</span>
</span></span><span style="display:flex;"><span>depends<span style="color:#81a1c1">=(</span><span style="color:#a3be8c">&#39;gconf&#39;</span> <span style="color:#a3be8c">&#39;libsecret&#39;</span> <span style="color:#a3be8c">&#39;gtk3&#39;</span> <span style="color:#a3be8c">&#39;python&#39;</span> <span style="color:#a3be8c">&#39;nss&#39;</span> <span style="color:#a3be8c">&#39;libxss&#39;</span> <span style="color:#a3be8c">&#39;nodejs&#39;</span> <span style="color:#a3be8c">&#39;dnsmasq&#39;</span><span style="color:#81a1c1">)</span>
</span></span><span style="display:flex;"><span>source<span style="color:#81a1c1">=(</span><span style="color:#a3be8c">&#34;https://sdpdownloads.cyxtera.com/AppGate-SDP-</span><span style="color:#a3be8c">${</span>_download_pkgver<span style="color:#a3be8c">}</span><span style="color:#a3be8c">/clients/</span><span style="color:#a3be8c">${</span>pkgname<span style="color:#a3be8c">}</span><span style="color:#a3be8c">_</span><span style="color:#a3be8c">${</span>pkgver<span style="color:#a3be8c">}</span><span style="color:#a3be8c">_amd64.deb&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#a3be8c">&#34;appgatedriver.service&#34;</span><span style="color:#81a1c1">)</span>
</span></span><span style="display:flex;"><span>options<span style="color:#81a1c1">=(</span>staticlibs<span style="color:#81a1c1">)</span>
</span></span><span style="display:flex;"><span>prepare<span style="color:#81a1c1">()</span> <span style="color:#81a1c1">{</span>
</span></span><span style="display:flex;"><span> tar -xf data.tar.xz
</span></span><span style="display:flex;"><span><span style="color:#81a1c1">}</span>
</span></span><span style="display:flex;"><span>package<span style="color:#81a1c1">()</span> <span style="color:#81a1c1">{</span>
</span></span><span style="display:flex;"><span> cp -dpr <span style="color:#a3be8c">&#34;</span><span style="color:#a3be8c">${</span>srcdir<span style="color:#a3be8c">}</span><span style="color:#a3be8c">&#34;</span>/<span style="color:#81a1c1">{</span>etc,lib,opt,usr<span style="color:#81a1c1">}</span> <span style="color:#a3be8c">&#34;</span><span style="color:#a3be8c">${</span>pkgdir<span style="color:#a3be8c">}</span><span style="color:#a3be8c">&#34;</span>
</span></span><span style="display:flex;"><span> mv -v <span style="color:#a3be8c">&#34;</span>$pkgdir<span style="color:#a3be8c">/lib/systemd/system&#34;</span> <span style="color:#a3be8c">&#34;</span>$pkgdir<span style="color:#a3be8c">/usr/lib/systemd/&#34;</span>
</span></span><span style="display:flex;"><span> rm -vrf <span style="color:#a3be8c">&#34;</span>$pkgdir<span style="color:#a3be8c">/lib&#34;</span>
</span></span><span style="display:flex;"><span> cp -v <span style="color:#a3be8c">&#34;</span>$srcdir<span style="color:#a3be8c">/appgatedriver.service&#34;</span> <span style="color:#a3be8c">&#34;</span>$pkgdir<span style="color:#a3be8c">/usr/lib/systemd/system/appgatedriver.service&#34;</span>
</span></span><span style="display:flex;"><span> mkdir -vp <span style="color:#a3be8c">&#34;</span>$pkgdir<span style="color:#a3be8c">/usr/share/licenses/appgate-sdp&#34;</span>
</span></span><span style="display:flex;"><span> cp -v <span style="color:#a3be8c">&#34;</span>$pkgdir<span style="color:#a3be8c">/usr/share/doc/appgate/copyright&#34;</span> <span style="color:#a3be8c">&#34;</span>$pkgdir<span style="color:#a3be8c">/usr/share/licenses/appgate-sdp&#34;</span>
</span></span><span style="display:flex;"><span> cp -v <span style="color:#a3be8c">&#34;</span>$pkgdir<span style="color:#a3be8c">/usr/share/doc/appgate/LICENSE.github&#34;</span> <span style="color:#a3be8c">&#34;</span>$pkgdir<span style="color:#a3be8c">/usr/share/licenses/appgate-sdp&#34;</span>
</span></span><span style="display:flex;"><span> cp -v <span style="color:#a3be8c">&#34;</span>$pkgdir<span style="color:#a3be8c">/usr/share/doc/appgate/LICENSES.chromium.html.bz2&#34;</span> <span style="color:#a3be8c">&#34;</span>$pkgdir<span style="color:#a3be8c">/usr/share/licenses/appgate-sdp&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#81a1c1">}</span>
</span></span><span style="display:flex;"><span>md5sums<span style="color:#81a1c1">=(</span><span style="color:#a3be8c">&#39;17101aac7623c06d5fbb95f50cf3dbdc&#39;</span>
</span></span><span style="display:flex;"><span> <span style="color:#a3be8c">&#39;002644116e20b2d79fdb36b7677ab4cf&#39;</span><span style="color:#81a1c1">)</span>
</span></span></code></pre></div><p>Let&rsquo;s first make sure we have some dependencies. If you do not have <a href="https://github.com/Jguer/yay">yay</a>, check it out.</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>yay -S dnsmasq gconf
</span></span></code></pre></div><p>Now, let&rsquo;s install it:</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>makepkg -si
</span></span></code></pre></div><h1 id="running-the-client">Running the client</h1>
<p>Ok, let&rsquo;s run the client by executing <code>appgate</code>.</p>
<p>It complains about not being able to connect.</p>
<p>Easy fix:</p>
<pre><code class="language-shell">sudo systemctl start appgatedriver.service
</code></pre>
<p>Now we should be connected&hellip; but DNS is not working?</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo systemctl start appgatedriver.service
</span></span></code></pre></div><p>Now we should be connected&hellip; but DNS is not working?</p>
<h1 id="fixing-the-dns">Fixing the DNS</h1>
<p>Running <code>resolvectl</code> should display that something is not right.</p>
<p>Why is the DNS not being set by appgate?</p>
<pre><code class="language-shell">$ head -3 /opt/appgate/linux/set_dns
#!/usr/bin/env python3
'''
This is used to set and unset the DNS.
</code></pre>
<p>It seems like python3 is required for the DNS setting to happen.
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ head -3 /opt/appgate/linux/set_dns
</span></span><span style="display:flex;"><span><span style="color:#616e87;font-style:italic">#!/usr/bin/env python3</span>
</span></span><span style="display:flex;"><span><span style="color:#a3be8c">&#39;&#39;</span><span style="color:#bf616a">&#39;</span>
</span></span><span style="display:flex;"><span>This is used to <span style="color:#81a1c1">set</span> and <span style="color:#81a1c1">unset</span> the DNS.
</span></span></code></pre></div><p>It seems like python3 is required for the DNS setting to happen.
Let&rsquo;s try to run it.</p>
<pre><code class="language-shell">$ sudo /opt/appgate/linux/set_dns
/opt/appgate/linux/set_dns:88: SyntaxWarning: &quot;is&quot; with a literal. Did you mean &quot;==&quot;?
servers = [( socket.AF_INET if x.version is 4 else socket.AF_INET6, map(int, x.packed)) for x in servers]
Traceback (most recent call last):
File &quot;/opt/appgate/linux/set_dns&quot;, line 30, in &lt;module&gt;
import dbus
ModuleNotFoundError: No module named 'dbus'
</code></pre>
<p>Ok, let&rsquo;s install it:</p>
<pre><code class="language-shell">$ sudo python3.8 -m pip install dbus-python
</code></pre>
<p>Will it work now? Not yet. There&rsquo;s another issue:</p>
<pre><code class="language-shell">$ sudo /opt/appgate/linux/set_dns
/opt/appgate/linux/set_dns:88: SyntaxWarning: &quot;is&quot; with a literal. Did you mean &quot;==&quot;?
servers = [( socket.AF_INET if x.version is 4 else socket.AF_INET6, map(int, x.packed)) for x in servers]
module 'platform' has no attribute 'linux_distribution'
</code></pre>
<p>This is a breaking change in Python3.8.</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ sudo /opt/appgate/linux/set_dns
</span></span><span style="display:flex;"><span>/opt/appgate/linux/set_dns:88: SyntaxWarning: <span style="color:#a3be8c">&#34;is&#34;</span> with a literal. Did you mean <span style="color:#a3be8c">&#34;==&#34;</span>?
</span></span><span style="display:flex;"><span> servers <span style="color:#81a1c1">=</span> <span style="color:#81a1c1">[(</span> socket.AF_INET <span style="color:#81a1c1;font-weight:bold">if</span> x.version is <span style="color:#b48ead">4</span> <span style="color:#81a1c1;font-weight:bold">else</span> socket.AF_INET6, map<span style="color:#81a1c1">(</span>int, x.packed<span style="color:#81a1c1">))</span> <span style="color:#81a1c1;font-weight:bold">for</span> x in servers<span style="color:#81a1c1">]</span>
</span></span><span style="display:flex;"><span>Traceback <span style="color:#81a1c1">(</span>most recent call last<span style="color:#81a1c1">)</span>:
</span></span><span style="display:flex;"><span> File <span style="color:#a3be8c">&#34;/opt/appgate/linux/set_dns&#34;</span>, line 30, in &lt;module&gt;
</span></span><span style="display:flex;"><span> import dbus
</span></span><span style="display:flex;"><span>ModuleNotFoundError: No module named <span style="color:#a3be8c">&#39;dbus&#39;</span>
</span></span></code></pre></div><p>Ok, let&rsquo;s install it:</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ sudo python3.8 -m pip install dbus-python
</span></span></code></pre></div><p>Will it work now? Not yet. There&rsquo;s another issue:</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ sudo /opt/appgate/linux/set_dns
</span></span><span style="display:flex;"><span>/opt/appgate/linux/set_dns:88: SyntaxWarning: <span style="color:#a3be8c">&#34;is&#34;</span> with a literal. Did you mean <span style="color:#a3be8c">&#34;==&#34;</span>?
</span></span><span style="display:flex;"><span> servers <span style="color:#81a1c1">=</span> <span style="color:#81a1c1">[(</span> socket.AF_INET <span style="color:#81a1c1;font-weight:bold">if</span> x.version is <span style="color:#b48ead">4</span> <span style="color:#81a1c1;font-weight:bold">else</span> socket.AF_INET6, map<span style="color:#81a1c1">(</span>int, x.packed<span style="color:#81a1c1">))</span> <span style="color:#81a1c1;font-weight:bold">for</span> x in servers<span style="color:#81a1c1">]</span>
</span></span><span style="display:flex;"><span>module <span style="color:#a3be8c">&#39;platform&#39;</span> has no attribute <span style="color:#a3be8c">&#39;linux_distribution&#39;</span>
</span></span></code></pre></div><p>This is a breaking change in Python3.8.</p>
<p>So what is calling <code>platform.linux_distribution</code>?</p>
<p>Let&rsquo;s search for it:</p>
<pre><code class="language-shell">$ sudo grep -r 'linux_distribution' /opt/appgate/linux/
/opt/appgate/linux/nm.py: if platform.linux_distribution()[0] != 'Fedora':
</code></pre>
<p>Aha! So this is in the local AppGate source code. This should be an easy fix. Let&rsquo;s just replace this line with:</p>
<pre><code class="language-python">if True: # Since we are not using Fedora :)
</code></pre>
<h1 id="wrapping-up">Wrapping up</h1>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>$ sudo grep -r <span style="color:#a3be8c">&#39;linux_distribution&#39;</span> /opt/appgate/linux/
</span></span><span style="display:flex;"><span>/opt/appgate/linux/nm.py: <span style="color:#81a1c1;font-weight:bold">if</span> platform.linux_distribution<span style="color:#81a1c1">()[</span>0<span style="color:#81a1c1">]</span> !<span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#39;Fedora&#39;</span>:
</span></span></code></pre></div><p>Aha! So this is in the local AppGate source code. This should be an easy fix. Let&rsquo;s just replace this line with:</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-python" data-lang="python"><span style="display:flex;"><span><span style="color:#81a1c1;font-weight:bold">if</span> <span style="color:#81a1c1;font-weight:bold">True</span><span style="color:#eceff4">:</span> <span style="color:#616e87;font-style:italic"># Since we are not using Fedora :)</span>
</span></span></code></pre></div><h1 id="wrapping-up">Wrapping up</h1>
<p>It turns out there are <a href="https://docs.python.org/3.7/library/platform.html#platform.linux_distribution">breaking changes</a> in Python3.8.</p>
<p>The docs say <code>Deprecated since version 3.5, will be removed in version 3.8: See alternative like the distro package.</code></p>
<p>I suppose this highlights one of the caveats of relying upon the system&rsquo;s python, rather than having an isolated, dedicated environment for all dependencies.</p></section>
@@ -271,6 +265,9 @@ module 'platform' has no attribute 'linux_distribution'
<div class="footer-info">
2023 Dave Gallant
</div>
<script src="/js/copy-code-button.js"></script>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -20,8 +20,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -68,7 +68,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -91,6 +91,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
<main>
@@ -221,6 +223,7 @@ Containers have much less overhead in terms of boot time and storage allocation.
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -20,8 +20,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -68,7 +68,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -91,6 +91,12 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
<link href="/css/copy-code-button.min.css" rel="stylesheet">
</header>
<main>
@@ -107,10 +113,9 @@
<h2 id="what-is-home-manager">What is home-manager?<a href="#what-is-home-manager" class="hanchor" ariaLabel="Anchor">#</a></h2>
<p>Before understanding home-manager, it is worth briefly discussing what nix is. <a href="https://nixos.org/">nix</a> is a package manager that originally spawned from a <a href="https://edolstra.github.io/pubs/phd-thesis.pdf">PhD thesis</a>. Unlike other package managers, it uses symbolic links to keep track of the currently installed packages, keeping around the old ones in case you may want to rollback.</p>
<p>For example, I have used nix to install the package <a href="https://search.nixos.org/packages?channel=unstable&amp;show=bind&amp;from=0&amp;size=50&amp;sort=relevance&amp;type=packages&amp;query=bind">bind</a> which includes <code>dig</code>. You can see that it is available on multiple platforms. The absolute path of <code>dig</code> can be found by running:</p>
<pre><code class="language-console">$ ls -lh $(which dig)
lrwxr-xr-x 73 root 31 Dec 1969 /run/current-system/sw/bin/dig -&gt; /nix/store/0r4qdyprljd3dki57jn6c6a8dh2rbg9g-bind-9.16.16-dnsutils/bin/dig
</code></pre>
<p>Notice that there is a hash included in the file path? This is a nix store path and is computed by the nix package manager. This <a href="https://nixos.org/guides/nix-pills/nix-store-paths.html">nix pill</a> does a good job explaining how this hash is computed. All of the nix pills are worth a read, if you are interested in learning more about nix itself. However, using home-manager does not require extensive knowledge of nix.</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-console" data-lang="console"><span style="display:flex;"><span><span style="color:#4c566a;font-weight:bold">$</span> ls -lh <span style="color:#81a1c1;font-weight:bold">$(</span>which dig<span style="color:#81a1c1;font-weight:bold">)</span>
</span></span><span style="display:flex;"><span>lrwxr-xr-x 73 root 31 Dec 1969 /run/current-system/sw/bin/dig -&gt; /nix/store/0r4qdyprljd3dki57jn6c6a8dh2rbg9g-bind-9.16.16-dnsutils/bin/dig
</span></span></code></pre></div><p>Notice that there is a hash included in the file path? This is a nix store path and is computed by the nix package manager. This <a href="https://nixos.org/guides/nix-pills/nix-store-paths.html">nix pill</a> does a good job explaining how this hash is computed. All of the nix pills are worth a read, if you are interested in learning more about nix itself. However, using home-manager does not require extensive knowledge of nix.</p>
<p>Part of the nix ecosystem includes <a href="https://github.com/NixOS/nixpkgs">nixpkgs</a>. Many popular tools can be found already packaged in this repository. As you can see with these <a href="https://repology.org/repositories/statistics/total">stats</a>, there is a large number of existing packages that are being maintained by the community. Contributing a new package is easy, and anyone can do it!</p>
<p>home-manager leverages the nix package manager (and nixpkgs), as well the nix language so that you can declaratively define your system configuration. I store my <a href="https://github.com/davegallant/nix-config">nix-config</a> in git so that I can keep track of my packages and configurations, and retain a clean and informative git commit history so that I can understand what changed and why.</p>
<h2 id="setting-up-home-manager">Setting up home-manager<a href="#setting-up-home-manager" class="hanchor" ariaLabel="Anchor">#</a></h2>
@@ -118,123 +123,119 @@ lrwxr-xr-x 73 root 31 Dec 1969 /run/current-system/sw/bin/dig -&gt; /nix/store/
<p>⚠️ If you run this on your main machine, make sure you backup your configuration files first. home-manager is pretty good about not overwriting existing configuration, but it is better to have a backup! Alternatively, you could test this out on a VM or cloud instance.</p>
</blockquote>
<p>The first thing you should do is <a href="https://nixos.org/guides/install-nix.html">install nix</a>:</p>
<pre><code class="language-shell">curl -L https://nixos.org/nix/install | sh
</code></pre>
<p>It&rsquo;s generally not a good idea to curl and execute files from the internet (without verifying integrity), so you might want to download the install script first and take a look before executing it!</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>curl -L https://nixos.org/nix/install <span style="color:#eceff4">|</span> sh
</span></span></code></pre></div><p>It&rsquo;s generally not a good idea to curl and execute files from the internet (without verifying integrity), so you might want to download the install script first and take a look before executing it!</p>
<p>Open up a new shell in your terminal and running <code>nix</code> <em>should</em> work. If not, run <code>. ~/.nix-profile/etc/profile.d/nix.sh</code></p>
<p>Now, <a href="https://github.com/nix-community/home-manager#installation">install home-manager</a>:</p>
<pre><code class="language-shell">nix-channel --add https://github.com/nix-community/home-manager/archive/master.tar.gz home-manager
nix-channel --update
nix-shell '&lt;home-manager&gt;' -A install
</code></pre>
<p>You should see a wave of <code>/nix/store/*</code> paths being displayed on your screen.</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>nix-channel --add https://github.com/nix-community/home-manager/archive/master.tar.gz home-manager
</span></span><span style="display:flex;"><span>nix-channel --update
</span></span><span style="display:flex;"><span>nix-shell <span style="color:#a3be8c">&#39;&lt;home-manager&gt;&#39;</span> -A install
</span></span></code></pre></div><p>You should see a wave of <code>/nix/store/*</code> paths being displayed on your screen.</p>
<p>Now, to start off with a basic configuration, open up <code>~/.config/nixpkgs/home.nix</code> in the editor of your choice and paste this in (you will want to change <code>userName</code> and <code>homeDirectory</code>):</p>
<pre><code class="language-nix">{ config, pkgs, ... }:
{
programs.home-manager.enable = true;
home = {
username = &quot;dave&quot;;
homeDirectory = &quot;/home/dave&quot;;
stateVersion = &quot;21.11&quot;;
packages = with pkgs; [
bind
exa
fd
ripgrep
];
};
programs = {
git = {
enable = true;
aliases = {
aa = &quot;add -A .&quot;;
br = &quot;branch&quot;;
c = &quot;commit -S&quot;;
ca = &quot;commit -S --amend&quot;;
cb = &quot;checkout -b&quot;;
co = &quot;checkout&quot;;
d = &quot;diff&quot;;
l =
&quot;log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an&gt;%Creset' --abbrev-commit&quot;;
};
delta = {
enable = true;
options = {
features = &quot;line-numbers decorations&quot;;
whitespace-error-style = &quot;22 reverse&quot;;
plus-style = &quot;green bold ul '#198214'&quot;;
decorations = {
commit-decoration-style = &quot;bold yellow box ul&quot;;
file-style = &quot;bold yellow ul&quot;;
file-decoration-style = &quot;none&quot;;
};
};
};
extraConfig = {
push = { default = &quot;current&quot;; };
pull = { rebase = true; };
};
};
starship = {
enable = true;
enableZshIntegration = true;
settings = {
add_newline = false;
scan_timeout = 10;
};
};
zsh = {
enable = true;
enableAutosuggestions = true;
enableSyntaxHighlighting = true;
history.size = 1000000;
localVariables = {
CASE_SENSITIVE = &quot;true&quot;;
DISABLE_UNTRACKED_FILES_DIRTY = &quot;true&quot;;
RPROMPT = &quot;&quot;; # override because macOS defaults to filepath
ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE = &quot;fg=#838383,underline&quot;;
ZSH_DISABLE_COMPFIX = &quot;true&quot;;
};
initExtra = ''
export PAGER=less
'';
shellAliases = {
&quot;..&quot; = &quot;cd ..&quot;;
grep = &quot;rg --smart-case&quot;;
ls = &quot;exa -la --git&quot;;
};
&quot;oh-my-zsh&quot; = {
enable = true;
plugins = [
&quot;gitfast&quot;
&quot;last-working-dir&quot;
];
};
};
};
}
</code></pre>
<p>Save the file and run:</p>
<pre><code>home-manager switch
</code></pre>
<p>You should see another wave of <code>/nix/store/*</code> paths. The new configuration should now be active.</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-nix" data-lang="nix"><span style="display:flex;"><span><span style="color:#eceff4">{</span> config<span style="color:#81a1c1">,</span> pkgs<span style="color:#81a1c1">,</span> <span style="color:#81a1c1">...</span> <span style="color:#eceff4">}:</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#eceff4">{</span>
</span></span><span style="display:flex;"><span> programs<span style="color:#81a1c1">.</span>home-manager<span style="color:#81a1c1">.</span>enable <span style="color:#81a1c1">=</span> <span style="color:#8fbcbb">true</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> home <span style="color:#81a1c1">=</span> <span style="color:#eceff4">{</span>
</span></span><span style="display:flex;"><span> username <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;dave&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> homeDirectory <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;/home/dave&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> stateVersion <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;21.11&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> packages <span style="color:#81a1c1">=</span> <span style="color:#81a1c1;font-weight:bold">with</span> pkgs<span style="color:#eceff4">;</span> <span style="color:#eceff4">[</span>
</span></span><span style="display:flex;"><span> bind
</span></span><span style="display:flex;"><span> exa
</span></span><span style="display:flex;"><span> fd
</span></span><span style="display:flex;"><span> ripgrep
</span></span><span style="display:flex;"><span> <span style="color:#eceff4">];</span>
</span></span><span style="display:flex;"><span> <span style="color:#eceff4">};</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> programs <span style="color:#81a1c1">=</span> <span style="color:#eceff4">{</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> git <span style="color:#81a1c1">=</span> <span style="color:#eceff4">{</span>
</span></span><span style="display:flex;"><span> enable <span style="color:#81a1c1">=</span> <span style="color:#8fbcbb">true</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> aliases <span style="color:#81a1c1">=</span> <span style="color:#eceff4">{</span>
</span></span><span style="display:flex;"><span> aa <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;add -A .&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> br <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;branch&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> c <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;commit -S&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> ca <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;commit -S --amend&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> cb <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;checkout -b&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> co <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;checkout&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> d <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;diff&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> l <span style="color:#81a1c1">=</span>
</span></span><span style="display:flex;"><span> <span style="color:#a3be8c">&#34;log --graph --pretty=format:&#39;%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)&lt;%an&gt;%Creset&#39; --abbrev-commit&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> <span style="color:#eceff4">};</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> delta <span style="color:#81a1c1">=</span> <span style="color:#eceff4">{</span>
</span></span><span style="display:flex;"><span> enable <span style="color:#81a1c1">=</span> <span style="color:#8fbcbb">true</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> options <span style="color:#81a1c1">=</span> <span style="color:#eceff4">{</span>
</span></span><span style="display:flex;"><span> features <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;line-numbers decorations&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> whitespace-error-style <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;22 reverse&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> plus-style <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;green bold ul &#39;#198214&#39;&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> decorations <span style="color:#81a1c1">=</span> <span style="color:#eceff4">{</span>
</span></span><span style="display:flex;"><span> commit-decoration-style <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;bold yellow box ul&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> file-style <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;bold yellow ul&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> file-decoration-style <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;none&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> <span style="color:#eceff4">};</span>
</span></span><span style="display:flex;"><span> <span style="color:#eceff4">};</span>
</span></span><span style="display:flex;"><span> <span style="color:#eceff4">};</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> extraConfig <span style="color:#81a1c1">=</span> <span style="color:#eceff4">{</span>
</span></span><span style="display:flex;"><span> push <span style="color:#81a1c1">=</span> <span style="color:#eceff4">{</span> default <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;current&#34;</span><span style="color:#eceff4">;</span> <span style="color:#eceff4">};</span>
</span></span><span style="display:flex;"><span> pull <span style="color:#81a1c1">=</span> <span style="color:#eceff4">{</span> rebase <span style="color:#81a1c1">=</span> <span style="color:#8fbcbb">true</span><span style="color:#eceff4">;</span> <span style="color:#eceff4">};</span>
</span></span><span style="display:flex;"><span> <span style="color:#eceff4">};</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#eceff4">};</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> starship <span style="color:#81a1c1">=</span> <span style="color:#eceff4">{</span>
</span></span><span style="display:flex;"><span> enable <span style="color:#81a1c1">=</span> <span style="color:#8fbcbb">true</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> enableZshIntegration <span style="color:#81a1c1">=</span> <span style="color:#8fbcbb">true</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> settings <span style="color:#81a1c1">=</span> <span style="color:#eceff4">{</span>
</span></span><span style="display:flex;"><span> add_newline <span style="color:#81a1c1">=</span> <span style="color:#8fbcbb">false</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> scan_timeout <span style="color:#81a1c1">=</span> <span style="color:#b48ead">10</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> <span style="color:#eceff4">};</span>
</span></span><span style="display:flex;"><span> <span style="color:#eceff4">};</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> zsh <span style="color:#81a1c1">=</span> <span style="color:#eceff4">{</span>
</span></span><span style="display:flex;"><span> enable <span style="color:#81a1c1">=</span> <span style="color:#8fbcbb">true</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> enableAutosuggestions <span style="color:#81a1c1">=</span> <span style="color:#8fbcbb">true</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> enableSyntaxHighlighting <span style="color:#81a1c1">=</span> <span style="color:#8fbcbb">true</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> history<span style="color:#81a1c1">.</span>size <span style="color:#81a1c1">=</span> <span style="color:#b48ead">1000000</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> localVariables <span style="color:#81a1c1">=</span> <span style="color:#eceff4">{</span>
</span></span><span style="display:flex;"><span> CASE_SENSITIVE <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;true&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> DISABLE_UNTRACKED_FILES_DIRTY <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;true&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> RPROMPT <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;&#34;</span><span style="color:#eceff4">;</span> <span style="color:#616e87;font-style:italic"># override because macOS defaults to filepath</span>
</span></span><span style="display:flex;"><span> ZSH_AUTOSUGGEST_HIGHLIGHT_STYLE <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;fg=#838383,underline&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> ZSH_DISABLE_COMPFIX <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;true&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> <span style="color:#eceff4">};</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> initExtra <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#39;&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#a3be8c"> export PAGER=less
</span></span></span><span style="display:flex;"><span><span style="color:#a3be8c"> &#39;&#39;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> shellAliases <span style="color:#81a1c1">=</span> <span style="color:#eceff4">{</span>
</span></span><span style="display:flex;"><span> <span style="color:#a3be8c">&#34;..&#34;</span> <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;cd ..&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> grep <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;rg --smart-case&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> ls <span style="color:#81a1c1">=</span> <span style="color:#a3be8c">&#34;exa -la --git&#34;</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> <span style="color:#eceff4">};</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#a3be8c">&#34;oh-my-zsh&#34;</span> <span style="color:#81a1c1">=</span> <span style="color:#eceff4">{</span>
</span></span><span style="display:flex;"><span> enable <span style="color:#81a1c1">=</span> <span style="color:#8fbcbb">true</span><span style="color:#eceff4">;</span>
</span></span><span style="display:flex;"><span> plugins <span style="color:#81a1c1">=</span> <span style="color:#eceff4">[</span>
</span></span><span style="display:flex;"><span> <span style="color:#a3be8c">&#34;gitfast&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#a3be8c">&#34;last-working-dir&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#eceff4">];</span>
</span></span><span style="display:flex;"><span> <span style="color:#eceff4">};</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#eceff4">};</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#eceff4">};</span>
</span></span><span style="display:flex;"><span><span style="color:#eceff4">}</span>
</span></span></code></pre></div><p>Save the file and run:</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>home-manager switch
</span></span></code></pre></div><p>You should see another wave of <code>/nix/store/*</code> paths. The new configuration should now be active.</p>
<p>If you run <code>zsh</code>, you should see that you have <a href="https://starship.rs/">starship</a> and access to several other utils such as <code>rg</code>, <code>fd</code>, and <code>exa</code>.</p>
<p>This basic configuration above is also defining your <code>~/.config/git/config</code> and <code>.zshrc</code>. If you already have either of these files, home-manager will complain about them already existing.</p>
<p>If you run <code>cat ~/.zshrc</code>, you will see the way these configuration files are generated.</p>
@@ -315,6 +316,9 @@ nix-shell '&lt;home-manager&gt;' -A install
<div class="footer-info">
2023 Dave Gallant
</div>
<script src="/js/copy-code-button.js"></script>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -20,8 +20,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -68,7 +68,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -91,6 +91,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
<main>
@@ -179,6 +181,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -20,8 +20,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -68,7 +68,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -91,6 +91,12 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
<link href="/css/copy-code-button.min.css" rel="stylesheet">
</header>
<main>
@@ -119,74 +125,65 @@
<p>I&rsquo;ve also observed that so far my 2019 16&quot; Macbook Pro hasn&rsquo;t sounded like a jet engine, although I haven&rsquo;t performed any disk-intensive operations yet.</p>
<h3 id="installing-podman">Installing Podman<a href="#installing-podman" class="hanchor" ariaLabel="Anchor">#</a></h3>
<p>Running Podman on macOS is more involved than on Linux, because the podman-machine must run Linux inside of a virtual machine. Fortunately, the installation is made simple with <a href="https://formulae.brew.sh/formula/podman">brew</a> (read <a href="https://podman.io/getting-started/installation#linux-distributions">this</a> if you&rsquo;re installing Podman on Linux):</p>
<pre><code class="language-sh">brew install podman
</code></pre>
<p>The podman-machine must be started:</p>
<pre><code class="language-sh"># This is not necessary on Linux
podman machine init
podman machine start
</code></pre>
<h3 id="running-a-container">Running a container<a href="#running-a-container" class="hanchor" ariaLabel="Anchor">#</a></h3>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>brew install podman
</span></span></code></pre></div><p>The podman-machine must be started:</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span><span style="color:#616e87;font-style:italic"># This is not necessary on Linux</span>
</span></span><span style="display:flex;"><span>podman machine init
</span></span><span style="display:flex;"><span>podman machine start
</span></span></code></pre></div><h3 id="running-a-container">Running a container<a href="#running-a-container" class="hanchor" ariaLabel="Anchor">#</a></h3>
<p>Let&rsquo;s try to pull an image:</p>
<pre><code class="language-console">$ podman pull alpine
Trying to pull docker.io/library/alpine:latest...
Getting image source signatures
Copying blob sha256:a0d0a0d46f8b52473982a3c466318f479767577551a53ffc9074c9fa7035982e
Copying config sha256:14119a10abf4669e8cdbdff324a9f9605d99697215a0d21c360fe8dfa8471bab
Writing manifest to image destination
Storing signatures
14119a10abf4669e8cdbdff324a9f9605d99697215a0d21c360fe8dfa8471bab
</code></pre>
<blockquote>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-console" data-lang="console"><span style="display:flex;"><span><span style="color:#4c566a;font-weight:bold">$</span> podman pull alpine
</span></span><span style="display:flex;"><span>Trying to pull docker.io/library/alpine:latest...
</span></span><span style="display:flex;"><span>Getting image source signatures
</span></span><span style="display:flex;"><span>Copying blob sha256:a0d0a0d46f8b52473982a3c466318f479767577551a53ffc9074c9fa7035982e
</span></span><span style="display:flex;"><span>Copying config sha256:14119a10abf4669e8cdbdff324a9f9605d99697215a0d21c360fe8dfa8471bab
</span></span><span style="display:flex;"><span>Writing manifest to image destination
</span></span><span style="display:flex;"><span>Storing signatures
</span></span><span style="display:flex;"><span>14119a10abf4669e8cdbdff324a9f9605d99697215a0d21c360fe8dfa8471bab
</span></span></code></pre></div><blockquote>
<p>If you&rsquo;re having an issue pulling images, you may need to remove <code>~/.docker/config.json</code> or remove the set of auths in the configuration as mentioned <a href="https://stackoverflow.com/a/69121873/1191286">here</a>.</p>
</blockquote>
<p>and then run and exec into the container:</p>
<pre><code class="language-console">$ podman run --rm -ti alpine
Error: error preparing container 99ace1ef8a78118e178372d91fd182e8166c399fbebe0f676af59fbf32ce205b for attach: error configuring network namespace for container 99ace1ef8a78118e178372d91fd182e8166c399fbebe0f676af59fbf32ce205b: error adding pod unruffled_bohr_unruffled_bohr to CNI network &quot;podman&quot;: unexpected end of JSON input
</code></pre>
<p>What does this error mean? A bit of searching lead to <a href="https://github.com/containers/podman/issues/11837">this github issue</a>.</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-console" data-lang="console"><span style="display:flex;"><span><span style="color:#4c566a;font-weight:bold">$</span> podman run --rm -ti alpine
</span></span><span style="display:flex;"><span>Error: error preparing container 99ace1ef8a78118e178372d91fd182e8166c399fbebe0f676af59fbf32ce205b for attach: error configuring network namespace for container 99ace1ef8a78118e178372d91fd182e8166c399fbebe0f676af59fbf32ce205b: error adding pod unruffled_bohr_unruffled_bohr to CNI network &#34;podman&#34;: unexpected end of JSON input
</span></span></code></pre></div><p>What does this error mean? A bit of searching lead to <a href="https://github.com/containers/podman/issues/11837">this github issue</a>.</p>
<p>Until the fix is released, a workaround is to just specify a port (even when it&rsquo;s not needed):</p>
<pre><code class="language-sh">podman run -p 4242 --rm -ti alpine
</code></pre>
<p>If you&rsquo;re reading this from the future, there is a good chance specifying a port won&rsquo;t be needed.</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>podman run -p <span style="color:#b48ead">4242</span> --rm -ti alpine
</span></span></code></pre></div><p>If you&rsquo;re reading this from the future, there is a good chance specifying a port won&rsquo;t be needed.</p>
<p>Another example of running a container with Podman can be found in the <a href="https://jellyfin.org/docs/general/administration/installing.html#podman">Jellyfin Documentation</a>.</p>
<h3 id="aliasing-docker-with-podman">Aliasing docker with podman<a href="#aliasing-docker-with-podman" class="hanchor" ariaLabel="Anchor">#</a></h3>
<p>Force of habit (or other scripts) may have you calling <code>docker</code>. To work around this:</p>
<pre><code class="language-sh">alias docker=podman
</code></pre>
<h3 id="podman-compose">podman-compose<a href="#podman-compose" class="hanchor" ariaLabel="Anchor">#</a></h3>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span><span style="color:#81a1c1">alias</span> docker<span style="color:#81a1c1">=</span>podman
</span></span></code></pre></div><h3 id="podman-compose">podman-compose<a href="#podman-compose" class="hanchor" ariaLabel="Anchor">#</a></h3>
<p>You may be wondering: what about docker-compose? Well, there <em>claims</em> to be a drop-in replacement for it: <a href="https://github.com/containers/podman-compose">podman-compose</a>.</p>
<pre><code class="language-sh">pip3 install --user podman-compose
</code></pre>
<p>Now let&rsquo;s create a <code>docker-compose.yml</code> file to test:</p>
<pre><code class="language-sh">cat &lt;&lt; EOF &gt;&gt; docker-compose.yml
version: '2'
services:
hello_world:
image: ubuntu
command: [/bin/echo, 'Hello world']
EOF
</code></pre>
<p>Now run:</p>
<pre><code class="language-console">$ podman-compose up
podman pod create --name=davegallant.github.io --share net
40d61dc6e95216c07d2b21cea6dcb30205bfcaf1260501fe652f05bddf7e595e
0
podman create --name=davegallant.github.io_hello_world_1 --pod=davegallant.github.io -l io.podman.compose.config-hash=123 -l io.podman.compose.project=davegallant.github.io -l io.podman.compose.version=0.0.1 -l com.docker.compose.container-number=1 -l com.docker.compose.service=hello_world --add-host hello_world:127.0.0.1 --add-host davegallant.github.io_hello_world_1:127.0.0.1 ubuntu /bin/echo Hello world
Resolved &quot;ubuntu&quot; as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)
Trying to pull docker.io/library/ubuntu:latest...
Getting image source signatures
Copying blob sha256:f3ef4ff62e0da0ef761ec1c8a578f3035bef51043e53ae1b13a20b3e03726d17
Copying blob sha256:f3ef4ff62e0da0ef761ec1c8a578f3035bef51043e53ae1b13a20b3e03726d17
Copying config sha256:597ce1600cf4ac5f449b66e75e840657bb53864434d6bd82f00b172544c32ee2
Writing manifest to image destination
Storing signatures
1a68b2fed3fdf2037b7aef16d770f22929eec1d799219ce30541df7876918576
0
podman start -a davegallant.github.io_hello_world_1
Hello world
</code></pre>
<p>This should more or less provide the same results you would come to expect with docker. The README does clearly state that podman-compose is under development.</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>pip3 install --user podman-compose
</span></span></code></pre></div><p>Now let&rsquo;s create a <code>docker-compose.yml</code> file to test:</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-sh" data-lang="sh"><span style="display:flex;"><span>cat <span style="color:#a3be8c">&lt;&lt; EOF &gt;&gt; docker-compose.yml
</span></span></span><span style="display:flex;"><span><span style="color:#a3be8c">version: &#39;2&#39;
</span></span></span><span style="display:flex;"><span><span style="color:#a3be8c">services:
</span></span></span><span style="display:flex;"><span><span style="color:#a3be8c"> hello_world:
</span></span></span><span style="display:flex;"><span><span style="color:#a3be8c"> image: ubuntu
</span></span></span><span style="display:flex;"><span><span style="color:#a3be8c"> command: [/bin/echo, &#39;Hello world&#39;]
</span></span></span><span style="display:flex;"><span><span style="color:#a3be8c">EOF</span>
</span></span></code></pre></div><p>Now run:</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-console" data-lang="console"><span style="display:flex;"><span><span style="color:#4c566a;font-weight:bold">$</span> podman-compose up
</span></span><span style="display:flex;"><span>podman pod create --name=davegallant.github.io --share net
</span></span><span style="display:flex;"><span>40d61dc6e95216c07d2b21cea6dcb30205bfcaf1260501fe652f05bddf7e595e
</span></span><span style="display:flex;"><span>0
</span></span><span style="display:flex;"><span>podman create --name=davegallant.github.io_hello_world_1 --pod=davegallant.github.io -l io.podman.compose.config-hash=123 -l io.podman.compose.project=davegallant.github.io -l io.podman.compose.version=0.0.1 -l com.docker.compose.container-number=1 -l com.docker.compose.service=hello_world --add-host hello_world:127.0.0.1 --add-host davegallant.github.io_hello_world_1:127.0.0.1 ubuntu /bin/echo Hello world
</span></span><span style="display:flex;"><span>Resolved &#34;ubuntu&#34; as an alias (/etc/containers/registries.conf.d/000-shortnames.conf)
</span></span><span style="display:flex;"><span>Trying to pull docker.io/library/ubuntu:latest...
</span></span><span style="display:flex;"><span>Getting image source signatures
</span></span><span style="display:flex;"><span>Copying blob sha256:f3ef4ff62e0da0ef761ec1c8a578f3035bef51043e53ae1b13a20b3e03726d17
</span></span><span style="display:flex;"><span>Copying blob sha256:f3ef4ff62e0da0ef761ec1c8a578f3035bef51043e53ae1b13a20b3e03726d17
</span></span><span style="display:flex;"><span>Copying config sha256:597ce1600cf4ac5f449b66e75e840657bb53864434d6bd82f00b172544c32ee2
</span></span><span style="display:flex;"><span>Writing manifest to image destination
</span></span><span style="display:flex;"><span>Storing signatures
</span></span><span style="display:flex;"><span>1a68b2fed3fdf2037b7aef16d770f22929eec1d799219ce30541df7876918576
</span></span><span style="display:flex;"><span>0
</span></span><span style="display:flex;"><span>podman start -a davegallant.github.io_hello_world_1
</span></span><span style="display:flex;"><span>Hello world
</span></span></code></pre></div><p>This should more or less provide the same results you would come to expect with docker. The README does clearly state that podman-compose is under development.</p>
<h3 id="summary">Summary<a href="#summary" class="hanchor" ariaLabel="Anchor">#</a></h3>
<p>Installing Podman on macOS was not seamless, but it was manageable well within 30 minutes. I would recommend giving Podman a try to anyone who is unhappy with experiencing forced docker updates, or who is interested in using a more modern technology for running containers.</p>
<p>One caveat to mention is that there isn&rsquo;t an official graphical user interface for Podman, but there is an <a href="https://github.com/containers/podman/issues/11494">open issue</a> considering one. If you rely heavily on Docker Desktop&rsquo;s UI, you may not be as interested in using podman yet.</p>
@@ -266,6 +263,9 @@ Hello world
<div class="footer-info">
2023 Dave Gallant
</div>
<script src="/js/copy-code-button.js"></script>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -20,8 +20,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -68,7 +68,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -91,6 +91,12 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
<link href="/css/copy-code-button.min.css" rel="stylesheet">
</header>
<main>
@@ -110,18 +116,16 @@
<p>This <a href="https://gist.github.com/triangletodd/02f595cd4c0dc9aac5f7763ca2264185">gist</a> contains snippets and discussion on how to deploy K3s in LXC on Proxmox. It mentions that <code>bridge-nf-call-iptables</code> should be loaded, but I did not understand the benefit of doing this.</p>
<h2 id="disable-swap">Disable swap<a href="#disable-swap" class="hanchor" ariaLabel="Anchor">#</a></h2>
<p>There is an issue on Kubernetes regarding swap <a href="https://github.com/kubernetes/kubernetes/issues/53533">here</a>. There claims to be support for swap in 1.22, but for now let&rsquo;s disable it:</p>
<pre><code>sysctl vm.swappiness=0
swapoff -a
</code></pre>
<p>It might be worth experimenting with swap enabled in the future to see how that might affect performance.</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo sysctl vm.swappiness<span style="color:#81a1c1">=</span><span style="color:#b48ead">0</span>
</span></span><span style="display:flex;"><span>sudo swapoff -a
</span></span></code></pre></div><p>It might be worth experimenting with swap enabled in the future to see how that might affect performance.</p>
<h3 id="enable-ip-forwarding">Enable IP Forwarding<a href="#enable-ip-forwarding" class="hanchor" ariaLabel="Anchor">#</a></h3>
<p>To avoid IP Forwarding issues with Traefik, run the following on the host:</p>
<pre><code class="language-sh">sudo sysctl net.ipv4.ip_forward=1
sudo sysctl net.ipv6.conf.all.forwarding=1
sudo sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/g' /etc/sysctl.conf
sudo sed -i 's/#net.ipv6.conf.all.forwarding=1/net.ipv6.conf.all.forwarding=1/g' /etc/sysctl.conf
</code></pre>
<h2 id="create-lxc-container">Create LXC container<a href="#create-lxc-container" class="hanchor" ariaLabel="Anchor">#</a></h2>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>sudo sysctl net.ipv4.ip_forward<span style="color:#81a1c1">=</span><span style="color:#b48ead">1</span>
</span></span><span style="display:flex;"><span>sudo sysctl net.ipv6.conf.all.forwarding<span style="color:#81a1c1">=</span><span style="color:#b48ead">1</span>
</span></span><span style="display:flex;"><span>sudo sed -i <span style="color:#a3be8c">&#39;s/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/g&#39;</span> /etc/sysctl.conf
</span></span><span style="display:flex;"><span>sudo sed -i <span style="color:#a3be8c">&#39;s/#net.ipv6.conf.all.forwarding=1/net.ipv6.conf.all.forwarding=1/g&#39;</span> /etc/sysctl.conf
</span></span></code></pre></div><h2 id="create-lxc-container">Create LXC container<a href="#create-lxc-container" class="hanchor" ariaLabel="Anchor">#</a></h2>
<p>Create an LXC container in the Proxmox interface as you normally would. Remember to:</p>
<ul>
<li>Uncheck <code>unprivileged container</code></li>
@@ -132,12 +136,11 @@ sudo sed -i 's/#net.ipv6.conf.all.forwarding=1/net.ipv6.conf.all.forwarding=1/g'
<h3 id="modify-container-config">Modify container config<a href="#modify-container-config" class="hanchor" ariaLabel="Anchor">#</a></h3>
<p>Now back on the host run <code>pct list</code> to determine what VMID it was given.</p>
<p>Open <code>/etc/pve/lxc/$VMID.conf</code> and append:</p>
<pre><code class="language-sh">lxc.apparmor.profile: unconfined
lxc.cap.drop:
lxc.mount.auto: &quot;proc:rw sys:rw&quot;
lxc.cgroup2.devices.allow: c 10:200 rwm
</code></pre>
<p>All of the above configurations are described in the <a href="https://linuxcontainers.org/lxc/manpages/man5/lxc.container.conf.5.html">manpages</a>.
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>lxc.apparmor.profile: unconfined
</span></span><span style="display:flex;"><span>lxc.cap.drop:
</span></span><span style="display:flex;"><span>lxc.mount.auto: &#34;proc:rw sys:rw&#34;
</span></span><span style="display:flex;"><span>lxc.cgroup2.devices.allow: c 10:200 rwm
</span></span></code></pre></div><p>All of the above configurations are described in the <a href="https://linuxcontainers.org/lxc/manpages/man5/lxc.container.conf.5.html">manpages</a>.
Notice that <code>cgroup2</code> is used since Proxmox VE 7.0 has switched to a <a href="https://pve.proxmox.com/pve-docs/chapter-pct.html#pct_cgroup">pure cgroupv2 environment</a>.</p>
<p>Thankfully cgroup v2 support has been supported in k3s with these contributions:</p>
<ul>
@@ -146,47 +149,44 @@ Notice that <code>cgroup2</code> is used since Proxmox VE 7.0 has switched to a
</ul>
<h2 id="enable-shared-host-mounts">Enable shared host mounts<a href="#enable-shared-host-mounts" class="hanchor" ariaLabel="Anchor">#</a></h2>
<p>From within the container, run:</p>
<pre><code class="language-sh">echo '#!/bin/sh -e
ln -s /dev/console /dev/kmsg
mount --make-rshared /' &gt; /etc/rc.local
chmod +x /etc/rc.local
reboot
</code></pre>
<h2 id="install-k3s">Install K3s<a href="#install-k3s" class="hanchor" ariaLabel="Anchor">#</a></h2>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span><span style="color:#81a1c1">echo</span> <span style="color:#a3be8c">&#39;#!/bin/sh -e
</span></span></span><span style="display:flex;"><span><span style="color:#a3be8c">ln -s /dev/console /dev/kmsg
</span></span></span><span style="display:flex;"><span><span style="color:#a3be8c">mount --make-rshared /&#39;</span> &gt; /etc/rc.local
</span></span><span style="display:flex;"><span>chmod +x /etc/rc.local
</span></span><span style="display:flex;"><span>reboot
</span></span></code></pre></div><h2 id="install-k3s">Install K3s<a href="#install-k3s" class="hanchor" ariaLabel="Anchor">#</a></h2>
<p>One of the simplest ways to install K3s on a remote host is to use <a href="https://github.com/alexellis/k3sup">k3sup</a>.
Ensure that you supply a valid <code>CONTAINER_IP</code> and choose the <code>k3s-version</code> you prefer.
As of 2021/11, it is still defaulting to the 1.19 channel, so I overrode it to 1.22 for cgroup v2 support. See the published releases <a href="https://github.com/k3s-io/k3s/releases">here</a>.</p>
<pre><code class="language-sh">ssh-copy-id root@$CONTAINER_IP
k3sup install --ip $CONTAINER_IP --user root --k3s-version v1.22.3+k3s1
</code></pre>
<p>If all goes well, you should see a path to the <code>kubeconfig</code> generated. I moved this into <code>~/.kube/config</code> so that kubectl would read this by default.</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-shell" data-lang="shell"><span style="display:flex;"><span>ssh-copy-id root@$CONTAINER_IP
</span></span><span style="display:flex;"><span>k3sup install --ip $CONTAINER_IP --user root --k3s-version v1.22.3+k3s1
</span></span></code></pre></div><p>If all goes well, you should see a path to the <code>kubeconfig</code> generated. I moved this into <code>~/.kube/config</code> so that kubectl would read this by default.</p>
<h2 id="wrapping-up">Wrapping up<a href="#wrapping-up" class="hanchor" ariaLabel="Anchor">#</a></h2>
<p>Installing K3s in LXC on Proxmox works with a few tweaks to the default configuration. I later followed the Tekton&rsquo;s <a href="https://tekton.dev/docs/getting-started/">Getting Started</a> guide and was able to deploy it in a few commands.</p>
<pre><code class="language-console">$ kubectl get all --namespace tekton-pipelines
NAME READY STATUS RESTARTS AGE
pod/tekton-pipelines-webhook-8566ff9b6b-6rnh8 1/1 Running 1 (50m ago) 12h
pod/tekton-dashboard-6bf858f977-qt4hr 1/1 Running 1 (50m ago) 11h
pod/tekton-pipelines-controller-69fd7498d8-f57m4 1/1 Running 1 (50m ago) 12h
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/tekton-pipelines-controller ClusterIP 10.43.44.245 &lt;none&gt; 9090/TCP,8080/TCP 12h
service/tekton-pipelines-webhook ClusterIP 10.43.183.242 &lt;none&gt; 9090/TCP,8008/TCP,443/TCP,8080/TCP 12h
service/tekton-dashboard ClusterIP 10.43.87.97 &lt;none&gt; 9097/TCP 11h
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/tekton-pipelines-webhook 1/1 1 1 12h
deployment.apps/tekton-dashboard 1/1 1 1 11h
deployment.apps/tekton-pipelines-controller 1/1 1 1 12h
NAME DESIRED CURRENT READY AGE
replicaset.apps/tekton-pipelines-webhook-8566ff9b6b 1 1 1 12h
replicaset.apps/tekton-dashboard-6bf858f977 1 1 1 11h
replicaset.apps/tekton-pipelines-controller-69fd7498d8 1 1 1 12h
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
horizontalpodautoscaler.autoscaling/tekton-pipelines-webhook Deployment/tekton-pipelines-webhook 9%/100% 1 5 1 12h
</code></pre>
<p>I made sure to install Tailscale in the container so that I can easily access K3s from anywhere.</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-console" data-lang="console"><span style="display:flex;"><span><span style="color:#4c566a;font-weight:bold">$</span> kubectl get all --namespace tekton-pipelines
</span></span><span style="display:flex;"><span>NAME READY STATUS RESTARTS AGE
</span></span><span style="display:flex;"><span>pod/tekton-pipelines-webhook-8566ff9b6b-6rnh8 1/1 Running 1 (50m ago) 12h
</span></span><span style="display:flex;"><span>pod/tekton-dashboard-6bf858f977-qt4hr 1/1 Running 1 (50m ago) 11h
</span></span><span style="display:flex;"><span>pod/tekton-pipelines-controller-69fd7498d8-f57m4 1/1 Running 1 (50m ago) 12h
</span></span><span style="display:flex;"><span><span style="color:#bf616a">
</span></span></span><span style="display:flex;"><span><span style="color:#bf616a"></span>NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
</span></span><span style="display:flex;"><span>service/tekton-pipelines-controller ClusterIP 10.43.44.245 &lt;none&gt; 9090/TCP,8080/TCP 12h
</span></span><span style="display:flex;"><span>service/tekton-pipelines-webhook ClusterIP 10.43.183.242 &lt;none&gt; 9090/TCP,8008/TCP,443/TCP,8080/TCP 12h
</span></span><span style="display:flex;"><span>service/tekton-dashboard ClusterIP 10.43.87.97 &lt;none&gt; 9097/TCP 11h
</span></span><span style="display:flex;"><span><span style="color:#bf616a">
</span></span></span><span style="display:flex;"><span><span style="color:#bf616a"></span>NAME READY UP-TO-DATE AVAILABLE AGE
</span></span><span style="display:flex;"><span>deployment.apps/tekton-pipelines-webhook 1/1 1 1 12h
</span></span><span style="display:flex;"><span>deployment.apps/tekton-dashboard 1/1 1 1 11h
</span></span><span style="display:flex;"><span>deployment.apps/tekton-pipelines-controller 1/1 1 1 12h
</span></span><span style="display:flex;"><span><span style="color:#bf616a">
</span></span></span><span style="display:flex;"><span><span style="color:#bf616a"></span>NAME DESIRED CURRENT READY AGE
</span></span><span style="display:flex;"><span>replicaset.apps/tekton-pipelines-webhook-8566ff9b6b 1 1 1 12h
</span></span><span style="display:flex;"><span>replicaset.apps/tekton-dashboard-6bf858f977 1 1 1 11h
</span></span><span style="display:flex;"><span>replicaset.apps/tekton-pipelines-controller-69fd7498d8 1 1 1 12h
</span></span><span style="display:flex;"><span><span style="color:#bf616a">
</span></span></span><span style="display:flex;"><span><span style="color:#bf616a"></span>NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
</span></span><span style="display:flex;"><span>horizontalpodautoscaler.autoscaling/tekton-pipelines-webhook Deployment/tekton-pipelines-webhook 9%/100% 1 5 1 12h
</span></span></code></pre></div><p>I made sure to install Tailscale in the container so that I can easily access K3s from anywhere.</p>
<p>If I&rsquo;m feeling adventurous, I might experiment with <a href="https://rancher.com/docs/k3s/latest/en/advanced/#running-k3s-with-rootless-mode-experimental">K3s rootless</a>.</p></section>
<script
@@ -262,6 +262,9 @@ horizontalpodautoscaler.autoscaling/tekton-pipelines-webhook Deployment/tekton
<div class="footer-info">
2023 Dave Gallant
</div>
<script src="/js/copy-code-button.js"></script>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -20,8 +20,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -68,7 +68,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -91,6 +91,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
<main>
@@ -199,6 +201,7 @@ Encrypting your shared volumes should also be done, since unfortunately <a href=
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -20,8 +20,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -68,7 +68,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -91,6 +91,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
<main>
@@ -221,6 +223,7 @@ When setting up the machine, I mostly went with all of the defaults. Configurati
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -20,8 +20,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -68,7 +68,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -91,6 +91,12 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
<link href="/css/copy-code-button.min.css" rel="stylesheet">
</header>
<main>
@@ -110,49 +116,48 @@
<p>A few days ago, yewtu.be went down briefly, and that motivated me enough to self-host invidious. There are several other hosted instances listed <a href="https://docs.invidious.io/instances/">here</a>, but being able to easily backup my own instance (including subscriptions and watch history) is more compelling in my case.</p>
<h3 id="hosting-invidious">Hosting invidious<a href="#hosting-invidious" class="hanchor" ariaLabel="Anchor">#</a></h3>
<p>The quickest way to get invidious up is with docker-compose as mentioned in the <a href="https://docs.invidious.io/installation/">docs</a>.</p>
<p>I made a few modifications (such as pinning the container&rsquo;s tag), and ended up with:</p>
<pre><code class="language-yaml">version: &quot;3&quot;
services:
invidious:
image: quay.io/invidious/invidious:5160d8bae39dc5cc5d51abee90571a03c08d0f2b
restart: unless-stopped
ports:
- &quot;0.0.0.0:3000:3000&quot;
environment:
INVIDIOUS_CONFIG: |
db:
dbname: invidious
user: kemal
password: kemal
host: invidious-db
port: 5432
check_tables: true
healthcheck:
test: wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/comments/jNQXAC9IVRw || exit 1
interval: 30s
timeout: 5s
retries: 2
depends_on:
- invidious-db
invidious-db:
image: docker.io/library/postgres:14
restart: unless-stopped
volumes:
- postgresdata:/var/lib/postgresql/data
- ./config/sql:/config/sql
- ./docker/init-invidious-db.sh:/docker-entrypoint-initdb.d/init-invidious-db.sh
environment:
POSTGRES_DB: invidious
POSTGRES_USER: kemal
POSTGRES_PASSWORD: kemal
healthcheck:
test: [&quot;CMD-SHELL&quot;, &quot;pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB&quot;]
volumes:
postgresdata:
</code></pre>
<p>After invidious was up and running, I installed <a href="https://tailscale.com/">Tailscale</a> on it to leverage its MagicDNS, and I&rsquo;m now able to access this instance from anywhere at <a href="http://invidious:3000/feed/subscriptions">http://invidious:3000/feed/subscriptions</a>.</p>
<p>I made a few modifications, and ended up with:</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#81a1c1">version</span><span style="color:#eceff4">:</span> <span style="color:#a3be8c">&#34;3&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#81a1c1">services</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">invidious</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">image</span><span style="color:#eceff4">:</span> quay.io/invidious/invidious
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">restart</span><span style="color:#eceff4">:</span> unless-stopped
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">ports</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> - <span style="color:#a3be8c">&#34;0.0.0.0:3000:3000&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">environment</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">INVIDIOUS_CONFIG</span><span style="color:#eceff4">:</span> <span style="color:#eceff4">|</span><span style="color:#616e87">
</span></span></span><span style="display:flex;"><span><span style="color:#616e87"> db:
</span></span></span><span style="display:flex;"><span><span style="color:#616e87"> dbname: invidious
</span></span></span><span style="display:flex;"><span><span style="color:#616e87"> user: kemal
</span></span></span><span style="display:flex;"><span><span style="color:#616e87"> password: kemal
</span></span></span><span style="display:flex;"><span><span style="color:#616e87"> host: invidious-db
</span></span></span><span style="display:flex;"><span><span style="color:#616e87"> port: 5432
</span></span></span><span style="display:flex;"><span><span style="color:#616e87"> check_tables: true</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">healthcheck</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">test</span><span style="color:#eceff4">:</span> wget -nv --tries=1 --spider http://127.0.0.1:3000/api/v1/comments/jNQXAC9IVRw || exit 1
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">interval</span><span style="color:#eceff4">:</span> 30s
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">timeout</span><span style="color:#eceff4">:</span> 5s
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">retries</span><span style="color:#eceff4">:</span> <span style="color:#b48ead">2</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">depends_on</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> - invidious-db
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">invidious-db</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">image</span><span style="color:#eceff4">:</span> docker.io/library/postgres:14
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">restart</span><span style="color:#eceff4">:</span> unless-stopped
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">volumes</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> - postgresdata:/var/lib/postgresql/data
</span></span><span style="display:flex;"><span> - ./config/sql:/config/sql
</span></span><span style="display:flex;"><span> - ./docker/init-invidious-db.sh:/docker-entrypoint-initdb.d/init-invidious-db.sh
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">environment</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">POSTGRES_DB</span><span style="color:#eceff4">:</span> invidious
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">POSTGRES_USER</span><span style="color:#eceff4">:</span> kemal
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">POSTGRES_PASSWORD</span><span style="color:#eceff4">:</span> kemal
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">healthcheck</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">test</span><span style="color:#eceff4">:</span> <span style="color:#eceff4">[</span><span style="color:#a3be8c">&#34;CMD-SHELL&#34;</span><span style="color:#eceff4">,</span> <span style="color:#a3be8c">&#34;pg_isready -U $$POSTGRES_USER -d $$POSTGRES_DB&#34;</span><span style="color:#eceff4">]</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#81a1c1">volumes</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">postgresdata</span><span style="color:#eceff4">:</span>
</span></span></code></pre></div><p>After invidious was up and running, I installed <a href="https://tailscale.com/">Tailscale</a> on it to leverage its MagicDNS, and I&rsquo;m now able to access this instance from anywhere at <a href="http://invidious:3000/feed/subscriptions">http://invidious:3000/feed/subscriptions</a>.</p>
<h3 id="redirecting-youtube-links">Redirecting YouTube links<a href="#redirecting-youtube-links" class="hanchor" ariaLabel="Anchor">#</a></h3>
<p>I figured it would be nice to redirect existing YouTube links that others send me, so that I could seamlessly watch the videos using invidious.</p>
<p>I went looking for a way to redirect paths at the browser level. I found the lightweight proxy <a href="https://requestly.io/">requestly</a>, which can be used to modify http requests in my browser. I created the following rules:</p>
@@ -239,6 +244,9 @@ volumes:
<div class="footer-info">
2023 Dave Gallant
</div>
<script src="/js/copy-code-button.js"></script>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -20,8 +20,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -68,7 +68,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -91,6 +91,12 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
<link href="/css/copy-code-button.min.css" rel="stylesheet">
</header>
<main>
@@ -124,15 +130,14 @@ If this sounds more appealing, install <a href="https://github.com/antitree/krew
<p>It&rsquo;s possible to use this sort of proxy to connect to a private DB, but is it any simpler than using a virtual machine as a jumphost? It wasn&rsquo;t until I stumbled upon <a href="https://github.com/yokawasa/kubectl-plugin-socks5-proxy">kubectl-plugin-socks5-proxy</a> that I was convinced that using SOCKS could be made simple.</p>
<p>So how does it work? By installing the kubectl plugin and then running <code>kubectl socks5-proxy</code>, a SOCKS proxy server is spun up in a pod and then opens up port-forwarding session using kubectl.</p>
<p>As you can see below, this k8s plugin is wrapped up nicely:</p>
<pre><code class="language-console">$ kubectl socks5-proxy
using: namespace=default
using: port=1080
using: name=davegallant-proxy
using: image=serjs/go-socks5-proxy
Creating SOCKS5 Proxy (Pod)...
pod/davegallant-proxy created
</code></pre>
<p>With the above proxy connection open, it is possible to access both the DNS and private IPs accessible within the k8s cluster. In this case, I am able to access the private database, since there is network connectivity between the k8s cluster and the database.</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-console" data-lang="console"><span style="display:flex;"><span><span style="color:#4c566a;font-weight:bold">$</span> kubectl socks5-proxy
</span></span><span style="display:flex;"><span>using: namespace=default
</span></span><span style="display:flex;"><span>using: port=1080
</span></span><span style="display:flex;"><span>using: name=davegallant-proxy
</span></span><span style="display:flex;"><span>using: image=serjs/go-socks5-proxy
</span></span><span style="display:flex;"><span>Creating SOCKS5 Proxy (Pod)...
</span></span><span style="display:flex;"><span>pod/davegallant-proxy created
</span></span></code></pre></div><p>With the above proxy connection open, it is possible to access both the DNS and private IPs accessible within the k8s cluster. In this case, I am able to access the private database, since there is network connectivity between the k8s cluster and the database.</p>
<h2 id="caveats-and-conclusion">Caveats and Conclusion<a href="#caveats-and-conclusion" class="hanchor" ariaLabel="Anchor">#</a></h2>
<p>The above outlined solution makes some assumptions:</p>
<ul>
@@ -232,6 +237,9 @@ pod/davegallant-proxy created
<div class="footer-info">
2023 Dave Gallant
</div>
<script src="/js/copy-code-button.js"></script>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -20,8 +20,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -68,7 +68,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -91,6 +91,12 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
<link href="/css/copy-code-button.min.css" rel="stylesheet">
</header>
<main>
@@ -122,68 +128,65 @@
</ul>
<p>My preferred approach to deploying code in a homelab environment is with docker compose. I have deployed this in a <a href="https://pve.proxmox.com/wiki/Linux_Container">proxmox lxc container</a> based on debian with a hostname <code>gitea</code>. This could be deployed in any environment and with any hostname (as long you updated the tailscale machine name to your preferred subdomain for magic dns).</p>
<p>The <code>docker-compose.yaml</code> file looks like:</p>
<pre><code class="language-yaml">version: &quot;3.7&quot;
services:
gitea:
image: gitea/gitea:1.21.1
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
- GITEA__server__DOMAIN=gitea.my-tailnet-name.ts.net
- GITEA__server__ROOT_URL=https://gitea.my-tailnet-name.ts.net
- GITEA__server__HTTP_ADDR=0.0.0.0
- GITEA__server__LFS_JWT_SECRET=my-secret-jwt
restart: always
volumes:
- ./data:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
traefik:
image: traefik:v3.0.0-beta4
container_name: traefik
security_opt:
- no-new-privileges:true
restart: unless-stopped
ports:
- 80:80
- 443:443
volumes:
- ./traefik/data/traefik.yaml:/traefik.yaml:ro
- ./traefik/data/dynamic.yaml:/dynamic.yaml:ro
- /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock
</code></pre>
<p><code>traefik/data/traefik.yaml</code>:</p>
<pre><code class="language-yaml">entryPoints:
https:
address: &quot;:443&quot;
providers:
file:
filename: dynamic.yaml
certificatesResolvers:
myresolver:
tailscale: {}
log:
level: INFO
</code></pre>
<p>and finally <code>traefik/data/dynamic/dynamic.yaml</code>:</p>
<pre><code class="language-yaml">http:
routers:
gitea:
rule: Host(`gitea.my-tailnet-name.ts.net`)
entrypoints:
- &quot;https&quot;
service: gitea
tls:
certResolver: myresolver
services:
gitea:
loadBalancer:
servers:
- url: &quot;http://gitea:3000&quot;
</code></pre>
<p>Something to consider is whether or not you want to use ssh with git. One method to get this to work with containers is to use <a href="https://docs.gitea.com/installation/install-with-docker#ssh-container-passthrough">ssh container passthrough</a>. I decided to keep it simple and not use ssh, since communicating over https is perfectly fine for my use case.</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#81a1c1">version</span><span style="color:#eceff4">:</span> <span style="color:#a3be8c">&#34;3.7&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#81a1c1">services</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">gitea</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">image</span><span style="color:#eceff4">:</span> gitea/gitea:1.21.1
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">container_name</span><span style="color:#eceff4">:</span> gitea
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">environment</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> - USER_UID=1000
</span></span><span style="display:flex;"><span> - USER_GID=1000
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span> - GITEA__server__DOMAIN=gitea.my-tailnet-name.ts.net
</span></span><span style="display:flex;"><span> - GITEA__server__ROOT_URL=https://gitea.my-tailnet-name.ts.net
</span></span><span style="display:flex;"><span> - GITEA__server__HTTP_ADDR=0.0.0.0
</span></span><span style="display:flex;"><span> - GITEA__server__LFS_JWT_SECRET=my-secret-jwt
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">restart</span><span style="color:#eceff4">:</span> always
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">volumes</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> - ./data:/data
</span></span><span style="display:flex;"><span> - /etc/timezone:/etc/timezone:ro
</span></span><span style="display:flex;"><span> - /etc/localtime:/etc/localtime:ro
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">traefik</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">image</span><span style="color:#eceff4">:</span> traefik:v3.0.0-beta4
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">container_name</span><span style="color:#eceff4">:</span> traefik
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">security_opt</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> - <span style="color:#81a1c1;font-weight:bold">no</span>-new-privileges:true
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">restart</span><span style="color:#eceff4">:</span> unless-stopped
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">ports</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> - <span style="color:#b48ead">80</span><span style="color:#eceff4">:</span><span style="color:#b48ead">80</span>
</span></span><span style="display:flex;"><span> - <span style="color:#b48ead">443</span><span style="color:#eceff4">:</span><span style="color:#b48ead">443</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">volumes</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> - ./traefik/data/traefik.yaml:/traefik.yaml:ro
</span></span><span style="display:flex;"><span> - ./traefik/data/dynamic.yaml:/dynamic.yaml:ro
</span></span><span style="display:flex;"><span> - /var/run/tailscale/tailscaled.sock:/var/run/tailscale/tailscaled.sock
</span></span></code></pre></div><p><code>traefik/data/traefik.yaml</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#81a1c1">entryPoints</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">https</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">address</span><span style="color:#eceff4">:</span> <span style="color:#a3be8c">&#34;:443&#34;</span>
</span></span><span style="display:flex;"><span><span style="color:#81a1c1">providers</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">file</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">filename</span><span style="color:#eceff4">:</span> dynamic.yaml
</span></span><span style="display:flex;"><span><span style="color:#81a1c1">certificatesResolvers</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">myresolver</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">tailscale</span><span style="color:#eceff4">:</span> {}
</span></span><span style="display:flex;"><span><span style="color:#81a1c1">log</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">level</span><span style="color:#eceff4">:</span> INFO
</span></span></code></pre></div><p>and finally <code>traefik/data/dynamic/dynamic.yaml</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#81a1c1">http</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">routers</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">gitea</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">rule</span><span style="color:#eceff4">:</span> Host(`gitea.my-tailnet-name.ts.net`)
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">entrypoints</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> - <span style="color:#a3be8c">&#34;https&#34;</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">service</span><span style="color:#eceff4">:</span> gitea
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">tls</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">certResolver</span><span style="color:#eceff4">:</span> myresolver
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">services</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">gitea</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">loadBalancer</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">servers</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> - <span style="color:#81a1c1">url</span><span style="color:#eceff4">:</span> <span style="color:#a3be8c">&#34;http://gitea:3000&#34;</span>
</span></span></code></pre></div><p>Something to consider is whether or not you want to use ssh with git. One method to get this to work with containers is to use <a href="https://docs.gitea.com/installation/install-with-docker#ssh-container-passthrough">ssh container passthrough</a>. I decided to keep it simple and not use ssh, since communicating over https is perfectly fine for my use case.</p>
<p>After adding the above configuration, running <code>docker compose up -d</code> should be enough to get an instance up and running. It will be accessible at <a href="https://gitea.my-tailnet-name.ts.net">https://gitea.my-tailnet-name.ts.net</a> from within the tailnet.</p>
<h2 id="connecting-a-runner">Connecting a Runner<a href="#connecting-a-runner" class="hanchor" ariaLabel="Anchor">#</a></h2>
<p>I installed the runner by <a href="https://docs.gitea.com/usage/actions/quickstart#set-up-runner">following the docs</a>. I opted for installing it on a separate host (another lxc container) as recommended in the docs. I used the systemd unit file to ensure that the runner comes back online after system reboots. I installed tailscale on this act runner as well, so that it can have the same &ldquo;networking privileges&rdquo; as the main instance.</p>
@@ -193,64 +196,63 @@ log:
<p>Now it&rsquo;s time start running some automation. I used the <a href="https://docs.gitea.com/usage/actions/quickstart#use-actions">demo workflow</a> as a starting point to verify that the runner is executing workflows.</p>
<p>After this, I wanted to make sure that some of my existing workflows could be migrated over.</p>
<p>The following workflow uses a matrix to run a job for several of my hosts using ansible playbooks that will do various tasks such as patching os updates and updating container images.</p>
<pre><code class="language-yaml">name: Run ansible
on:
push:
schedule:
- cron: &quot;0 */12 * * *&quot;
jobs:
run-ansible-playbook:
runs-on: ubuntu-latest
strategy:
matrix:
host:
- changedetection
- homelab
- invidious
- jackett
- ladder
- miniflux
- plex
- qbittorrent
- tailscale-exit-node
- uptime-kuma
steps:
- name: Check out repository code
uses: actions/checkout@v4
- name: Install ansible
run: |
apt update &amp;&amp; apt install ansible -y
- name: Run playbook
uses: dawidd6/action-ansible-playbook@v2
with:
playbook: playbooks/main.yml
requirements: requirements.yml
key: ${{ secrets.SSH_PRIVATE_KEY}}
options: |
--inventory inventory
--ssh-extra-args &quot;-o StrictHostKeyChecking=no&quot;
--limit ${{ matrix.host }}
send-failure-notification:
needs: run-ansible-playbook
runs-on: ubuntu-latest
if: always() &amp;&amp; failure()
steps:
- name: Send failure notification
uses: dawidd6/action-send-mail@v3
with:
server_address: smtp.gmail.com
server_port: 465
secure: true
username: myuser
password: ${{ secrets.MAIL_PASSWORD }}
subject: gitea job ${{github.repository}} failed!
to: me@davegallant.ca
from: Gitea
body: |
${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_number }}
</code></pre>
<p>And voilà:</p>
<div class="highlight"><pre tabindex="0" style="color:#d8dee9;background-color:#2e3440;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#81a1c1">name</span><span style="color:#eceff4">:</span> Run ansible
</span></span><span style="display:flex;"><span><span style="color:#81a1c1">on</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">push</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">schedule</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> - <span style="color:#81a1c1">cron</span><span style="color:#eceff4">:</span> <span style="color:#a3be8c">&#34;0 */12 * * *&#34;</span>
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#81a1c1">jobs</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">run-ansible-playbook</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">runs-on</span><span style="color:#eceff4">:</span> ubuntu-latest
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">strategy</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">matrix</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">host</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> - changedetection
</span></span><span style="display:flex;"><span> - homelab
</span></span><span style="display:flex;"><span> - invidious
</span></span><span style="display:flex;"><span> - jackett
</span></span><span style="display:flex;"><span> - ladder
</span></span><span style="display:flex;"><span> - miniflux
</span></span><span style="display:flex;"><span> - plex
</span></span><span style="display:flex;"><span> - qbittorrent
</span></span><span style="display:flex;"><span> - tailscale-exit-node
</span></span><span style="display:flex;"><span> - uptime-kuma
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">steps</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> - <span style="color:#81a1c1">name</span><span style="color:#eceff4">:</span> Check out repository code
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">uses</span><span style="color:#eceff4">:</span> actions/checkout@v4
</span></span><span style="display:flex;"><span> - <span style="color:#81a1c1">name</span><span style="color:#eceff4">:</span> Install ansible
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">run</span><span style="color:#eceff4">:</span> <span style="color:#eceff4">|</span><span style="color:#616e87">
</span></span></span><span style="display:flex;"><span><span style="color:#616e87"> apt update &amp;&amp; apt install ansible -y</span>
</span></span><span style="display:flex;"><span> - <span style="color:#81a1c1">name</span><span style="color:#eceff4">:</span> Run playbook
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">uses</span><span style="color:#eceff4">:</span> dawidd6/action-ansible-playbook@v2
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">with</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">playbook</span><span style="color:#eceff4">:</span> playbooks/main.yml
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">requirements</span><span style="color:#eceff4">:</span> requirements.yml
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">key</span><span style="color:#eceff4">:</span> ${{ secrets.SSH_PRIVATE_KEY}}
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">options</span><span style="color:#eceff4">:</span> <span style="color:#eceff4">|</span><span style="color:#616e87">
</span></span></span><span style="display:flex;"><span><span style="color:#616e87"> --inventory inventory
</span></span></span><span style="display:flex;"><span><span style="color:#616e87"> --ssh-extra-args &#34;-o StrictHostKeyChecking=no&#34;
</span></span></span><span style="display:flex;"><span><span style="color:#616e87"> --limit ${{ matrix.host }}</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">send-failure-notification</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">needs</span><span style="color:#eceff4">:</span> run-ansible-playbook
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">runs-on</span><span style="color:#eceff4">:</span> ubuntu-latest
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">if</span><span style="color:#eceff4">:</span> always() &amp;&amp; failure()
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">steps</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> - <span style="color:#81a1c1">name</span><span style="color:#eceff4">:</span> Send failure notification
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">uses</span><span style="color:#eceff4">:</span> dawidd6/action-send-mail@v3
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">with</span><span style="color:#eceff4">:</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">server_address</span><span style="color:#eceff4">:</span> smtp.gmail.com
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">server_port</span><span style="color:#eceff4">:</span> <span style="color:#b48ead">465</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">secure</span><span style="color:#eceff4">:</span> <span style="color:#81a1c1;font-weight:bold">true</span>
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">username</span><span style="color:#eceff4">:</span> myuser
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">password</span><span style="color:#eceff4">:</span> ${{ secrets.MAIL_PASSWORD }}
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">subject</span><span style="color:#eceff4">:</span> gitea job ${{github.repository}} failed!
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">to</span><span style="color:#eceff4">:</span> me@davegallant.ca
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">from</span><span style="color:#eceff4">:</span> Gitea
</span></span><span style="display:flex;"><span> <span style="color:#81a1c1">body</span><span style="color:#eceff4">:</span> <span style="color:#eceff4">|</span><span style="color:#616e87">
</span></span></span><span style="display:flex;"><span><span style="color:#616e87"> ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_number }}</span>
</span></span></code></pre></div><p>And voilà:</p>
<video controls preload="auto" width="100%" playsinline class="html-video">
<source src="/blog/2023/12/10/setting-up-gitea-actions-with-tailscale/gitea-workflow.webm" type="video/webm">
<span>Your browser doesn't support embedded videos, but don't worry, you can <a href="/blog/2023/12/10/setting-up-gitea-actions-with-tailscale/gitea-workflow.webm">download it</a> and watch it with your favorite video player!</span>
@@ -336,6 +338,9 @@ jobs:
<div class="footer-info">
2023 Dave Gallant
</div>
<script src="/js/copy-code-button.js"></script>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
<h1 class="page-title">All tags</h1>
@@ -148,6 +150,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

1
public/css/copy-code-button.min.css vendored Normal file
View File

@@ -0,0 +1 @@
.highlight-wrapper{display:block}.highlight-wrapper .lntd pre{padding:0}.chroma .lntd pre{border:0 solid #ccc}.chroma .lntd:first-child{padding:7px 7px 7px 10px;margin:0}.chroma .lntd:last-child{padding:7px 10px 7px 7px;margin:0}.highlight{position:relative;z-index:0;padding:0;margin:40px 0 10px;border-radius:4px}.highlight>.chroma{position:static;z-index:1;border-top-left-radius:0;border-top-right-radius:0;border-bottom-left-radius:4px;border-bottom-right-radius:4px;padding:10px}.copy-code-button{position:absolute;z-index:2;right:0;top:-29px;font-size:13px;font-weight:700;line-height:14px;letter-spacing:.5px;width:65px;color:#fff;background-color:#000;border:1.25px solid #232326;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0;white-space:nowrap;padding:6px 6px 7px;margin:0 0 0 1px;cursor:pointer;opacity:.6}.copy-code-button:hover,.copy-code-button:focus,.copy-code-button:active,.copy-code-button:active:hover{color:#222225;background-color:#b3b3b3;opacity:.8}.copyable-text-area{position:absolute;height:0;z-index:-1;opacity:.01}.chroma [data-lang]:before{position:absolute;z-index:0;top:-29px;left:0;content:attr(data-lang);font-size:13px;font-weight:700;color:#fff;background-color:#000;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-left-radius:0;border-bottom-right-radius:0;padding:6px 6px 7px;line-height:14px;opacity:.6;position:absolute;letter-spacing:.5px;border:1.25px solid #232326;margin:0 0 0 1px}

View File

@@ -7,7 +7,9 @@
font-weight: 600;
}
h2:hover a, h3:hover a, h4:hover a {
h2:hover a,
h3:hover a,
h4:hover a {
visibility: visible;
text-decoration: none;
}

View File

@@ -111,49 +111,49 @@ a:hover {
color: var(--darkMaincolor);
background-color: blue;
}
.highlight pre code[class=language-javaScript]::before,
.highlight pre code[class="language-javaScript"]::before,
.highlight pre code[class="language-js"]::before {
content: "js";
background: #f7df1e;
color: black;
}
.highlight pre code[class*='language-yml']::before,
.highlight pre code[class*='language-yaml']::before {
content: 'yaml';
.highlight pre code[class*="language-yml"]::before,
.highlight pre code[class*="language-yaml"]::before {
content: "yaml";
background: #f71e6a;
color: white;
}
.highlight pre code[class*='language-shell']::before,
.highlight pre code[class*='language-bash']::before,
.highlight pre code[class*='language-sh']::before {
content: 'shell';
.highlight pre code[class*="language-shell"]::before,
.highlight pre code[class*="language-bash"]::before,
.highlight pre code[class*="language-sh"]::before {
content: "shell";
background: rgb(118, 137, 118);
color:white
color: white;
}
.highlight pre code[class*='language-json']::before{
content: 'json';
.highlight pre code[class*="language-json"]::before {
content: "json";
background: dodgerblue;
color: #000000
color: #000000;
}
.highlight pre code[class*='language-python']::before,
.highlight pre code[class*='language-py']::before {
content: 'py';
.highlight pre code[class*="language-python"]::before,
.highlight pre code[class*="language-py"]::before {
content: "py";
background: blue;
color: yellow ;
color: yellow;
}
.highlight pre code[class*='language-css']::before{
content: 'css';
.highlight pre code[class*="language-css"]::before {
content: "css";
background: cyan;
color: black ;
color: black;
}
.highlight pre code[class*='language-go']::before{
content: 'Go';
.highlight pre code[class*="language-go"]::before {
content: "Go";
background: cyan;
color: royalblue ;
color: royalblue;
}
.highlight pre code[class*='language-md']::before,
.highlight pre code[class*='language-md']::before{
content: 'Markdown';
.highlight pre code[class*="language-md"]::before,
.highlight pre code[class*="language-md"]::before {
content: "Markdown";
background: royalblue;
color: whitesmoke ;
color: whitesmoke;
}

View File

@@ -1,18 +1,18 @@
/* Markdown */
:root{
--maincolor: #a15bc2;
--bordercl: #a15bc2;
--callouctcolor:dodgerblue;
--hovercolor: #a15bc2;
--darkMaincolor: #a15bc2;
:root {
--maincolor: #a15bc2;
--bordercl: #a15bc2;
--callouctcolor: dodgerblue;
--hovercolor: #a15bc2;
--darkMaincolor: #a15bc2;
}
html {
color: #232333;
font-family: 'Roboto Mono', monospace;
font-family: "Roboto Mono", monospace;
font-size: 18px;
line-height: 1.6em;
}
body{
body {
display: block;
background-color: #f8f8f8;
margin: 8px;
@@ -27,7 +27,7 @@ body{
}
p {
font-family: 'Roboto Light', sans-serif;
font-family: "Roboto Light", sans-serif;
line-height: 1.5;
}
@@ -50,8 +50,8 @@ a {
text-decoration: none;
}
a:hover {
background-color: var(--hovercolor);
color: #fff;
background-color: var(--hovercolor);
color: #fff;
}
ul {
@@ -62,7 +62,7 @@ ul li {
text-indent: -2ch;
}
ul > li::before {
content: '* ';
content: "* ";
font-weight: bold;
}
@@ -95,13 +95,13 @@ figure h4 {
margin-bottom: 1em;
}
figure h4::before {
content: '';
content: "";
}
/* Code blocks */
code {
background-color: #f1f1f1;
padding: 0em .0.1em;
padding: 0em 0.1em;
}
pre {
@@ -145,17 +145,40 @@ header {
header .main {
font-size: 1.5rem;
}
h1, h2, h3, h4, h5, h6 {
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: 1.2rem;
margin-top: 2em;
}
h1::before { color: var(--maincolor); content: '# '; }
h2::before { color: var(--maincolor); content: '## '; }
h3::before { color: var(--maincolor); content: '### '; }
h4::before { color: var(--maincolor); content: '#### '; }
h5::before { color: var(--maincolor); content: '##### '; }
h6::before { color: var(--maincolor); content: '###### '; }
h1::before {
color: var(--maincolor);
content: "# ";
}
h2::before {
color: var(--maincolor);
content: "## ";
}
h3::before {
color: var(--maincolor);
content: "### ";
}
h4::before {
color: var(--maincolor);
content: "#### ";
}
h5::before {
color: var(--maincolor);
content: "##### ";
}
h6::before {
color: var(--maincolor);
content: "###### ";
}
.meta {
color: #999;
@@ -198,7 +221,6 @@ article .title {
margin-bottom: 1em;
}
/* Callout */
.callout {
background-color: var(--callouctcolor);
@@ -207,7 +229,7 @@ article .title {
}
.callout p {
font-family: 'IBM Plex Mono', monospace;
font-family: "IBM Plex Mono", monospace;
margin: 0;
}
@@ -221,32 +243,32 @@ article .title {
}
.site-description {
display: flex;
justify-content: space-between;
display: flex;
justify-content: space-between;
}
.tags li::before{
.tags li::before {
content: "- ";
}
.tags a{
.tags a {
border-bottom: 3px solid var(--maincolor);
}
.tags a:hover{
color:white;
.tags a:hover {
color: white;
background-color: var(--hovercolor);
}
svg{
svg {
max-height: 15px;
}
.soc:hover{
.soc:hover {
color: white;
}
.draft-label{
color: var(--bordercl);
text-decoration: none;
padding: 2px 4px;
border-radius: 4px;
margin-left: 6px;
background-color: #f9f2f4;
.draft-label {
color: var(--bordercl);
text-decoration: none;
padding: 2px 4px;
border-radius: 4px;
margin-left: 6px;
background-color: #f9f2f4;
}
.highlight {
position: relative;
@@ -269,51 +291,51 @@ svg{
top: 0;
}
.highlight pre code[class=language-javaScript]::before,
.highlight pre code[class="language-javaScript"]::before,
.highlight pre code[class="language-js"]::before {
content: "js";
background: #f7df1e;
color: black;
content: "js";
background: #f7df1e;
color: black;
}
.highlight pre code[class*='language-yml']::before,
.highlight pre code[class*='language-yaml']::before {
content: 'yaml';
background: #f71e6a;
color: white;
.highlight pre code[class*="language-yml"]::before,
.highlight pre code[class*="language-yaml"]::before {
content: "yaml";
background: #f71e6a;
color: white;
}
.highlight pre code[class*='language-shell']::before,
.highlight pre code[class*='language-bash']::before,
.highlight pre code[class*='language-sh']::before {
content: 'shell';
background: green;
color:white
.highlight pre code[class*="language-shell"]::before,
.highlight pre code[class*="language-bash"]::before,
.highlight pre code[class*="language-sh"]::before {
content: "shell";
background: green;
color: white;
}
.highlight pre code[class*='language-json']::before{
content: 'json';
background: dodgerblue;
color: #000000
.highlight pre code[class*="language-json"]::before {
content: "json";
background: dodgerblue;
color: #000000;
}
.highlight pre code[class*='language-python']::before,
.highlight pre code[class*='language-py']::before {
content: 'py';
background: blue;
color: yellow ;
.highlight pre code[class*="language-python"]::before,
.highlight pre code[class*="language-py"]::before {
content: "py";
background: blue;
color: yellow;
}
.highlight pre code[class*='language-css']::before{
content: 'css';
background: cyan;
color: black ;
.highlight pre code[class*="language-css"]::before {
content: "css";
background: cyan;
color: black;
}
.highlight pre code[class*='language-go']::before{
content: 'Go';
background: cyan;
color: royalblue ;
.highlight pre code[class*="language-go"]::before {
content: "Go";
background: cyan;
color: royalblue;
}
.highlight pre code[class*='language-md']::before,
.highlight pre code[class*='language-md']::before{
content: 'Markdown';
background: royalblue;
color: whitesmoke ;
.highlight pre code[class*="language-md"]::before,
.highlight pre code[class*="language-md"]::before {
content: "Markdown";
background: royalblue;
color: whitesmoke;
}
/* table */
@@ -322,13 +344,13 @@ table {
border-collapse: collapse;
}
table th{
table th {
padding: 6px 13px;
border: 1px solid #dfe2e5;
font-size: large;
}
table td{
table td {
padding: 6px 13px;
border: 1px solid #dfe2e5;
}

View File

@@ -21,8 +21,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -69,7 +69,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
@@ -94,6 +94,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -238,6 +240,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -0,0 +1,58 @@
function createCopyButton(highlightDiv) {
const button = document.createElement("button");
button.className = "copy-code-button";
button.type = "button";
button.innerText = "Copy";
button.addEventListener("click", () =>
copyCodeToClipboard(button, highlightDiv)
);
highlightDiv.insertBefore(button, highlightDiv.firstChild);
const wrapper = document.createElement("div");
wrapper.className = "highlight-wrapper";
highlightDiv.parentNode.insertBefore(wrapper, highlightDiv);
wrapper.appendChild(highlightDiv);
}
document
.querySelectorAll(".highlight")
.forEach((highlightDiv) => createCopyButton(highlightDiv));
async function copyCodeToClipboard(button, highlightDiv) {
const codeToCopy = highlightDiv.querySelector(
"pre > code"
).innerText;
try {
var result = await navigator.permissions.query({ name: "clipboard-write" });
if (result.state == "granted" || result.state == "prompt") {
await navigator.clipboard.writeText(codeToCopy);
} else {
copyCodeBlockExecCommand(codeToCopy, highlightDiv);
}
} catch (_) {
copyCodeBlockExecCommand(codeToCopy, highlightDiv);
} finally {
button.blur();
button.innerText = "Copied!";
setTimeout(function () {
button.innerText = "Copy";
}, 2000);
}
}
function copyCodeBlockExecCommand(codeToCopy, highlightDiv) {
const textArea = document.createElement("textArea");
textArea.contentEditable = "true";
textArea.readOnly = "false";
textArea.className = "copyable-text-area";
textArea.value = codeToCopy;
highlightDiv.insertBefore(textArea, highlightDiv.firstChild);
const range = document.createRange();
range.selectNodeContents(textArea);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
textArea.setSelectionRange(0, 999999);
document.execCommand("copy");
highlightDiv.removeChild(textArea);
}

View File

View File

@@ -21,8 +21,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -69,7 +69,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
@@ -94,6 +94,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -148,6 +150,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -161,6 +163,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -143,6 +145,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -143,6 +145,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
<h1 class="page-title">All tags</h1>
@@ -360,6 +362,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -143,6 +145,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -143,6 +145,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -149,6 +151,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -145,6 +147,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -19,8 +19,8 @@
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono" rel="stylesheet">
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.b242ee667697ab3e792722deba86ca7575ca26cc470cedac147b7fd89e24b22f.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.0b62b6a8c9bc96baf3dd0b8690823918769e50f96220bad87969bb570d1febfe.css" disabled />
<link rel="stylesheet" type="text/css" media="screen" href="/css/main.3547bd7f78e8240e2e642eaae27e96ba31dec26821aff923eb7ffc098ac3aaee.css" />
<link id="darkModeStyle" rel="stylesheet" type="text/css" href="/css/dark.c0351bf49d41b33a222e1a32cc0387e850b010f77ab2d79a9d39c72c03afcfdb.css" disabled />
@@ -67,7 +67,7 @@
<link rel="stylesheet" type="text/css" href="/css/custom.2e59ff60a2d9c7e42e3c1af2aff0ba627da46f910a234867e98d178eb05c87dc.css">
<link rel="stylesheet" type="text/css" href="/css/custom.d96bfb9e3314a7699144ab6ae7331d424cbd7fb34a2e890b17e7bb7db4e30f3a.css">
</head>
<body>
@@ -90,6 +90,8 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="/js/themetoggle.js"></script>
</header>
@@ -141,6 +143,7 @@
<div class="footer-info">
2023 Dave Gallant
</div>
</footer>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-V8WJDERTX9"></script>

View File

@@ -0,0 +1,103 @@
.highlight-wrapper {
display: block;
}
/* Start: Turn off individual column border, margin, and padding when line numbers are showing */
.highlight-wrapper .lntd pre {
padding: 0;
}
.chroma .lntd pre {
border: 0px solid #ccc;
}
.chroma .lntd:first-child {
padding: 7px 7px 7px 10px;
margin: 0;
}
.chroma .lntd:last-child {
padding: 7px 10px 7px 7px;
margin: 0;
}
/* End: Turn off individual column border, margin, and padding when line numbers are showing */
.highlight {
position: relative;
z-index: 0;
padding: 0;
margin: 40px 0 10px 0;
border-radius: 4px;
}
.highlight > .chroma {
position: static;
z-index: 1;
border-top-left-radius: 0px;
border-top-right-radius: 0px;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
padding: 10px;
}
.copy-code-button {
position: absolute;
z-index: 2;
right: 0;
top: -29px;
font-size: 13px;
font-weight: 700;
line-height: 14px;
letter-spacing: 0.5px;
width: 65px;
color: #ffffff;
background-color: #000000;
border: 1.25px solid #232326;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
border-bottom-right-radius: 0px;
border-bottom-left-radius: 0px;
white-space: nowrap;
padding: 6px 6px 7px 6px;
margin: 0 0 0 1px;
cursor: pointer;
opacity: 0.6;
}
.copy-code-button:hover,
.copy-code-button:focus,
.copy-code-button:active,
.copy-code-button:active:hover {
color: #222225;
background-color: #b3b3b3;
opacity: 0.8;
}
.copyable-text-area {
position: absolute;
height: 0;
z-index: -1;
opacity: 0.01;
}
.chroma [data-lang]:before {
position: absolute;
z-index: 0;
top: -29px;
left: 0;
content: attr(data-lang);
font-size: 13px;
font-weight: 700;
color: white;
background-color: black;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
padding: 6px 6px 7px 6px;
line-height: 14px;
opacity: 0.6;
position: absolute;
letter-spacing: 0.5px;
border: 1.25px solid #232326;
margin: 0 0 0 1px;
}

View File

@@ -111,49 +111,49 @@ a:hover {
color: var(--darkMaincolor);
background-color: blue;
}
.highlight pre code[class=language-javaScript]::before,
.highlight pre code[class="language-javaScript"]::before,
.highlight pre code[class="language-js"]::before {
content: "js";
background: #f7df1e;
color: black;
}
.highlight pre code[class*='language-yml']::before,
.highlight pre code[class*='language-yaml']::before {
content: 'yaml';
.highlight pre code[class*="language-yml"]::before,
.highlight pre code[class*="language-yaml"]::before {
content: "yaml";
background: #f71e6a;
color: white;
}
.highlight pre code[class*='language-shell']::before,
.highlight pre code[class*='language-bash']::before,
.highlight pre code[class*='language-sh']::before {
content: 'shell';
.highlight pre code[class*="language-shell"]::before,
.highlight pre code[class*="language-bash"]::before,
.highlight pre code[class*="language-sh"]::before {
content: "shell";
background: rgb(118, 137, 118);
color:white
color: white;
}
.highlight pre code[class*='language-json']::before{
content: 'json';
.highlight pre code[class*="language-json"]::before {
content: "json";
background: dodgerblue;
color: #000000
color: #000000;
}
.highlight pre code[class*='language-python']::before,
.highlight pre code[class*='language-py']::before {
content: 'py';
.highlight pre code[class*="language-python"]::before,
.highlight pre code[class*="language-py"]::before {
content: "py";
background: blue;
color: yellow ;
color: yellow;
}
.highlight pre code[class*='language-css']::before{
content: 'css';
.highlight pre code[class*="language-css"]::before {
content: "css";
background: cyan;
color: black ;
color: black;
}
.highlight pre code[class*='language-go']::before{
content: 'Go';
.highlight pre code[class*="language-go"]::before {
content: "Go";
background: cyan;
color: royalblue ;
color: royalblue;
}
.highlight pre code[class*='language-md']::before,
.highlight pre code[class*='language-md']::before{
content: 'Markdown';
.highlight pre code[class*="language-md"]::before,
.highlight pre code[class*="language-md"]::before {
content: "Markdown";
background: royalblue;
color: whitesmoke ;
color: whitesmoke;
}

View File

@@ -1,41 +1,57 @@
/* fira-sans-regular - latin */
@font-face {
font-display: swap;
font-family: 'Fira Sans';
font-family: "Fira Sans";
font-style: normal;
font-weight: 400;
src: url('../fonts/fira-sans-v10-latin-regular.eot'); /* IE9 Compat Modes */
src: local('Fira Sans Regular'), local('FiraSans-Regular'),
url('../fonts/fira-sans-v10-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('../fonts/fira-sans-v10-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
url('../fonts/fira-sans-v10-latin-regular.woff') format('woff'), /* Modern Browsers */
url('../fonts/fira-sans-v10-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
url('../fonts/fira-sans-v10-latin-regular.svg#FiraSans') format('svg'); /* Legacy iOS */
src: url("../fonts/fira-sans-v10-latin-regular.eot"); /* IE9 Compat Modes */
src: local("Fira Sans Regular"), local("FiraSans-Regular"),
url("../fonts/fira-sans-v10-latin-regular.eot?#iefix")
format("embedded-opentype"),
/* IE6-IE8 */ url("../fonts/fira-sans-v10-latin-regular.woff2")
format("woff2"),
/* Super Modern Browsers */ url("../fonts/fira-sans-v10-latin-regular.woff")
format("woff"),
/* Modern Browsers */ url("../fonts/fira-sans-v10-latin-regular.ttf")
format("truetype"),
/* Safari, Android, iOS */
url("../fonts/fira-sans-v10-latin-regular.svg#FiraSans") format("svg"); /* Legacy iOS */
}
/* roboto-mono-regular - latin */
@font-face {
font-display: swap;
font-family: 'Roboto Mono';
font-family: "Roboto Mono";
font-style: normal;
font-weight: 400;
src: url('../fonts/roboto-mono-v12-latin-regular.eot'); /* IE9 Compat Modes */
src: url('../fonts/roboto-mono-v12-latin-regular.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('../fonts/roboto-mono-v12-latin-regular.woff2') format('woff2'), /* Super Modern Browsers */
url('../fonts/roboto-mono-v12-latin-regular.woff') format('woff'), /* Modern Browsers */
url('../fonts/roboto-mono-v12-latin-regular.ttf') format('truetype'), /* Safari, Android, iOS */
url('../fonts/roboto-mono-v12-latin-regular.svg#RobotoMono') format('svg'); /* Legacy iOS */
src: url("../fonts/roboto-mono-v12-latin-regular.eot"); /* IE9 Compat Modes */
src: url("../fonts/roboto-mono-v12-latin-regular.eot?#iefix")
format("embedded-opentype"),
/* IE6-IE8 */ url("../fonts/roboto-mono-v12-latin-regular.woff2")
format("woff2"),
/* Super Modern Browsers */
url("../fonts/roboto-mono-v12-latin-regular.woff") format("woff"),
/* Modern Browsers */ url("../fonts/roboto-mono-v12-latin-regular.ttf")
format("truetype"),
/* Safari, Android, iOS */
url("../fonts/roboto-mono-v12-latin-regular.svg#RobotoMono") format("svg"); /* Legacy iOS */
}
/* ibm-plex-mono-500italic - latin */
@font-face {
font-display: swap;
font-family: 'IBM Plex Mono';
font-family: "IBM Plex Mono";
font-style: italic;
font-weight: 500;
src: url('../fonts/ibm-plex-mono-v6-latin-500italic.eot'); /* IE9 Compat Modes */
src: local('IBM Plex Mono Medium Italic'), local('IBMPlexMono-MediumItalic'),
url('../fonts/ibm-plex-mono-v6-latin-500italic.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('../fonts/ibm-plex-mono-v6-latin-500italic.woff2') format('woff2'), /* Super Modern Browsers */
url('../fonts/ibm-plex-mono-v6-latin-500italic.woff') format('woff'), /* Modern Browsers */
url('../fonts/ibm-plex-mono-v6-latin-500italic.ttf') format('truetype'), /* Safari, Android, iOS */
url('../fonts/ibm-plex-mono-v6-latin-500italic.svg#IBMPlexMono') format('svg'); /* Legacy iOS */
src: url("../fonts/ibm-plex-mono-v6-latin-500italic.eot"); /* IE9 Compat Modes */
src: local("IBM Plex Mono Medium Italic"), local("IBMPlexMono-MediumItalic"),
url("../fonts/ibm-plex-mono-v6-latin-500italic.eot?#iefix")
format("embedded-opentype"),
/* IE6-IE8 */ url("../fonts/ibm-plex-mono-v6-latin-500italic.woff2")
format("woff2"),
/* Super Modern Browsers */
url("../fonts/ibm-plex-mono-v6-latin-500italic.woff") format("woff"),
/* Modern Browsers */ url("../fonts/ibm-plex-mono-v6-latin-500italic.ttf")
format("truetype"),
/* Safari, Android, iOS */
url("../fonts/ibm-plex-mono-v6-latin-500italic.svg#IBMPlexMono")
format("svg"); /* Legacy iOS */
}

View File

@@ -1,18 +1,18 @@
/* Markdown */
:root{
--maincolor: #a15bc2;
--bordercl: #a15bc2;
--callouctcolor:dodgerblue;
--hovercolor: #a15bc2;
--darkMaincolor: #a15bc2;
:root {
--maincolor: #a15bc2;
--bordercl: #a15bc2;
--callouctcolor: dodgerblue;
--hovercolor: #a15bc2;
--darkMaincolor: #a15bc2;
}
html {
color: #232333;
font-family: 'Roboto Mono', monospace;
font-family: "Roboto Mono", monospace;
font-size: 18px;
line-height: 1.6em;
}
body{
body {
display: block;
background-color: #f8f8f8;
margin: 8px;
@@ -27,7 +27,7 @@ body{
}
p {
font-family: 'Roboto Light', sans-serif;
font-family: "Roboto Light", sans-serif;
line-height: 1.5;
}
@@ -50,8 +50,8 @@ a {
text-decoration: none;
}
a:hover {
background-color: var(--hovercolor);
color: #fff;
background-color: var(--hovercolor);
color: #fff;
}
ul {
@@ -62,7 +62,7 @@ ul li {
text-indent: -2ch;
}
ul > li::before {
content: '* ';
content: "* ";
font-weight: bold;
}
@@ -95,13 +95,13 @@ figure h4 {
margin-bottom: 1em;
}
figure h4::before {
content: '';
content: "";
}
/* Code blocks */
code {
background-color: #f1f1f1;
padding: 0em .0.1em;
padding: 0em 0.1em;
}
pre {
@@ -145,17 +145,40 @@ header {
header .main {
font-size: 1.5rem;
}
h1, h2, h3, h4, h5, h6 {
h1,
h2,
h3,
h4,
h5,
h6 {
font-size: 1.2rem;
margin-top: 2em;
}
h1::before { color: var(--maincolor); content: '# '; }
h2::before { color: var(--maincolor); content: '## '; }
h3::before { color: var(--maincolor); content: '### '; }
h4::before { color: var(--maincolor); content: '#### '; }
h5::before { color: var(--maincolor); content: '##### '; }
h6::before { color: var(--maincolor); content: '###### '; }
h1::before {
color: var(--maincolor);
content: "# ";
}
h2::before {
color: var(--maincolor);
content: "## ";
}
h3::before {
color: var(--maincolor);
content: "### ";
}
h4::before {
color: var(--maincolor);
content: "#### ";
}
h5::before {
color: var(--maincolor);
content: "##### ";
}
h6::before {
color: var(--maincolor);
content: "###### ";
}
.meta {
color: #999;
@@ -198,7 +221,6 @@ article .title {
margin-bottom: 1em;
}
/* Callout */
.callout {
background-color: var(--callouctcolor);
@@ -207,7 +229,7 @@ article .title {
}
.callout p {
font-family: 'IBM Plex Mono', monospace;
font-family: "IBM Plex Mono", monospace;
margin: 0;
}
@@ -221,32 +243,32 @@ article .title {
}
.site-description {
display: flex;
justify-content: space-between;
display: flex;
justify-content: space-between;
}
.tags li::before{
.tags li::before {
content: "- ";
}
.tags a{
.tags a {
border-bottom: 3px solid var(--maincolor);
}
.tags a:hover{
color:white;
.tags a:hover {
color: white;
background-color: var(--hovercolor);
}
svg{
svg {
max-height: 15px;
}
.soc:hover{
.soc:hover {
color: white;
}
.draft-label{
color: var(--bordercl);
text-decoration: none;
padding: 2px 4px;
border-radius: 4px;
margin-left: 6px;
background-color: #f9f2f4;
.draft-label {
color: var(--bordercl);
text-decoration: none;
padding: 2px 4px;
border-radius: 4px;
margin-left: 6px;
background-color: #f9f2f4;
}
.highlight {
position: relative;
@@ -269,51 +291,51 @@ svg{
top: 0;
}
.highlight pre code[class=language-javaScript]::before,
.highlight pre code[class="language-javaScript"]::before,
.highlight pre code[class="language-js"]::before {
content: "js";
background: #f7df1e;
color: black;
content: "js";
background: #f7df1e;
color: black;
}
.highlight pre code[class*='language-yml']::before,
.highlight pre code[class*='language-yaml']::before {
content: 'yaml';
background: #f71e6a;
color: white;
.highlight pre code[class*="language-yml"]::before,
.highlight pre code[class*="language-yaml"]::before {
content: "yaml";
background: #f71e6a;
color: white;
}
.highlight pre code[class*='language-shell']::before,
.highlight pre code[class*='language-bash']::before,
.highlight pre code[class*='language-sh']::before {
content: 'shell';
background: green;
color:white
.highlight pre code[class*="language-shell"]::before,
.highlight pre code[class*="language-bash"]::before,
.highlight pre code[class*="language-sh"]::before {
content: "shell";
background: green;
color: white;
}
.highlight pre code[class*='language-json']::before{
content: 'json';
background: dodgerblue;
color: #000000
.highlight pre code[class*="language-json"]::before {
content: "json";
background: dodgerblue;
color: #000000;
}
.highlight pre code[class*='language-python']::before,
.highlight pre code[class*='language-py']::before {
content: 'py';
background: blue;
color: yellow ;
.highlight pre code[class*="language-python"]::before,
.highlight pre code[class*="language-py"]::before {
content: "py";
background: blue;
color: yellow;
}
.highlight pre code[class*='language-css']::before{
content: 'css';
background: cyan;
color: black ;
.highlight pre code[class*="language-css"]::before {
content: "css";
background: cyan;
color: black;
}
.highlight pre code[class*='language-go']::before{
content: 'Go';
background: cyan;
color: royalblue ;
.highlight pre code[class*="language-go"]::before {
content: "Go";
background: cyan;
color: royalblue;
}
.highlight pre code[class*='language-md']::before,
.highlight pre code[class*='language-md']::before{
content: 'Markdown';
background: royalblue;
color: whitesmoke ;
.highlight pre code[class*="language-md"]::before,
.highlight pre code[class*="language-md"]::before {
content: "Markdown";
background: royalblue;
color: whitesmoke;
}
/* table */
@@ -322,13 +344,13 @@ table {
border-collapse: collapse;
}
table th{
table th {
padding: 6px 13px;
border: 1px solid #dfe2e5;
font-size: large;
}
table td{
table td {
padding: 6px 13px;
border: 1px solid #dfe2e5;
}

View File

@@ -11,6 +11,9 @@
<div class="footer-info">
{{ dateFormat "2006" now }} {{ with .Site.Copyright }} {{ . }} {{ end }}
</div>
{{ if (findRE "<pre" .Content 1) }}
<script src="/js/copy-code-button.js"></script>
{{ end }}
</footer>
{{ if not .Site.IsServer }} {{ template "_internal/google_analytics.html" . }}
{{ end }} {{- if (isset .Site.Params "social") -}}

View File

@@ -14,4 +14,10 @@
<span id="dark-mode-toggle" onclick="toggleTheme()"></span>
<script src="{{ .Site.BaseURL }}js/themetoggle.js"></script>
{{ end }}
{{ if (findRE "<pre" .Content 1) }}
{{ $copyCss := resources.Get "css/copy-code-button.css" | minify }}
<link href="{{ $copyCss.RelPermalink }}" rel="stylesheet">
{{ end }}
</header>

View File

@@ -0,0 +1,58 @@
function createCopyButton(highlightDiv) {
const button = document.createElement("button");
button.className = "copy-code-button";
button.type = "button";
button.innerText = "Copy";
button.addEventListener("click", () =>
copyCodeToClipboard(button, highlightDiv)
);
highlightDiv.insertBefore(button, highlightDiv.firstChild);
const wrapper = document.createElement("div");
wrapper.className = "highlight-wrapper";
highlightDiv.parentNode.insertBefore(wrapper, highlightDiv);
wrapper.appendChild(highlightDiv);
}
document
.querySelectorAll(".highlight")
.forEach((highlightDiv) => createCopyButton(highlightDiv));
async function copyCodeToClipboard(button, highlightDiv) {
const codeToCopy = highlightDiv.querySelector(
"pre > code"
).innerText;
try {
var result = await navigator.permissions.query({ name: "clipboard-write" });
if (result.state == "granted" || result.state == "prompt") {
await navigator.clipboard.writeText(codeToCopy);
} else {
copyCodeBlockExecCommand(codeToCopy, highlightDiv);
}
} catch (_) {
copyCodeBlockExecCommand(codeToCopy, highlightDiv);
} finally {
button.blur();
button.innerText = "Copied!";
setTimeout(function () {
button.innerText = "Copy";
}, 2000);
}
}
function copyCodeBlockExecCommand(codeToCopy, highlightDiv) {
const textArea = document.createElement("textArea");
textArea.contentEditable = "true";
textArea.readOnly = "false";
textArea.className = "copyable-text-area";
textArea.value = codeToCopy;
highlightDiv.insertBefore(textArea, highlightDiv.firstChild);
const range = document.createRange();
range.selectNodeContents(textArea);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
textArea.setSelectionRange(0, 999999);
document.execCommand("copy");
highlightDiv.removeChild(textArea);
}