Fix remote-build bootstrap: retry on partial failure, fix sudo-less root Nix install

Two bugs found running scripts/create-proxmox-resource.sh against a real
Proxmox node for the first time:

- The Nix installer's single-user root path still shells out to `sudo`
  to create /nix even though it already detected it's running as root,
  which fails outright on a minimal, sudo-less Debian/Proxmox node.
  codex-setup.sh now pre-creates /nix itself so that branch of the
  installer is skipped.

- ensure_remote_repo() only ran scripts/codex-setup.sh right after a
  fresh git clone, so a bootstrap that cloned the repo but then failed
  installing Nix (exactly the failure above) left every subsequent run
  silently building with a `nix` that was never actually installed,
  since the repo already existing skipped tooling setup entirely. It
  now checks `command -v nix` (via the same ensure_nix_profile used
  elsewhere, since a non-interactive ssh session won't otherwise have a
  single-user install on PATH) on every run and re-bootstraps if it's
  missing. Both remote build heredocs also now source
  scripts/lib/nix-bootstrap.sh themselves for the same PATH reason.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 14:39:43 +00:00
co-authored by Claude Sonnet 5
parent 90e3397b42
commit f5ef3194d4
2 changed files with 35 additions and 1 deletions
+11
View File
@@ -41,6 +41,17 @@ warn-dirty = false
build-users-group = nixbld build-users-group = nixbld
EOF EOF
# The official installer's single-user root path still shells out to
# `sudo` to create /nix even though it already knows it's running as
# root -- confirmed live against a sudo-less minimal Debian/Proxmox
# node, where it fails with "sudo: not found" and prints this exact
# mkdir/chown as the manual fix. Pre-create it so that branch of the
# installer is skipped entirely.
if [ ! -d /nix ]; then
mkdir -m 0755 /nix
chown root /nix
fi
sh <(curl -L https://nixos.org/nix/install) --no-daemon sh <(curl -L https://nixos.org/nix/install) --no-daemon
else else
sh <(curl -L https://nixos.org/nix/install) --no-daemon sh <(curl -L https://nixos.org/nix/install) --no-daemon
+24 -1
View File
@@ -524,7 +524,7 @@ ensure_remote_repo() {
echo echo
echo "==> Ensuring ${remote_repo_dir} exists and is current on ${node}..." echo "==> Ensuring ${remote_repo_dir} exists and is current on ${node}..."
if [[ "$dry_run" -eq 1 ]]; then if [[ "$dry_run" -eq 1 ]]; then
echo "[dry-run] would ensure ${remote_repo_dir} exists on ${node} (clone + scripts/codex-setup.sh if missing, git pull if present)" echo "[dry-run] would ensure ${remote_repo_dir} exists on ${node} (clone if missing, git pull if present), and would verify/bootstrap build tooling there (scripts/codex-setup.sh) if \`nix\` isn't already on PATH"
return return
fi fi
@@ -542,6 +542,22 @@ ensure_remote_repo() {
fi fi
echo "Not present -- cloning from ${origin_url}..." echo "Not present -- cloning from ${origin_url}..."
ssh "$ssh_target" "git clone '${origin_url}' '${remote_repo_dir}'" ssh "$ssh_target" "git clone '${origin_url}' '${remote_repo_dir}'"
fi
# Checked (and bootstrapped if missing) every run, not just right after a
# fresh clone -- confirmed live: a first bootstrap can clone the repo
# successfully and still leave the node without a working `nix` (e.g. the
# node had no `sudo`, which the Nix installer's root path depends on --
# see the fix in scripts/codex-setup.sh), and a later run with the repo
# already present would otherwise never retry it. Sources
# scripts/lib/nix-bootstrap.sh's ensure_nix_profile the same way the
# build commands below do -- a single-user Nix install typically only
# gets sourced into login shells, and ssh's non-interactive command
# execution is neither, so a freshly-installed `nix` still wouldn't be on
# PATH here without it.
if ssh "$ssh_target" "cd '${remote_repo_dir}' && . scripts/lib/nix-bootstrap.sh && ensure_nix_profile && command -v nix" >/dev/null 2>&1; then
echo "Build tooling already present on ${node}."
else
echo "==> Bootstrapping build tooling on ${node} (scripts/codex-setup.sh)..." echo "==> Bootstrapping build tooling on ${node} (scripts/codex-setup.sh)..."
ssh "$ssh_target" "cd '${remote_repo_dir}' && bash scripts/codex-setup.sh" ssh "$ssh_target" "cd '${remote_repo_dir}' && bash scripts/codex-setup.sh"
fi fi
@@ -625,6 +641,11 @@ repo_dir="$1"; target="$2"; dest_dir="$3"; dest_name="$4"; nix_extra_opts_str="$
declare -a NIX_OPTS=() declare -a NIX_OPTS=()
[[ -n "$nix_extra_opts_str" ]] && eval "NIX_OPTS=(${nix_extra_opts_str})" [[ -n "$nix_extra_opts_str" ]] && eval "NIX_OPTS=(${nix_extra_opts_str})"
cd "$repo_dir" cd "$repo_dir"
# A single-user Nix install only gets sourced into login shells; this ssh
# session is neither, so `nix` wouldn't otherwise be on PATH here even
# right after a successful install.
. scripts/lib/nix-bootstrap.sh
ensure_nix_profile
NIXOS_HOST_KEYS_DIR="$(pwd)/host-keys" nix build --impure \ NIXOS_HOST_KEYS_DIR="$(pwd)/host-keys" nix build --impure \
--no-use-registries --no-accept-flake-config "${NIX_OPTS[@]}" \ --no-use-registries --no-accept-flake-config "${NIX_OPTS[@]}" \
".#nixosConfigurations.${target}.config.system.build.tarball" \ ".#nixosConfigurations.${target}.config.system.build.tarball" \
@@ -672,6 +693,8 @@ repo_dir="$1"; target="$2"; dest_dir="$3"; dest_name="$4"; nix_extra_opts_str="$
declare -a NIX_OPTS=() declare -a NIX_OPTS=()
[[ -n "$nix_extra_opts_str" ]] && eval "NIX_OPTS=(${nix_extra_opts_str})" [[ -n "$nix_extra_opts_str" ]] && eval "NIX_OPTS=(${nix_extra_opts_str})"
cd "$repo_dir" cd "$repo_dir"
. scripts/lib/nix-bootstrap.sh
ensure_nix_profile
nix build --no-use-registries --no-accept-flake-config "${NIX_OPTS[@]}" \ nix build --no-use-registries --no-accept-flake-config "${NIX_OPTS[@]}" \
".#nixosConfigurations.${target}.config.system.build.diskoImagesScript" \ ".#nixosConfigurations.${target}.config.system.build.diskoImagesScript" \
--out-link "result-${target}" --out-link "result-${target}"