Edit and public Nix Blog article

This commit is contained in:
Danila Fedorin 2022-04-10 13:04:03 -07:00
parent fe1c05fe46
commit 6145d9e804

View File

@ -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!