I used Nix flakes to customize the version of Hugo for a development shell.
I didn’t realize I was going to have to override Nix’s built-in buildGoModule
function.
I’ve been
learning
Nix for about a month,
and it’s been an interesting experience.
Despite all the documentation surrounding Nix,
I’ve found I learn the most by looking at the Nix codebase or other people’s flakes on GitHub.
After learning how to use nix shell
to drop into a development environment for this blog,
the next logical step seemed to write a flake that codified this and would allow me to simply type nix develop
.
nix shell
solved the initial problem for me, but it installs whatever version of Hugo is found in the nixpkgs-unstable
branch.
I want to always install the same version of Hugo as the one I use for CI to create the blog before it is published.
The Hugo package in Nix is derived using buildGoModule
, which makes sense since Hugo is Go application.
All I needed to do was to tweak the version and fetchFromGitHub
attributes within the function.
My initial attempt at this was to use
overrideAttrs
.
I wasn’t able to get this to work,
and I think it was because buildGoModule
is more than mkDerivation
,
which is what overrideAttrs
modifies.
Instead, I used an
overlay
to
override
Hugo’s arguments,
and Hugo’s only argument is the buildGoModule
function.
It feels kind of crazy, but it works!
Below is the full flake for my blog.
The highlighted lines say it all :).
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
| {
description = "My blog";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem
(system:
let
overlays = [
# Override Hugo's package to use the version used by the blog. Note:
# using whatever version is provided by `nixpkgs` would probably be
# fine, but I'm doing this to learn about nix and flakes :).
#
# `final` is the result of the fix point calculation. Use it to
# access packages that could be modified somewhere else in the
# overlay stack. `prev` is one overlay down in the stack (and base
# nixpkgs for the first overlay). Use it to access the package
# recipes you want to customize and for library functions.
(final: prev: {
hugo = prev.hugo.override {
# The Hugo package is built using the built-in function
# `buildGoModule`. We need to override it to update the version
# of Hugo. We do this by overriding the `buildGoModule` function
# used by the package derivation with a function that calls the
# built-in `buildGoModule` with the output of a lambda function
# that returns the previous args updated with some new values.
# *dizzy*
#
# Therefore, when hugo's `buildGoModule` is called with its
# currently defined attribute set, we intercept it and update
# its attribute set before calling `buildGoModule`.
#
# This is crazy, but it works.
buildGoModule = previousArgs: prev.buildGoModule (previousArgs // rec {
version = "0.121.2";
# `fetchFromGitHub` is a built-in function, hence `prev` is
# used to access it
src = prev.fetchFromGitHub {
owner = "gohugoio";
repo = "hugo";
rev = "refs/tags/v${version}";
# Leave blank, run `nix develop`, and use the correct hash
# from the resulting error message.
hash = "sha256-YwwvxkS+oqTMZzwq6iiB/0vLHIyeReQi76B7fCgqtcY=";
};
});
};
})
];
pkgs = import nixpkgs {
inherit system overlays;
};
in
with pkgs;
{
devShells.default = mkShell {
packages = [
brotli
gzip
hugo
];
};
}
);
}
|
Fin.