背景
Nix Flakes 入門 nix build
& nix run
編です。このブログはシェルスクリプトでビルドしていますが、 nix build
コマンドでビルドできるように、簡単な flake.nix
を作成しました。これで reproducible なブログ (html) になったかと思います。
実装
以下では Nix Flakes の環境構築を前提に、 flake.nix
を作って行きます。
Nix Flakes の書き方
Nix Flakes を使って純粋な環境でビルドするためには、 Flake Outputs
の packages
を定義します。 flake-utils
のおかげで、 system 名 (x86_64_linux 等) に惑わされず、簡潔に flake.nix
を作成できます:
{
description = "A basic flake for my devlog";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs =
{ nixpkgs, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = import nixpkgs { inherit system; };
in
{
# nix build の対象一覧
packages = {
# nix build .#devlog でビルドできる:
devlog = pkgs.stdenvNoCC.mkDerivation {
# 後述
};
};
}
);
}
参考:
Hello, nix build
!
packages
中に devlog
パッケージを記載してみます:
# nix build .#devlog でビルドできる:
devlog = pkgs.stdenvNoCC.mkDerivation {
name = "devlog";
src = ./.; # 1
nativeBuildInputs = []; # 2
buildPhase = '' # 3
ls > ls.txt
pwd > pwd.txt
'';
installPhase = '' # 4
mkdir -p $out
mv ls.txt $out/
mv pwd.txt $out/
'';
};
};
flake.nix
全文
{
description = "A basic flake for my devlog";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs =
{ nixpkgs, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = import nixpkgs { inherit system; };
in
{
# nix build の対象一覧
packages = {
# nix build .#devlog でビルドできる:
devlog = pkgs.stdenvNoCC.mkDerivation {
name = "devlog";
src = ./.;
nativeBuildInputs = [];
buildPhase = ''
ls > ls.txt
pwd > pwd.txt
'';
installPhase = ''
mkdir -p $out
mv ls.txt $out/
mv pwd.txt $out/
'';
};
};
}
);
}
nix build
は隔離された環境で実行されます。
- 1
src
で隔離環境に持っていく (コピーする) ファイルを指定します。 Git 管理外のファイルはコピーされないため、手元のキャッシュファイル等は無視されます。 - 2 隔離環境で必要なパッケージがあれば
nativeBuildInputs
かbuildInputs
で指定します。 - 3 Build phase では必要なファイルを生成します。今はダミーファイルを作っています。
- 4 Install phase で build phase の成果物を
/nix/store
下に移動します。$out
変数は Nix 側が注入してくれます。
devlog
package を nix build <path>#<package>
の形でビルドできます:
$ git add flake.nix
$ nix build .#devlog
現ディレクトリ下に install phase の $out
ディレクトリへの symlink (result
) が生成されており、 install phase で保存したファイルを確認できます:
$ ls -lA result
lrwxrwxrwx 1 tbm 50 May 10 06:33 result -> /nix/store/71c50cjbmairbhv20mar337i1jrg4iyg-devlog/
$ ls result/
ls.txt pwd.txt
$ cat result/ls.txt # Git 管理化のファイルのみが見える
~~省略~~
$ cat result/pwd.txt
/build/2mxrczxdffffd75aj2wgz7mml9mzww8g-source
なお /build
ディレクトリは存在せず、実際は /tmp
の一時ディレクトリで build phase が実行されるようです。この辺りの sandbox 環境は、今はブラックボックスとしておきます:
$ bin/
bin/ boot/ d/ dev/ etc/ home/ lib/ lib64/ lost+found/ media/ nix/ opt/ proc/ root/ run/ srv/ sys/ tmp/ usr/ var/
Devlog をビルドする
仮置きの buildPhase
と installPhase
を書き換えて、実際に devlog をビルドします:
devlog = pkgs.stdenvNoCC.mkDerivation {
name = "devlog";
src = ./.;
nativeBuildInputs = with pkgs; [ # 1
(emacs.pkgs.withPackages (epkgs: with epkgs; [ seq esxml ]))
nodePackages.prettier
];
buildPhase = ''
export HOME="$(mktemp -d)" # 2
emacs -Q --script "./build.el" -- "--release" # 3
prettier --print-width 100 --write out/*.html out/diary/*.html
'';
installPhase = ''
mkdir -p $out
mv out $out/site
'';
};
- 1: ビルドに必要な依存を書きます。
- 2: 隔離環境では
HOME
変数が存在しないディレクトリ (/homeless-shelter
) を指すため、 Emacs がファイル生成できるように一時ディレクトリを設定しました。 - 3: HTML の生成方法は、ビルドスクリプトを呼び出すだけです。
nix build
の使い道
nix build
ではキャッシュが活かせないため、開発環境ではすべての記事をビルドすることになって無駄です。開発中は devShell を使ったり、ビルド用コマンドを flake.nix
で定義して、 nix run
でユーザ環境のファイルを直接読み書きしたほうが良いかもしれません。
追記: nix run
用の定義を作成しました。 packages
の隣に置くと、 nix run .#build
で実行できます:
apps.build = flake-utils.lib.mkApp {
drv = pkgs.writeShellApplication {
name = "build";
runtimeInputs = with pkgs; [
(emacs.pkgs.withPackages (epkgs: with epkgs; [ seq esxml ]))
nodePackages.prettier
];
text = ''
emacs -Q --script "./build.el" -- "--release"
prettier --print-width 100 --write out/*.html out/diary/*.html
'';
};
};
ビルドコマンドを apps.build
と packages.devlog
の間でDRY すると、 flake.nix
全体はこうなります:
flake.nix
{
description = "A basic flake for my devlog";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
flake-utils.url = "github:numtide/flake-utils";
};
outputs =
{ nixpkgs, flake-utils, ... }:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = import nixpkgs { inherit system; };
buildCommand = pkgs.writeShellApplication { # 1
name = "buildCommand"; # 2
runtimeInputs = with pkgs; [
(emacs.pkgs.withPackages (epkgs: with epkgs; [ seq esxml ]))
nodePackages.prettier
];
text = ''
emacs -Q --script "./build.el" -- "--release"
prettier --print-width 100 --write out/*.html out/diary/*.html
'';
};
in
{
apps.build = flake-utils.lib.mkApp {
drv = buildCommand; # 3
};
packages = {
devlog = pkgs.stdenvNoCC.mkDerivation {
name = "devlog";
src = ./.;
nativeBuildInputs = with pkgs; [
buildCommand # 4
];
buildPhase = ''
export HOME="$(mktemp -d)"
buildCommand # 5
'';
installPhase = ''
mkdir -p $out
mv out $out/out
'';
};
};
}
);
}
GitHub Actions のセットアップ
手元で nix build
を使う意味が無かったので、 GitHub Actions で使うことにします。参考:
name: "Main"
# main branch への push 時に実行
on:
push:
branches: 'main'
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v31
- name: Build the devlog
run: nix build .#devlog
- name: Upload devlog artifact # 1
uses: actions/upload-pages-artifact@v3
with:
path: result # 2
deploy:
name: Deploy
runs-on: ubuntu-latest
needs: build
# Grant GITHUB_TOKEN the permissions required to make a Pages deployment
permissions:
pages: write # to deploy to Pages
id-token: write # to verify the deployment originates from an appropriate source
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4 # 3
- 1:
actions/upload-pages-artifact
で GitHub Pages 専用の artifact を作成します - 2:
nix build
の出力 (への symlink) を指定しています - 3:
actions/deploy-pages
が deploy してくれます
まとめ
nix build
を使い、隔離された環境で devlog をビルドできるようになりました。 nix build
は、 PATH はもちろん、ファイルシステムとしても隔離された環境で実行されることが認識できました。
ローカルでは nix build
の使い道が無かったため、 GitHub Actions に利用してみました。 html の diff はローカルで見ちゃえば良いかと思います。 cachix-action
を使えば高速化できそうですが、今は導入していません。
この記事は GitHub Actions によってデプロイされる予定です。