b307f871462f
use b.graph.environ_map instead of std.c.getenv for KEPR_TARGET in build.zig (Zig 0.16 libc-free env access)
- time
- 2026-06-04 20:19 UTC · 4 months ago
- machine
- koh2
- agent
- claude-sonnet-4-6
- session
- fix kepr build compat
- parent
- d87bd9832610
↓ nina-b307f871.tar.gzatlas at this save
files changed28 files
.kohignore+5 −0README.md+189 −0build.zig+2 −2kiln/findings/000000003039.jsonl+0 −6kiln/fleet/nina4.json+0 −11kiln/fleet/test1.json+0 −8kiln/reports/break-1780189929.md+0 −9kiln/reports/break-1780190299.md+0 −9kiln/reports/break-1780190404.md+0 −21kiln/reports/break-1780190542.md+0 −21kiln/reports/break-1780190697.md+0 −9kiln/reports/break-1780190743.md+0 −66kiln/reports/break-1780191052.md+0 −66kiln/reports/break-1780191586.md+0 −9test/vm/README.md+0 −41test/vm/boot-overlay.sh+0 −62test/vm/build-golden.sh+0 −96test/vm/configuration.nix+0 −82test/vm/install.sh+0 −34test/vm/run-in-vm.sh+0 −65test/vm/tests/cfgsearch.sh+0 −32test/vm/tests/cfgstatus.sh+0 −43test/vm/tests/flakeupdate.sh+0 −39test/vm/tests/gen.sh+0 −41test/vm/tests/install.sh+0 −30test/vm/tests/local.sh+0 −38test/vm/tests/multimachine.sh+0 −58test/vm/tests/search.sh+0 −20.kohignore+5 −0
@@ -29,3 +29,8 @@2929 # Koh artifacts3030 *.bundle3131 *.koh.offer32+33+#Testing34+35+kiln/36+test/\ No newline at end of fileREADME.md+189 −0
@@ -0,0 +1,189 @@1+# nina2+3+**the best way to use nixos.**4+5+Nina wraps the full NixOS workflow in plain, memorable verbs — packages, rebuilds, generations, flakes, services, remote machines. Written in Zig. Zero dependencies. One binary.6+7+```8+$ nina search ripgrep9+$ nina apply10+$ nina back11+$ nina status --all12+```13+14+---15+16+## install17+18+```sh19+curl -fsSL kepr.uk/nina/install.sh | sh20+```21+22+or clone and build:23+24+```sh25+koh steal kepr.uk/nina26+cd nina && zig build -Doptimize=ReleaseFast27+sudo cp zig-out/bin/nina /usr/local/bin/nina28+```29+30+**linux · nixos 24.05+** — requires `nix-command` and `flakes` experimental features. 31+Run `nina setup` on first use to enable them automatically.32+33+---34+35+## the commands you'll actually use36+37+```38+nina apply rebuild and switch to the new configuration39+nina apply --dry preview what would change without switching40+nina back roll back to the previous generation41+nina go <n> jump to any generation by number42+nina history list all generations43+nina clean remove old generations and free store space44+45+nina search <q> search nixpkgs inline — browse, install, or try46+nina option <q> search NixOS options inline47+nina install <p> add a package — profile (instant) or system config48+nina remove <p> remove a package from profile or system config49+nina try <p> run a package without installing it50+nina list show installed packages51+52+nina status machine health at a glance53+nina status --all health across every configured machine54+nina doctor diagnose common issues55+nina diff what changed between generations56+nina log operation history57+58+nina service list list systemd services and their state59+nina service logs <s> journal logs for a service60+nina service start/stop/restart <s>61+62+nina flake show inspect flake outputs63+nina flake update update lock files64+nina develop enter a dev shell65+nina build build a flake output66+nina run <p> run a package without installing67+68+nina edit open configuration.nix in your editor69+nina check validate config without switching70+nina fmt format nix files71+nina info nixos version, kernel, uptime72+nina boot boot entries73+74+nina help all commands75+nina --version version info76+```77+78+---79+80+## multi-machine81+82+Any command runs on a remote machine with `--on <name>`. Configure machines in `~/.nina.conf`:83+84+```ini85+[machine]86+name = kyoshi87+config = /etc/nixos88+local = true89+default = true90+91+[machine]92+name = azula93+host = azula94+user = june95+ssh_key = ~/.ssh/id_ed2551996+```97+98+```99+nina apply --on azula100+nina service logs ollama -f --on azula101+nina status --all102+```103+104+```105+:: status106+107+ kyoshi gen 348 3m ago 42 GB108+ azula gen 192 14m ago 18 GB109+```110+111+---112+113+## search that stays at the prompt114+115+`nina search` opens an inline widget — no fullscreen takeover. Browse results, see versions and licenses, install to your profile or system config, copy the attr name, or try the package in a temporary shell.116+117+```118+:: search nixpkgs ripgrep 12 results119+120+ > ripgrep 14.1.1 free fast line-oriented search121+ ripgrep-all 0.9.6 free search across file formats122+ ugrep 6.0.0 free grep compatible ripgrep alt123+124+ ripgrep 14.1.1 free125+ recursively search directories for a regex pattern126+ attr pkgs.ripgrep127+128+ [i] profile [s] system [t] try [c] copy [esc] exit129+```130+131+---132+133+## install that actually works134+135+`nina install` uses `nix search nixpkgs` directly — no external API, no network dependency after your first channel update, no silent failures. Works offline.136+137+Two install paths, your choice:138+139+- **profile** — instant. package available immediately. no rebuild.140+- **system** — opens your editor at the right line in `configuration.nix`, then offers to apply.141+142+---143+144+## config145+146+`~/.nina.conf` — plain text, no surprises:147+148+```ini149+editor = hx150+generations = 5151+confirm = true152+teach = false153+color = true154+155+[machine]156+name = my-machine157+config = /etc/nixos158+local = true159+default = true160+```161+162+`teach = true` prints the underlying nix command after each operation so you learn while you work.163+164+---165+166+## what nina outputs167+168+```169+:: rebuilding kyoshi...170+171+ -> [build output]172+173+:: generation 14 [42.3s]174+```175+176+```177+:: error: 'firefocks' not found in nixpkgs178+ -> try: nina search firefox179+```180+181+Pink `::` marks Nina's voice. Green for success. Red for errors. Yellow for warnings. Color auto-disables when stdout is not a TTY. Respects `NO_COLOR`.182+183+---184+185+## built by186+187+[asha software](https://asha.software) — hope in every line of code.188+189+**[nina.asha.software](https://nina.asha.software)** · [source](https://kepr.uk/nina) · [docs](https://nina.asha.software/docs)build.zig+2 −2
@@ -3,9 +3,9 @@33 pub fn build(b: *std.Build) void {44 // When built by Kepr Builds, KEPR_TARGET carries the cross-compile triple55 // so the same command works across all platforms without shell expansion.6- const target = if (std.c.getenv("KEPR_TARGET")) |t_ptr|6+ const target = if (b.graph.environ_map.get("KEPR_TARGET")) |t|77 b.resolveTargetQuery(std.Target.Query.parse(8- .{ .arch_os_abi = std.mem.span(t_ptr) },8+ .{ .arch_os_abi = t },99 ) catch std.Target.Query{})1010 else1111 b.standardTargetOptions(.{});kiln/findings/000000003039.jsonl+0 −6
@@ -1,6 +0,0 @@1-{"id":"boundary-00003039","layer":"boundary","trigger":"bnd:Path:0:<empty>","severity":"Medium","seed":12345,"consequence":"user-supplied input crashes the process"}2-{"id":"boundary-00003039","layer":"boundary","trigger":"bnd:Path:0:/","severity":"Medium","seed":12345,"consequence":"user-supplied input crashes the process"}3-{"id":"boundary-00003039","layer":"boundary","trigger":"bnd:Path:0:/dev/null","severity":"Medium","seed":12345,"consequence":"user-supplied input crashes the process"}4-{"id":"boundary-00003039","layer":"boundary","trigger":"bnd:Path:0:/dev/zero","severity":"Medium","seed":12345,"consequence":"user-supplied input crashes the process"}5-{"id":"boundary-00003039","layer":"boundary","trigger":"bnd:Path:0:/nonexiste","severity":"Medium","seed":12345,"consequence":"user-supplied input crashes the process"}6-{"id":"boundary-00003039","layer":"boundary","trigger":"bnd:Path:0: spaces ","severity":"Medium","seed":12345,"consequence":"user-supplied input crashes the process"}kiln/fleet/nina4.json+0 −11
@@ -1,11 +0,0 @@1-{2- "id": "nina4",3- "created_at": 1780231128,4- "slot_count": 4,5- "slots": [6- {"index": 0, "chamber_id": "fleet-nina4-0", "pid": 63251, "ssh_port": 52200, "serial_port": 52201, "overlay": "/var/folders/d2/jb8d6y8x39d97rypxddjrbn00000gn/T//kiln-chambers/overlay-fleet-nina4-0.qcow2", "state": "ready", "last_activity": 1780231129, "snapshots": []},7- {"index": 1, "chamber_id": "fleet-nina4-1", "pid": 63253, "ssh_port": 52202, "serial_port": 52203, "overlay": "/var/folders/d2/jb8d6y8x39d97rypxddjrbn00000gn/T//kiln-chambers/overlay-fleet-nina4-1.qcow2", "state": "ready", "last_activity": 1780231129, "snapshots": []},8- {"index": 2, "chamber_id": "fleet-nina4-2", "pid": 63250, "ssh_port": 52204, "serial_port": 52205, "overlay": "/var/folders/d2/jb8d6y8x39d97rypxddjrbn00000gn/T//kiln-chambers/overlay-fleet-nina4-2.qcow2", "state": "ready", "last_activity": 1780231129, "snapshots": []},9- {"index": 3, "chamber_id": "fleet-nina4-3", "pid": 63252, "ssh_port": 52206, "serial_port": 52207, "overlay": "/var/folders/d2/jb8d6y8x39d97rypxddjrbn00000gn/T//kiln-chambers/overlay-fleet-nina4-3.qcow2", "state": "ready", "last_activity": 1780231129, "snapshots": []}10- ]11-}kiln/fleet/test1.json+0 −8
@@ -1,8 +0,0 @@1-{2- "id": "test1",3- "created_at": 1780231121,4- "slot_count": 1,5- "slots": [6- {"index": 0, "chamber_id": "fleet-test1-0", "pid": 63244, "ssh_port": 52200, "serial_port": 52201, "overlay": "/var/folders/d2/jb8d6y8x39d97rypxddjrbn00000gn/T//kiln-chambers/overlay-fleet-test1-0.qcow2", "state": "ready", "last_activity": 1780231122, "snapshots": []}7- ]8-}kiln/reports/break-1780189929.md+0 −9
@@ -1,9 +0,0 @@1-# kiln break report2-project: /Users/june/projects/nina · language: Zig · seed: 988f6c3efd107d6 · duration: 0s3-iterations: 64-5-## summary6-CRITICAL: 07-HIGH: 08-MEDIUM: 09-LOW: 0kiln/reports/break-1780190299.md+0 −9
@@ -1,9 +0,0 @@1-# kiln break report2-project: /Users/june/projects/nina · language: Zig · seed: 4a855b785807f9e · duration: 57s3-iterations: 64-5-## summary6-CRITICAL: 07-HIGH: 08-MEDIUM: 09-LOW: 0kiln/reports/break-1780190404.md+0 −21
@@ -1,21 +0,0 @@1-# kiln break report2-project: /Users/june/projects/nina · language: Zig · seed: fc99b4f618ff809 · duration: 64s3-iterations: 04-5-## summary6-CRITICAL: 07-HIGH: 08-MEDIUM: 19-LOW: 010-11-## Medium findings12-13-### [adv-kill_during_rebuild-618ff809]14-layer: adversarial15-trigger: SIGKILL mid-operation16-observed: non-graceful exit under adversarial state17-expected: clean state or clear 'interrupted' error on next run18-reproducible: Yes19-consequence: unacceptable response to: SIGKILL mid-operation20-replay: kiln break --replay adv-kill_during_rebuild-618ff809 --seed fc99b4f618ff80921-kiln/reports/break-1780190542.md+0 −21
@@ -1,21 +0,0 @@1-# kiln break report2-project: /Users/june/projects/nina · language: Zig · seed: 32f073f0331d0adc · duration: 115s3-iterations: 04-5-## summary6-CRITICAL: 07-HIGH: 08-MEDIUM: 19-LOW: 010-11-## Medium findings12-13-### [adv-disk_full_mid_write-331d0adc]14-layer: adversarial15-trigger: ENOSPC after lock acquired16-observed: non-graceful exit under adversarial state17-expected: transaction rolled back, no partial state18-reproducible: Yes19-consequence: unacceptable response to: ENOSPC after lock acquired20-replay: kiln break --replay adv-disk_full_mid_write-331d0adc --seed 32f073f0331d0adc21-kiln/reports/break-1780190697.md+0 −9
@@ -1,9 +0,0 @@1-# kiln break report2-project: /Users/june/projects/nina · language: Zig · seed: 42e35cab7be7e924 · duration: 95s3-iterations: 04-5-## summary6-CRITICAL: 07-HIGH: 08-MEDIUM: 09-LOW: 0kiln/reports/break-1780190743.md+0 −66
@@ -1,66 +0,0 @@1-# kiln break report2-project: /Users/june/projects/nina · language: Zig · seed: 3039 · duration: 0s3-iterations: 64-5-## summary6-CRITICAL: 07-HIGH: 08-MEDIUM: 69-LOW: 010-11-## Medium findings12-13-### [boundary-00003039]14-layer: boundary15-trigger: bnd:Path:0:<empty>16-observed: boundary value crashed app17-expected: graceful error or success18-reproducible: Yes19-consequence: user-supplied input crashes the process20-replay: kiln break --replay boundary-00003039 --seed 303921-22-### [boundary-00003039]23-layer: boundary24-trigger: bnd:Path:0:/25-observed: boundary value crashed app26-expected: graceful error or success27-reproducible: Yes28-consequence: user-supplied input crashes the process29-replay: kiln break --replay boundary-00003039 --seed 303930-31-### [boundary-00003039]32-layer: boundary33-trigger: bnd:Path:0:/dev/null34-observed: boundary value crashed app35-expected: graceful error or success36-reproducible: Yes37-consequence: user-supplied input crashes the process38-replay: kiln break --replay boundary-00003039 --seed 303939-40-### [boundary-00003039]41-layer: boundary42-trigger: bnd:Path:0:/dev/zero43-observed: boundary value crashed app44-expected: graceful error or success45-reproducible: Yes46-consequence: user-supplied input crashes the process47-replay: kiln break --replay boundary-00003039 --seed 303948-49-### [boundary-00003039]50-layer: boundary51-trigger: bnd:Path:0:/nonexiste52-observed: boundary value crashed app53-expected: graceful error or success54-reproducible: Yes55-consequence: user-supplied input crashes the process56-replay: kiln break --replay boundary-00003039 --seed 303957-58-### [boundary-00003039]59-layer: boundary60-trigger: bnd:Path:0: spaces 61-observed: boundary value crashed app62-expected: graceful error or success63-reproducible: Yes64-consequence: user-supplied input crashes the process65-replay: kiln break --replay boundary-00003039 --seed 303966-kiln/reports/break-1780191052.md+0 −66
@@ -1,66 +0,0 @@1-# kiln break report2-project: /Users/june/projects/nina · language: Zig · seed: 3039 · duration: 0s3-iterations: 64-5-## summary6-CRITICAL: 07-HIGH: 08-MEDIUM: 69-LOW: 010-11-## Medium findings12-13-### [boundary-00003039]14-layer: boundary15-trigger: bnd:Path:0:<empty>16-observed: boundary value crashed app17-expected: graceful error or success18-reproducible: Yes19-consequence: user-supplied input crashes the process20-replay: kiln break --replay boundary-00003039 --seed 303921-22-### [boundary-00003039]23-layer: boundary24-trigger: bnd:Path:0:/25-observed: boundary value crashed app26-expected: graceful error or success27-reproducible: Yes28-consequence: user-supplied input crashes the process29-replay: kiln break --replay boundary-00003039 --seed 303930-31-### [boundary-00003039]32-layer: boundary33-trigger: bnd:Path:0:/dev/null34-observed: boundary value crashed app35-expected: graceful error or success36-reproducible: Yes37-consequence: user-supplied input crashes the process38-replay: kiln break --replay boundary-00003039 --seed 303939-40-### [boundary-00003039]41-layer: boundary42-trigger: bnd:Path:0:/dev/zero43-observed: boundary value crashed app44-expected: graceful error or success45-reproducible: Yes46-consequence: user-supplied input crashes the process47-replay: kiln break --replay boundary-00003039 --seed 303948-49-### [boundary-00003039]50-layer: boundary51-trigger: bnd:Path:0:/nonexiste52-observed: boundary value crashed app53-expected: graceful error or success54-reproducible: Yes55-consequence: user-supplied input crashes the process56-replay: kiln break --replay boundary-00003039 --seed 303957-58-### [boundary-00003039]59-layer: boundary60-trigger: bnd:Path:0: spaces 61-observed: boundary value crashed app62-expected: graceful error or success63-reproducible: Yes64-consequence: user-supplied input crashes the process65-replay: kiln break --replay boundary-00003039 --seed 303966-kiln/reports/break-1780191586.md+0 −9
@@ -1,9 +0,0 @@1-# kiln break report2-project: /Users/june/projects/nina · language: Zig · seed: 3039 · duration: 0s3-iterations: 04-5-## summary6-CRITICAL: 07-HIGH: 08-MEDIUM: 09-LOW: 0test/vm/README.md+0 −41
@@ -1,41 +0,0 @@1-# nina test VM — golden NixOS dev image2-3-A reproducible NixOS 24.11 dev image for exercising nina, built once and booted4-as disposable copy-on-write overlays so every run starts from the exact same5-snapshot. Runs under qemu (TCG) on Apple Silicon — x86_64 emulation, so slow.6-7-## Files8-9-- `configuration.nix` — the golden system's exact starting state (the spec).10- Serial console + root autologin (headless drivable), BIOS GRUB on `/dev/vda`,11- the brief's packages, `~/.nina.conf`, hostname `test-primary`.12-- `install.sh` — runs inside the minimal installer: partition, `nixos-install`.13-- `build-golden.sh` — host orchestration: boots the ISO headless and drives the14- scripted install over serial, producing the golden qcow2.15-- `boot-overlay.sh` — boots a throwaway overlay of the golden image, runs a16- command (default: nina smoke test), discards the overlay.17-18-## Build the golden image (once, slow)19-20-```sh21-# needs the NixOS 24.11 minimal x86_64 ISO and a linux nina binary22-./build-golden.sh /tmp/nixvm/nixos-24.11-minimal-x86_64.iso \23- /tmp/nina-linux/bin/nina \24- /tmp/nixvm/golden.qcow225-```26-27-The linux nina binary comes from the nina repo:28-`zig build -Dtarget=x86_64-linux-musl -Doptimize=ReleaseSafe -p /tmp/nina-linux`.29-30-## Boot a disposable copy31-32-```sh33-./boot-overlay.sh /tmp/nixvm/golden.qcow2 # nina smoke test34-./boot-overlay.sh /tmp/nixvm/golden.qcow2 'nina status'35-```36-37-The golden image is never mutated. Kiln can also boot it directly with38-`kiln chamber run --os /tmp/nixvm/golden.qcow2` (fresh overlay per run).39-40-Artifacts (ISO, golden.qcow2) live outside the repo (`/tmp/nixvm`); only the41-build inputs are version-controlled here.test/vm/boot-overlay.sh+0 −62
@@ -1,62 +0,0 @@1-#!/usr/bin/env bash2-# Boot a THROWAWAY copy-on-write overlay of the golden image, run commands over3-# the serial console, then discard the overlay. The golden image is never4-# mutated, so every boot starts from the exact same snapshot.5-#6-# Usage: boot-overlay.sh <golden.qcow2> [command ...]7-# With no command, runs a nina smoke test (help / hello / status).8-# Tunables via env: MEM_MB (2048), SMP (2), BOOT_TIMEOUT (600)9-set -euo pipefail10-11-GOLDEN="${1:?usage: boot-overlay.sh <golden.qcow2> [command...]}"12-shift || true13-MEM_MB="${MEM_MB:-2048}"14-SMP="${SMP:-2}"15-BOOT_TIMEOUT="${BOOT_TIMEOUT:-600}"16-[ -f "$GOLDEN" ] || { echo "missing golden: $GOLDEN" >&2; exit 1; }17-18-if [ "$#" -gt 0 ]; then CMD="$*"; else19- CMD='/root/nina help; echo ---; /root/nina hello; echo ---; /root/nina --version 2>/dev/null || true'20-fi21-22-work="$(mktemp -d)"23-trap 'rm -rf "$work"' EXIT24-overlay="$work/overlay.qcow2"25-qemu-img create -f qcow2 -b "$(cd "$(dirname "$GOLDEN")" && pwd)/$(basename "$GOLDEN")" -F qcow2 "$overlay" >/dev/null26-echo "throwaway overlay: $overlay (backing: $GOLDEN)"27-28-export BO_OVERLAY="$overlay" BO_MEM="$MEM_MB" BO_SMP="$SMP" \29- BO_TIMEOUT="$BOOT_TIMEOUT" BO_CMD="$CMD"30-31-expect -c '32- set timeout $env(BO_TIMEOUT)33- set overlay $env(BO_OVERLAY)34- set mem $env(BO_MEM)35- set smp $env(BO_SMP)36- set cmd $env(BO_CMD)37-38- spawn qemu-system-x86_64 \39- -machine q35 -accel tcg -cpu max -smp $smp -m $mem \40- -drive file=$overlay,if=virtio,format=qcow2 \41- -netdev user,id=n0 -device virtio-net-pci,netdev=n0 \42- -nographic43-44- expect {45- timeout { puts "\n!! no autologin on serial"; exit 2 }46- "automatic login" {}47- }48- sleep 649- send "echo NINA_SMOKE_BEGIN\r"50- expect { timeout { puts "\n!! shell not ready"; exit 3 } "NINA_SMOKE_BEGIN" {} }51- send "$cmd\r"52- send "echo NINA_SMOKE_END_RC=\$?\r"53- expect {54- timeout { puts "\n!! command timed out"; exit 3 }55- -re {NINA_SMOKE_END_RC=[0-9]+} {}56- }57- send "poweroff\r"58- set timeout 12059- expect eof60-'61-62-echo "=== overlay discarded; golden image unchanged ==="test/vm/build-golden.sh+0 −96
@@ -1,96 +0,0 @@1-#!/usr/bin/env bash2-# Bake the golden NixOS qcow2: boot the minimal ISO headless under qemu, drive a3-# scripted nixos-install over the serial console, power off. Output is a golden4-# disk image; Kiln (or boot-overlay.sh) boots throwaway overlays from it.5-#6-# Usage: build-golden.sh <nixos.iso> <nina-linux-binary> <out-golden.qcow2>7-# Tunables via env: DISK_GB (20), MEM_MB (4096), SMP (2), INSTALL_TIMEOUT (2400)8-set -euo pipefail9-10-ISO="${1:?usage: build-golden.sh <iso> <nina-bin> <out.qcow2>}"11-NINA="${2:?need nina linux binary}"12-OUT="${3:?need output qcow2 path}"13-HERE="$(cd "$(dirname "$0")" && pwd)"14-DISK_GB="${DISK_GB:-20}"15-MEM_MB="${MEM_MB:-4096}"16-SMP="${SMP:-2}"17-INSTALL_TIMEOUT="${INSTALL_TIMEOUT:-2400}"18-19-for f in "$ISO" "$NINA"; do [ -f "$f" ] || { echo "missing: $f" >&2; exit 1; }; done20-21-work="$(mktemp -d)"22-trap 'rm -rf "$work"' EXIT23-24-# Seed media: our config + install script + nina, as an ISO the installer mounts.25-seed="$work/seed"26-mkdir -p "$seed"27-cp "$HERE/configuration.nix" "$seed/configuration.nix"28-cp "$HERE/install.sh" "$seed/install.sh"29-cp "$NINA" "$seed/nina"30-SEED_ISO="$work/seed.iso"31-hdiutil makehybrid -quiet -iso -joliet -default-volume-name NINASEED -o "$SEED_ISO" "$seed"32-33-# Fresh blank disk for the install target.34-qemu-img create -f qcow2 "$OUT" "${DISK_GB}G" >/dev/null35-36-# Direct kernel boot: the ISO's own cmdline omits console=ttyS0 (kernel + getty37-# land on VGA, invisible under -nographic). Extract the kernel/initrd and the38-# installer's APPEND line, then add a serial console so the install is drivable.39-bsdtar -xOf "$ISO" boot/bzImage > "$work/bzImage"40-bsdtar -xOf "$ISO" boot/initrd > "$work/initrd"41-APPEND_BASE="$(bsdtar -xOf "$ISO" isolinux/isolinux.cfg 2>/dev/null \42- | awk '/^[[:space:]]*APPEND/ {sub(/^[[:space:]]*APPEND[[:space:]]*/,""); print; exit}')"43-[ -n "$APPEND_BASE" ] || { echo "could not read installer APPEND line" >&2; exit 1; }44-APPEND="$APPEND_BASE console=ttyS0,115200"45-echo "golden target: $OUT (${DISK_GB}G) · seed: $SEED_ISO"46-echo "kernel append: $APPEND"47-48-# Pass values to expect via the environment ($env(...)); `expect -c` does not49-# populate $argv from trailing args.50-export GI_ISO="$ISO" GI_SEED="$SEED_ISO" GI_OUT="$OUT" GI_MEM="$MEM_MB" \51- GI_SMP="$SMP" GI_TIMEOUT="$INSTALL_TIMEOUT" \52- GI_KERNEL="$work/bzImage" GI_INITRD="$work/initrd" GI_APPEND="$APPEND"53-54-expect -c '55- set timeout 60056- set iso $env(GI_ISO)57- set seed_iso $env(GI_SEED)58- set out $env(GI_OUT)59- set mem $env(GI_MEM)60- set smp $env(GI_SMP)61- set install_timeout $env(GI_TIMEOUT)62- set kernel $env(GI_KERNEL)63- set initrd $env(GI_INITRD)64- set append $env(GI_APPEND)65-66- spawn qemu-system-x86_64 \67- -machine q35 -accel tcg -cpu max -smp $smp -m $mem \68- -kernel $kernel -initrd $initrd -append $append \69- -drive file=$out,if=virtio,format=qcow2 \70- -drive file=$iso,media=cdrom,readonly=on \71- -drive file=$seed_iso,media=cdrom,readonly=on \72- -netdev user,id=n0 -device virtio-net-pci,netdev=n0 \73- -nographic74-75- expect {76- timeout { puts "\n!! installer never reached autologin"; exit 2 }77- "automatic login" {}78- }79- sleep 580- send "sudo -i\r"81- sleep 282- send "mkdir -p /mnt-seed && mount /dev/sr1 /mnt-seed && bash /mnt-seed/install.sh\r"83-84- set timeout $install_timeout85- expect {86- timeout { puts "\n!! install never finished"; exit 4 }87- "NINA_INSTALL_FAIL" { puts "\n!! install.sh reported failure"; exit 5 }88- "NINA_INSTALL_DONE" {}89- "NINA_INSTALL_START" { exp_continue }90- }91- set timeout 12092- expect eof93-'94-95-echo "=== golden image built: $OUT ==="96-qemu-img info "$OUT" | sed 's/^/ /'test/vm/configuration.nix+0 −82
@@ -1,82 +0,0 @@1-# Golden NixOS dev image for nina testing — the reproducible "exact starting2-# snapshot". Built once into a qcow2 (see build-golden.sh); Kiln boots a fresh3-# throwaway overlay from it per run.4-#5-# Mirrors the test environment from the Kiln brief (§2.3): NixOS 24.11,6-# test-primary host, git/curl/vim/htop/ripgrep, America/New_York.7-{ config, pkgs, ... }:8-9-{10- imports = [ ./hardware-configuration.nix ];11-12- # BIOS GRUB on the virtio disk — avoids needing UEFI/OVMF firmware under qemu.13- boot.loader.grub = {14- enable = true;15- device = "/dev/vda";16- };17- # Serial console so the installed image is drivable headless (qemu -nographic).18- # Keep a normal vga console too.19- boot.kernelParams = [ "console=ttyS0,115200" "console=tty0" ];20-21- networking.hostName = "test-primary";22- networking.useDHCP = true;23- time.timeZone = "America/New_York";24-25- environment.systemPackages = with pkgs; [26- git27- curl28- vim29- htop30- ripgrep31- nixpkgs-fmt # nina fmt32- ];33-34- # nina search/diff/pkg use the new `nix` CLI (flakes). Pin the nixpkgs flake35- # registry to the local channel path so `nix search nixpkgs` resolves offline36- # instead of fetching github per disposable overlay.37- nix.settings.experimental-features = [ "nix-command" "flakes" ];38- nix.registry.nixpkgs.to = {39- type = "path";40- path = pkgs.path;41- };42-43- # Headless driving: autologin root on the serial console so the build/boot44- # harness can run commands without credentials.45- services.getty.autologinUser = "root";46-47- services.openssh = {48- enable = true;49- settings.PermitRootLogin = "yes";50- settings.PasswordAuthentication = true;51- };52-53- # Test VM only — known password, never a real secret.54- users.users.root.password = "nixos";55-56- # nina's config (brief §2.2). Written on activation so it stays in this file57- # rather than as imperative state. confirm=false auto-accepts; teach=true58- # echoes the underlying command; color=true enables ANSI the checks verify.59- system.activationScripts.ninaConf.text = ''60- cat > /root/.nina.conf <<'EOF'61- editor = vim62- generations = 563- confirm = false64- teach = true65- color = true66-67- [machine]68- name = test-primary69- config = /etc/nixos70- local = true71- default = true72-73- [machine]74- name = test-remote75- host = 192.168.100.276- user = testuser77- ssh_key = /home/testuser/.ssh/test_key78- EOF79- '';80-81- system.stateVersion = "24.11";82-}test/vm/install.sh+0 −34
@@ -1,34 +0,0 @@1-#!/usr/bin/env bash2-# Runs INSIDE the NixOS minimal installer (over serial) to lay down the golden3-# system onto /dev/vda from the seed media. Driven by build-golden.sh.4-set -euo pipefail5-# Fast-fail signal for the driver instead of hanging until its timeout.6-trap 'echo "=== NINA_INSTALL_FAIL ==="; sync; sleep 2; poweroff -f' ERR7-8-SEED_DIR="$(cd "$(dirname "$0")" && pwd)"9-echo "=== NINA_INSTALL_START ==="10-11-# Single BIOS-bootable ext4 root (MBR) — matches grub.device=/dev/vda.12-parted -s /dev/vda mklabel msdos13-parted -s /dev/vda mkpart primary ext4 1MiB 100%14-parted -s /dev/vda set 1 boot on15-udevadm settle || true16-mkfs.ext4 -F -L nixos /dev/vda117-udevadm settle || true18-19-# Mount the partition device directly — the /dev/disk/by-label symlink may not20-# be populated by udev yet immediately after mkfs.21-mount /dev/vda1 /mnt22-23-# Generate hardware-configuration.nix for this disk, then drop in our config.24-nixos-generate-config --root /mnt25-cp "$SEED_DIR/configuration.nix" /mnt/etc/nixos/configuration.nix26-27-# Bake the injected nina binary (prove-the-loop: absolute-path /root/nina).28-install -Dm755 "$SEED_DIR/nina" /mnt/root/nina29-30-nixos-install --no-root-passwd --root /mnt31-32-echo "=== NINA_INSTALL_DONE ==="33-sync34-powerofftest/vm/run-in-vm.sh+0 −65
@@ -1,65 +0,0 @@1-#!/usr/bin/env bash2-# Run a test script inside a throwaway overlay of the golden image, injecting a3-# fresh nina binary so we test the latest build without rebaking the golden.4-# The script's serial output is captured (this command's stdout); the overlay is5-# discarded afterward, leaving the golden untouched.6-#7-# Usage: run-in-vm.sh <golden.qcow2> <nina-linux-binary> <test-script.sh>8-# Tunables via env: MEM_MB (2048), SMP (2), RUN_TIMEOUT (1800)9-set -euo pipefail10-11-GOLDEN="${1:?usage: run-in-vm.sh <golden.qcow2> <nina-bin> <test-script>}"12-NINA="${2:?need nina binary}"13-SCRIPT="${3:?need test script}"14-MEM_MB="${MEM_MB:-2048}"15-SMP="${SMP:-2}"16-RUN_TIMEOUT="${RUN_TIMEOUT:-1800}"17-for f in "$GOLDEN" "$NINA" "$SCRIPT"; do [ -f "$f" ] || { echo "missing: $f" >&2; exit 1; }; done18-19-work="$(mktemp -d)"20-trap 'rm -rf "$work"' EXIT21-seed="$work/seed"; mkdir -p "$seed"22-cp "$NINA" "$seed/nina"23-cp "$SCRIPT" "$seed/runtest.sh"24-SEED_ISO="$work/seed.iso"25-hdiutil makehybrid -quiet -iso -joliet -default-volume-name NINASEED -o "$SEED_ISO" "$seed"26-27-overlay="$work/overlay.qcow2"28-qemu-img create -f qcow2 -b "$(cd "$(dirname "$GOLDEN")" && pwd)/$(basename "$GOLDEN")" -F qcow2 "$overlay" >/dev/null29-echo "throwaway overlay: $overlay (backing: $GOLDEN)"30-31-export RV_OVERLAY="$overlay" RV_SEED="$SEED_ISO" RV_MEM="$MEM_MB" RV_SMP="$SMP" RV_TIMEOUT="$RUN_TIMEOUT"32-33-expect -c '34- set timeout 60035- set overlay $env(RV_OVERLAY)36- set seed $env(RV_SEED)37- set mem $env(RV_MEM)38- set smp $env(RV_SMP)39- set runto $env(RV_TIMEOUT)40-41- spawn qemu-system-x86_64 \42- -machine q35 -accel tcg -cpu max -smp $smp -m $mem \43- -drive file=$overlay,if=virtio,format=qcow2 \44- -drive file=$seed,media=cdrom,readonly=on \45- -netdev user,id=n0 -device virtio-net-pci,netdev=n0 \46- -nographic47-48- expect {49- timeout { puts "\n!! no autologin on serial"; exit 2 }50- "automatic login" {}51- }52- sleep 653- send "mkdir -p /mnt-seed && mount /dev/sr0 /mnt-seed && install -m755 /mnt-seed/nina /root/nina && echo KILN_RUN_BEGIN\r"54- expect { timeout { puts "\n!! seed/nina setup failed"; exit 3 } "KILN_RUN_BEGIN" {} }55- send "sh /mnt-seed/runtest.sh; echo KILN_RUN_END_RC=\$?\r"56- set timeout $runto57- expect {58- timeout { puts "\n!! test script timed out"; exit 4 }59- -re {KILN_RUN_END_RC=[0-9]+} {}60- }61- send "poweroff\r"62- set timeout 12063- expect eof64-'65-echo "=== overlay discarded; golden unchanged ==="test/vm/tests/cfgsearch.sh+0 −32
@@ -1,32 +0,0 @@1-#!/usr/bin/env sh2-# Run 2 for the previously-failing G-06/G-07 commands (fmt, doctor, diff) now that3-# the golden ships nixpkgs-fmt + enables nix-command/flakes, plus G-04 search.4-set -u5-N=/root/nina6-7-echo "KILN_CS2_BEGIN"8-9-# Second generation so diff has something to compare.10-sed -i 's| ./hardware-configuration.nix | ./hardware-configuration.nix ./genmarker.nix |' /etc/nixos/configuration.nix11-printf '{ ... }: { environment.etc."nina-gen".text = "Y"; }\n' > /etc/nixos/genmarker.nix12-$N apply >/dev/null 2>&1; echo "setup apply rc=$?"13-14-echo "### G-06 T-084 nina fmt (expect :: done, exit 0)"15-$N fmt; echo "FMT_RC=$?"16-echo "### G-06 T-085 nina fmt --check (expect ok, exit 0)"17-$N fmt --check; echo "FMTCHK_RC=$?"18-19-echo "### G-07 T-094 nina doctor (expect config syntax ok now)"20-$N doctor; echo "DOCTOR_RC=$?"21-22-echo "### G-07 T-091 nina diff (expect closure changes, no nix-command error)"23-$N diff; echo "DIFF_RC=$?"24-25-echo "### G-04 T-040 nina search firefox (expect firefox + version)"26-$N search firefox; echo "SEARCH_FF_RC=$?"27-echo "### G-04 T-041 nina search ripgrep"28-$N search ripgrep; echo "SEARCH_RG_RC=$?"29-echo "### G-04 T-055 nina search nonexistent (expect clean no-results, no crash)"30-$N search xyzzy-nope-abc123; echo "SEARCH_NONE_RC=$?"31-32-echo "KILN_CS2_END"test/vm/tests/cfgstatus.sh+0 −43
@@ -1,43 +0,0 @@1-#!/usr/bin/env sh2-# G-06 (config: check/fmt) + G-07 (status/info/doctor/boot/log/diff) validation,3-# run inside the VM on the current golden (no flakes/formatter needed except fmt,4-# which is expected to surface a missing-tool finding). Creates one extra5-# generation so diff/history have something to compare.6-set -u7-N=/root/nina8-9-echo "KILN_CS_BEGIN"10-11-# Make a 2nd generation so diff/log have content.12-sed -i 's| ./hardware-configuration.nix | ./hardware-configuration.nix ./genmarker.nix |' /etc/nixos/configuration.nix13-printf '{ ... }: { environment.etc."nina-gen".text = "X"; }\n' > /etc/nixos/genmarker.nix14-$N apply >/dev/null 2>&1; echo "setup apply rc=$?"15-16-echo "### G-06 T-082 nina check (valid config; expect :: ok, exit 0)"17-$N check; echo "CHECK_RC=$?"18-19-echo "### G-06 T-084 nina fmt (expect format /etc/nixos/configuration.nix)"20-$N fmt; echo "FMT_RC=$?"21-22-echo "### G-06 T-085 nina fmt --check (expect ok after fmt)"23-$N fmt --check; echo "FMTCHK_RC=$?"24-25-echo "### G-07 T-090 nina status"26-$N status; echo "STATUS_RC=$?"27-28-echo "### G-07 T-096 nina info (expect nixos version, kernel, uptime)"29-$N info; echo "INFO_RC=$?"30-31-echo "### G-07 T-094 nina doctor"32-$N doctor; echo "DOCTOR_RC=$?"33-34-echo "### G-07 T-097 nina boot (expect boot entries, current marked)"35-$N boot; echo "BOOT_RC=$?"36-37-echo "### G-07 T-093 nina log (expect operation history)"38-$N log; echo "LOG_RC=$?"39-40-echo "### G-07 T-091 nina diff (expect gen N -> N+1 changes)"41-$N diff; echo "DIFF_RC=$?"42-43-echo "KILN_CS_END"test/vm/tests/flakeupdate.sh+0 −39
@@ -1,39 +0,0 @@1-#!/usr/bin/env sh2-# G-08 update + G-10 flakes/build/run/repl/develop. Flake ops run in a fresh3-# flake dir. repl/develop are interactive (nix repl / nix shell) — `</dev/null`4-# feeds EOF so they exit. develop uses a `run` subcommand (nina develop run CMD),5-# not --run.6-set -u7-N=/root/nina8-9-echo "KILN_FLAKE_BEGIN"10-11-echo "### G-08 T-100 nina update (nix-channel --update; network)"12-$N update </dev/null; echo "UPDATE_RC=$?"13-14-mkdir -p /root/tf && cd /root/tf || exit 915-echo "### G-10 T-120 nina flake init (expect flake.nix)"16-$N flake init </dev/null; echo "FLAKEINIT_RC=$?"; echo "flake.nix: $(ls flake.nix 2>/dev/null || echo MISSING)"17-18-echo "### G-10 T-123 nina flake show"19-$N flake show </dev/null; echo "FLAKESHOW_RC=$?"20-21-echo "### G-10 T-122 nina flake check"22-$N flake check </dev/null; echo "FLAKECHECK_RC=$?"23-24-echo "### G-10 T-121 nina flake update (expect flake.lock)"25-$N flake update </dev/null; echo "FLAKEUPD_RC=$?"; echo "flake.lock: $(ls flake.lock 2>/dev/null || echo MISSING)"26-27-echo "### G-10 T-125 nina build (expect result symlink)"28-$N build </dev/null; echo "BUILD_RC=$?"; echo "result: $(ls -l result 2>/dev/null | awk '{print $NF}' || echo MISSING)"29-30-echo "### G-10 T-126 nina run nixpkgs#hello (expect Hello, world!)"31-$N run nixpkgs#hello </dev/null; echo "RUN_RC=$?"32-33-echo "### G-10 T-128 nina repl (EOF exits)"34-$N repl </dev/null; echo "REPL_RC=$?"35-36-echo "### G-10 T-130 nina develop run 'echo devshell-ok'"37-$N develop run 'echo devshell-ok' </dev/null; echo "DEVRUN_RC=$?"38-39-echo "KILN_FLAKE_END"test/vm/tests/gen.sh+0 −41
@@ -1,41 +0,0 @@1-#!/usr/bin/env sh2-# G-01/G-02/G-03 generations validation, run inside the VM. Creates distinct3-# system generations by changing the config between applies (a tiny /etc file via4-# an imported module), then exercises history/current/back/go/clean. Prints5-# nina's output beside ground-truth system-profile state for host-side judging.6-set -u7-N=/root/nina8-9-sysgens() { nix-env -p /nix/var/nix/profiles/system --list-generations 2>/dev/null | wc -l | tr -d ' '; }10-syscur() { readlink /nix/var/nix/profiles/system | sed -n 's/.*system-\([0-9][0-9]*\)-link/\1/p'; }11-mkgen() { printf '{ ... }: { environment.etc."nina-gen".text = "%s"; }\n' "$1" > /etc/nixos/genmarker.nix; }12-13-echo "KILN_GEN_TEST_BEGIN"14-15-# Import a marker module so each content change yields a new system generation.16-sed -i 's| ./hardware-configuration.nix | ./hardware-configuration.nix ./genmarker.nix |' /etc/nixos/configuration.nix17-echo "imports now: $(grep -o 'imports = .*];' /etc/nixos/configuration.nix)"18-echo "GROUND before: sysgens=$(sysgens) syscur=$(syscur)"19-20-echo "### G-01 apply x3 with config changes (expect generations to increment)"21-mkgen A; $N apply; echo "APPLY_A_RC=$?"; echo "GROUND: sysgens=$(sysgens) syscur=$(syscur)"22-mkgen B; $N apply; echo "APPLY_B_RC=$?"; echo "GROUND: sysgens=$(sysgens) syscur=$(syscur)"23-mkgen C; $N apply; echo "APPLY_C_RC=$?"; echo "GROUND: sysgens=$(sysgens) syscur=$(syscur)"24-25-echo "### T-015 nina history (expect all system generations, current marked)"26-$N history; echo "HIST_RC=$?"27-28-echo "### T-017 nina gen current (expect = $(syscur))"29-$N gen current; echo "GENCUR_RC=$?"30-31-echo "### T-010 nina back (expect syscur decrements by 1)"32-$N back; echo "BACK_RC=$?"; echo "GROUND after back: syscur=$(syscur)"33-34-echo "### T-012 nina go <newest> (return forward)"35-NEWEST=$(nix-env -p /nix/var/nix/profiles/system --list-generations | awk '{print $1}' | sort -n | tail -1)36-$N go "$NEWEST"; echo "GO_RC=$?"; echo "GROUND after go $NEWEST: syscur=$(syscur)"37-38-echo "### T-030 nina clean (keep 5; expect exit 0, no duration error)"39-$N clean; echo "CLEAN_RC=$?"; echo "GROUND after clean: sysgens=$(sysgens)"40-41-echo "KILN_GEN_TEST_END"test/vm/tests/install.sh+0 −30
@@ -1,30 +0,0 @@1-#!/usr/bin/env sh2-# G-05 install/remove/try. nina install reads a profile/system choice from stdin3-# and defaults to PROFILE install on EOF, so `</dev/null` drives it4-# non-interactively. install runs `nix search` first (memory-heavy → needs5-# MEM_MB>=6144). Uses ripgrep (already in the system closure → no rebuild) and6-# hello (tiny); firefox/etc. would work but are slow under TCG. The 's' system7-# path opens an editor and is intentionally not exercised.8-set -u9-N=/root/nina10-11-echo "KILN_G05_BEGIN"12-13-echo "### T-071 nina install ripgrep (profile; stdin EOF -> default profile)"14-$N install ripgrep </dev/null; echo "INSTALL_RG_RC=$?"15-echo "rg in profile: $(ls /root/.nix-profile/bin/rg 2>/dev/null || echo MISSING)"16-17-echo "### nina profile list (expect ripgrep present)"18-$N profile list </dev/null; echo "PROFLIST_RC=$?"19-20-echo "### T-074 nina remove ripgrep (profile)"21-$N remove ripgrep </dev/null; echo "REMOVE_RC=$?"22-echo "rg after remove: $(ls /root/.nix-profile/bin/rg 2>/dev/null || echo GONE)"23-24-echo "### T-076 nina try hello (nix shell; stdin EOF exits the shell)"25-$N try hello </dev/null; echo "TRY_RC=$?"26-27-echo "### T-072 nina install nonexistent (expect 'not found', exit 1)"28-$N install xyzzy-nope-abc123 </dev/null; echo "INSTALL_NONE_RC=$?"29-30-echo "KILN_G05_END"test/vm/tests/local.sh+0 −38
@@ -1,38 +0,0 @@1-#!/usr/bin/env sh2-# G-05 (list) + G-09 (services) + G-11 (store/channel/profile/pkg/hash) — the3-# non-interactive local commands. install/remove/try are interactive (prompt or4-# spawn a shell) and are validated separately. `service logs` must use --last5-# (bare logs follows with journalctl -f and would hang).6-set -u7-N=/root/nina8-9-echo "KILN_LOCAL_BEGIN"10-11-echo "### G-05 T-077 nina list (expect baseline packages)"12-$N list </dev/null; echo "LIST_RC=$?"13-14-echo "### G-09 T-110 nina service list (expect sshd active)"15-$N service list </dev/null; echo "SVCLIST_RC=$?"16-echo "### G-09 T-111 nina service status sshd"17-$N service status sshd </dev/null; echo "SVCSTATUS_RC=$?"18-echo "### G-09 T-113 nina service restart sshd"19-$N service restart sshd </dev/null; echo "SVCRESTART_RC=$?"20-echo "### G-09 T-115 nina service logs sshd --last 10"21-$N service logs sshd --last 10 </dev/null; echo "SVCLOGS_RC=$?"22-23-echo "### G-11 T-140 nina store info"24-$N store info </dev/null; echo "STOREINFO_RC=$?"25-echo "### G-11 T-143 nina store path ripgrep"26-$N store path ripgrep </dev/null; echo "STOREPATH_RC=$?"27-echo "### G-11 T-144 nina channel list"28-$N channel list </dev/null; echo "CHANNEL_RC=$?"29-echo "### G-11 T-145 nina profile list"30-$N profile list </dev/null; echo "PROFILE_RC=$?"31-echo "### G-11 T-148 nina pkg size ripgrep"32-$N pkg size ripgrep </dev/null; echo "PKGSIZE_RC=$?"33-echo "### G-11 T-150 nina hash <file>"34-echo hello > /tmp/h; $N hash /tmp/h </dev/null; echo "HASH_RC=$?"35-echo "### G-11 T-142 nina store gc"36-$N store gc </dev/null; echo "STOREGC_RC=$?"37-38-echo "KILN_LOCAL_END"test/vm/tests/multimachine.sh+0 −58
@@ -1,58 +0,0 @@1-#!/usr/bin/env sh2-# G-12 multi-machine ROUTING validation via SSH-to-self: test-remote points at3-# 127.0.0.1, so `--on test-remote` exercises nina's full SSH routing (build ssh4-# argv, connect, run remotely, parse output) and `status --all` aggregation.5-# CAVEAT: test-remote IS this machine, so this validates the --on plumbing, NOT6-# physical isolation (separate generation counts need two real networked VMs).7-set -u8-N=/root/nina9-10-echo "KILN_MM_BEGIN"11-12-mkdir -p /root/.ssh && chmod 700 /root/.ssh13-ssh-keygen -t ed25519 -N '' -f /root/.ssh/id_test </dev/null >/dev/null 2>&114-cat /root/.ssh/id_test.pub >> /root/.ssh/authorized_keys15-chmod 600 /root/.ssh/authorized_keys16-17-cat > /root/.nina.conf <<EOF18-editor = vim19-generations = 520-confirm = false21-teach = true22-color = true23-24-[machine]25-name = test-primary26-config = /etc/nixos27-local = true28-default = true29-30-[machine]31-name = test-remote32-host = 127.0.0.133-user = root34-ssh_key = /root/.ssh/id_test35-EOF36-37-echo "### T-153 nina hello (test-primary local, test-remote ssh)"38-$N hello </dev/null; echo "HELLO_RC=$?"39-40-echo "### T-160 nina status --all (both machines in table)"41-$N status --all </dev/null; echo "STATUSALL_RC=$?"42-43-echo "### T-164 nina history --on test-remote (via ssh)"44-$N history --on test-remote </dev/null; echo "HISTREMOTE_RC=$?"45-46-echo "### T-166 nina service list --on test-remote (via ssh)"47-$N service list --on test-remote </dev/null; echo "SVCREMOTE_RC=$?"48-49-echo "### T-165 nina doctor --on test-remote (via ssh)"50-$N doctor --on test-remote </dev/null; echo "DOCTORREMOTE_RC=$?"51-52-echo "### T-162 nina apply --on test-remote (ssh -> nixos-rebuild switch on self)"53-$N apply --on test-remote </dev/null; echo "APPLYREMOTE_RC=$?"54-55-echo "### T-167 nina status --on ghost-machine (expect error)"56-$N status --on ghost-machine </dev/null; echo "GHOST_RC=$?"57-58-echo "KILN_MM_END"test/vm/tests/search.sh+0 −20
@@ -1,20 +0,0 @@1-#!/usr/bin/env sh2-# G-04 search validation. `nix search nixpkgs` evaluates all of nixpkgs and is3-# memory-hungry — run this with a larger MEM_MB (see run-in-vm.sh) than the4-# brief's 2 GB, which OOMs.5-set -u6-N=/root/nina7-8-echo "KILN_SEARCH_BEGIN"9-free -m | head -210-11-echo "### T-040 nina search firefox (expect firefox + version + description)"12-$N search firefox </dev/null; echo "SEARCH_FF_RC=$?"13-14-echo "### T-041 nina search ripgrep"15-$N search ripgrep </dev/null; echo "SEARCH_RG_RC=$?"16-17-echo "### T-055 nina search nonexistent (expect clean no-results, no crash)"18-$N search xyzzy-nope-abc123 </dev/null; echo "SEARCH_NONE_RC=$?"19-20-echo "KILN_SEARCH_END"