From 6145d9e80409c3dc519fff122cd3b7397c447bfc Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Sun, 10 Apr 2022 13:04:03 -0700 Subject: [PATCH] Edit and public Nix Blog article --- content/blog/blog_with_nix.md | 45 +++++++++++++++++------------------ 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/content/blog/blog_with_nix.md b/content/blog/blog_with_nix.md index 60fedcd..7513993 100644 --- a/content/blog/blog_with_nix.md +++ b/content/blog/blog_with_nix.md @@ -1,7 +1,6 @@ --- title: "Declaratively Deploying Multiple Blog Versions with NixOS and Flakes" date: 2022-04-10T00:24:58-07:00 -draft: true tags: ["Hugo", "Nix"] --- @@ -40,21 +39,21 @@ I wanted to be able to represent all of this complexity in my NixOS configuratio that's what this post is about! ### Why Flakes -This article is about using Nix flakes to manage my configuration. But what is it that made -me use flakes? Well, two things: +I decided to use Nix flakes to manage my configuration. But what is it that made +me do so? Well, two things: * __Adding custom packages__. The Nix code for my blog provides a package / derivation for each - version of my website, and I want to use these packages in my `configuration.nix` so that - I can point various Nginx virtual hosts to each of them. This is typically done using + version of my website, and I want to use these packages in my `configuration.nix`. Adding + custom packages is typically done using overlays; however, how should my system configuration get my overlay Nix expression? I would like to be able to separate my build-the-blog code from my describe-the-server code, and so I need a clean way to let my system access the former from the latter. - flakes solve this issue by letting me specify a blog flake, and pull it in as one - of the inputs. -* __Versioning__. My process for deploying new versions of the site prior to flakes boiled down to fethcing + Flakes solve this issue by letting me specify a blog flake, and pull it in as one + of the system configuration's inputs. +* __Versioning__. My process for deploying new versions of the site prior to flakes boiled down to fetching the latest commit from the `master` branch of my blog repository, and updating the `default.nix` - file with that commit. This way, I could reliably fetch the version of my site that - I want published. Flakes do the same thing: the `flake.lock` file + file with the corresponding hash. This way, I could reliably fetch the version of my site that + I wanted published. Flakes do the same thing: the `flake.lock` file contains the hashes of the Git-based dependencies of a flake, and thus prevents builds from accidentally pulling in something else. However, unlike my approach, which relies on custom scripts and extra tools such as `jq`, the locking mechanism used by flakes is provided with @@ -71,7 +70,7 @@ it's very clear from the configuration what I want from my server: three virtual hosts, one with HTTPS, one with drafts, and one with drafts and _in Russian_. Second, there's plenty of code reuse. I'm using two builder functions, `english` and `russian`, but under the hood, the exact same code is being -used to run Hugo and all the necessary post-processing. Finally, all of this can be +used to run Hugo and perform all the necessary post-processing. Finally, all of this can be used pretty much immediately given my blog flake, which reduces the amount of glue code I have to write. @@ -90,7 +89,7 @@ There are a few things here: * On line 7, the settings `src`, `ssl`, and `host` are inherited into the derivation. The `src` setting provides a handle on the source code of the blog. I haven't had much time to test and fine-tune the changes enabling multi-language support on the site, - so they reside on a separate branch. It's up to the caller to provide which version of the + so they reside on a separate branch. It's up to the caller to specify which version of the source code should be used for building. The `host` and `ssl` settings are interesting because __they don't actually matter for the derivation itself__ -- they just aren't used in the builder. However, attributes given to a derivation are accessible from "outside", @@ -98,15 +97,15 @@ There are a few things here: * Lines 10 through 14 deal with setting the base URL of the site. Hugo does not know how to interpret the `--baseURL` option when a blog has multiple languages. What this means is that in the end, - it is impossible to configure the base URL used in links from the command line, - and I need to apply some manual changes to the configuration file. I need to be able to adjust - the base URL becasue each version of my website is hosted in a different place: the default (english) + it is impossible to configure the base URL used in links from the command line. + I need to apply some manual changes to the configuration file. It's necessary to adjust + the base URL because each version of my website is hosted in a different place: the default (English) website is hosted on `danilafe.com`, the version with drafts on `drafts.danilafe.com`, and so on. However, the configuration file only knows one base URL per language, and so it _doesn't_ know when or when not to use the `drafts.` prefix. The `urlSub` variable is used in the builder. * On line 15, the `publicPath` variable is set; while single-language Hugo puts all the generated HTML into the `public` folder, the multi-language configuration places it into `public/[language-code]`. - Thus, depending on the configuration, the builer needs to look in a different place for final output. + Thus, depending on the configuration, the builder needs to look in a different place for final output. This new `website` function is general enough to represent all my blog versions, but it's too low-level. Do I really want to specify the `publicPath` each time I want to describe a version of the site? @@ -161,7 +160,7 @@ is the same as `replaceUrl.to` (since I'd want the Nginx virtual host for a blog version to handle links within that version). The `ssl` ghost parameter corresponds precisely to whether or not a virtual host will need SSL (and thus ACME, and thus the `systemd` setting). For each derivation built using `website`, I can access -the attributes like `ssl` or `host` to generate the corresponding piece of the Nginx configuation. +the attributes like `ssl` or `host` to generate the corresponding piece of the Nginx configuration. To make this _really_ nice, I wanted all of this to be "just another section of my configuration file". That is, I wanted to control my site deployment via regular @@ -220,7 +219,7 @@ We now have two "things" that handle the deployment of the blog: the builder functions `english` and `russian` which help describe various blog versions, and the NixOS module that configures the server's Nginx to serve said versions. We now want to expose these to the NixOS system configuration, -which describes the entire server. I decided to do this using a flake. +which describes the entire server. This is where flakes finally come in. [Yanik Sander](https://blog.ysndr.de/posts/internals/2021-01-01-flake-ification/index.html) wrote up a pretty comprehensive explanation of how their blog is deployed using flakes, which I often consulted while getting started -- check it out if you are looking for more details. @@ -263,8 +262,8 @@ The flake `output` schema provides a standard option for exposing modules, `nixo exposing my `module.nix` file from the flake is simply a matter of importing it, as on line 31. There is, however, no standard way for exposing a _function_. The good news is that any attribute defined on a flake is accessible from code that imports that flake. Thus, I simply -added a `buildersFor` function, which, given an operating system, fetches the `nixpkgs` collection -and LaTeX builder script for that system, and feeds them to the file that defines the `english` +added a `buildersFor` function, which fetches the `nixpkgs` collection +and LaTeX builder script for a given system, and feeds them to the file that defines the `english` and `russian` builders. This `buildersFor` function also provides the builders with the two different blog sources they reference, `blog-source` and `blog-source-localized`. @@ -276,7 +275,7 @@ Finally, the last little bit on lines 32 through 34 defines a default package fo is the package that is built if a user runs `nix build .#`. This isn't strictly necessary for my purposes, but it's nice to be able to test that the builders still work by running a test build. The `eachDefaultSystem` function generates a `defaultPackage` attribute for each of the "default" -operating systems, so that the package is buildable on more than just my server architecture. +systems, so that the package is buildable on more than just my server architecture. And that's it for the blog flake! I simply push it to Git, and move on to actually _using_ it from elsewhere. @@ -293,12 +292,12 @@ this can be done using the `specialArgs` attribute. The whole `flake.nix` file i {{< codeblock "Nix" "server-config/flake.nix" >}} -Finally, in `configuration.nix`, taking `builders` as one of the inputs, I finally write what you saw above: +Finally, in `configuration.nix`, taking `builders` as one of the inputs, I write what you saw above: {{< codelines "Nix" "server-config/configuration.nix" 42 59 >}} ### Wrapping Up -So there you have it, a flake-based blog deployment written in a declarative style. You can +So there you have it, a flake-based multi-version blog deployment written in a declarative style. You can check out both my [system configuration flake](https://dev.danilafe.com/Nix-Configs/server-config) and my [blog flake](https://dev.danilafe.com/Nix-Configs/blog-static-flake) on my Git server. If you want more, check out the articles by Xe and Yannik linked above. Thanks for reading!