d9dc8bacd4
Add ka-install verb — the third of the three writing verbs (ka-promote →
ka-build → ka-install). Always read-only: computes and prints the install
plan that ka-install <host> would execute, without touching any host.
New files:
bin/ka-install — Python3, 243 lines
tests/ka-install/run-tests.sh — 20 hermetic tests, 337 lines
tests/ka-install/fixtures/make-sandbox.sh — fixture builder, 275 lines
What --render computes:
1. Read fleet/<host>.yaml for packaging metadata
2. Read build/<host>/<baseline>/manifest.lock (from ka-promote)
3. Detect drift: build receipt missing/empty → exit 3
4. Determine backup destination from manifest template
5. Detect Image vs vmlinuz naming, select mkinitcpio hook
6. Print structured YAML plan, exit 0
Exit codes: 0 (plan ready), 2 (missing input), 3 (drift), 4 (error).
Test coverage (20/20 PASS):
- Argument parsing: requires host, --version, --dry-run alias
- Error paths: missing fleet manifest, missing manifest.lock,
unknown host
- Drift detection: no build receipt -> exit 3
- Happy path: full build receipt -> exit 0, plan printed
- Plan fields: source package, host, backup path, hook, version
- Image/vmlinuz detection for ARM and x86
- mkinitcpio --hook flag for Image (ARM), plain for vmlinuz
- /boot/firmware/ path (ampere-style boot layout)
- Grep-implementation: no scp/ssh/pacman/reboot calls
Existing suites unaffected (ka-promote: 6/6 PASS).