veilor-os/test
veilor-org 1881c14ea7 v0.5.27: rd.luks.uuid via grubby, GRUB rebrand, fbcon=nodefer, ASCII gum cursor
Critical install bug fix + cosmetic round-up + first formal test
procedure document.

## Critical: LUKS unlock on first boot

Generated installer kickstart's %post was injecting `rd.luks.uuid=…`
into `/etc/default/grub` only. Fedora 43 uses BLS (Boot Loader
Specification) entries in `/boot/loader/entries/*.conf`; those are
NOT regenerated by `grub2-mkconfig`. Result: the kernel boots without
`rd.luks.uuid=`, dracut's cryptsetup-generator never spawns the
unlock unit, plymouth has no password to ask for, and dracut-initqueue
loops on dev-disk-by-uuid for ~3min before dropping to emergency
shell.

The fix layers both write paths:
- `/etc/default/grub` — keeps the args around for future kernels
  (kernel-install reads this when adding new entries).
- `grubby --update-kernel=ALL --args=...` — rewrites the `options`
  line of every existing BLS entry so the kernel that boots NEXT
  actually has the args.

Verified by reading `/proc/cmdline` from the dracut emergency shell
on a v0.5.26 install; old cmdline had only `root=UUID=… ro
rootflags=subvol=root` and was missing the LUKS arg entirely.

## GRUB / branding

- `/etc/default/grub` is sed'd to `GRUB_DISTRIBUTOR="veilor-os"` (was
  already there, kept).
- BLS entries' `title` line is rewritten in-place to "veilor-os
  (<kver>)" for every kernel — `grub2-mkconfig` does not touch BLS
  titles, so this is the only path.
- `/boot/loader/entries/*-0-rescue-*.conf` is removed: the auto-built
  rescue entry was leaking "Fedora Linux" into the GRUB menu and
  showing a second boot option that nobody asked for. The rescue
  kernel image itself is left in /boot.
- Hostname defaults to `veilor` (was inheriting the `localhost-live`
  name anaconda writes when the kickstart's network directive is
  ignored under cmdline mode).
- `/etc/machine-info` adds `PRETTY_HOSTNAME="veilor-os"` so
  `hostnamectl status` and any consumer reading machine-info see the
  brand.

## Boot UX

- `fbcon=nodefer` added to live-ISO bootloader cmdline. On real
  laptops with a hardware GPU, the kernel modeset blanks the
  framebuffer console mid-boot; without `nodefer` the installer
  banner draws into a frozen framebuffer and the user sees a black
  screen with a blinking cursor for ~30s. virtio-vga in QEMU doesn't
  trigger this so it never reproduced in VM. Symptom report on
  v0.5.26 was the trigger to investigate.

## Installer cosmetics

- `GUM_CHOOSE_CURSOR` and `GUM_INPUT_PROMPT` switched from `❯ ` to
  `> `. The unicode arrow falls back to a fixed-width block on the
  linux fbcon font and lipgloss then duplicates that block at col +23,
  producing the "Install Install" double-render and the stray-T
  artifact in password fields. Plain ASCII renders identically across
  fbcon, virtio-vga, and X/Wayland gum runs.
- `VERSION_ID` bumped 0.5.8 → 0.5.27 in the os-release drop-in. The
  installer banner reads this at runtime, so the live ISO + installed
  system both now show "veilor-os 0.5.27".

## Test procedure

- `test/TESTING.md` — first canonical test procedure document. Splits
  VM (cheap iteration, hybrid sendkey + human passwords) from real
  hardware (mandatory for tag). Documents the standard test passwords
  (`veilortest1` for both LUKS and admin), the kill-and-relaunch step
  to skip CD on second boot, and the per-step pass/fail contract.
- `test/METHOD-CHANGELOG.md` — append-only audit trail for changes to
  the procedure. Future releases that alter the test method must add
  an entry here with the why.
- `test/test-runs/_TEMPLATE.md` — per-run report template. Each
  tagged release should land a filled report alongside it.

## test/run-vm.sh

Decoupled QEMU monitor sock setup from auto-inject. Previously
`NO_INJECT=1` (used to suppress autotype noise into prompts) also
killed the monitor sock, leaving the VM undriveable. Monitor sock is
now always exposed; only the inject helper is gated on the pubkey
detection.
2026-05-05 01:43:00 +01:00
..
test-runs v0.5.27: rd.luks.uuid via grubby, GRUB rebrand, fbcon=nodefer, ASCII gum cursor 2026-05-05 01:43:00 +01:00
auto-install-keymap.sh v0.5.5: autonomous install test harness (#12) 2026-05-02 22:49:51 +01:00
auto-install.sh test/auto-install.sh: auto-fetch + reassemble chunked ISO from ci-latest 2026-05-02 22:50:37 +01:00
boot-checklist.md veilor-os v0.1 scaffold — kickstart + hardening + 3-mode power + DuckSans-ready KDE black theme 2026-04-30 03:43:33 +01:00
METHOD-CHANGELOG.md v0.5.27: rd.luks.uuid via grubby, GRUB rebrand, fbcon=nodefer, ASCII gum cursor 2026-05-05 01:43:00 +01:00
README.md v0.5.5: autonomous install test harness (#12) 2026-05-02 22:49:51 +01:00
run-vm.sh v0.5.27: rd.luks.uuid via grubby, GRUB rebrand, fbcon=nodefer, ASCII gum cursor 2026-05-05 01:43:00 +01:00
TESTING.md v0.5.27: rd.luks.uuid via grubby, GRUB rebrand, fbcon=nodefer, ASCII gum cursor 2026-05-05 01:43:00 +01:00

test/

Test harnesses for veilor-os ISO builds.

Files

File Purpose
run-vm.sh Manual smoke test — boot the latest ISO interactively in QEMU/KVM. SSH key injection via cloud-init seed + monitor sendkey fallback for live-image login.
auto-install.sh Autonomous end-to-end install test. Boots ISO, drives the gum installer via QEMU monitor sendkey, waits for anaconda to finish + reboot, SSHs into the installed system, runs validation checklist. Prints PASS/FAIL summary.
auto-install-keymap.sh Sourced helper. Provides km_send_str, km_send_chord, km_send_key, km_screendump, km_wait_socket, etc. Reusable by other automation.
boot-checklist.md Manual post-install checklist (run on a real spare laptop).

Running the autonomous installer test

./test/auto-install.sh build/out/veilor-os-*.iso

Hardcoded inputs (deterministic — do not edit during a test run):

  • Disk: first /dev/vda (the only disk in QEMU)
  • Hostname: veilor (installer hardcoded since v0.5.4)
  • LUKS passphrase: testpass1234
  • Admin password: adminpass1234
  • Locale: en_GB.UTF-8

Expected runtime: 2030 minutes wall clock (anaconda dominates).

Outputs

  • /tmp/veilor-auto-install.log — full driver log
  • /tmp/veilor-auto-install-NN-<step>.png — milestone screenshots
  • /tmp/veilor-auto-install-final-ssh.txt — final SSH session capture (uname/lsblk/cmdline/failed units)

Exit codes

  • 0 — all validation checks passed
  • 1 — any failure (anaconda crashed, SSH never came up, validation check failed)
  • 2 — preflight failure (missing tool, bad ISO arg, missing OVMF)

Prerequisites

  • qemu-system-x86_64, qemu-img, socat, ssh, ssh-keygen
  • edk2-ovmf (OVMF UEFI firmware at /usr/share/edk2/ovmf/OVMF_{CODE,VARS}.fd)
  • mkisofs or xorriso (for cloud-init seed ISO; harness falls back to TTY1 driving if seed cannot be built or cloud-init does not run on the installed system)
  • convert from ImageMagick (optional — converts PPM screendumps to PNG; harness keeps PPM if absent)
  • KVM access (/dev/kvm readable by the user)

What it validates

Post-install on the booted system:

  • /etc/os-releaseNAME=veilor-os
  • hostnamectl --staticveilor
  • systemctl is-activeactive for sshd fail2ban usbguard tuned auditd firewalld chronyd sddm
  • getenforceEnforcing (preferred) or Permissive (acceptable for v0.5.x)
  • lsblk -f shows crypto_LUKS + btrfs
  • /etc/crypttab has a LUKS entry
  • getent passwd admin returns the user
  • /usr/local/bin/{veilor-power,veilor-doctor,veilor-update} are present and executable
  • /proc/cmdline contains init_on_alloc=1

Troubleshooting

  • Stuck at boot banner: ISO didn't autostart veilor-installer on tty1. Check serial.log and auto-install-vm-NN-*.png screenshots. The harness aborts after 5 minutes of identical screen frames.
  • SSH never up: cloud-init may not have run on the installed system (no cidata mount). The harness falls back to TTY1 driving — typing the LUKS passphrase, logging in as admin, and hand-injecting the SSH key. If both paths fail, validation cannot proceed.
  • screendump produces unreadable PPM: install ImageMagick (dnf install ImageMagick) so the harness converts to PNG.