From 7be45ac44c14b87da51ca469f8b8fb67441b3309 Mon Sep 17 00:00:00 2001 From: alexion Date: Tue, 15 Sep 2026 17:08:49 -0400 Subject: [PATCH] fix(nixos): deploy from source checkout --- configs/nixos/AGENTS.md | 7 +- configs/nixos/modules/shell/fish/default.nix | 2 +- .../shell/fish/functions/deploy-to.fish | 115 ++++++++---------- 3 files changed, 60 insertions(+), 64 deletions(-) diff --git a/configs/nixos/AGENTS.md b/configs/nixos/AGENTS.md index 3c757c8..c127e3a 100644 --- a/configs/nixos/AGENTS.md +++ b/configs/nixos/AGENTS.md @@ -50,7 +50,10 @@ directories already exist inside the read-only source. For aggregate Copyparty volumes such as `/srv` with nested `/srv/inbox`, keep the guest bind mount writable and enforce read-only behavior through Copyparty access plus service `ReadWritePaths`. -- Pikachu deploys from the monorepo checkout copied to `~/dev`. - Do not use the old `~/wrk/dotfiles` path for remote rebuild commands. +- `deploy-to` builds from the source checkout and activates an imported closure on the target. + Do not rely on a target-local monorepo checkout for remote deployment. +- On Pikachu, `nix copy --to ssh://pikachu` accepted a small `nix store add-file` probe, + but rejected locally built system paths that lacked trusted signatures. + Use a sudo `nix-store --import` path for unsigned deploy closures. - Do not hand-author `hardware-configuration.nix` for a host that has not been generated on that machine. Generate it on the target system or live ISO, then stage it before evaluating the flake. diff --git a/configs/nixos/modules/shell/fish/default.nix b/configs/nixos/modules/shell/fish/default.nix index de7e8cd..7808133 100644 --- a/configs/nixos/modules/shell/fish/default.nix +++ b/configs/nixos/modules/shell/fish/default.nix @@ -99,7 +99,7 @@ in }; deploy-to = { - description = "Deploy the current dotfiles branch to a host through target-local nixos-rebuild"; + description = "Build the current checkout locally, import it on a host, and activate it"; body = builtins.readFile ./functions/deploy-to.fish; }; diff --git a/configs/nixos/modules/shell/fish/functions/deploy-to.fish b/configs/nixos/modules/shell/fish/functions/deploy-to.fish index 6f98a04..4e3c043 100644 --- a/configs/nixos/modules/shell/fish/functions/deploy-to.fish +++ b/configs/nixos/modules/shell/fish/functions/deploy-to.fish @@ -1,16 +1,9 @@ -if test (count $argv) -lt 1; or test (count $argv) -gt 2 - echo "usage: deploy-to [branch]" >&2 +if test (count $argv) -ne 1 + echo "usage: deploy-to " >&2 return 2 end set host $argv[1] -set branch $argv[2] -set branch_source explicit - -if test -z "$branch" - set branch_source current - set branch (git branch --show-current) -end git rev-parse --show-toplevel >/dev/null 2>&1 or begin @@ -18,69 +11,69 @@ or begin return 2 end -git fetch origin +set repository_root (git rev-parse --show-toplevel) or return $status -if test -z "$branch"; or not git rev-parse --verify --quiet refs/remotes/origin/$branch >/dev/null - if test "$branch_source" = explicit - echo "deploy-to: origin/$branch does not exist" >&2 - return 1 - end - - set branch_source upstream - set upstream_ref (git rev-parse --abbrev-ref --symbolic-full-name '@{upstream}' 2>/dev/null) - set branch (string replace -r '^origin/' '' -- $upstream_ref) +set untracked_nix_files (git -C $repository_root ls-files --others --exclude-standard -- '*.nix' flake.nix flake.lock) +if test (count $untracked_nix_files) -gt 0 + echo "deploy-to: warning: untracked Nix files are not part of Git flake evaluation" >&2 + printf ' %s\n' $untracked_nix_files >&2 end -if test -z "$branch"; or not git rev-parse --verify --quiet refs/remotes/origin/$branch >/dev/null - set branch_source default - set origin_head (git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null) - set branch (string replace -r '^origin/' '' -- $origin_head) -end +set installable "$repository_root#nixosConfigurations.$host.config.system.build.toplevel" -if test -z "$branch" - echo "deploy-to: could not determine a deploy branch" >&2 - return 2 -end - -set remote_ref refs/remotes/origin/$branch -git rev-parse --verify --quiet $remote_ref >/dev/null +echo "deploy-to: building $installable" >&2 +set system_path (nix build --no-link --print-out-paths $installable) or begin - echo "deploy-to: origin/$branch does not exist" >&2 + echo "deploy-to: build failed for $host" >&2 + return $status +end + +if test (count $system_path) -ne 1 + echo "deploy-to: expected one build output, got "(count $system_path) >&2 return 1 end -set has_local_changes false -if not git diff --quiet $remote_ref -- - set has_local_changes true +set nar_file (mktemp --suffix=.nar) +or return $status +set remote_nar "/tmp/deploy-to-$host-"(date +%s)"-$fish_pid.nar" + +set closure_paths (nix-store -qR $system_path) +or begin + set query_status $status + rm -f $nar_file + echo "deploy-to: closure query failed for $host" >&2 + return $query_status end -set escaped_branch (string escape -- $branch) -set escaped_host (string escape -- $host) -set remote_command "cd ~/dev && git fetch origin && git switch -- $escaped_branch && git reset --hard origin/$escaped_branch && git clean -fd" - -set patch_file "" -set remote_patch_file "" -if test "$has_local_changes" = true - set patch_file (mktemp) - set remote_patch_file "/tmp/deploy-to-$host.patch" - git diff --binary $remote_ref -- >$patch_file - or begin - rm -f $patch_file - return $status - end - - scp $patch_file $host:$remote_patch_file - set scp_status $status - rm -f $patch_file - if test $scp_status -ne 0 - return $scp_status - end - - set escaped_remote_patch_file (string escape -- $remote_patch_file) - set remote_command "$remote_command && git apply --index $escaped_remote_patch_file && rm -f $escaped_remote_patch_file" +echo "deploy-to: exporting closure for $system_path" >&2 +nix-store --export $closure_paths >$nar_file +or begin + set export_status $status + rm -f $nar_file + echo "deploy-to: closure export failed for $host" >&2 + return $export_status end -set remote_command "$remote_command && sudo nixos-rebuild switch --flake .#$escaped_host --print-build-logs" +echo "deploy-to: copying closure archive to $host:$remote_nar" >&2 +scp $nar_file $host:$remote_nar +set scp_status $status +rm -f $nar_file +if test $scp_status -ne 0 + echo "deploy-to: closure archive copy failed for $host" >&2 + return $scp_status +end -ssh -t $host $remote_command +echo "deploy-to: importing closure on $host" >&2 +ssh -t $host "sudo sh -c 'nix-store --import < $remote_nar' && rm -f $remote_nar" +or begin + echo "deploy-to: closure import failed for $host" >&2 + return $status +end + +echo "deploy-to: activating $system_path on $host" >&2 +ssh -t $host sudo "$system_path/bin/switch-to-configuration" switch +or begin + echo "deploy-to: activation failed for $host" >&2 + return $status +end -- 2.47.3