fix(nixos): deploy from source checkout #49

Merged
alexion merged 1 commits from feat/source-driven-deploy into main 2026-09-15 17:38:25 -04:00
3 changed files with 60 additions and 64 deletions

View File

@@ -50,7 +50,10 @@
directories already exist inside the read-only source. directories already exist inside the read-only source.
For aggregate Copyparty volumes such as `/srv` with nested `/srv/inbox`, keep the guest bind mount 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`. writable and enforce read-only behavior through Copyparty access plus service `ReadWritePaths`.
- Pikachu deploys from the monorepo checkout copied to `~/dev`. - `deploy-to` builds from the source checkout and activates an imported closure on the target.
Do not use the old `~/wrk/dotfiles` path for remote rebuild commands. 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. - 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. Generate it on the target system or live ISO, then stage it before evaluating the flake.

View File

@@ -99,7 +99,7 @@ in
}; };
deploy-to = { 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; body = builtins.readFile ./functions/deploy-to.fish;
}; };

View File

@@ -1,16 +1,9 @@
if test (count $argv) -lt 1; or test (count $argv) -gt 2 if test (count $argv) -ne 1
echo "usage: deploy-to <host> [branch]" >&2 echo "usage: deploy-to <host>" >&2
return 2 return 2
end end
set host $argv[1] 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 git rev-parse --show-toplevel >/dev/null 2>&1
or begin or begin
@@ -18,69 +11,69 @@ or begin
return 2 return 2
end end
git fetch origin set repository_root (git rev-parse --show-toplevel)
or return $status or return $status
if test -z "$branch"; or not git rev-parse --verify --quiet refs/remotes/origin/$branch >/dev/null set untracked_nix_files (git -C $repository_root ls-files --others --exclude-standard -- '*.nix' flake.nix flake.lock)
if test "$branch_source" = explicit if test (count $untracked_nix_files) -gt 0
echo "deploy-to: origin/$branch does not exist" >&2 echo "deploy-to: warning: untracked Nix files are not part of Git flake evaluation" >&2
return 1 printf ' %s\n' $untracked_nix_files >&2
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)
end end
if test -z "$branch"; or not git rev-parse --verify --quiet refs/remotes/origin/$branch >/dev/null set installable "$repository_root#nixosConfigurations.$host.config.system.build.toplevel"
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
if test -z "$branch" echo "deploy-to: building $installable" >&2
echo "deploy-to: could not determine a deploy branch" >&2 set system_path (nix build --no-link --print-out-paths $installable)
return 2
end
set remote_ref refs/remotes/origin/$branch
git rev-parse --verify --quiet $remote_ref >/dev/null
or begin 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 return 1
end end
set has_local_changes false set nar_file (mktemp --suffix=.nar)
if not git diff --quiet $remote_ref -- or return $status
set has_local_changes true 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 end
set escaped_branch (string escape -- $branch) echo "deploy-to: exporting closure for $system_path" >&2
set escaped_host (string escape -- $host) nix-store --export $closure_paths >$nar_file
set remote_command "cd ~/dev && git fetch origin && git switch -- $escaped_branch && git reset --hard origin/$escaped_branch && git clean -fd" or begin
set export_status $status
set patch_file "" rm -f $nar_file
set remote_patch_file "" echo "deploy-to: closure export failed for $host" >&2
if test "$has_local_changes" = true return $export_status
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"
end 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