Compare commits

..
Author SHA1 Message Date
beatzaplentyandClaude Sonnet 4.6 4ba9b141fe fix(gc-hosts): filter discovered guests against flake to exclude non-NixOS hosts
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m32s
pve1 runs non-NixOS guests (FreeIPA domain-controller, Proxmox Data Manager, etc.)
alongside NixOS ones. Validate each discovered hostname against the set of
hostnames defined in nixosConfigurations before adding it to the gc list.

Brings back the flake eval but uses it correctly: extract all hostname values
(not target-name keys) to build a filter set, then only include pve1 guests
whose name matches a flake-managed NixOS host.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 14:06:17 +10:00
beatzaplentyandClaude Sonnet 4.6 a5308a7ee5 fix(gc-hosts): use pct/qm list output directly as hostnames
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m32s
The flake hostname map approach was wrong: create-proxmox-resource.sh names
every guest after its NixOS hostname (pct create --hostname <nixos-hostname>,
qm create --name <nixos-hostname>), so pct/qm list already output the final
hostname directly. Looking those names up in a map keyed by flake target names
(lxc-docker, proxmox-server, ...) found nothing and the dynamic list was
always empty.

Drop the flake eval entirely; use the pct/qm list output as-is, filtering
only nix-cache and already-seen entries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 13:58:45 +10:00
beatzaplentyandClaude Sonnet 4.6 7b4794211d fix(gc-hosts): fix pve1 PATH and discovery sudo prompts
Check NixOS configurations / eval-hosts (pull_request) Successful in 10m45s
Two bugs:

1. Dynamic discovery used BatchMode=yes for the pct/qm list SSH call, which
   silently suppressed any sudo password prompt and returned empty output.
   Fix: split SSH_OPTS into SSH_OPTS (BatchMode, for gc) and SSH_QUERY_OPTS
   (no BatchMode, for discovery) so sudo can prompt when needed.

2. pve1 gc used a non-login SSH session which doesn't source /etc/profile,
   so nix-collect-garbage was never on PATH. The Nix installer registers
   itself via /etc/profile.d/nix-daemon.sh which only runs in login shells.
   Fix: use "bash -l -c 'nix-collect-garbage -d'" instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-29 13:53:02 +10:00
2 changed files with 43 additions and 34 deletions
+43 -33
View File
@@ -14,8 +14,8 @@
# "nix-collect-garbage -d" if sudo needs a password — still collects # "nix-collect-garbage -d" if sudo needs a password — still collects
# unreferenced store paths and old nixos-user profile generations, but leaves # unreferenced store paths and old nixos-user profile generations, but leaves
# old system generations in place. # old system generations in place.
# pve1: runs "nix-collect-garbage -d" as the login user (no system generations # pve1: runs "bash -l -c nix-collect-garbage -d" as the login user so
# on a non-NixOS host). # /etc/profile is sourced and the Nix daemon's PATH is set up automatically.
# #
# Usage (from repo root): # Usage (from repo root):
# bash scripts/gc-hosts.sh [--dry-run] # bash scripts/gc-hosts.sh [--dry-run]
@@ -30,7 +30,12 @@ source scripts/lib/nix-eval.sh 2>/dev/null || true
: "${MAX_JOBS:=8}" : "${MAX_JOBS:=8}"
: "${NIXOS_USER:=nixos}" : "${NIXOS_USER:=nixos}"
: "${PVE1_SSH_USER:=${PROXMOX_SSH_USER:-wayne}}" : "${PVE1_SSH_USER:=${PROXMOX_SSH_USER:-wayne}}"
# GC connections use BatchMode — no interactive prompts, just succeed or fail.
SSH_OPTS=(-o StrictHostKeyChecking=no -o BatchMode=yes -o ConnectTimeout=10) SSH_OPTS=(-o StrictHostKeyChecking=no -o BatchMode=yes -o ConnectTimeout=10)
# Discovery connections do NOT use BatchMode so that sudo can prompt if needed
# (pct/qm list require root access on Proxmox).
SSH_QUERY_OPTS=(-o StrictHostKeyChecking=no -o ConnectTimeout=10)
DRY_RUN=0 DRY_RUN=0
for arg in "$@"; do for arg in "$@"; do
@@ -44,7 +49,7 @@ done
# ORDERED_HOSTS: names in display/execution order. # ORDERED_HOSTS: names in display/execution order.
# HOST_TARGET[name]: SSH target string (user@host). # HOST_TARGET[name]: SSH target string (user@host).
# HOST_TYPE[name]: "nixos" (try sudo gc, fallback user) | "nix" (user gc only). # HOST_TYPE[name]: "nixos" (try sudo gc, fallback user) | "nix" (login-shell gc).
declare -a ORDERED_HOSTS=() declare -a ORDERED_HOSTS=()
declare -A HOST_TARGET=() declare -A HOST_TARGET=()
declare -A HOST_TYPE=() declare -A HOST_TYPE=()
@@ -66,42 +71,47 @@ _add_host "nixos" "${NIXOS_USER}@nixos" "nixos"
_add_host "pve1" "${PVE1_SSH_USER}@${PVE1_HOST}" "nix" _add_host "pve1" "${PVE1_SSH_USER}@${PVE1_HOST}" "nix"
# 3. Dynamically discover running NixOS guests on pve1 # 3. Dynamically discover running NixOS guests on pve1
#
# create-proxmox-resource.sh names every guest after its NixOS hostname:
# pct create ... --hostname <nixos-hostname> (LXC)
# qm create ... --name <nixos-hostname> (VM)
# So pct/qm list output already contains the NixOS hostname directly.
# We validate against the flake to filter out non-NixOS guests on pve1
# (e.g. FreeIPA, Proxmox Backup Server) that share the same Proxmox node.
echo "Discovering running guests on ${PVE1_HOST}..." echo "Discovering running guests on ${PVE1_HOST}..."
# Evaluate the full flake hostname map in one shot. # Eval the flake once to get the set of hostnames that are actually NixOS.
hostname_map="{}" # Values are NixOS hostnames (e.g. "docker"); keys are flake targets ("lxc-docker").
if ! hostname_map="$( nixos_hostnames=""
nixos_hostnames="$(
nix eval --json "${NIX_EVAL_FLAGS[@]}" .#nixosConfigurations \ nix eval --json "${NIX_EVAL_FLAGS[@]}" .#nixosConfigurations \
--apply 'cfgs: builtins.mapAttrs (_: cfg: cfg.config.networking.hostName) cfgs' \ --apply 'cfgs: builtins.attrValues (builtins.mapAttrs (_: cfg: cfg.config.networking.hostName) cfgs)' \
2>/dev/null 2>/dev/null | jq -r '.[]' | sort -u
)"; then )" || { echo " warning: flake eval failed — non-NixOS guests will not be filtered" >&2; }
echo " warning: flake eval failed — skipping dynamic host discovery" >&2
fi
# Get names of all currently running guests from pve1. # SSH_QUERY_OPTS (no BatchMode) so sudo can prompt if needed for pct/qm.
if ssh "${SSH_OPTS[@]}" "${PVE1_SSH_USER}@${PVE1_HOST}" "true" 2>/dev/null; then if ssh "${SSH_QUERY_OPTS[@]}" "${PVE1_SSH_USER}@${PVE1_HOST}" "true" 2>/dev/null; then
running_guests="$( running_guests="$(
ssh "${SSH_OPTS[@]}" "${PVE1_SSH_USER}@${PVE1_HOST}" bash <<'REMOTE' ssh "${SSH_QUERY_OPTS[@]}" "${PVE1_SSH_USER}@${PVE1_HOST}" bash -s <<'DISCOVER'
{ sudo pct list 2>/dev/null | awk 'NR>1 && $2=="running" { print $NF }'; sudo pct list 2>/dev/null | awk 'NR>1 && $2=="running" { print $NF }'
sudo qm list 2>/dev/null | awk 'NR>1 && $3=="running" { print $2 }'; } | sort -u sudo qm list 2>/dev/null | awk 'NR>1 && $3=="running" { print $2 }'
REMOTE DISCOVER
)" || running_guests="" )" || running_guests=""
while IFS= read -r guest; do while IFS= read -r hostname; do
[[ -z "$guest" ]] && continue
# Resolve flake target name → NixOS hostname.
hostname="$(printf '%s' "$hostname_map" \
| jq -r --arg g "$guest" '.[$g] // empty' 2>/dev/null || true)"
[[ -z "$hostname" ]] && continue [[ -z "$hostname" ]] && continue
# Exclude nix-cache.
# Exclude nix-cache and any target whose hostname is already in our list. case "$hostname" in *nix-cache*) continue ;; esac
case "$hostname" in nix-cache) continue ;; esac # Skip if not a flake-managed NixOS host (filters non-NixOS pve1 guests).
if [[ -n "$nixos_hostnames" ]] && ! grep -qxF "$hostname" <<< "$nixos_hostnames"; then
continue
fi
# Skip if already in the list (e.g. a proxmox-gui guest whose hostname is nixos).
if [[ -n "${_SEEN_HOSTNAMES[$hostname]+_}" ]]; then continue; fi if [[ -n "${_SEEN_HOSTNAMES[$hostname]+_}" ]]; then continue; fi
echo " + $guest$hostname" echo " + $hostname"
_add_host "$hostname" "${NIXOS_USER}@${hostname}" "nixos" _add_host "$hostname" "${NIXOS_USER}@${hostname}" "nixos"
done <<< "$running_guests" done <<< "$(echo "$running_guests" | sort -u)"
else else
echo " warning: ${PVE1_HOST} unreachable — skipping dynamic host discovery" >&2 echo " warning: ${PVE1_HOST} unreachable — skipping dynamic host discovery" >&2
fi fi
@@ -121,7 +131,7 @@ if [[ "$DRY_RUN" -eq 1 ]]; then
echo " ssh ${SSH_OPTS[*]} $target 'sudo -n nix-collect-garbage -d'" echo " ssh ${SSH_OPTS[*]} $target 'sudo -n nix-collect-garbage -d'"
echo " # fallback: ssh ... $target 'nix-collect-garbage -d'" echo " # fallback: ssh ... $target 'nix-collect-garbage -d'"
else else
echo " ssh ${SSH_OPTS[*]} $target '. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh && nix-collect-garbage -d'" echo " ssh ${SSH_OPTS[*]} $target 'bash -l -c nix-collect-garbage -d'"
fi fi
done done
exit 0 exit 0
@@ -147,10 +157,10 @@ gc_one() {
echo "ok(user)"; return echo "ok(user)"; return
fi fi
else else
# Non-NixOS node: BatchMode SSH doesn't source the Nix daemon profile, so # Non-NixOS node: use a login shell so /etc/profile is sourced and the
# nix-collect-garbage won't be on PATH unless we source it explicitly. # Nix daemon's bin dir is on PATH (set up by /etc/profile.d/nix-daemon.sh
local nix_profile='. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh 2>/dev/null || true' # which the Nix installer adds to /etc/profile).
if ssh "${SSH_OPTS[@]}" "$target" "$nix_profile && nix-collect-garbage -d" \ if ssh "${SSH_OPTS[@]}" "$target" "bash -l -c 'nix-collect-garbage -d'" \
>>"$logfile" 2>>"$logfile"; then >>"$logfile" 2>>"$logfile"; then
echo "ok"; return echo "ok"; return
fi fi
-1
View File
@@ -58,7 +58,6 @@
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBZ9WKKAlP9Z7GQdgaZ1Xgw9C+vja2lqEZO5rJFpVqYN root@ha-server-1" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBZ9WKKAlP9Z7GQdgaZ1Xgw9C+vja2lqEZO5rJFpVqYN root@ha-server-1"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEGNKlaaMckd8nLWNGz4B2QokXjnnIvM+rEUv+R6h0sp root@ha-server-2" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEGNKlaaMckd8nLWNGz4B2QokXjnnIvM+rEUv+R6h0sp root@ha-server-2"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK+XeMco7OxUpjrjZm54HogMs9QB5xlcKmElASRvrmlW root@nixos" "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK+XeMco7OxUpjrjZm54HogMs9QB5xlcKmElASRvrmlW root@nixos"
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMseQwPpmaa6cgV5U8KhUsiVSYARG85zGa9rho0LJWks wayne@pve1"
]; ];
# Admin SSH public key, authorized on the primary user of every host and # Admin SSH public key, authorized on the primary user of every host and