前の記事の続編です。blog.anqou.net のビルドを Nix に載せました。
#モチベーション
blog.anqou.net は、各記事の一番下に表示されているように、soupault という OCaml 製のツールを使って生成されています。soupault 自体は HTML タグをパーズして Lua を使って好きに書き換えてから出力するという機能を持った静的サイトジェネレータ(SSG)になっていて、入力する HTML ファイルは外部コマンドを起動して生成させることができます。この機能を使い、例えば記事の内容を書いてある Markdown は cmark-gfm を起動して HTML に変換し、あるいはコードブロックは Shiki(を使う Node.js の自作プログラム)を起動して HTML に変換し、それぞれ適切に変形して最終的な HTML ファイルに組み込んでいます。より詳細な話は過去に書いた記事を参考にしてください。
外部コマンドを適宜起動して処理を行うという soupault の仕組み上、blog.anqou.net のビルドを行う環境には、cmark-gfm などのビルドに必要なコマンドを予めインストールしておく必要があります。これを愚直にやるのは面倒なので、いままでは Docker を使っていました。つまり Dockerfile で soupault を含めた必要なツールを全てインストールするようにしておき、blog.anqou.net をビルドする際にはこの Docker イメージを通してビルドを行っていました。この方法は普通に動くのですが、Dockerfile を正しく書くのは結構面倒なのと、コンテナの中に入力ファイルを見せる必要があったりして、細かいところで色々と面倒でした。また、ビルド自体には使わないものの、記事を書く際に必要なソフトウェア(具体的には http-server や inotifywait など)を別で入れる必要もありました。
そこで、最近 NixOS に乗換えたこともあり、このブログビルド環境を Nix に載せ替えてみました。Nix であればコンテナ周りの面倒とは無縁ですし、nixpkgs にすでにあるソフトウェアを自分でビルドする必要もありません。また、Nix Flake の devShell の仕組みを使えば、記事を書くときにだけ使う(ビルド自体には使わない)ソフトウェアを nixpkgs から導入し、envdir を経由してブログ用のディレクトリの下でだけ有効化することもできます。
#実装
Nix Flake を使って環境を整えます。blog.anqou.net のビルドに必要な soupault・cmark-gfm は共に nixpkgs でパッケージングされているので、これをそのまま使います。nix flake init
を打つとスケルトンの flake.nix を作ってくれるので、これをベースに以下のような flake.nix を書きます:
{
description = "A very basic flake";
inputs = {
# nixpkgs には nixos-24.11 のブランチを採用。
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-24.11";
# Shiki を使った自作 highlight コマンドも Nix Flake 管理にして
# ここで参照する。inputs.nixpkgs.follows を指定することで、
# 同じ nixpkgs のレビジョンを見るようにする。
highlight = {
url = "github:ushitora-anqou/highlight";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs = {
self,
nixpkgs,
highlight,
}: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages."${system}";
lib = nixpkgs.lib;
nodejs = pkgs.nodejs_23;
pnpm = pkgs.pnpm_10;
in {
# Nix ファイル用のフォーマッタの設定。nix fmt で動く。
formatter."${system}" = pkgs.alejandra;
packages."${system}" = rec {
# メインの部分。blog.anqou.net をビルドするのに必要な依存パッケージと
# ビルドの手順を詰め込んだ derivation を作る。default に書いたものが
# nix build コマンドで実行される。
default = pkgs.stdenv.mkDerivation rec {
name = "blog.anqou.net";
phases = "buildPhase"; # buildPhase だけ動けば十分。
nativeBuildInputs =
# nixpkgs に存在する soupault と cmark-gfm を入れておく。
(with pkgs; [coreutils soupault cmark-gfm])
# 加えて 自作の highlight コマンドを入れる。
++ [highlight.packages."${system}".default];
PATH = lib.makeBinPath nativeBuildInputs;
src = ./.;
# src に指定したディレクトリが $src にあるので、そこへ cd して
# soupault を起動する。出力は $out に作る必要がある。
builder = pkgs.writeShellScript "builder.sh" ''
cd $src
soupault --build-dir $out
'';
};
};
# 開発時(つまり記事執筆時)に必要なパッケージを devShells に書く。
# nix develop すると起動できる他、envdir を設定しておくと、
# flake.nix と同じディレクトリに cd するだけで勝手に使えるようになる。
devShells."${system}" = rec {
default = pkgs.mkShell {
packages =
[nodejs pnpm]
++ (with pkgs; [
gnumake
http-server
inotify-tools
]);
};
};
};
}
以上のような flake.nix を書いたうえで nix build
を打つと soupault が起動して HTML の生成を行ってくれます。結果は(Nix ではおなじみの)result
ディレクトリに入ります。あとはこの result
ディレクトリの中身を http-server
でローカルにホストすれば、好きなブラウザから記事を見ることができます。また GitHub Action も nix build
を使うように設定しなおしておいたので、git push
すると GHA 上で nix build
が走り、その結果が Cloudflare Pages を通して blog.anqou.net でホストされるようになりました。
よかったよかった……と書きかけて思い出したのですが、実は blog.anqou.net は素の cmark-gfm ではなく、日本語用にカスタムしたものを使っているのでした(この記事を書き始めるまで完全に忘れていました)。詳細は以前書いたブログ記事を参照してください。
ということで、このパッチを当てた cmark-gfm を使うように改造します。といってもやることは簡単で、nixpkgs の cmark-gfm を override して src
をカスタムの cmark-gfm の GitHub レポジトリに向けるだけです。こんな形で簡単にパッチを当てられるのが Nix の使い勝手のいいところ(の一つ)です:
# 諸々省略
outputs = {
self,
nixpkgs,
highlight,
}: let
# ...
# overrideAttrs を使って cmark-gfm の src を
# カスタム cmark-gfm の GitHub レポジトリに向ける。
cmark-gfm = pkgs.cmark-gfm.overrideAttrs (finalAttrs: previousAttrs: {
src = pkgs.fetchFromGitHub {
owner = "ushitora-anqou";
repo = "cmark-gfm";
rev = "8ec199d8a3665a40c13f732ce4c24c28a500193e";
sha256 = "sha256-2EdfoXDvkLoagkVQsm0+Hb73tfxQOwSDrmvha2sQ0Yo=";
};
});
in {
# ...
packages."${system}" = rec {
default = pkgs.stdenv.mkDerivation rec {
# ...
# nixpkgs にあるものではなく、
# カスタムの cmark-gfm を使うようにする。
nativeBuildInputs =
(with pkgs; [coreutils soupault])
++ [cmark-gfm highlight.packages."${system}".default];
# ...
};
};
# ...
}
ということで、無事 blog.anqou.net は Nix で完全にビルドできるようになりました。よかったよかった。