fix(setup): 宣言変更時に cutover を再適用する - #76
Merged
Merged
Conversation
migrate は cutover の manifest success を「必須バイナリが実在するか」だけで 再検証していたため、別端末で main を pull して Homebrew 宣言が変わっても skip され、`sudo zsh setup/migrate.zsh --apply` が新しいアプリを適用しなかった。 必須バイナリは前回の switch のまま残っているので postcondition は満たされ続ける。 直近 success 時の desired-input fingerprint を manifest の detail に記録し、 現在値と一致するときだけ skip するようにした。fingerprint は nix/ 配下の構成 (ファイル内容と symlink の readlink 値) と flake.lock、/etc/dotfiles-role、 ~/.config/dotfiles/homebrew.local.nix の内容/不在を材料にする。記録する値は switch を起動する直前に確定させ、switch 実行中に宣言が変わった場合はその変更を 適用済みとして取りこぼさない。fingerprint の記録が無い旧 manifest は安全側に 1 度だけ再実行し、その実行で記録して以降は通常の skip に戻る。 宣言変更で cutover が日常的に走るようになるため、PAM の扱いも 2 点締める。 cutover 直前に退避した Touch ID ファイル (<target>.before-restore.<timestamp>) は switch 後の pam 再適用が success し、かつ内容が再適用後の pam.d/sudo_local と byte-for-byte 一致するときだけ削除する。cutover / pam の失敗時と、手書き追加ルール等で 内容が一致しない場合はその内容の唯一の控えなので残す。削除はこの実行が作った 1 パスだけを 追跡して行い、別の実行が残した退避ファイルには触れない。 また <target> が pristine でないのに .before-setup が無い場合は、pristine が何だったかの 記録が無いまま上書きすることになるため switch 前に停止する (fail-closed)。 <target> 自体が無い真の初回はこれまでどおり通す。 既存の必須バイナリ postcondition と PAM の fail-closed 保証はそのまま維持する。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
問題
setup/migrate.zshは cutover の manifest success を「必須 Homebrew バイナリ (mise/starship) が実在するか」だけで再検証していた。必須バイナリは前回の switch で入ったまま残るため、別端末でmainを pull してnix/modules/darwin/homebrew.nixにアプリが増えても postcondition は満たされ続け、cutover が skip される。結果としてsudo zsh ${HOME}/.dotfiles/setup/migrate.zsh --applyが新しいアプリを適用しなかった。対応: desired-input fingerprint による再適用
直近 success 時の desired-input fingerprint を manifest の detail に記録し、現在値と一致するときだけ skip する。fingerprint の材料は次のとおりで、いずれも switch の desired set を変え得る入力:
nix/配下の構成 — 全ファイルの相対パスと内容ハッシュ、および symlink の相対パスとreadlink値 (リンク先を辿らないので張り替えも検出でき、dangling link で揺れない)/etc/dotfiles-roleの内容 / 不在 (flake.nixが role で desired set を切り替える)~/.config/dotfiles/homebrew.local.nixの内容 / 不在 (homebrew.nixが絶対パスで import する PC ローカル overlay)記録する値は switch を起動する直前に確定させる。switch 完了後に取ると、switch 実行中に宣言が変わった場合 (実行中に
git pullが着地する等) その変更を「適用済み」として記録してしまい、以後 skip され続けて永遠に適用されない。fingerprint の記録が無い旧 manifest は安全側に 1 度だけ再実行し、その実行で記録して以降は通常の skip に戻る。
--dry-runで何が再実行されるか事前に確認できる。nix/配下は switch に効かないファイル (README 等) も含めて丸ごと覆っている。実際に効く入力だけを allowlist すると宣言側の構造が変わったときに allowlist だけが古くなり検知漏れが生まれるため、余分な再実行 (switch は冪等) を選んだ。PAM の fail-closed と退避ファイルの保全
宣言変更で cutover が日常的に走るようになるため、
migrate::pam_restore_pristine_if_safe周辺を 2 点締めた。退避ファイルの後始末: cutover 直前に退避した Touch ID ファイル (
<target>.before-restore.<timestamp>) は、switch 後の pam 再適用が success し、かつ内容が再適用後のpam.d/sudo_localと byte-for-byte 一致するときだけ削除する。pam.zshは自前の固定テンプレートで書き直すので、この PC で手書き追加したルール (pam_reattach行など) は退避コピーにしか残らず、通常ファイルであることだけを根拠に消すとその唯一の控えを壊す。cutover / pam の失敗時も残す。削除はこの実行が作った 1 パスだけを追跡して行い、glob で他の実行が残した退避ファイルを巻き込まない。バックアップ不在時の fail-closed:
<target>が pristine でないのに.before-setupが無い場合は、pristine が何だったかの記録が無いまま上書きすることになるため switch 前に停止する。<target>自体が無い真の初回はこれまでどおり通す。既存の必須バイナリ postcondition、cutover 再実行時の pam 強制再適用、no-automatic-rollback はそのまま維持している。
検証
setup/tests/migrate.batsはテスト既定のsudo_localを「macOS static default への symlink」に変更した。従来の既定 (target 不在) はpam.zshの backup 作成を飛ばしてしまい、cutover 再実行での pristine 復元が一度も成り立たない非現実的な形だった。新規テストは fingerprint 側 (旧 manifest / nix 宣言変更 / symlink 追加・張り替え / role / local overlay / switch 中の宣言変更) と PAM 側 (退避ファイルの削除・保全・内容不一致時の保全・backup 不在時の fail-closed) を覆う。
zsh -n setup/migrate.zsh— exit 0bats setup/tests/*.bats— exit 0、131 passed / 0 failedbats nix/tests/*.bats nix/scripts/tests/*.bats— exit 0、39 passed / 0 failed実機での
--applyは未実行 (root 権限と実際のdarwin-rebuild switchを伴うため)。🤖 Generated with Claude Code