1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
## 1. Directory-driven host discovery
- [x] 1.1 Add `hosts/template/system` containing a placeholder system string (e.g. `x86_64-linux`, no trailing newline) so the template stays a complete copy-source — verify the file exists and contains no trailing newline (`wc -c` matches the string length) (verified: 12 bytes, matches `x86_64-linux` exactly)
- [x] 1.2 In `flake.nix`, replace the hardcoded `nixosConfigurations = { };` with discovery logic that reads `./hosts`, filters out non-directories and `template`, and for each remaining directory builds `lib.mkNixos { hostname = <name>; system = builtins.readFile (./hosts + "/${name}/system"); }` — verify by grep confirming `nixosConfigurations` no longer contains a literal `{ };` and instead derives from `builtins.readDir` (verified via grep)
- [x] 1.3 Confirm `hosts/template` is still excluded from discovery by name — verify by re-reading the filter logic and confirming `template` remains an explicit exclusion even though it now has a `system` file too (confirmed: `name != "template"` filter present)
## 2. Bootstrap script
- [x] 2.1 Create `scripts/bootstrap-host.sh` accepting a positional `<hostname>` and flags `--root <path>` (required), `--state-version <version>` (required), `--system <system>` (optional, default `x86_64-linux`), `--force` (optional) — verify with `bash -n scripts/bootstrap-host.sh` (syntax check) and a manual read-through against the interface documented in design.md (`bash -n` passed; shellcheck not available locally to double-check, see note at end of file)
- [x] 2.2 Implement argument validation: fail with a clear error if `--root` or `--state-version` is missing, or if `hosts/<hostname>` already exists without `--force` — verify by tracing each failure path in the script against the corresponding spec scenario (all three checks present: missing `--root`, missing `--state-version`, existing `hosts/<hostname>` without `--force`)
- [x] 2.3 Implement the scaffold step: `cp -r hosts/template hosts/<hostname>`, then replace the `CHANGEME` placeholders for `networking.hostName` and `system.stateVersion` in the copied `configuration.nix` — verify by reading the sed/replacement logic against the exact placeholder text in `hosts/template/configuration.nix` (sed patterns match the literal `"CHANGEME"` text in both fields)
- [x] 2.4 Write `hosts/<hostname>/system` from the `--system` value (or default), with no trailing newline, using `printf '%s'` — verify by reading the write logic (`printf '%s' "$system" >"$host_dir/system"`)
- [x] 2.5 Implement hardware-configuration resolution: check for `<root>/etc/nixos/hardware-configuration.nix`; if absent, run `nixos-generate-config --root <root>` only if that command is on `PATH`; if still absent, fail with a clear error — verify the logic never references `/etc/nixos` directly without the `<root>` prefix (grep the script for any bare `/etc/nixos` reference and confirm none exist) (grep confirms every `/etc/nixos` occurrence is prefixed with `$root/`)
- [x] 2.6 Copy the resolved hardware config over `hosts/<hostname>/hardware-configuration.nix`, replacing the template stub — verify by reading the copy logic (`cp "$hw_config_src" "$host_dir/hardware-configuration.nix"`)
- [x] 2.7 Print next-step instructions on success: review the boot loader stanza, `git add hosts/<hostname>`, and the exact `nixos-install --root <root> --flake .#<hostname>` / `sudo nixos-rebuild switch --flake .#<hostname>` commands — verify the printed text uses `<flake>#<hostname>` syntax matching the spec requirement (final heredoc prints both commands using `"$repo_root#$hostname"`)
## 3. Flake app wiring
- [x] 3.1 In `flake.nix`, add `apps.<system>.bootstrap-host` (using the existing `forAllSystems`) built with `pkgs.writeShellApplication` wrapping `scripts/bootstrap-host.sh` — verify by grep confirming the `apps` output references `scripts/bootstrap-host.sh` and is defined for the same systems as `formatter`/`devShells` (grep confirmed; also added `runtimeInputs = [ pkgs.coreutils pkgs.gnused ]` since `writeShellApplication` scopes `PATH`, needed beyond what the task literally specified — noting this as a necessary correctness detail, not scope creep: without it `cp`/`sed`/`rm` inside the wrapped app would not be found)
## 4. Documentation
- [x] 4.1 Rewrite the "Installing NixOS on a new machine" section of `README.md` to use `nix run .#bootstrap-host -- <hostname> --root /mnt --state-version <ver>` in place of the old manual copy/edit/flake.nix-edit steps, keeping the surrounding partition/mount/`nixos-generate-config`/`nixos-install` steps that remain manual — verify by reading the updated section end-to-end for consistency with the script's actual behavior (read through end-to-end; the manual `nixos-generate-config` step was folded into the script's own resolution logic per design.md rather than kept as a separate manual step, since the script now does that itself)
- [x] 4.2 Note in the README that non-NixOS/standalone hosts are unaffected by this script and continue to use the existing clone + `home-manager switch` workflow — verify the note is present and doesn't contradict the "Installing on a non-NixOS system" section (note added at the top of that section; contents otherwise unchanged)
## 5. Validation
- [x] 5.1 Run `nix flake check` and confirm the auto-discovery logic and new `apps` output evaluate without error (blocked — Nix is not installed on this machine; run this once Nix is available, e.g. via a real install or CI — this is the real acceptance gate, not the individual checkboxes above) (run by the user on a Linux machine with Nix, 2026-09-18: passed with no errors, only a benign `apps.aarch64-linux.bootstrap-host lacks attribute 'meta'` warning, since fixed by adding `meta.description` to the `writeShellApplication` in `flake.nix`)
- [x] 5.2 Manually trace through each spec scenario in `specs/nix-flake-bootstrap/spec.md` against the implemented script and flake logic, confirming each is satisfied by inspection given #5.1 cannot run locally (all 8 scenarios traced and satisfied: discovery + template exclusion in `flake.nix`; scaffold, overwrite-refusal, no-root, missing-hardware-config failure modes all exit before any mutation in `scripts/bootstrap-host.sh`; live-installer scenario and install-target-matching confirmed via grep and directory-name-to-attribute-name correspondence)
---
**Verification note:** as with the prior change, Nix is not installed on this machine, so task 5.1 (`nix flake check`) could not be executed and remains the outstanding acceptance gate. `shellcheck` was also unavailable locally to double-check `scripts/bootstrap-host.sh` beyond `bash -n` and manual review. One implementation detail beyond the literal task wording: `pkgs.writeShellApplication` scopes `PATH` to `runtimeInputs`, so `runtimeInputs = [ pkgs.coreutils pkgs.gnused ]` was added to the `bootstrap-host` app in `flake.nix` (task 3.1) — without it `cp`/`sed`/`rm` inside the wrapped script would not resolve at runtime. `nixos-generate-config` is deliberately left out of `runtimeInputs` per design.md's decision (relies on it being ambient on the invoking NixOS system); `writeShellApplication` prepends `runtimeInputs` to the inherited `$PATH` rather than replacing it, so this should still resolve when present — unverified locally, same blocker as 5.1.
|