From a5d834a4dcf0a0d372406745c8e3266e1cdc28bb Mon Sep 17 00:00:00 2001 From: gotomts Date: Sun, 30 Aug 2026 00:07:10 +0900 Subject: [PATCH] =?UTF-8?q?docs(nix):=20experimental=20features=E3=81=AE?= =?UTF-8?q?=E5=91=BC=E3=81=B3=E5=87=BA=E3=81=97=E5=A5=91=E7=B4=84=E3=82=92?= =?UTF-8?q?=E6=98=8E=E6=96=87=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nix/darwin.nix の `nix.enable = false` により nix-darwin は /etc/nix/nix.conf を 生成せず、nix-command / flakes の有効・無効はホスト任せになる。Determinate Nix を 入れてあっても無効なホストは実在するため、「Determinate がデフォルト有効化済み」と いう記述を撤回し、bare な nix サブコマンドを呼ぶ側が用途単位で `--extra-experimental-features "nix-command flakes"` を明示する契約を明文化する。 Nix 設定そのものは所有せず、恒久的な有効化は Determinate 公式の管理経路に委ねる。 Co-Authored-By: Claude Opus 5 (1M context) --- AGENTS.md | 13 +++++-- README.md | 7 +++- nix/README.md | 68 ++++++++++++++++++++++++++++++------- nix/darwin.nix | 20 +++++++---- nix/scripts/install-nix.zsh | 11 +++--- setup/cutover.zsh | 11 +++--- setup/tests/cutover.bats | 15 ++++---- 7 files changed, 107 insertions(+), 38 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index cae3a78..5b3f61d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -96,7 +96,10 @@ Hermes はオーケストレーター役の AI エージェントで、実装は cd ~/.dotfiles/nix # 副作用なしビルド確認 (CI と同じ検証を手元で) -USER=ciuser nix build .#darwinConfigurations.default.system --no-link --impure +# --extra-experimental-features: このリポジトリは nix.conf を所有しないため、 +# bare な nix サブコマンドには用途単位で明示する (nix/README.md の「前提」を参照) +USER=ciuser nix --extra-experimental-features "nix-command flakes" \ + build .#darwinConfigurations.default.system --no-link --impure # 適用 (sudo 必須、USER=$USER は sudo の env_reset で USER=root になるのを回避、--impure は username 動的解決のため必須) sudo USER=$USER darwin-rebuild switch --flake .#default --impure @@ -110,7 +113,11 @@ darwin-rebuild --list-generations ## 重要な設計判断 -- **`nix.enable = false`**: ローカル PC に Determinate Nix がインストールされている前提。nix-darwin の native Nix 管理は Determinate daemon と競合するため、`nix/darwin.nix` で明示的に無効化している。実験的機能 (nix-command / flakes) は Determinate がデフォルト有効化しているため別途宣言不要 +- **`nix.enable = false`**: ローカル PC に Determinate Nix がインストールされている前提。nix-darwin の native Nix 管理は Determinate daemon と競合するため、`nix/darwin.nix` で明示的に無効化している +- **experimental-features はこのリポジトリの所有物ではない**: `nix.enable = false` により nix-darwin は `/etc/nix/nix.conf` を生成しないため、nix-command / flakes の有効・無効はホスト任せ(Determinate Nix を入れてあっても無効なホストは実在する)。恒久的に有効化したい場合は Determinate 公式の管理経路で行い、dotfiles / nix-darwin では宣言しない + - 実行契約: bare な `nix` サブコマンドを呼ぶ側が用途単位で `--extra-experimental-features "nix-command flakes"` を明示する。`darwin-rebuild` は自前で有効化して呼ぶ生成物なので明示不要 + - CI 例外: `nix-check` workflow は installer action が有効化するため、workflow 内は bare のままでよい + - 診断手順と背景は `nix/README.md` の「前提: experimental-features は用途単位で明示する」を参照 - **PC 名・ユーザー名のリポジトリ非格納**: `darwinConfigurations.default` で output 名を hostname フリーに固定し、`username = builtins.getEnv "USER"` で macOS ローカルアカウント名を実行時解決する。公開リポジトリに PC 名や個人アカウント名を晒さないための設計。`--impure` フラグが必須になる代償と引き換え (S15) - **`homebrew.onActivation.cleanup = "zap"`**: 宣言外パッケージは Cellar ごと削除する強い管理。宣言外のパッケージが残らないよう破壊的に同期する (`nix/modules/darwin/homebrew.nix` のコメント参照) @@ -133,6 +140,8 @@ triage で「無視」マークした項目は OS デフォルト値が PC 間 - `nix flake check` (構文・型・依存解決) - `USER=ciuser nix build .#darwinConfigurations.default.system --no-link --impure` (closure ビルド) +workflow 内が bare な `nix` なのは、`DeterminateSystems/nix-installer-action` が nix-command / flakes を有効化するため。ローカルで同じ検証を回すときはホスト任せになるので `--extra-experimental-features "nix-command flakes"` を明示する(「主要コマンド」節の形が対応する)。 + `darwin-rebuild switch` の activation 自体は CI 範囲外 (環境差で消耗するため)。実機での `darwin-rebuild build` → `switch` で検証する方針。 # 公開リポジトリでの参照ポリシー diff --git a/README.md b/README.md index 8d61af0..fbc941b 100644 --- a/README.md +++ b/README.md @@ -32,9 +32,14 @@ echo sub-1 | sudo tee /etc/dotfiles-role # sub-1 で運用する場合 5. Install Nix and apply (Homebrew パッケージ層) ```terminal zsh ~/.dotfiles/nix/scripts/install-nix.zsh -cd ~/.dotfiles/nix && sudo USER=$USER nix run nix-darwin -- switch --flake .#default --impure +cd ~/.dotfiles/nix && sudo USER=$USER nix --extra-experimental-features "nix-command flakes" \ + run nix-darwin -- switch --flake .#default --impure ``` +`--extra-experimental-features` は、このリポジトリが `/etc/nix/nix.conf` を所有せず +nix-command / flakes の有効・無効をホスト任せにしているため必要。理由と他のコマンド例は +[`nix/README.md`](nix/README.md) の「前提」節を参照。 + See [`nix/README.md`](nix/README.md) for details. 6. Place dotfiles symlinks (Tier 1) and run explicit setup scripts (Tier 2) diff --git a/nix/README.md b/nix/README.md index 39af886..0f59a6c 100644 --- a/nix/README.md +++ b/nix/README.md @@ -7,6 +7,36 @@ - spec: `docs/superpowers/specs/2026-05-02-nix-migration-design.md` - plan: `docs/superpowers/plans/2026-05-02-nix-migration.md` +## 前提: experimental-features は用途単位で明示する + +このリポジトリは Nix 本体の設定 (`/etc/nix/nix.conf`) を所有しない。`nix/darwin.nix` の +`nix.enable = false` により nix-darwin は nix.conf を生成せず(nix-darwin 側で +`mkIf nix.enable` に括られている)、`nix.settings.experimental-features` を書いても効かない。 +nix-command / flakes を有効にするかどうかは Determinate Nix 側の設定とホストの実測次第で、 +**Determinate を入れてあっても無効なホストは実在する**。 + +そのため、このドキュメントの `nix` コマンド例はすべて用途単位でフラグを明示する: + +```sh +nix --extra-experimental-features "nix-command flakes" ... +``` + +- `darwin-rebuild` は nix-command / flakes を自前で有効化して呼ぶ生成物なので明示不要 +- `setup/cutover.zsh` の pre-flight build と `nix/tests/*.bats` も同じ契約で書かれている + (回帰テスト: `setup/tests/cutover.bats`) +- 恒久的に有効化したい場合は Determinate 公式の管理経路で行う。dotfiles 側では宣言しない +- 手元のホストの直接の設定は以下で見られる。ファイル不在は「設定が無い」という正常な観測なので、 + stderr を捨てて非ゼロ終了を握り潰す + + ```sh + grep -H experimental-features /etc/nix/nix.conf ~/.config/nix/nix.conf 2>/dev/null || true + ``` + + この 2 ファイルに直接の設定が見つからなくても「無効」と断定はできない。`include` / `!include` + で読み込まれる別ファイルや `NIX_CONFIG` 環境変数など間接的な設定経路まではここでは診断しない。 + 結論としてホストの状態に賭けず、常に用途単位でフラグを付ける。フラグ付きで `nix config show` + を叩くとフラグ自身の効果を読んでしまい判定にならない + ## ロールバック 直前世代に戻す: @@ -33,8 +63,8 @@ sudo zsh ~/.dotfiles/setup/rollback.zsh ## flake.lock の更新運用 -- `nix flake update` で全 input を最新に更新できる -- 特定 input だけ更新する場合: `nix flake lock --update-input nixpkgs` +- 全 input を最新に更新: `nix --extra-experimental-features "nix-command flakes" flake update` +- 特定 input だけ更新: `nix --extra-experimental-features "nix-command flakes" flake lock --update-input nixpkgs` - 更新後は必ず `darwin-rebuild build --flake .#default --impure` で検証してからコミット - `flake.lock` は必ずコミットする(再現性確保のため) - 更新頻度の方針: **必要時のみ**(依存ライブラリの脆弱性 / nixpkgs に必要なパッケージが入ったタイミング等) @@ -140,15 +170,26 @@ name か id と照合する) で飛ばす。 `brew install` 即試用の代替手段: -| やりたいこと | コマンド | -|---|---| -| nixpkgs にある CLI を一時的に試す | `nix shell nixpkgs#ripgrep`(その shell セッション限定 / `exit` で消える) | -| nixpkgs 最新で試す | `nix run nixpkgs/master#foo` | -| 1 回だけ実行 | `nix run nixpkgs#foo -- --args` | -| nixpkgs に無い GUI を試す | 現実的には手動 `brew install` → 気に入ったら `casks` に追加 → switch / 気に入らなければ `brew uninstall` | +```sh +# nixpkgs にある CLI を一時的に試す (その shell セッション限定 / exit で消える) +nix --extra-experimental-features "nix-command flakes" shell nixpkgs#ripgrep + +# nixpkgs 最新で試す +nix --extra-experimental-features "nix-command flakes" run nixpkgs/master#foo + +# 1 回だけ実行 +nix --extra-experimental-features "nix-command flakes" run nixpkgs#foo -- --args +``` + +nixpkgs に無い GUI は、現実的には手動 `brew install` → 気に入ったら `casks` に追加 → switch / +気に入らなければ `brew uninstall`。 `nix shell` / `nix run` は永続インストールしないので、後片付けが要らない。お試しは基本これに倒すこと。 +`--extra-experimental-features` を落とすと `experimental Nix feature 'nix-command' is disabled` +で即座に失敗する。理由は冒頭の「前提」を参照。他リポジトリの Makefile 等から `nix shell` で +ツールチェインを固定する場合も、同じ理由で呼び出し側にフラグを持たせる。 + ## Per-host 構成 (/etc/dotfiles-role) 複数 Mac で dotfiles を運用する際、PC ごとに異なる subset を入れるための仕組み。`/etc/dotfiles-role` (root 所有、machine-wide 設定) で切り替える。 @@ -183,10 +224,12 @@ echo sub-1 | sudo tee /etc/dotfiles-role # 3. ビルド確認 (副作用なし) cd ~/.dotfiles/nix -nix build .#darwinConfigurations.default.system --no-link --impure +nix --extra-experimental-features "nix-command flakes" \ + build .#darwinConfigurations.default.system --no-link --impure # 4. 初回ブートストラップ + 実機適用 -sudo USER=$USER nix run nix-darwin -- switch --flake .#default --impure +sudo USER=$USER nix --extra-experimental-features "nix-command flakes" \ + run nix-darwin -- switch --flake .#default --impure ``` ### role の切り替え @@ -245,7 +288,7 @@ sudo USER=$USER darwin-rebuild switch --flake .#default --impure ```sh git restore nix/flake.lock -nix flake update +nix --extra-experimental-features "nix-command flakes" flake update ``` ### `darwin-rebuild` がビルドエラーで失敗する @@ -268,7 +311,8 @@ nix-darwin が未インストールの状態で初めて適用する場合: ```sh cd ~/.dotfiles/nix -nix run nix-darwin -- switch --flake .#default --impure +nix --extra-experimental-features "nix-command flakes" \ + run nix-darwin -- switch --flake .#default --impure ``` ### 宣言から外した Homebrew パッケージが残る diff --git a/nix/darwin.nix b/nix/darwin.nix index df713b7..80e08fc 100644 --- a/nix/darwin.nix +++ b/nix/darwin.nix @@ -32,15 +32,23 @@ system.primaryUser = username; # Determinate Nix (公式インストーラの最新版) と nix-darwin の native Nix 管理は - # 同時稼働できないため、nix-darwin 側の管理を無効化する。 - # Determinate Nix は nix-command / flakes をデフォルトで有効化済みなので、 - # 旧来の `nix.settings.experimental-features` 宣言は不要。 - # - # 本修正は CI 検証 (S14) で `darwin-rebuild switch` が以下のエラーで失敗した - # ことから発見した本番ブロッカー: + # 同時稼働できないため、nix-darwin 側の管理を無効化する。有効にすると activation が + # 以下で止まる: # error: Determinate detected, aborting activation # Determinate uses its own daemon to manage the Nix installation that # conflicts with nix-darwin's native Nix management. + # + # この宣言の帰結として、/etc/nix/nix.conf は nix-darwin の管理対象から外れる + # (nix-darwin は nix.settings 由来の設定一式を `mkIf nix.enable` で括っており、 + # off の間は nix.conf を一切生成しない)。したがって experimental-features + # (nix-command / flakes) の所有者は Determinate 側であり、このリポジトリは + # その値を宣言も保証もしない。Determinate を入れてあっても + # nix-command が無効なホストは実在する。 + # + # 契約: bare な `nix` サブコマンドを呼ぶ側が用途単位で + # `--extra-experimental-features "nix-command flakes"` を明示する + # (setup/cutover.zsh の pre-flight build、nix/tests/*.bats、nix/README.md の例)。 + # `darwin-rebuild` は自前で有効化して呼ぶ生成物なので明示不要。 nix.enable = false; # ユーザー宣言(nix-darwin が users.users. として要求する最低限の宣言) diff --git a/nix/scripts/install-nix.zsh b/nix/scripts/install-nix.zsh index d00e56e..ffc2bf7 100755 --- a/nix/scripts/install-nix.zsh +++ b/nix/scripts/install-nix.zsh @@ -69,8 +69,11 @@ util::info "Full Disk Access is granted." # ---- 3. Determinate Nix インストーラ -------------------------------------- # Determinate Nix は nix-darwin の native Nix 管理 (nix.enable) と競合するため、 -# nix-darwin 側で `nix.enable = false` を宣言している (nix/darwin.nix)。 -# experimental-features (nix-command / flakes) は Determinate がデフォルト有効化済み。 +# nix-darwin 側で `nix.enable = false` を宣言している (nix/darwin.nix)。その帰結として +# experimental-features (nix-command / flakes) は Determinate 側の所有物になり、 +# このリポジトリは値を宣言も保証もしない。インストール直後に有効とは限らないため、 +# bare な `nix` サブコマンドを呼ぶ側が用途単位で +# `--extra-experimental-features "nix-command flakes"` を明示する (nix/README.md の「前提」)。 util::info "Installing Determinate Nix..." if ! curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix \ @@ -85,5 +88,5 @@ util::info "" util::info "NEXT STEPS:" util::info " 1. Restart your terminal (or run: source /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh)" util::info " 2. cd ${HOME}/.dotfiles/nix" -util::info " 3. nix build .#darwinConfigurations.default.system --no-link --impure # 副作用なし closure 確認" -util::info " 4. sudo USER=\$USER nix run nix-darwin -- switch --flake .#default --impure # 初回ブートストラップ + 実機適用" +util::info " 3. nix --extra-experimental-features \"nix-command flakes\" build .#darwinConfigurations.default.system --no-link --impure # 副作用なし closure 確認" +util::info " 4. sudo USER=\$USER nix --extra-experimental-features \"nix-command flakes\" run nix-darwin -- switch --flake .#default --impure # 初回ブートストラップ + 実機適用" diff --git a/setup/cutover.zsh b/setup/cutover.zsh index e00aaa3..5b2930a 100755 --- a/setup/cutover.zsh +++ b/setup/cutover.zsh @@ -42,11 +42,12 @@ darwin-rebuild --list-generations > "${generations_log}" util::info "=== pre-flight: nix build ===" # --extra-experimental-features: darwin-rebuild 自体は nix-command/flakes を内蔵で # 有効化して呼び出す生成物だが、ここは darwin-rebuild を経由しない素の `nix build` -# 直接呼び出しのため、システムの nix.conf が nix-command/flakes をデフォルト有効化 -# していない環境(Determinate Nix 前提が崩れている、または未設定の素の Nix)では -# 明示しないと `experimental Nix feature 'nix-command' is disabled` で失敗する。 -# CI (.github/workflows/nix-check.yml) はインストール直後の Determinate Nix で -# デフォルト有効なため未指定で足りるが、実機は環境依存のため明示して揃える。 +# 直接呼び出しのため、システムの nix.conf が nix-command/flakes を有効化していない +# ホストでは `experimental Nix feature 'nix-command' is disabled` で失敗する。 +# このリポジトリは nix.conf を所有しない (nix/darwin.nix の `nix.enable = false`) +# ため有効かどうかはホスト任せで、Determinate Nix を入れてあっても無効なホストは +# 実在する。CI (.github/workflows/nix-check.yml) は installer action が有効化するので +# 未指定でも通るが、実機に合わせて常に明示する。 if ! nix build "${DOTFILES_ROOT}/nix#darwinConfigurations.default.system" --no-link --impure \ --extra-experimental-features "nix-command flakes"; then util::error "nix build に失敗しました。switch は実行しません" diff --git a/setup/tests/cutover.bats b/setup/tests/cutover.bats index ff13773..bde9401 100644 --- a/setup/tests/cutover.bats +++ b/setup/tests/cutover.bats @@ -67,14 +67,13 @@ setup() { } @test "cutover.zsh's pre-flight nix build explicitly enables nix-command/flakes" { - # Regression test: on a machine where nix-command/flakes isn't enabled by - # default (e.g. a plain Nix install rather than Determinate Nix, or a - # host nix.conf without the experimental-features line), a bare `nix - # build` on this direct (non darwin-rebuild-wrapped) invocation fails - # with "experimental Nix feature 'nix-command' is disabled" before ever - # reaching darwin-rebuild switch. Reproduced and confirmed fixed against - # the real flake on 2026-08-22 (see migrate.zsh single-invocation - # recovery incident). darwin-rebuild itself already bakes this flag in, + # Regression test: this repo does not own nix.conf (nix/darwin.nix sets + # `nix.enable = false`), so whether nix-command/flakes is enabled is up to + # the host — a Determinate Nix install is no guarantee, and hosts with + # Determinate but the feature disabled do exist. Without the flag, this + # direct (non darwin-rebuild-wrapped) `nix build` fails with "experimental + # Nix feature 'nix-command' is disabled" before ever reaching + # darwin-rebuild switch. darwin-rebuild itself already bakes the flag in, # so only this standalone call needs it. _install_stubs "${STUB_BIN}" 0 PATH="${STUB_BIN}:${PATH}" run zsh "${SETUP_DIR}/cutover.zsh"