<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>Lucy McPhail&#x27;s blog</title>
    <link rel="self" type="application/atom+xml" href="https://lucymcphail.com/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://lucymcphail.com"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-07-22T00:00:00+00:00</updated>
    <id>https://lucymcphail.com/atom.xml</id>
    <entry xml:lang="en">
        <title>Things I know about beets</title>
        <published>2026-07-22T00:00:00+00:00</published>
        <updated>2026-07-22T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Lucy McPhail
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucymcphail.com/posts/things-i-know-about-beets/"/>
        <id>https://lucymcphail.com/posts/things-i-know-about-beets/</id>
        
        <content type="html" xml:base="https://lucymcphail.com/posts/things-i-know-about-beets/">&lt;blockquote&gt;
&lt;p&gt;god damn lucy I need you to just list everything you know about &lt;code&gt;beet&lt;&#x2F;code&gt;. You keep saying things that make me go “whaaa what I wish I knew that before”&lt;&#x2F;p&gt;
&lt;p&gt;&lt;cite&gt;&lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;lpil.uk&#x2F;&quot;&gt;Louis Pilfold&lt;&#x2F;a&gt;&lt;&#x2F;cite&gt;&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Sure thing! I’ve been using beets for a few years now to organise my music library, and I’ve picked up a few tricks along the way. In short, beets is a media library management system, which helps you catalogue your music collection and improve its metadata by drawing from databases like MusicBrainz and Discogs. I’m not going to cover the basics here though; for that, I’d recommend going straight to their &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;beets.readthedocs.io&#x2F;en&#x2F;stable&#x2F;guides&#x2F;main.html&quot;&gt;getting started&lt;&#x2F;a&gt; guide. These are a handful of slightly less common features that I find myself using :)&lt;&#x2F;p&gt;
&lt;p&gt;First, a note on enabling plugins, since I’m about to talk about a few of them. In order to enable a plugin, you can add that plugin’s name to the &lt;code&gt;plugins&lt;&#x2F;code&gt; option in your &lt;code&gt;config.yaml&lt;&#x2F;code&gt; file. You can find the location of your config file either by running &lt;code&gt;beet config -p&lt;&#x2F;code&gt;, or by checking &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;beets.readthedocs.io&#x2F;en&#x2F;stable&#x2F;reference&#x2F;config.html&quot;&gt;the docs&lt;&#x2F;a&gt;. Open your config file in your text editor, and add a section that looks like this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;lugins&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; f&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;ish&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; r&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;eplaygain&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; f&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;etchart&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; c&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;onvert&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; m&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;issing&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;  -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; d&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;uplicates&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);font-style: italic;&quot;&gt;  #&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);font-style: italic;&quot;&gt; whatever else you like&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h1 id=&quot;fish-completions&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#fish-completions&quot; aria-label=&quot;Anchor link for: fish-completions&quot;&gt;fish completions&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;I use the fish shell, so I really like that beets comes with a plugin to easily get high-quality shell completions for fish. Enable the &lt;code&gt;fish&lt;&#x2F;code&gt; plugin, then run &lt;code&gt;beet fish&lt;&#x2F;code&gt;, and beets will create a file called &lt;code&gt;~&#x2F;.config&#x2F;fish&#x2F;completions&#x2F;beet.fish&lt;&#x2F;code&gt;. You should now be able to tab-complete beets commands!&lt;&#x2F;p&gt;
&lt;h1 id=&quot;replaygain&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#replaygain&quot; aria-label=&quot;Anchor link for: replaygain&quot;&gt;ReplayGain&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;ReplayGain is a standard for measuring and normalising the perceived loudness of audio tracks. If your music player supports ReplayGain, it can read the relative loudness of each track or album, and adjust the playback gain so that they all sound equally loud.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;dialog&quot;&gt;
  &lt;img class=&quot;dialog-speaker&quot; src=&quot;&#x2F;lily.svg&quot; class=&quot;lily&quot; alt=&quot;Lily says:&quot; title=&quot;Lily says:&quot;&gt;
  &lt;p class=&quot;dialog-text&quot;&gt;what did you mean by &amp;quot;track or album&amp;quot;?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;When you’re playing back your music, you can choose whether you’d like every track to sound equally loud, or for gain to be applied equally to every track in an album. With the latter option, differences in perceived loudness between tracks in an album are preserved, but whole albums will be normalised relative to each other. Beets’ ReplayGain analysis will compute both, so you can usually choose which you’d like in your music player’s settings. Personally, I use album mode.&lt;&#x2F;p&gt;
&lt;p&gt;To generate ReplayGain metadata from beets, install &lt;code&gt;beets[replaygain]&lt;&#x2F;code&gt; and enable the &lt;code&gt;replaygain&lt;&#x2F;code&gt; plugin, and optionally choose an alternate backend. I use the &lt;code&gt;ffmpeg&lt;&#x2F;code&gt; backend, since it’s pretty quick and supports lots of audio formats. If you’d like to do that, make sure you have &lt;code&gt;ffmpeg&lt;&#x2F;code&gt; installed, and then add this to your configuration:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;r&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;eplaygain&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  b&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;ackend&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; f&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;fmpeg&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;By default, the ReplayGain plugin will automatically analyse new tracks as you import them (which is a little slow, don’t worry if it looks like it’s hanging for a bit), but if you’d like to analyse your existing library, run &lt;code&gt;beet replaygain&lt;&#x2F;code&gt; and it’ll go through everything that isn’t already analysed.&lt;&#x2F;p&gt;
&lt;p&gt;For more details, I refer you to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;beets.readthedocs.io&#x2F;en&#x2F;stable&#x2F;plugins&#x2F;replaygain.html&quot;&gt;the docs&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;fetching-album-art&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#fetching-album-art&quot; aria-label=&quot;Anchor link for: fetching-album-art&quot;&gt;fetching album art&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;I like seeing album art when I’m scrolling through my library! Sometimes you’ll get album art when you download an album, like when you get one from Bandcamp, but sometimes you’ll have to get it yourself. Mercifully, beets can automate the process of trawling the internet looking for album art. Install &lt;code&gt;beets[fetchart]&lt;&#x2F;code&gt; and enable the &lt;code&gt;fetchart&lt;&#x2F;code&gt; plugin, and then whenever you import an album, it’ll automatically search the internet for album art, and save it in the album’s folder as &lt;code&gt;cover.jpg&lt;&#x2F;code&gt;. Like the ReplayGain plugin, you can also add album art to your existing library by running &lt;code&gt;beet fetchart&lt;&#x2F;code&gt;, which will look for album art for any albums in your library that don’t already have it.&lt;&#x2F;p&gt;
&lt;p&gt;Some music players, such as iPods, don’t check for a &lt;code&gt;cover.jpg&lt;&#x2F;code&gt; file, so you’ll have to embed the album art into each audio file. Beets also has a plugin for this: install &lt;code&gt;beets[embedart]&lt;&#x2F;code&gt; and enable the &lt;code&gt;embedart&lt;&#x2F;code&gt; plugin (you’ll also need &lt;code&gt;fetchart&lt;&#x2F;code&gt; to get the images in the first place), and then beets will automatically embed images whenever &lt;code&gt;fetchart&lt;&#x2F;code&gt; gets a new cover.&lt;&#x2F;p&gt;
&lt;p&gt;Once again, for more details, see the docs pages for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;beets.readthedocs.io&#x2F;en&#x2F;stable&#x2F;plugins&#x2F;fetchart.html&quot;&gt;fetchart&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;beets.readthedocs.io&#x2F;en&#x2F;stable&#x2F;plugins&#x2F;embedart.html&quot;&gt;embedart&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;converting-between-formats&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#converting-between-formats&quot; aria-label=&quot;Anchor link for: converting-between-formats&quot;&gt;converting between formats&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;The beets &lt;code&gt;convert&lt;&#x2F;code&gt; plugin is a real multi-tool. You can use it to re-encode files in your library, replacing the old ones, or to automatically encode files as you import them. My favourite use, though, is to transcode lossless files to opus in order to save space on music players, while keeping the original lossless files in my library. It’s also useful if you’re using a music player which has limited codec support, like the stock firmware on an iPod, and you need to transcode your library in order to play it at all.&lt;&#x2F;p&gt;
&lt;p&gt;In order to do that, enable the &lt;code&gt;convert&lt;&#x2F;code&gt; plugin, and then add this to your configuration:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;yaml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;c&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;onvert&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  f&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;ormat&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; o&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;pus&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  n&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;ever_convert_lossy_files&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;:&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#FE640B, #F5A97F);&quot;&gt; yes&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Then, run &lt;code&gt;beet convert --dest &#x2F;mnt&#x2F;media&#x2F;your_music_player&lt;&#x2F;code&gt;, changing the destination path to the mount point of your own music player. With the settings above, this will transcode any lossless files to opus, but leave lossy files untouched since converting from one lossy codec to another will always decrease quality without necessarily saving much space. If you’re using an iPod and need to convert everything to AAC, for example, then omit the &lt;code&gt;never_convert_lossy_files&lt;&#x2F;code&gt; option from your configuration and change the format to &lt;code&gt;aac&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;You can also include a query, in order to transcode only part of your library, but I’ll go into queries later on. Here’s the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.beets.io&#x2F;en&#x2F;latest&#x2F;plugins&#x2F;convert.html&quot;&gt;doc page for convert&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;finding-missing-or-duplicate-tracks&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#finding-missing-or-duplicate-tracks&quot; aria-label=&quot;Anchor link for: finding-missing-or-duplicate-tracks&quot;&gt;finding missing or duplicate tracks&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Nice quick one: do you ever accidentally end up with tracks missing from your albums, or import something twice and end up with duplicates? Enable the &lt;code&gt;missing&lt;&#x2F;code&gt; and &lt;code&gt;duplicates&lt;&#x2F;code&gt; plugins, then you can run &lt;code&gt;beet missing&lt;&#x2F;code&gt; to see which of your albums are missing tracks, or &lt;code&gt;beet duplicates&lt;&#x2F;code&gt; to see duplicate tracks. Here are the docs for &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;beets.readthedocs.io&#x2F;en&#x2F;stable&#x2F;plugins&#x2F;missing.html&quot;&gt;missing&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;beets.readthedocs.io&#x2F;en&#x2F;stable&#x2F;plugins&#x2F;duplicates.html&quot;&gt;duplicates&lt;&#x2F;a&gt;.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;querying-and-list-formats&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#querying-and-list-formats&quot; aria-label=&quot;Anchor link for: querying-and-list-formats&quot;&gt;querying and list formats&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Pretty much any time you run a command in beets that acts on your library, you have the option to supply a query to limit which part of your library the command will act on. The simplest type of query is a keyword search: if I run &lt;code&gt;beet ls gold&lt;&#x2F;code&gt;, I see&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;David Bowie - Best of Bowie - Golden Years&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Fleetwood Mac - Rumours - Gold Dust Woman&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;MARINA - FROOT - Gold&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Panic! at the Disco - Death of a Bachelor - Golden Days&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Periphery - Periphery III: Select Difficulty - Marigold&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Lena Raine - Celeste Original Soundtrack - Golden&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Sting - Ten Summoner’s Tales - Fields of Gold&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Beets can also search query by specific fields: &lt;code&gt;beet ls artist:green&quot;&lt;&#x2F;code&gt; will turn up Grant Green and Ted Greene, but not Eddi Reader’s &lt;em&gt;Green Grow the Rashes O.&lt;&#x2F;em&gt; I’ll not go through every query feature – the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.beets.io&#x2F;en&#x2F;latest&#x2F;reference&#x2F;query.html&quot;&gt;query docs&lt;&#x2F;a&gt; do a good job of that already – but I do have some neat uses for it.&lt;&#x2F;p&gt;
&lt;p&gt;Let’s say we want to figure out the total length of an album. We can start off by getting all the tracks in the album we’re interested in, using the &lt;code&gt;:=&lt;&#x2F;code&gt; syntax to search for exact matches, and quote marks to look for a whole phrase rather than a keyword:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ beet ls album:=&amp;quot;The Rainbow Goblins&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;高中正義 - The Rainbow Goblins - Prologue&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;高中正義 - The Rainbow Goblins - Once Upon a Song&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;高中正義 - The Rainbow Goblins - Seven Goblins&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;高中正義 - The Rainbow Goblins - The Sunset Valley&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;高中正義 - The Rainbow Goblins - The Moon Rose&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;高中正義 - The Rainbow Goblins - Soon&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;高中正義 - The Rainbow Goblins - Magical Night Light&#x2F;Rainbow Paradise&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;高中正義 - The Rainbow Goblins - Thunderstorm&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;高中正義 - The Rainbow Goblins - Rising Arch&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;高中正義 - The Rainbow Goblins - Just Chuckle&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;高中正義 - The Rainbow Goblins - Rainbow Was Reborn&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;高中正義 - The Rainbow Goblins - Plumed Bird&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;高中正義 - The Rainbow Goblins - You Can Never Come To This Place&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So now we know which songs are in the album, but we’d rather see how long they all are rather than their names. We can use beets’ &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;docs.beets.io&#x2F;en&#x2F;latest&#x2F;reference&#x2F;pathformat.html&quot;&gt;format syntax&lt;&#x2F;a&gt; to get there:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ beet ls album:=&amp;quot;The Rainbow Goblins&amp;quot; -f &amp;#39;$length&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;3:15&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;5:10&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;3:12&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[...]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We’ll want those in plain seconds:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ beet ls album:=&amp;quot;The Rainbow Goblins&amp;quot; -f &amp;#39;$length&amp;#39; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      | xargs -I &amp;#39;{}&amp;#39; date -u -d &amp;quot;1 jan 1970 0:{}&amp;quot; +%s&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;195&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;310&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;192&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;[...]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;blockquote class=&quot;dialog&quot;&gt;
  &lt;img class=&quot;dialog-speaker&quot; src=&quot;&#x2F;lily.svg&quot; class=&quot;lily&quot; alt=&quot;Lily says:&quot; title=&quot;Lily says:&quot;&gt;
  &lt;p class=&quot;dialog-text&quot;&gt;sorry, what did you just do with date??&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Ah, that! It takes a duration like 3:15, and turns it into “1 jan 1970 0:3:15” – 3 minutes and 15 seconds after the epoch – and then outputs that timestamp as seconds since the epoch. It’s a hack, and it breaks if you have tracks that are over an hour long, but it’s good enough here.&lt;&#x2F;p&gt;
&lt;p&gt;Now, we can sum up the seconds with &lt;code&gt;awk&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ beet ls album:=&amp;quot;The Rainbow Goblins&amp;quot; -f &amp;#39;$length&amp;#39; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      | xargs -I &amp;#39;{}&amp;#39; date -u -d &amp;quot;1 jan 1970 0:{}&amp;quot; +%s \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      | awk &amp;#39;{ total += $1 } END { printf &amp;quot;%s\n&amp;quot;, total }&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;3797&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And then use &lt;code&gt;date&lt;&#x2F;code&gt; again to turn it into hours, minutes, and seconds:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ beet ls album:=&amp;quot;The Rainbow Goblins&amp;quot; -f &amp;#39;$length&amp;#39; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      | xargs -I &amp;#39;{}&amp;#39; date -u -d &amp;quot;1 jan 1970 0:{}&amp;quot; +%s \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      | awk &amp;#39;{ total += $1 } END { printf &amp;quot;%s\n&amp;quot;, total }&amp;#39; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      | xargs -I &amp;#39;{}&amp;#39; date -u -d &amp;quot;@{}&amp;quot; +%H:%M:%S&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;01:03:17&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;And we’re done! It’s worth having a look at the list of format fields that beets can output, since there are heaps of them, and I’ve found it really useful on a few occasions now. I wanted to transcode all of my ALAC files to FLAC a while back, and finding the ALAC files was as easy as &lt;code&gt;beet ls format:flac -f &#x27;$path&#x27;&lt;&#x2F;code&gt;. Then I can pass that whole list to &lt;code&gt;beet format&lt;&#x2F;code&gt; with the &lt;code&gt;--keep-new&lt;&#x2F;code&gt; flag, and that was that!&lt;&#x2F;p&gt;
&lt;p&gt;I really like beets, and it’s helped me keep my music library far more organised than it ever would have been otherwise, and enabled some really esoteric workflows with how scriptable it is. Please let me know if you’ve got any weird beets tricks of your own, and hopefully I can make a part 2!&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Using Caddy as a link shortener</title>
        <published>2026-01-17T00:00:00+00:00</published>
        <updated>2026-01-17T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Lucy McPhail
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucymcphail.com/posts/using-caddy-as-a-link-shortener/"/>
        <id>https://lucymcphail.com/posts/using-caddy-as-a-link-shortener/</id>
        
        <content type="html" xml:base="https://lucymcphail.com/posts/using-caddy-as-a-link-shortener/">&lt;p&gt;A few days ago, I was chatting to &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cassidoo.co&#x2F;&quot;&gt;Cassidy&lt;&#x2F;a&gt; after finding out she plays go, and she casually dropped a link to her profile at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cass.run&#x2F;ogs&quot;&gt;cass.run&#x2F;ogs&lt;&#x2F;a&gt;. I link people to my own OGS account from time to time, but that link looks like &lt;code&gt;online-go.com&#x2F;user&#x2F;view&#x2F;1687228&lt;&#x2F;code&gt;, which is a little tough to remember. Usually I just tell people my username and let them look it up, but that’s a bit more hassle for them.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;dialog&quot;&gt;
  &lt;img class=&quot;dialog-speaker&quot; src=&quot;&#x2F;lily.svg&quot; class=&quot;lily&quot; alt=&quot;Lily says:&quot; title=&quot;Lily says:&quot;&gt;
  &lt;p class=&quot;dialog-text&quot;&gt;seven digits? that doesn&amp;#x27;t look so bad&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Well I guess I could remember my account number, but I use it infrequently enough that it’s easy to forget in between the times that I need it. And besides, I have plenty more URLs that would be useful to have on hand, but that are pretty tough to remember. All this is to say, I really like Cassidy’s idea of a personal URL shortener.&lt;&#x2F;p&gt;
&lt;p&gt;Hers lives, aptly, at &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;cass.run&#x2F;shortener&quot;&gt;cass.run&#x2F;shortener&lt;&#x2F;a&gt;, but it’s deployed as a Netlify application. I already have all of my domains pointing to my reverse proxy, Caddy, so I’d rather just use that if I can. Turns out it’s actually pretty easy! Just add this to your Caddyfile:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;rosy.systems {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    map {path} {redirect-uri} {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &#x2F;ogs    https:&#x2F;&#x2F;online-go.com&#x2F;user&#x2F;view&#x2F;1687228&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &#x2F;path   https:&#x2F;&#x2F;whatever.other.url.you.like&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        default https:&#x2F;&#x2F;lucymcphail.com&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    redir {redirect-uri}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Replace the domain at the top with your own, and make sure to leave in the &lt;code&gt;default&lt;&#x2F;code&gt; mapping if you want it to gracefully handle paths that you haven’t added a redirect for. It works fine if you don’t, but you’ll get an ugly default error page if you try and hit &lt;code&gt;&#x2F;wibble&lt;&#x2F;code&gt; or something else you haven’t defined. From there, just add any extra shortened URLs you’d like! Happy shortening :)&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Outlook keeps logging me out</title>
        <published>2025-10-06T00:00:00+00:00</published>
        <updated>2025-10-06T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Lucy McPhail
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucymcphail.com/posts/outlook-keeps-logging-me-out/"/>
        <id>https://lucymcphail.com/posts/outlook-keeps-logging-me-out/</id>
        
        <content type="html" xml:base="https://lucymcphail.com/posts/outlook-keeps-logging-me-out/">&lt;p&gt;I started at a new job a few months ago, and as it so often does, it came along with a new Microsoft email address. This time though, I’m using a Linux machine, and I don’t have permission to use a mail client other than Outlook. That leaves the web version of Outlook as my only choice. Predictably, it hasn’t been smooth sailing.&lt;&#x2F;p&gt;
&lt;p&gt;So here’s what happens when I try and open Outlook in Firefox: I log in with my password and TOTP code, and… it lets me in.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;dialog&quot;&gt;
  &lt;img class=&quot;dialog-speaker&quot; src=&quot;&#x2F;lily.svg&quot; class=&quot;lily&quot; alt=&quot;Lily says:&quot; title=&quot;Lily says:&quot;&gt;
  &lt;p class=&quot;dialog-text&quot;&gt;Seems fine so far.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Sure! Until the next morning, when I have to log in again. I sign in, exactly the same way, and this is what happens:&lt;&#x2F;p&gt;



&lt;img
  src=&quot;https:&amp;#x2F;&amp;#x2F;lucymcphail.com&amp;#x2F;posts&amp;#x2F;outlook-keeps-logging-me-out&amp;#x2F;hang-on-a-moment.png&quot;
  alt=&quot;Outlook&amp;#x27;s login page, saying &amp;#x27;Hang on a moment while we sign you out.&amp;#x27;&quot;
  title=&quot;Outlook&amp;#x27;s login page, saying &amp;#x27;Hang on a moment while we sign you out.&amp;#x27;&quot;
  width=&quot;1062px&quot;
  height=&quot;779px&quot; &#x2F;&gt;
&lt;p&gt;So I let it sign me out, close the tab, and try again. Same thing. And better still, since TOTP codes are, well, one-time codes; I’m essentially rate limited to two attempts a minute while I wait for the codes to roll over. Usually it works eventually, but I’ve spent 15 minutes some mornings trying to get into my inbox.&lt;&#x2F;p&gt;
&lt;p&gt;In case anyone reading this is in the same boat, here’s what works for me: in Firefox settings, then Privacy &amp;amp; Security, under Cookies and Site Data, click on “Manage Data…”. From there, select microsoft.com and outlook.com, and click Remove Selected for both. For now, at least, that lets me log back into Outlook first try.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Fixing my scrollbars</title>
        <published>2024-11-04T00:00:00+00:00</published>
        <updated>2024-11-04T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Lucy McPhail
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucymcphail.com/posts/scrollbars/"/>
        <id>https://lucymcphail.com/posts/scrollbars/</id>
        
        <content type="html" xml:base="https://lucymcphail.com/posts/scrollbars/">&lt;p&gt;I don’t know when it started happening, or why. Maybe it’s because people are getting so used to touch screens on smartphones, maybe it’s because designers want to make their interfaces as minimal as possible; I can’t say. But I like a good, old-fashioned scrollbar, and unfortunately they seem to be going the way of the dodo.&lt;&#x2F;p&gt;
&lt;p&gt;I have a few problems with the new style of overlay scrollbar, the kind that disappear after a while if you leave them alone. For one, the scrollbar gives me two useful pieces of information if I’m reading an article: first, the size of the scrollbar lets me see roughly how long the article is; and second, I can see how far through the page I am. It makes me a little sad that some websites feel the need to add a fancy progress bar at the top of the page to let me see where I am – this is a solved problem! This is what scrollbars are for!&lt;&#x2F;p&gt;
&lt;p&gt;The other issue I have with overlay scrollbars is, y’know, using them for scrolling! If I want to scroll with a conventional scroll bar, I can see the whole scrollbar and I can either click on the thumb (the little rectangle that moves) and drag it, or click somewhere along the track (the big rectangle the thumb moves along) to jump there. With overlay scrollbars, I have to wiggle my mouse over the rough area I think the scrollbar is to summon it, and then drag the absolutely teensy thumb, and there isn’t a track to click on anymore, so that’s not an option.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;dialog&quot;&gt;
  &lt;img class=&quot;dialog-speaker&quot; src=&quot;&#x2F;lily.svg&quot; class=&quot;lily&quot; alt=&quot;Lily says:&quot; title=&quot;Lily says:&quot;&gt;
  &lt;p class=&quot;dialog-text&quot;&gt;wow, I didn&amp;#x27;t know anyone cared so much about scrollbars...&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Okay, perhaps I’m nitpicking, but I stand by the fact that having nice chunky, persistent scrollbars makes it easier to use my computer. Fortunately, it’s pretty easy to get them back!&lt;&#x2F;p&gt;
&lt;h1 id=&quot;firefox&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#firefox&quot; aria-label=&quot;Anchor link for: firefox&quot;&gt;firefox&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Most of the time if I’m on my computer, I’m either in emacs or firefox, and for the former I tend not to use scollbars anyway. After all, it’s not often I need to know how far down I am in a code file, and even when I do, the percentage progress indicator at the bottom is good enough for me. Suffice it to say, I really want my scrollbars in firefox to be good. Luckily, there are a few hidden settings we can use to get them that way.&lt;&#x2F;p&gt;
&lt;p&gt;First, there’s an option in the general settings menu to always show scrollbars. Enabling this will give you a fairly sensible scrollbar that stays on the side of the window where you left it.&lt;&#x2F;p&gt;



&lt;img
  src=&quot;https:&amp;#x2F;&amp;#x2F;lucymcphail.com&amp;#x2F;posts&amp;#x2F;scrollbars&amp;#x2F;.&amp;#x2F;always-show-scrollbars.png&quot;
  alt=&quot;a screenshot of the firefox settings menu, in particular the option to always show scrollbars&quot;
  title=&quot;a screenshot of the firefox settings menu, in particular the option to always show scrollbars&quot;
  width=&quot;434px&quot;
  height=&quot;312px&quot; &#x2F;&gt;
&lt;p&gt;This is a pretty good start, but there are some hidden settings in &lt;code&gt;about:config&lt;&#x2F;code&gt; that are worth playing with as well. Here are the ones I change:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;layout.css.scrollbar-width-thin.disabled = true&lt;&#x2F;code&gt; stops web pages from making your scrollbar thin in CSS&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;widget.gtk.overlay-scrollbars.enabled = false&lt;&#x2F;code&gt; seems to stop a few scrollbars in weird places from appearing? I’m not quite sure what this one does honestly, but it might be handy&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;widget.non-native-theme.gtk.scrollbar.allow-buttons = true&lt;&#x2F;code&gt; puts the buttons back at the top and bottom of the scrollbar if you’re on linux and your GTK theme has them; I’ll get to that in a moment!&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;widget.non-native-theme.scrollbar.style = 4&lt;&#x2F;code&gt; chooses between a few shapes for your scrollbar. I like 4 the best, since it gives you a lovely, chunky, deliciously clickable, rectangular scrollbar.&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;widget.non-native-theme.scrollbar.size.override = 20&lt;&#x2F;code&gt; changes the width of the scrollbars, so you can make the click target as big as you like&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Here’s what my scrollbars look like after all of that:&lt;&#x2F;p&gt;



&lt;img
  src=&quot;https:&amp;#x2F;&amp;#x2F;lucymcphail.com&amp;#x2F;posts&amp;#x2F;scrollbars&amp;#x2F;firefox-sexy-scrollbar.png&quot;
  alt=&quot;a screenshot of firefox with a horizontal scrollbar: it&amp;#x27;s a nice, chunky rectangle, with arrows at either end&quot;
  title=&quot;a screenshot of firefox with a horizontal scrollbar: it&amp;#x27;s a nice, chunky rectangle, with arrows at either end&quot;
  width=&quot;1024px&quot;
  height=&quot;194px&quot; &#x2F;&gt;
&lt;h1 id=&quot;gtk&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#gtk&quot; aria-label=&quot;Anchor link for: gtk&quot;&gt;GTK&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;I said I’d mention my GTK theme since Adwaita, the default in most places, doesn’t have arrows on scrollbars. I’m enjoying the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;grassmunk&#x2F;Chicago95&quot;&gt;chicago95&lt;&#x2F;a&gt; theme at the minute, which gives me my scrollbar buttons back in firefox, and as an added bonus it makes all my other GTK applications look delightfully charming:&lt;&#x2F;p&gt;



&lt;img
  src=&quot;https:&amp;#x2F;&amp;#x2F;lucymcphail.com&amp;#x2F;posts&amp;#x2F;scrollbars&amp;#x2F;chicago95.png&quot;
  alt=&quot;a screenshot of a file manager, styled to look like windows 95&quot;
  title=&quot;a screenshot of a file manager, styled to look like windows 95&quot;
  width=&quot;1350px&quot;
  height=&quot;1120px&quot; &#x2F;&gt;
&lt;p&gt;That’s all for now, but if any of you know any other scrollbar related tricks, I’d love to hear about them &amp;lt;3&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>RC 2024: halfway already!?</title>
        <published>2024-09-26T00:00:00+00:00</published>
        <updated>2024-09-26T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Lucy McPhail
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucymcphail.com/posts/rc-2024-week-6/"/>
        <id>https://lucymcphail.com/posts/rc-2024-week-6/</id>
        
        <content type="html" xml:base="https://lucymcphail.com/posts/rc-2024-week-6/">&lt;blockquote&gt;
&lt;p&gt;That’s all, see you next week! :)&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;— An overly optimistic Lucy, about four weeks ago&lt;&#x2F;p&gt;
&lt;p&gt;So, uh, it’s been a little while. The last few weeks have been a bit tough on me for reasons outside of RC or programming in general, and to be honest I’ve barely had the energy to work on my projects, let alone write here. I don’t think the weekly update format I’ve been aiming for so far works very well for me: it starts feeling like more of a chore, something I &lt;em&gt;have&lt;&#x2F;em&gt; to do rather than something I do because I &lt;em&gt;want&lt;&#x2F;em&gt; to. I do want to keep writing here, and more regularly than I have been, but I think in order to do that I need to give myself a little more flexibility. With any luck, it’ll help me be a little gentler on myself and I won’t stress myself out of writing anything. If anyone has any advice for dealing with this sort of writer’s block, I’d love to hear it.&lt;&#x2F;p&gt;
&lt;p&gt;Another thing that’s been getting in the way of me writing these posts is feeling like I don’t have a lot to write about. I’ve been making slow progress over the last few weeks, but a lot of it doesn’t feel interesting enough to write about. I’ve learned lots of little things along the way, but I don’t have progress that feels like it’s ‘worth’ showing off here, if that makes any sense? I recently read &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ntietz.com&#x2F;blog&#x2F;making-progress-with-content-driven-development&#x2F;&quot;&gt;Nicole’s blog post on content-driven development&lt;&#x2F;a&gt;, where she talks about projects getting stuck because of overwhelming task lists – a sentiment to which I can deeply relate – and reframing them in terms of sharable milestones. She says that being able to split projects into deliverable pieces can help with project direction (if it isn’t worth explaining to other people, maybe it doesn’t need to be there yet?) as well as generally staying motivated. It’s a great post, and I’m looking forward to trying some of her ideas in my own projects.&lt;&#x2F;p&gt;
&lt;p&gt;As for what I’m actually planning on doing, I definitely want to branch out into a few more projects so that if I’m losing momentum on one, I can pick up steam again working on another. I’m going to keep working on my chess engine; in the spirit of content-driven development, I’d like to finish the move generation so I can actually watch it play a game of chess (if not a good one quite yet), and potentially put it up on my server for people to play against.&lt;&#x2F;p&gt;
&lt;p&gt;Another thing I’ve been interested in for a little while is building a debugger, and there’s a group at RC already working through a course on that so I’d quite like to give that a go. It also seems like a good project to test &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ziglang.org&#x2F;&quot;&gt;Zig&lt;&#x2F;a&gt; with, since &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;anyahope.me&#x2F;&quot;&gt;Anya&lt;&#x2F;a&gt; has been speaking very highly of it lately and says it seems like exactly the kind of language I like.&lt;&#x2F;p&gt;
&lt;p&gt;I think I’d like to have one more, slightly more artsy and creative project going as well, for when I’m in that kind of mood, but I’m not sure exactly what I’d like that to be yet. I have a few ideas, but I think I’ll save that for next week.&lt;&#x2F;p&gt;
&lt;p&gt;One more thing I’d like to do: there are a handful of new faces in the category theory group so we’re doing a bit of a reset, and since Milewski’s book has proved to be a bit confusing and lacking in detail, we’re going to try and pick a different book to go through. So, along with the others, I’m going to sample a few different textbooks and see if any stand out as being particularly approachable without mathematical background. I’m not sure if I’ll follow through on this yet, but learning about homotopy type theory and univalent foundations is something I’ve wanted to do for a while, so I might pick up the HoTT book again and see if more of it sticks than last time I tried.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;halfway-there&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#halfway-there&quot; aria-label=&quot;Anchor link for: halfway-there&quot;&gt;halfway there&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;It’s an exciting time at RC at the moment, since this week marks the end of the Summer 2 batch and the beginning of Fall 2. It’s been bittersweet: I’m really enjoying meeting all the new people and I’m so excited to see what they get up to, but it does also mean saying goodbye to a few friends. I’m so grateful for the time I got to spend with all the Summer 2 folks, and I’ve met a few people I really want to stay in touch with as they go off and do other things.&lt;&#x2F;p&gt;
&lt;p&gt;Being in the second half of my batch also means I need to start thinking about my own life after RC, and in particular, it’s time to start looking for a job. I’m not sure if I’ll post anything about that here, since I don’t expect my job hunt to be particularly interesting, but it does seem like that’ll be a notable part of the next few weeks.&lt;&#x2F;p&gt;
&lt;p&gt;For now though, I’m still very happy to be at RC and I’m feeling optimistic about what I can do with the next six weeks. I’m not sure quite when the next post here is going to be, but with any luck there’ll be something interesting in there! And thanks so much to all my friends at RC who’ve helped me make cool stuff over the last couple of weeks, I love you all &amp;lt;3&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>RC 2024 week 2: on... beer and categories?</title>
        <published>2024-08-27T00:00:00+00:00</published>
        <updated>2024-08-27T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Lucy McPhail
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucymcphail.com/posts/rc-2024-week-2/"/>
        <id>https://lucymcphail.com/posts/rc-2024-week-2/</id>
        
        <content type="html" xml:base="https://lucymcphail.com/posts/rc-2024-week-2/">&lt;p&gt;Soooo. I was going to write this post a couple of days ago on Sunday, but some kind soul decided to douse me with beer from the passenger seat of his mate’s car while I was walking back to my flat. Cleaning the errant beverage from my clothes and myself, it turns out, is not an activity particularly conducive to getting in the mood to write. Some rest and a few episodes of Deep Space Nine later, I’m feeling a bit better, so here we are.&lt;&#x2F;p&gt;
&lt;p&gt;It’s been a bit of a low energy week for me in general, so I’ve been more focused on doing some reading and getting some smaller, less challenging work done. For example, I’ve tried to make this site a little more readable and added a form for folks to subscribe by email if they prefer. I haven’t done so much with my chess engine this week, but the FEN parsing and a basic UCI implementation are done so I just need to crack on with the move generation. I think next week I’d like to try and pair on it with some more people, if for no other reason than having some company while I work on the slightly less interesting bits.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;dialog&quot;&gt;
  &lt;img class=&quot;dialog-speaker&quot; src=&quot;&#x2F;lily.svg&quot; class=&quot;lily&quot; alt=&quot;Lily says:&quot; title=&quot;Lily says:&quot;&gt;
  &lt;p class=&quot;dialog-text&quot;&gt;*hugs*&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Thank you, Lily.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;category-theory-things&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#category-theory-things&quot; aria-label=&quot;Anchor link for: category-theory-things&quot;&gt;category theory things&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;In other news, I’ve had some interesting chats with the category theory group this week, especially about some of the exercises in Milewski’s book. In particular, this one from the first chapter:&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;When is a directed graph a category?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;This question confused a few people in the group, and in my opinion, rightfully so! Saying a directed graph is never a category seems to me a perfectly reasonable answer, in the same way a banana is never a grapefruit. A category simply has more structure than a directed graph – namely categories have composition – and there is no canonical way to add this structure to a digraph. To use a programming analogy, I might ask when a block of memory is a struct.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;dialog&quot;&gt;
  &lt;img class=&quot;dialog-speaker&quot; src=&quot;&#x2F;lily.svg&quot; class=&quot;lily&quot; alt=&quot;Lily says:&quot; title=&quot;Lily says:&quot;&gt;
  &lt;p class=&quot;dialog-text&quot;&gt;Right; a block of memory might be an instance of a struct, but you couldn&amp;#x27;t possibly know unless you knew what the struct was! If someone just handed you some raw bytes, then sure, you could turn it into a struct, but you&amp;#x27;d have to choose which structure to impose on it. You can&amp;#x27;t just say yes or no without making some decisions first.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Exactly! So let’s weaken the question a little bit: can we turn any directed graph into a category?&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;dialog&quot;&gt;
  &lt;img class=&quot;dialog-speaker&quot; src=&quot;&#x2F;lily.svg&quot; class=&quot;lily&quot; alt=&quot;Lily says:&quot; title=&quot;Lily says:&quot;&gt;
  &lt;p class=&quot;dialog-text&quot;&gt;Sure! Take the graph, throw it in the bin, and replace it with the empty category :D&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;I– yeah, I guess I can’t argue with that. I’m pretty sure the idea behind that question was to highlight the differences between a category and a directed graph: in a category, if we have any two composable morphisms, their composition must also exist; and every object must have a morphism, from it back to itself, that satisfies the identity laws. But instead, for our group, it ended up serving as a great example of why mathematicians are so insistent on being precise in wording our statements. My thanks to the category theory group for asking me some hard questions and making me think.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;wrapping-up&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#wrapping-up&quot; aria-label=&quot;Anchor link for: wrapping-up&quot;&gt;wrapping up&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Since I’m writing this post a little late, ‘next week’ is actually this week for me. Still, I have more than half the week left, so it still feels reasonable to talk about my plans. I’m very much enjoying my role in clarifying Milewski’s material for the rest of the reading group, so I’m tempted to put a bit of time into making some materials of my own to present. I’d love to see my chess bot actually playing a game of chess, so I’m going to try and push through writing the move generation code. And finally, Nicholas convinced me to give a talk with him on the basics of solving cryptic crosswords, so I’ll be writing up some notes for that. If it goes well, I might even turn it into a blog post!&lt;&#x2F;p&gt;
&lt;p&gt;That’s all, see you next week! :)&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>RC 2024 week 1: finding community</title>
        <published>2024-08-18T00:00:00+00:00</published>
        <updated>2024-08-18T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Lucy McPhail
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucymcphail.com/posts/rc-2024-week-1/"/>
        <id>https://lucymcphail.com/posts/rc-2024-week-1/</id>
        
        <content type="html" xml:base="https://lucymcphail.com/posts/rc-2024-week-1/">&lt;p&gt;So, that’s my first week at the Recurse Center over and done with! And my word, what a week it’s been. I tried to spend as much time as possible going to anything on the calendar that looked interesting, which means I didn’t have much time to write code, but I met a lot of people and got to see some awesome projects so I don’t regret my decision.&lt;&#x2F;p&gt;
&lt;p&gt;First of all, everything I’ve heard about the culture at RC is completely true so far. As a member of several marginalised groups, I often find myself feeling like an outsider in tech spaces. Being in a field heavily dominated by cis white men, it’s all too easy to feel somewhat othered, and that my voice isn’t being heard. At Recurse, though, I’ve felt absolutely none of that. It takes a lot of work to create and maintain such a diverse and welcoming culture, and the faculty as well as the rest of the RC community continuously work to do so. More than any other technical space I’ve been part of, I feel like I truly belong at RC.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;what-has-lucy-been-up-to&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-has-lucy-been-up-to&quot; aria-label=&quot;Anchor link for: what-has-lucy-been-up-to&quot;&gt;what has lucy been up to?&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;&lt;em&gt;So. Much.&lt;&#x2F;em&gt; I decided to keep a written log of what I’ve been doing with my time this week, and I’m incredibly glad because there’s no way I would have been able to remember everything myself.&lt;&#x2F;p&gt;
&lt;p&gt;My batch started with the introductory meet and greets. These were structured a bit like a speed dating event, so I met &lt;em&gt;a lot&lt;&#x2F;em&gt; of people in a very short time. It was an absolutely amazing start: only a couple of hours into the first day and already I’d met a whole bunch of my batchmates and heard about all the interesting things they were working on.&lt;&#x2F;p&gt;
&lt;p&gt;Then us remote folks got a chance to explore the software side of RC. As well as the usual suspects like a chat system (in this case, Zulip) and calendar, there’s also a whole virtual Recurse Center! It’s a 2D version of the physical hub that runs in a browser, where you can see which zoom rooms people are in because their little avatar is standing there. It’s a little thing but I think having a shared space really adds to the remote experience. There’s also a coffee chat bot that randomly assigns you another person to chat to on days of your choosing. I’ve had coffee chats nearly every day so far, and they’ve been a great way to get to know some of my batchmates.&lt;&#x2F;p&gt;
&lt;p&gt;Perhaps my favourite introductory event was the pairing workshop: you and another Recurser get a simple brief – for us, it was writing an implementation of Conway’s game of life – and then you have an hour to implement it together. It was a great way to start pairing with people in a low-pressure environment, and I definitely feel more comfortable asking people to pair on my actual projects now.&lt;&#x2F;p&gt;
&lt;p&gt;Most of the events at RC are organised not by the faculty, though, but by other Recursers. There are a few long-running events that have carried over from before my batch, since the Summer 2 batch is still here for another six weeks, and I decided to check out quite a few of them. Among my favourites were the groups on operating systems and systems programming, practical language design, and the Nix language and tooling. There were also a few of us who were interested in category theory, so this week marked the beginning of our category theory study group! Unfortunately I doubt I’ll have time to keep attending all of these events if I want to start making progress on my own projects, but I’m glad I checked out so many just to see what was happening. For now, I think I’m going to stick with the category theory and systems programming meet-ups.&lt;&#x2F;p&gt;
&lt;p&gt;And then there were the presentations! First, there are non-programming talks every week on Wednesdays. I’m not sure what I was expecting to hear about but it definitely wasn’t tiramisu and moss. It turns out tiramisu is a lot easier to make than I had assumed, so I’ll have to give that a go now (upon telling my partner and my parents this, they all suddenly seemed to be quite hungry – something tells me I’m not going to get a choice in the matter but you won’t hear me complaining about it heheh).&lt;&#x2F;p&gt;
&lt;p&gt;Then there are the programming talks that take place weekly on Thursdays. To name a few of my favourite topics: developing a command line poker game; automatically taking screenshots from a reMarkable tablet and embedding them in a note in Obsidian by calling one function; and connecting a Raspberry Pi to the internet by playing a T9-encoded WiFi password with DTMF tones into the handset of a landline phone. All of these, and the many others I didn’t mention here, were fantastic and I’m very glad I got to see them.&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;dialog&quot;&gt;
  &lt;img class=&quot;dialog-speaker&quot; src=&quot;&#x2F;lily.svg&quot; class=&quot;lily&quot; alt=&quot;Lily says:&quot; title=&quot;Lily says:&quot;&gt;
  &lt;p class=&quot;dialog-text&quot;&gt;woah dude that&amp;#x27;s... a lot&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Yep! It’s been a very social week for me, and I’ve enjoyed meeting everyone and going to all the events, but next week I definitely want to get some more technical work done and pair with more people. I did get a little bit of programming done this week though: I’ve been learning the basics of Go and getting the toolchain all set up, and started laying the groundwork for my chess engine. I’ll write a separate, more technical post about what exactly goes into that, but in short, I have to encode the rules of chess so I can tell which moves are legal from a given position, and teach the bot to speak to other chess programs so I can play against it.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;so-what-s-the-plan&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#so-what-s-the-plan&quot; aria-label=&quot;Anchor link for: so-what-s-the-plan&quot;&gt;so what’s the plan?&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;As I mentioned earlier, I’d like to dial down the social stuff from this week and start making more significant progress on my technical projects. With that in mind, here are my goals for the next week or so:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;for the chess engine, I’m hoping to have at least the legal move generation working by the end of next week. I’d also like to make sure I have comprehensive tests and some benchmarks set up: getting move generation right is extremely important, and since chess engines play with clocks, ‘thinking’ time is limited so I want to get benchmarking working from the beginning. So far, though, I’m really enjoying the support for testing and benchmarking built into Go.&lt;&#x2F;li&gt;
&lt;li&gt;I’ve been fascinated by eBPF since I discovered it a couple of years ago, and I’d love to try and write some actual tools with it. The application I had in mind back then was a sampling profiler, which still sounds fun, but I’m open to being distracted here.&lt;&#x2F;li&gt;
&lt;li&gt;I’ll carry on with the category theory group; so far I’ve read through the first three chapters of Bartosz Milewski’s &lt;em&gt;Category Theory for Programmers.&lt;&#x2F;em&gt; I have a formal background in category theory from my maths degree, but I’m curious to see the more advanced applications for working programmers as opposed to, say, homotopy theorists.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;blockquote class=&quot;dialog&quot;&gt;
  &lt;img class=&quot;dialog-speaker&quot; src=&quot;&#x2F;lily.svg&quot; class=&quot;lily&quot; alt=&quot;Lily says:&quot; title=&quot;Lily says:&quot;&gt;
  &lt;p class=&quot;dialog-text&quot;&gt;Don&amp;#x27;t worry, homotopy theory, we still love you &amp;lt;3&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;That’s about all I wanted to say about this week, so I suppose there’s nothing else to do but get on with it! So with that, dear reader, I bid you adieu; see you again this time next week :)&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>I’m going to the Recurse Center!</title>
        <published>2024-08-12T00:00:00+00:00</published>
        <updated>2024-08-12T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Lucy McPhail
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucymcphail.com/posts/im-going-to-the-recurse-center/"/>
        <id>https://lucymcphail.com/posts/im-going-to-the-recurse-center/</id>
        
        <content type="html" xml:base="https://lucymcphail.com/posts/im-going-to-the-recurse-center/">&lt;p&gt;Today is my first day at the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.recurse.com&quot;&gt;Recurse Center&lt;&#x2F;a&gt;! For anyone who hasn’t heard of it before, it’s a 12 week programming retreat: essentially, I have the next three months to work on whatever projects I like, and try and learn as much as possible, alongside a group of extremely cool people with exactly the same aims. I’m incredibly grateful to be able to attend, and I’m so excited to get started.&lt;&#x2F;p&gt;
&lt;p&gt;I first heard about RC through &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;jvns.ca&quot;&gt;Julia Evans&lt;&#x2F;a&gt;, and later again from &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;ntietz.com&quot;&gt;Nicole Tietz-Sokolskaya&lt;&#x2F;a&gt;, both of whom wrote regular updates during their time at RC. Reading about their experiences was by far my biggest inspiration to apply, and now that I’m finished with my maths degree, I’m finally starting a batch myself! It only seems right, then, that I should pick up the torch and write about my own experiences here. Perhaps, someday, someone will read this and it’ll help them decide whether applying to Recurse is the right choice for them :)&lt;&#x2F;p&gt;
&lt;blockquote class=&quot;dialog&quot;&gt;
  &lt;img class=&quot;dialog-speaker&quot; src=&quot;&#x2F;lily.svg&quot; class=&quot;lily&quot; alt=&quot;Lily says:&quot; title=&quot;Lily says:&quot;&gt;
  &lt;p class=&quot;dialog-text&quot;&gt;So... what are you actually going to do?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;Well, one post I found really helpful deciding how to set my goals is &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;rfong.github.io&#x2F;rflog&#x2F;2023&#x2F;07&#x2F;06&#x2F;maximizing-rc-experience&#x2F;&quot;&gt;this one&lt;&#x2F;a&gt; from RC alum Ray Fong on maximising your time at RC, so I’m going to steal a couple of their ideas: first, keeping my plans flexible so that I can change what I’m doing if something piques my interest; and second, having distinct technical and non-technical goals. First, the technical:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;My friend Cosmo, author of &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;cosmobobak&#x2F;viridithas&quot;&gt;one of the world’s strongest chess engines&lt;&#x2F;a&gt;, has been trying to convince me to write a chess bot of my own for years now. Now that I have 12 weeks set aside for this sort of project, I think it’s finally time to take them up on it!&lt;&#x2F;li&gt;
&lt;li&gt;I’d love to know more about programming language design, so I’m planning to go through &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;craftinginterpreters.com&#x2F;&quot;&gt;Crafting Interpreters&lt;&#x2F;a&gt; and potentially make some more interesting languages. The end goal here is to play with interesting type systems (linear types, session types, dependent types, and so on) so I’d like to make myself a framework to play with them in.&lt;&#x2F;li&gt;
&lt;li&gt;A few other little things I’d like to tackle if I have time: I’ve always been fascinated by modern multi-process OSs, specifically how their schedulers work, so I’d like to try and make my own toy one. I’ve also been meaning to play about with theorem provers like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;leanprover&#x2F;lean4&quot;&gt;Lean&lt;&#x2F;a&gt; for a while, and see if I can figure out how they tick.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;As for my non-technical goals:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Above all, I’d love to make some new friends. RC prides itself on being a diverse and inclusive community, so it seems like the perfect place to meet some wholesome, friendly programmers who share some of my interests.&lt;&#x2F;li&gt;
&lt;li&gt;I’d also like to take some time to look after myself. Doing a maths degree has left me feeling very tired and burned out, and I’m hoping RC will be the kick I need to get excited about making things again. On top of that, I’d like to try and improve my diet and get some more sunshine and exercise while I’m in my batch.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;So that’s my plan! I’m a big fan of working transparently, so I’ll make sure to post links to anything I’m working on in case anyone wants to look at the code. Plus, I’d like to write weekly updates on this blog; partly to keep myself accountable, but also so anyone can follow along. I’m so excited to get started, and to all the Recursers reading this, I can’t wait to meet you &amp;lt;3&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Deploying this blog on NixOS</title>
        <published>2024-08-08T00:00:00+00:00</published>
        <updated>2024-08-08T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Lucy McPhail
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucymcphail.com/posts/deploying-this-blog-on-nixos/"/>
        <id>https://lucymcphail.com/posts/deploying-this-blog-on-nixos/</id>
        
        <content type="html" xml:base="https://lucymcphail.com/posts/deploying-this-blog-on-nixos/">&lt;p&gt;For a while now I’ve been wanting to move this website from a managed service (in my case, sourcehut pages) to a server I run myself, and recently I got around to actually doing it. That said though, I really like the user experience these managed services provide: just &lt;code&gt;git push&lt;&#x2F;code&gt; your site and it gets automagically built and deployed for you.&lt;&#x2F;p&gt;
&lt;p&gt;I think I’ve managed to recreate that experience in NixOS, and I wanted to write down what I did for anyone else who’s curious.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;generating-the-site&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#generating-the-site&quot; aria-label=&quot;Anchor link for: generating-the-site&quot;&gt;generating the site&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;My blog is built with &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;getzola&#x2F;zola&quot;&gt;Zola&lt;&#x2F;a&gt;, a lightweight static site generator which comes as a single binary with no dependencies. As a result, it’s already very easy to build reproducibly; no faffing with &lt;code&gt;npm install&lt;&#x2F;code&gt;, just one &lt;code&gt;zola build&lt;&#x2F;code&gt; and we’re off to the races.&lt;&#x2F;p&gt;
&lt;p&gt;Still, there are a few reasons I like to build it with a Nix flake. Flakes are an experimental* feature of Nix which provide a standard format for declaring packages whose dependencies are pinned in a lock file. This is great for my site, since it lets me build it on any machine I like knowing that the &lt;code&gt;zola&lt;&#x2F;code&gt; binary is identical on each. It’s also handy for deployments, since the output of a flake gets added to the Nix store (&lt;code&gt;&#x2F;nix&#x2F;store&#x2F;&lt;&#x2F;code&gt;) each time it gets built, which means I can old builds are always around if I want to roll back.&lt;&#x2F;p&gt;
&lt;details&gt;&lt;summary&gt;*well, &#x27;experimental&#x27;&lt;&#x2F;summary&gt;
&lt;p&gt;Flakes have actually been around since 2019 and are mostly stable at this point, but there’s some controversy around them that hasn’t fully resolved itself yet. In short, the feature was implemented after &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;rfcs&#x2F;pull&#x2F;49&quot;&gt;the original RFC&lt;&#x2F;a&gt; was closed, which made some people feel like flakes were implemented without enough input from the community.&lt;&#x2F;p&gt;
&lt;p&gt;In the years since, a large ecosystem has sprung up around flakes, and I think it’s fair to say most of the community would like them to be stable. As of last year, there’s a &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;NixOS&#x2F;rfcs&#x2F;pull&#x2F;136&quot;&gt;new RFC&lt;&#x2F;a&gt; hoping to stabilize things, but for now flakes are still behind a feature flag.&lt;&#x2F;p&gt;
&lt;&#x2F;details&gt;
&lt;p&gt;Here’s the &lt;code&gt;flake.nix&lt;&#x2F;code&gt; for this site:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  description&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;A flake to generate my personal website&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  inputs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;    nixpkgs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;github:nixos&#x2F;nixpkgs?ref=nixos-unstable&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;    flake-utils&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;url&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;github:numtide&#x2F;flake-utils&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;  }&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  outputs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#E64553, #EE99A0);font-style: italic;&quot;&gt; self&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#E64553, #EE99A0);font-style: italic;&quot;&gt; nixpkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;,&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#E64553, #EE99A0);font-style: italic;&quot;&gt; flake-utils&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; }&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    flake-utils&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;lib&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;eachDefaultSystem&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#E64553, #EE99A0);font-style: italic;&quot;&gt;system&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#8839EF, #C6A0F6);&quot;&gt;      let&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;        pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; nixpkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;legacyPackages&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7287FD, #B7BDF8);&quot;&gt;system&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#8839EF, #C6A0F6);&quot;&gt;      in&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;      {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;        packages&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;site&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;stdenv&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;mkDerivation&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#8839EF, #C6A0F6);&quot;&gt; rec&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;          name&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;lucymcphail.com&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;          src&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#EA76CB, #F5BDE6);&quot;&gt; .&#x2F;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;          nativeBuildInputs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; [&lt;&#x2F;span&gt;&lt;span&gt; pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;zola&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;          buildPhase&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;zola build&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;          installPhase&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;cp -r public $out&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;        }&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;        defaultPackage&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; self&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;packages&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7287FD, #B7BDF8);&quot;&gt;system&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;site&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;        devShell&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;mkShell&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;          packages&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; [&lt;&#x2F;span&gt;&lt;span&gt; pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;zola&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;        }&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;      }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;    )&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Running &lt;code&gt;nix build&lt;&#x2F;code&gt; will build the site with Zola and then copy the output to &lt;code&gt;$out&lt;&#x2F;code&gt;, which Nix sets to somewhere inside &lt;code&gt;&#x2F;nix&#x2F;store&lt;&#x2F;code&gt;, and creates a symlink to it called &lt;code&gt;result&lt;&#x2F;code&gt; for convenience.&lt;&#x2F;p&gt;
&lt;p&gt;The other advantage of using flakes this way is that they can include local development shells, so I can preview my site locally without having to install Zola for my whole system. The &lt;code&gt;devShell&lt;&#x2F;code&gt; output lets you run &lt;code&gt;nix shell&lt;&#x2F;code&gt; to get a shell with Zola ready to use. Better still, I use &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;nix-community&#x2F;nix-direnv&quot;&gt;nix-direnv&lt;&#x2F;a&gt; to automatically load the development shell when I’m in the flake directory, so I can run &lt;code&gt;zola serve&lt;&#x2F;code&gt; without having to think about it.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;configuring-caddy&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#configuring-caddy&quot; aria-label=&quot;Anchor link for: configuring-caddy&quot;&gt;configuring caddy&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Now that we have the site content, it’s time to get it up on the server! I decided to try out caddy, and I’m very happy with it; the configuration language is simple, the documentation is good, and most impressive of all, it can automatically obtain and update your TLS certificates! NixOS provides a &lt;code&gt;services.caddy&lt;&#x2F;code&gt; module so it’s easy to set up. We’ll want to create a &lt;code&gt;www&lt;&#x2F;code&gt; group so that other users can write to &lt;code&gt;&#x2F;var&#x2F;www&lt;&#x2F;code&gt;, and make sure &lt;code&gt;&#x2F;var&#x2F;www&lt;&#x2F;code&gt; exists with &lt;code&gt;systemd.tmpfiles&lt;&#x2F;code&gt; (I’m not sure why, but this seems to be a common way to create folders with Nix, even if they’re not temporary). Then, we can enable caddy, tell Nix to add the automatically generated &lt;code&gt;caddy&lt;&#x2F;code&gt; user to the &lt;code&gt;www&lt;&#x2F;code&gt; group, and serve the site. We’ll add this to our server’s &lt;code&gt;&#x2F;etc&#x2F;nixos&#x2F;configuration.nix&lt;&#x2F;code&gt; (or wherever you keep your config):&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;users&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;groups&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;www&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; {&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;systemd&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;tmpfiles&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;rules&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;d &#x2F;var&#x2F;www 0775 caddy www&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;]&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;services&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;caddy&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  enable&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D20F39, #ED8796);&quot;&gt; true&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  group&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;www&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  extraConfig&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;    lucymcphail.com {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;      encode zstd gzip&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;      root * &#x2F;var&#x2F;www&#x2F;lucymcphail.com&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;      file_server&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;    }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;  &amp;#39;&amp;#39;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;One &lt;code&gt;nixos-rebuild switch&lt;&#x2F;code&gt; later and once caddy obtains some TLS certificates for us, we have a website! Of course, we could just update the contents of the site with &lt;code&gt;scp&lt;&#x2F;code&gt; and call it a day, but I think it’s much more fun to do it with git :)&lt;&#x2F;p&gt;
&lt;h1 id=&quot;running-a-git-server&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#running-a-git-server&quot; aria-label=&quot;Anchor link for: running-a-git-server&quot;&gt;running a git server&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Using git over &lt;code&gt;ssh&lt;&#x2F;code&gt; is actually shockingly easy: just run &lt;code&gt;git push &amp;lt;user&amp;gt;@&amp;lt;host&amp;gt;:&#x2F;path&#x2F;to&#x2F;git&#x2F;repo&lt;&#x2F;code&gt; and as long as the repository already exists on that machine, it should just work! There are a few things we can do to make it a bit nicer, though:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;users&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;users&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;git&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  isSystemUser&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D20F39, #ED8796);&quot;&gt; true&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  group&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;www&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  home&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&#x2F;srv&#x2F;git&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  createHome&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D20F39, #ED8796);&quot;&gt; true&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  shell&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;${&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7287FD, #B7BDF8);&quot;&gt;pkgs&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7287FD, #B7BDF8);&quot;&gt;git&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&#x2F;bin&#x2F;git-shell&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  openssh&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;authorizedKeys&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;keys&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);font-style: italic;&quot;&gt;    #&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);font-style: italic;&quot;&gt; your ssh key here&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;  ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So now we have a dedicated git user, complete with its own home directory for us to store our repos in. It’s also using the git shell as its login shell, which allows restricted access to git commands over ssh (see &lt;code&gt;man git-shell&lt;&#x2F;code&gt;). Now, if we create our repos in &lt;code&gt;&#x2F;srv&#x2F;git&lt;&#x2F;code&gt;, we can access them with &lt;code&gt;git@&amp;lt;host&amp;gt;:&amp;lt;repo-name&amp;gt;&lt;&#x2F;code&gt;.&lt;&#x2F;p&gt;
&lt;p&gt;It’s a good idea to use bare git repositories here, in case we have multiple users contributing. A bare git repo is the same as a regular one, but without a working tree; that is to say, a bare repo just contains the contents of the &lt;code&gt;.git&lt;&#x2F;code&gt; folder in a non-bare repo. This is perfect for a server, since otherwise we could end up with the working tree going out of sync and causing problems when we try to push.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bonus-git-frontend&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#bonus-git-frontend&quot; aria-label=&quot;Anchor link for: bonus-git-frontend&quot;&gt;bonus: git frontend&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;As an aside, we can also set up a git frontend so we can view our repositories from the web. I like &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;icyphox&#x2F;legit&quot;&gt;legit&lt;&#x2F;a&gt; for this, since it’s tiny and lightweight compared to larger software development services like gogs or gitea, and I don’t care about being able to edit files or create accounts. Once again, we can set it up using Nix:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;nix&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;services&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;legit&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  enable&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D20F39, #ED8796);&quot;&gt; true&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  user&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;git&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  settings&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;repo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;mainBranch&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; [&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;trunk&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;main&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; ]&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;  settings&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;repo&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);&quot;&gt;scanPath&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&#x2F;srv&#x2F;git&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;}&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;We’ll run it under the git user too, so that it can read our repos, and tell it where to read them from. I’m one of those strange people who likes using trunk as a branch name, so I’ll also tell legit to recognise that as a default branch. By default, it’ll serve on 127.0.0.1:5555.&lt;&#x2F;p&gt;
&lt;h2 id=&quot;building-the-site-with-a-hook&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#building-the-site-with-a-hook&quot; aria-label=&quot;Anchor link for: building-the-site-with-a-hook&quot;&gt;building the site with a hook&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Back to the website! We’ll create a new bare repo with &lt;code&gt;git init --bare &#x2F;srv&#x2F;git&#x2F;lucymcphail.com&lt;&#x2F;code&gt;, and now we can create a hook to build the site whenever new changes are pushed. Git hooks live inside the &lt;code&gt;hooks&lt;&#x2F;code&gt; folder (&lt;code&gt;.git&#x2F;hooks&lt;&#x2F;code&gt; in a non-bare repo), and usually there are a few example scripts in there in a new repo. Of particular interest to us is the &lt;code&gt;post-receive&lt;&#x2F;code&gt; hook, which runs whenever someone pushes to the server. Git hooks are just shell scripts, so we can build the site from there:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#EA76CB, #F5BDE6);font-style: italic;&quot;&gt;#!&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#EA76CB, #F5BDE6);font-style: italic;&quot;&gt;&#x2F;usr&#x2F;bin&#x2F;env bash&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D20F39, #ED8796);font-style: italic;&quot;&gt;set&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;eu&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;SITE&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;=&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;lucymcphail.com&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#8839EF, #C6A0F6);&quot;&gt;while&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#D20F39, #ED8796);font-style: italic;&quot;&gt; read&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; oldrev&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; newrev&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; ref&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#8839EF, #C6A0F6);&quot;&gt;do&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#8839EF, #C6A0F6);&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt; [[&lt;&#x2F;span&gt;&lt;span&gt; $&lt;&#x2F;span&gt;&lt;span&gt;ref&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt; =~&lt;&#x2F;span&gt;&lt;span&gt; .&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#179299, #8BD5CA);&quot;&gt;*&lt;&#x2F;span&gt;&lt;span&gt;&#x2F;trunk$ &lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;]]&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#7C7F93, #939AB7);&quot;&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#8839EF, #C6A0F6);&quot;&gt;        then&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D20F39, #ED8796);font-style: italic;&quot;&gt;                echo&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;Trunk received. Deploying...&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);font-style: italic;&quot;&gt;                mkdir&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;p&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &#x2F;tmp&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span&gt;SITE&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);font-style: italic;&quot;&gt;                git&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;-work-tree=&#x2F;tmp&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span&gt;SITE&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;-git-dir=&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span&gt;HOME&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span&gt;SITE&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; checkout&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;f&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; trunk&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                pushd &#x2F;tmp&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span&gt;SITE&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);font-style: italic;&quot;&gt;                nix&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; build&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &#x2F;tmp&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span&gt;SITE&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);font-style: italic;&quot;&gt;                rm&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;rf&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &#x2F;var&#x2F;www&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span&gt;SITE&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;*&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);font-style: italic;&quot;&gt;                cp&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;r&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;-no-preserve=mode&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; result&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;*&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &#x2F;var&#x2F;www&#x2F;&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span&gt;SITE&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                popd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#8839EF, #C6A0F6);&quot;&gt;        else&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#D20F39, #ED8796);font-style: italic;&quot;&gt;                echo&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;Ref &lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span&gt;ref&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; successfully received. Doing nothing: only trunk may be deployed on this server.&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#8839EF, #C6A0F6);&quot;&gt;        fi&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#8839EF, #C6A0F6);&quot;&gt;done&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;post-receive&lt;&#x2F;code&gt; hook gets called with a list of refs being passed through stdin, so we can read through them all to check if trunk has been pushed, since we don’t want to deploy the site from a feature branch. If there is an update to trunk, we’ll check out the repository into &lt;code&gt;&#x2F;tmp&#x2F;lucymcphail.com&lt;&#x2F;code&gt; and build it with Nix. Once Nix is done, it’ll store the site somewhere in &lt;code&gt;&#x2F;nix&#x2F;store&lt;&#x2F;code&gt; and symlink &lt;code&gt;&#x2F;var&#x2F;www&#x2F;lucymcphail.com&lt;&#x2F;code&gt; to it. Finally, caddy will start serving the updated version of the site. The whole deployment happens in just a few seconds. Here’s what that looks like:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;$ git push buttercup&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Pushing to buttercup:lucymcphail.com&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Writing objects: 100% (6&#x2F;6), 553 bytes | 553.00 KiB&#x2F;s, done.&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Total 6 (delta 5), reused 0 (delta 0), pack-reused 0 (from 0)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;remote: trunk received. deploying...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;remote: Already on &amp;#39;trunk&amp;#39;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;remote: &#x2F;tmp&#x2F;lucymcphail.com ~&#x2F;lucymcphail.com&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;remote: this derivation will be built:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;remote:   &#x2F;nix&#x2F;store&#x2F;xgij95fj95j4v9s1z2pfixajnx1ihrch-lucymcphail.com.drv&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;remote: building &amp;#39;&#x2F;nix&#x2F;store&#x2F;xgij95fj95j4v9s1z2pfixajnx1ihrch-lucymcphail.com.drv&amp;#39;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;remote: ~&#x2F;lucymcphail.com&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;To buttercup:lucymcphail.com&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;   0dffb7f..cdde960  trunk -&amp;gt; trunk&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;So that’s how this site runs! Updating it is really smooth now, just like github pages et al. but with significantly faster build times. I really like this setup so far, and everything being managed by Nix should mean it’s not too hard to maintain and I can roll it back if something breaks.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Emacs packages I like</title>
        <published>2024-07-29T00:00:00+00:00</published>
        <updated>2024-07-29T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Lucy McPhail
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucymcphail.com/posts/emacs-packages-i-like/"/>
        <id>https://lucymcphail.com/posts/emacs-packages-i-like/</id>
        
        <content type="html" xml:base="https://lucymcphail.com/posts/emacs-packages-i-like/">&lt;p&gt;During the time I’ve been using Emacs, a handful of packages have stood out to me as being particularly useful, reliable, and generally enjoyable to use. A few of them in particular, I like enough to write about :)&lt;&#x2F;p&gt;
&lt;h1 id=&quot;modus-themes&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#modus-themes&quot; aria-label=&quot;Anchor link for: modus-themes&quot;&gt;Modus themes&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;One of the first things I see when I start a new Emacs session is my colour theme, so we may as well start there. My theme of choice has been one of Protesilaos Stavrou’s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;protesilaos.com&#x2F;emacs&#x2F;modus-themes&quot;&gt;Modus themes&lt;&#x2F;a&gt; for almost as long as I’ve been using Emacs, and I’ve yet to find anything else that shares Prot’s attention to detail. The themes are designed with accessible readability in mind, so the contrast between the background and foreground colours is always high. On top of that, variants of both the dark and light themes are included which are optimised for users with red-green or blue-yellow colour deficiency.&lt;&#x2F;p&gt;
&lt;p&gt;Colours alone don’t make an Emacs theme though, and it’s everything else about the Modus themes that really shines. The highlighting is well thought out, with colours being used only where they’re useful; the coverage of third-party packages is great; and there are a staggering number of customisation options. They’re an unapologetically usable set of themes, and I love them for it.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;vertico-orderless-marginalia&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#vertico-orderless-marginalia&quot; aria-label=&quot;Anchor link for: vertico-orderless-marginalia&quot;&gt;Vertico&#x2F;orderless&#x2F;marginalia&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Lots of things in Emacs use the built-in &lt;code&gt;completing-read&lt;&#x2F;code&gt; function to get user input while completing from a list of candidates. The default behaviour is to provide an input field with bash-like tab-completions, but sometimes it’s nicer to show the list of candidates on screen and filter it with a fuzzy finder. Daniel Mendler’s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;minad&#x2F;vertico&quot;&gt;vertico&lt;&#x2F;a&gt;, for ‘VERTical Interactive COmpletion’, adds the first of these features, orderless and adds the second. For example, enabling &lt;code&gt;vertico-mode&lt;&#x2F;code&gt; will turn this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Find file: ~&#x2F;src&#x2F;some-project&#x2F;█&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;into this:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;*&#x2F;7    Find file: ~&#x2F;src&#x2F;some-project&#x2F;█&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Cargo.lock&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Cargo.toml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;LICENSE&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;README.md&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;src&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;target&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;.gitignore&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;where the list of files gets filtered down as you type. The benefit of vertico’s approach, using the &lt;code&gt;completing-read&lt;&#x2F;code&gt; mechanism instead of redefining each function, is that is plays nicely with other parts of Emacs. For example, Omar Antolín Camarena’s &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;oantolin&#x2F;orderless&quot;&gt;orderless&lt;&#x2F;a&gt; defines a &lt;em&gt;completion style&lt;&#x2F;em&gt; which tells Emacs how to filter and sort candidates, and vertico will use that ordering with no extra configuration. Orderless is a great companion to vertico, as it sorts completions based on how recently they were last used, and filters them fuzzily based on the input string (for example typing &lt;code&gt;car lo&lt;&#x2F;code&gt; will match &lt;code&gt;Cargo.lock&lt;&#x2F;code&gt;), making vertico act like the fuzzy finders in more modern editors. &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;minad&#x2F;marginalia&quot;&gt;Marginalia&lt;&#x2F;a&gt;, another of Daniel’s packages, improves vertico once again by showing extra details beside each candidate where possible: for example, showing file details in the file picker or function docstrings in &lt;code&gt;M-x&lt;&#x2F;code&gt;. For very minimal setup, these three packages vastly improve the completion experience in Emacs.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;project-el-and-magit&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#project-el-and-magit&quot; aria-label=&quot;Anchor link for: project-el-and-magit&quot;&gt;&lt;code&gt;project.el&lt;&#x2F;code&gt; and Magit&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Emacs’ built-in &lt;code&gt;project&lt;&#x2F;code&gt; package is a neat little feature that works right out of the box, but it’s far too useful not to mention here. It adds a handful of helpful functions for dealing with projects (which it automatically detects by looking for version controlled directories), of which I have a few favourites:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;C-x p p&lt;&#x2F;code&gt;: switch to a different project&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;C-x p f&lt;&#x2F;code&gt;: find files in project&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;C-x p c&lt;&#x2F;code&gt;: run &lt;code&gt;compile&lt;&#x2F;code&gt; in the project root&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;C-x p g&lt;&#x2F;code&gt;: project-wide grep&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;C-x p !&lt;&#x2F;code&gt;: execute a shell command from the project root&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;In particular, &lt;code&gt;C-x p f&lt;&#x2F;code&gt; in combination with vertico is a great fuzzy file finder, similar to Quick Open in Visual Studio Code or &lt;code&gt;fzf&lt;&#x2F;code&gt;&#x2F;telescope in (neo)vim.&lt;&#x2F;p&gt;
&lt;p&gt;Complementing &lt;code&gt;project&lt;&#x2F;code&gt;, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;magit.vc&#x2F;&quot;&gt;Magit&lt;&#x2F;a&gt; is one of the best Git frontends I’ve ever used anywhere, let alone for Emacs. As a testament to just how good it is, its developer, Jonas Bernoulli, started a Kickstarter campaign in 2017 asking for CHF 55 thousand to develop Magit full time for an entire year. He raised almost CHF 73 thousand. As a result, Magit is perhaps the most feature-complete Git frontend beside the Git command-line. It aims to look similar to the output of Git itself, but everything in a Magit buffer is actionable. You can stage and unstage files by pressing &lt;code&gt;s&lt;&#x2F;code&gt; and &lt;code&gt;u&lt;&#x2F;code&gt; in the status window, or rebase onto a particular commit by pressing &lt;code&gt;r&lt;&#x2F;code&gt; with the cursor on that commit in the log. Even if you don’t use Emacs for anything else, I still think Magit is worth a try on its own.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;eglot&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#eglot&quot; aria-label=&quot;Anchor link for: eglot&quot;&gt;Eglot&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;LSP is a relatively new addition to my setup compared to everything else here, since I was content with using keyword completion and tags for the features I needed – that is, until I tried &lt;code&gt;rust-analyzer&lt;&#x2F;code&gt;, and now writing Rust doesn’t feel the same without it. In Emacs, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;emacs-lsp&#x2F;lsp-mode&quot;&gt;&lt;code&gt;lsp-mode&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;joaotavora&#x2F;eglot&quot;&gt;&lt;code&gt;eglot&lt;&#x2F;code&gt;&lt;&#x2F;a&gt; seem to be the two most popular LSP implementations, the latter of which is included in Emacs 29 onwards. While &lt;code&gt;lsp-mode&lt;&#x2F;code&gt; seems to be the more feature-rich of the two, &lt;code&gt;eglot&lt;&#x2F;code&gt; takes an approach similar to Vertico of being minimal and building on core Emacs features wherever possible. As well as, in my opinion, feeling more responsive than &lt;code&gt;lsp-mode&lt;&#x2F;code&gt;, it also means any configuration you may already have for &lt;code&gt;flymake&lt;&#x2F;code&gt; or &lt;code&gt;completion-at-point-functions&lt;&#x2F;code&gt; will work identically with LSP features. In particular, &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;minad&#x2F;corfu&quot;&gt;Corfu&lt;&#x2F;a&gt; (once again from Daniel Mendler) works perfectly with &lt;code&gt;eglot&lt;&#x2F;code&gt; out of the box. I really don’t have much else to say about &lt;code&gt;eglot&lt;&#x2F;code&gt;; it just seems to work whenever needed, and stay out of the way when it isn’t.&lt;&#x2F;p&gt;
</content>
        
    </entry>
    <entry xml:lang="en">
        <title>Using an iPod after 20 years</title>
        <published>2024-06-14T00:00:00+00:00</published>
        <updated>2024-06-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Lucy McPhail
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://lucymcphail.com/posts/using-an-ipod-after-20-years/"/>
        <id>https://lucymcphail.com/posts/using-an-ipod-after-20-years/</id>
        
        <content type="html" xml:base="https://lucymcphail.com/posts/using-an-ipod-after-20-years/">


&lt;img
  src=&quot;https:&amp;#x2F;&amp;#x2F;lucymcphail.com&amp;#x2F;posts&amp;#x2F;using-an-ipod-after-20-years&amp;#x2F;ipod.jpg&quot;
  alt=&quot;an iPod mini, with the album listing for Electra Heart by Marina on the display&quot;
  title=&quot;an iPod mini, with the album listing for Electra Heart by Marina on the display&quot;
  width=&quot;3000px&quot;
  height=&quot;2000px&quot; &#x2F;&gt;
&lt;p&gt;I use an iPod mini, made in 2004, to listen to music every day. Why, when streaming makes music so much more convenient and accessible, would I choose to ignore two decades of technological progress? Well, there are two aspects to music streaming that I don’t find particularly satisfying.&lt;&#x2F;p&gt;
&lt;p&gt;The first is that I don’t agree with the business model of streaming. Letting users listen to an enormous catalogue for the price of a single record a month simply isn’t sustainable; it’s no coincidence that all of the major streaming services have either the backing of large companies like Google and Apple, or a large amount of VC funding, as in the case of Spotify. It also means musicians see much less money from streaming than they would from selling digital downloads or physical media, and as a musician myself I’m much more sympathetic to their cause than Spotify’s.&lt;&#x2F;p&gt;
&lt;p&gt;I also don’t love the way music is presented by streaming services. Often on Spotify I would find myself taking the path of least resistance and putting on a large playlist on shuffle, or listening to their automatically generated mixes, without much thought about what I was listening to. Maintaining my own collection of music and actively choosing what to play feels much more intentional, and I like that now my recommendations come from my friends instead of from an algorigthm.&lt;&#x2F;p&gt;
&lt;p&gt;This still doesn’t paint the complete picture, though: why carry around a 20 year old music player instead of just using the phone I already have to take with me anyway? Truthfully, part of the reason is nostalgia. I grew up listening to music on an iPod nano, and something about scrolling around with the clickwheel brings me a little bit of joy every time I use it. But it’s also nice having a device designed just for listening to music, instead of using my phone. It never pauses my music to let me know I have a message, and its always-on monochrome LCD lets me see the current track in direct sunlight.&lt;&#x2F;p&gt;
&lt;p&gt;With that said, there are still a few things we can do to bring an old iPod up to speed with modern music players. Part of the reason I chose the iPod mini specifically is that it’s relatively easy to get into compared to some newer models, and &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.ifixit.com&#x2F;Device&#x2F;iPod_Mini&quot;&gt;iFixit have a great guide&lt;&#x2F;a&gt; for taking them apart. Both the battery and the 4 GB hard drive that came with the iPod are starting to show signs of age, but both can be easily replaced. I swapped out the battery for a new one, and replaced the hard drive with an &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.iflash.xyz&#x2F;store&#x2F;sdcf-adapter&#x2F;&quot;&gt;SD to compact flash adapter from iFlash&lt;&#x2F;a&gt; and a spacious 256 GB SD card. With these modifications, this iPod is totally usable as my main music player.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;managing-a-music-library-with-beets&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#managing-a-music-library-with-beets&quot; aria-label=&quot;Anchor link for: managing-a-music-library-with-beets&quot;&gt;Managing a music library with beets&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Luckily, despite the rise of streaming, there are still plenty of ways to get digital music. Lots of indie musicians sell digital downloads on &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;bandcamp.com&#x2F;&quot;&gt;Bandcamp&lt;&#x2F;a&gt;, and some even let you buy CDs, vinyl, or cassettes if that’s more your style. For music more than a few years old, ripping second hand CDs is a great way to get high quality files and support local record shops in the process. The difficult part is organising files from all these disparate sources, and making sure they have consistent metadata.&lt;&#x2F;p&gt;
&lt;p&gt;Fortunately for us, the team behind &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;beets.readthedocs.io&#x2F;en&#x2F;stable&#x2F;&quot;&gt;beets&lt;&#x2F;a&gt; already thought of that! Beets is a handy little program that takes care of renaming files and sorting them into a sensible structure, as well as tagging each file with the correct metadata, and much more if you use the included plugins. I won’t explain how it works here, because the docs already do a great job of that, but I highly recommend it if you have a large library of music library you need to keep track of.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;switching-to-rockbox&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#switching-to-rockbox&quot; aria-label=&quot;Anchor link for: switching-to-rockbox&quot;&gt;Switching to rockbox&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;While I do enjoy the default firmware that ships with the iPod mini, partly for nostalgic reasons and partly because I find it elegantly simple, it does have a few notable shortcomings. For me, the most important of them are:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;not being able to play music in formats like vorbis, opus, and FLAC;&lt;&#x2F;li&gt;
&lt;li&gt;requiring the use of iTunes to transfer music; and&lt;&#x2F;li&gt;
&lt;li&gt;not understanding the ‘album artist’ id3 tag, which means albums featuring guest artists sometimes get split up (for example, ‘Panic! at the Disco feat. LOLO’ is considered its own artist, so &lt;em&gt;Miss Jackson&lt;&#x2F;em&gt; doesn’t get included in &lt;em&gt;Too Weird to Live, Too Rare to Die&lt;&#x2F;em&gt; if you search by artist).&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Rockbox is a community-made firmware for a collection of different music players, including my iPod mini. While it does have its own quirks, and feels a lot less simple than the stock firmware, it does fix each of my issues. It has robust support for modern codecs and gapless playback, it lets you add music just by copying it over USB, and it handles album artists properly.&lt;&#x2F;p&gt;
&lt;p&gt;Installing rockbox was easy using their &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.rockbox.org&#x2F;wiki&#x2F;RockboxUtility&quot;&gt;automated installer&lt;&#x2F;a&gt;, and getting it set up was painless. I still don’t like the way it looks and feels as much as the original firmware, and I haven’t tested it rigorously, but it seems like the battery isn’t lasting quite as long. Nevertheless, I do like rockbox enough to keep using it daily.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;aside-syncthing&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#aside-syncthing&quot; aria-label=&quot;Anchor link for: aside-syncthing&quot;&gt;Aside: syncthing&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;Unfortunately, using a 20 year old music player with third-party firmware isn’t the most stable thing. A few times now, I’ve pulled my iPod out of my bag only to find that the battery is completely empty despite it being plugged in the night before, and naturally these days it’s hard to find people with a 30-pin cable to borrow.&lt;&#x2F;p&gt;
&lt;p&gt;So, then, it’s useful to have a backup music player with me. For reasons beyond my understanding, my phone, despite being 20 years newer and several times more expensive, has neither expandable storage nor a headphone jack. Nonetheless, it will suffice as a replacement music player. Since I was already using it, syncthing seemed like a natural choice to keep my library up to date with the copy on my computer. Syncthing is a wonderful piece of software: simply set it up on each of the devices you want to sync between, and it’ll diligently keep whichever folders you choose in sync across each device, over LAN or the Internet. The upshot is all I need to do to add new music is run &lt;code&gt;beet import&lt;&#x2F;code&gt; on my laptop to add it to my library, and syncthing will automatically copy it to my phone. Ultimately, I still much prefer the experience of using a dedicated device for playing music, but it’s good to know I have a backup for whenever it decides not to work.&lt;&#x2F;p&gt;
&lt;h1 id=&quot;recreating-apple-s-sync&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#recreating-apple-s-sync&quot; aria-label=&quot;Anchor link for: recreating-apple-s-sync&quot;&gt;Recreating Apple’s sync&lt;&#x2F;a&gt;&lt;&#x2F;h1&gt;
&lt;p&gt;One thing I think Apple absolutely nailed about the iPod was the experience of syncing with iTunes. Being able to plug an iPod into your computer and have it automatically sync any new music is really convenient. Using rockbox, adding new music is just a matter of copying files, but wouldn’t it be nice if we could have the computer check which files are missing and just copy those? Well, that’s exactly what &lt;code&gt;rsync&lt;&#x2F;code&gt; does! I have this script to copy my library onto my iPod’s default mountpoint on my system:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo&quot; style=&quot;color-scheme: light dark; color: light-dark(#4C4F69, #CAD3F5); background-color: light-dark(#EFF1F5, #24273A);&quot;&gt;&lt;code data-lang=&quot;shellscript&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#EA76CB, #F5BDE6);font-style: italic;&quot;&gt;#!&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#EA76CB, #F5BDE6);font-style: italic;&quot;&gt;&#x2F;usr&#x2F;bin&#x2F;env bash&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#1E66F5, #8AADF4);font-style: italic;&quot;&gt;rsync&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;a&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;-progress&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt; -&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;-size-only&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#EA76CB, #F5BDE6);&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;	  &amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;$&lt;&#x2F;span&gt;&lt;span&gt;HOME&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&#x2F;Music&#x2F;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#EA76CB, #F5BDE6);&quot;&gt; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;	  &amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&#x2F;media&#x2F;lucy&#x2F;LUCY&amp;#39;S IPOD&#x2F;Music&lt;&#x2F;span&gt;&lt;span style=&quot;color: light-dark(#40A02B, #A6DA95);&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The &lt;code&gt;-a&lt;&#x2F;code&gt; flag runs &lt;code&gt;rsync&lt;&#x2F;code&gt; in archive mode, so it will copy the entire directory tree, and I use &lt;code&gt;--size-only&lt;&#x2F;code&gt; here to skip files that match in size since the iPod’s USB interface is pretty slow, so not having to do a checksum to see if each file has changed is a significant speedup.&lt;&#x2F;p&gt;
&lt;p&gt;I really like having a dedicated device for playing music. I can see myself switching to another device at some point, in particular the &lt;a rel=&quot;external&quot; href=&quot;https:&#x2F;&#x2F;www.crowdsupply.com&#x2F;cool-tech-zone&#x2F;tangara&quot;&gt;tangara&lt;&#x2F;a&gt; looks really promising, but I don’t think I’ll be going back to streaming as my main form of music consumption any time soon.&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
