Fix auto-install.sh to work standalone, not just baked into the image
Check NixOS configurations / eval-hosts (pull_request) Successful in 11m6s

Two real bugs, both hit live:

1. Shebang: #!/run/current-system/sw/bin/bash only resolves on an
   already-activated NixOS system -- running the checked-out script
   directly (e.g. from a stock ISO, cloned repo) failed with "cannot
   execute: required file not found" on a non-NixOS box. Switched to
   #!/usr/bin/env bash, which resolves identically on NixOS
   (environment.usrbinenv's own default) and any normal Linux distro.
   Also fixed the file's missing executable bit.

2. FLAKE_BASE_URL: previously depended on pkgs.replaceVars substituting
   a Nix-templated @lanDomain@ placeholder at build time -- meaning it
   only ever worked when baked into the built installer image, not when
   run straight from a checkout (the literal, unexpanded "@lanDomain@"
   string reached git as a bogus hostname). Replaced with LAN_DOMAIN in
   scripts/env.sh (manually kept in sync with variables.nix's lanDomain,
   same pattern as NIX_CACHE_HOST/nixCacheHost already), sourced by the
   script itself like every other script in scripts/. Dropped
   pkgs.replaceVars from modules/installer/common.nix entirely --
   scripts/env.sh is now baked into the image alongside auto-install.sh
   at a matching relative path (/etc/nixos-installer/env.sh next to
   /etc/nixos-installer/installer/auto-install.sh) so the script's own
   relative `source` line resolves the same way in both contexts.

loginShellInit's invocation path and docs/auto-installer.md updated to
match. Verified: shellcheck clean on both scripts, the baked files are
byte-identical to their checked-in sources (no templating left to
verify), and codex-maintenance.sh (secret grep, fmt, statix, full eval
of every host/package including the installer/pxe artifacts) passes
clean.
This commit is contained in:
2026-07-22 02:38:11 +00:00
parent 60c155327d
commit f565e9c2a1
5 changed files with 68 additions and 26 deletions
+21 -9
View File
@@ -179,13 +179,24 @@ instead of copying it.
- `scripts/installer/auto-install.sh` — the interactive install script - `scripts/installer/auto-install.sh` — the interactive install script
baked into the auto-installer image (see `docs/auto-installer.md`), kept baked into the auto-installer image (see `docs/auto-installer.md`), kept
as a real, version-controlled shell file rather than inline in as a real, version-controlled shell file rather than inline in
`modules/installer/common.nix`'s Nix. `common.nix` wires it in with `modules/installer/common.nix`'s Nix. It sources `scripts/env.sh` itself
`pkgs.replaceVars ../../scripts/installer/auto-install.sh { inherit (vars) lanDomain; }`, for `LAN_DOMAIN` (`export LAN_DOMAIN`/`: "${LAN_DOMAIN:=...}"`, matching
substituting the single `@lanDomain@` placeholder from `variables.nix` `variables.nix`'s `lanDomain` — manually kept in sync, same pattern as
every other `${...}` in the script is a literal bash reference, `NIX_CACHE_HOST` mirroring `nixCacheHost`), rather than Nix-level string
untouched by this. `replaceVars` fails the build if any `@name@`-shaped substitution — that's what makes it work identically whether run
placeholder is left unsubstituted, so a typo'd or renamed variable is straight from a git checkout or from inside the built installer image.
caught at eval time rather than silently shipping broken. `common.nix` bakes `scripts/env.sh` in alongside it at a matching
relative path (`/etc/nixos-installer/env.sh` next to
`/etc/nixos-installer/installer/auto-install.sh`) so the script's own
`source "$(dirname ...)/../env.sh"` line resolves the same way in both
contexts — this is also why it's invoked from
`/etc/nixos-installer/installer/auto-install.sh` rather than a flat
`/etc/auto-install.sh`. `#!/usr/bin/env bash`, not
`#!/run/current-system/sw/bin/bash`: the latter only resolves on an
already-activated NixOS system, breaking the checked-out-file case
entirely (confirmed live: "cannot execute: required file not found" on
a non-NixOS box); `/usr/bin/env` is reliably present on both NixOS
(`environment.usrbinenv`'s own default) and any normal Linux distro.
### `scripts/secrets/` ### `scripts/secrets/`
@@ -297,8 +308,9 @@ Sourced by the scripts above, never run directly:
### Top level ### Top level
- `scripts/env.sh` — shared config (`PROXMOX_HOST`, storage pool, bridge, - `scripts/env.sh` — shared config (`PROXMOX_HOST`, storage pool, bridge,
default cores/memory) sourced by `create-proxmox-resource.sh`. Add new default cores/memory, `NIX_CACHE_HOST`, `LAN_DOMAIN`) sourced by
cross-script config here instead of duplicating it per-script. `create-proxmox-resource.sh` and `scripts/installer/auto-install.sh`. Add
new cross-script config here instead of duplicating it per-script.
- `scripts/bump-nixpkgs-release.sh` — bumps `flake.nix`'s `nixpkgs.url`/ - `scripts/bump-nixpkgs-release.sh` — bumps `flake.nix`'s `nixpkgs.url`/
`home-manager.url` in place. Exists because flake input URLs can't `home-manager.url` in place. Exists because flake input URLs can't
reference `variables.nix` (confirmed empirically — `nix flake metadata` reference `variables.nix` (confirmed empirically — `nix flake metadata`
+22 -5
View File
@@ -8,10 +8,13 @@ lives here.
The installer provides a small NixOS install environment (ISO, or the same The installer provides a small NixOS install environment (ISO, or the same
image netbooted via PXE) with SSH access, Git support, and an interactive image netbooted via PXE) with SSH access, Git support, and an interactive
installation script. installation script.
Logging in as any user (root or `nixos`) runs `/etc/auto-install.sh`, Logging in as any user (root or `nixos`) runs
discovers available hosts from this same flake, lets the operator choose a `/etc/nixos-installer/installer/auto-install.sh` (the same file as
target, applies that host's Disko storage configuration, installs NixOS, and `scripts/installer/auto-install.sh` in this repo — see "Installer process"
reboots. below for why it's baked in at that path rather than a flat
`/etc/auto-install.sh`), discovers available hosts from this same flake,
lets the operator choose a target, applies that host's Disko storage
configuration, installs NixOS, and reboots.
**This applies to every `nixosConfigurations` target except `lxc-*` hosts — **This applies to every `nixosConfigurations` target except `lxc-*` hosts —
see "LXC hosts" immediately below for why those are different.** see "LXC hosts" immediately below for why those are different.**
@@ -217,7 +220,21 @@ entirely (see "LXC hosts" above), so it never reaches this code path.
## Installer process ## Installer process
`/etc/auto-install.sh`: `scripts/installer/auto-install.sh` is a real, version-controlled shell
script — not an inline Nix string. It sources `scripts/env.sh` for
`LAN_DOMAIN` itself (same as every other script in `scripts/`), so it
behaves identically whether it's run straight from a git checkout (e.g.
manually, from a stock NixOS ISO that isn't this repo's own installer
image) or from inside the built installer image. That's also why it's
baked in at `/etc/nixos-installer/installer/auto-install.sh` rather than a
flat `/etc/auto-install.sh``modules/installer/common.nix` bakes
`scripts/env.sh` in alongside it at `/etc/nixos-installer/env.sh`,
preserving the same relative layout (`installer/auto-install.sh` ->
`../env.sh`) the checked-out repo has, so the script's own
`source ".../env.sh"` line resolves correctly in both places without any
Nix-level templating.
Once running, it:
1. Queries `nixosConfigurations` from this flake over the network (`git+https://<lanDomain>/beatzaplenty/nixos.git`) — this happens at *install* time, not build time, so a generic installer image always sees whatever hosts are currently committed, without needing a rebuild. 1. Queries `nixosConfigurations` from this flake over the network (`git+https://<lanDomain>/beatzaplenty/nixos.git`) — this happens at *install* time, not build time, so a generic installer image always sees whatever hosts are currently committed, without needing a rebuild.
2. Presents them as a menu; confirms the choice. 2. Presents them as a menu; confirms the choice.
+14 -10
View File
@@ -46,16 +46,20 @@
]; ];
# Auto-install script, kept as a real, version-controlled shell file at # Auto-install script, kept as a real, version-controlled shell file at
# scripts/installer/auto-install.sh rather than an inline Nix string -- # scripts/installer/auto-install.sh rather than an inline Nix string.
# replaceVars only substitutes the one value (lanDomain) that genuinely # It sources scripts/env.sh itself (for LAN_DOMAIN, same as every other
# needs to come from variables.nix; every other "@..." pattern in the # script in this repo) rather than relying on Nix-level templating, so
# script is a literal `${...}` bash reference, untouched by this. # it behaves identically whether it's run straight from a git checkout
etc."auto-install.sh" = { # or from here -- baking scripts/env.sh in alongside it at a matching
source = pkgs.replaceVars ../../scripts/installer/auto-install.sh { # relative path (installer/auto-install.sh -> ../env.sh) is what makes
inherit (vars) lanDomain; # that resolve correctly in both places.
}; etc = {
"nixos-installer/env.sh".source = ../../scripts/env.sh;
mode = "0755"; "nixos-installer/installer/auto-install.sh" = {
source = ../../scripts/installer/auto-install.sh;
mode = "0755";
};
}; };
}; };
@@ -69,7 +73,7 @@
# file-copying/chown. # file-copying/chown.
programs.bash.loginShellInit = '' programs.bash.loginShellInit = ''
if [ -n "$PS1" ] && [ ! -e "$HOME/.auto_install_ran" ]; then if [ -n "$PS1" ] && [ ! -e "$HOME/.auto_install_ran" ]; then
sudo /etc/auto-install.sh sudo /etc/nixos-installer/installer/auto-install.sh
touch "$HOME/.auto_install_ran" touch "$HOME/.auto_install_ran"
fi fi
''; '';
+6
View File
@@ -82,6 +82,12 @@ export PVE1_HOST PVE_TEST_HOST PROXMOX_HOST PROXMOX_SSH_USER PROXMOX_STORAGE \
: "${NIX_CACHE_HOST:=nix-cache}" : "${NIX_CACHE_HOST:=nix-cache}"
export NIX_CACHE_HOST export NIX_CACHE_HOST
# Matches variables.nix's lanDomain (the Gitea host this flake's own repo
# is served from -- see scripts/installer/auto-install.sh's FLAKE_BASE_URL)
# -- update both if it ever changes.
: "${LAN_DOMAIN:=gitea.lan.ddnsgeek.com}"
export LAN_DOMAIN
# nix_extra_opts: call as a plain statement (NOT inside $(...)/<(...) -- # nix_extra_opts: call as a plain statement (NOT inside $(...)/<(...) --
# that forks a subshell, and the whole point is exporting a decision back # that forks a subshell, and the whole point is exporting a decision back
# into *this* shell) to populate the global NIX_OPTS array with whatever # into *this* shell) to populate the global NIX_OPTS array with whatever
+5 -2
View File
@@ -1,9 +1,12 @@
#!/run/current-system/sw/bin/bash #!/usr/bin/env bash
set -eux set -eux
set -euo pipefail set -euo pipefail
export FLAKE_BASE_URL="git+https://@lanDomain@/beatzaplenty/nixos.git" # shellcheck source=../env.sh
source "$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/env.sh"
export FLAKE_BASE_URL="git+https://${LAN_DOMAIN}/beatzaplenty/nixos.git"
echo "Fetching available NixOS hosts from flake..." echo "Fetching available NixOS hosts from flake..."
# Two categories deliberately excluded from the menu: # Two categories deliberately excluded from the menu: