All checks were successful
Check / eval (push) Successful in 4m7s
The guest's unfaithful rofi geometry was already documented in TESTING.md §5. What was not, and what this closes, is that the bullet's fallback advice — "measure text against the 40% window width instead" — is exactly how #131 stayed wrong for weeks, and my own #131 fix made it stale on top of that (the width is calc( 84ch min 65% ) now). Measuring needs a width and a font, and both are per-theme: text menus render through themes/<slug>/rofi.rasi, where boreal + neon-glass pinned a fixed 620px while the rest used 40%, in fonts from Inter 11 to JetBrainsMono 14. The item measured one combination no theme ships, concluded "only truncates below 1920", and the truncation was live on a 2560 panel. So §5 now says: judge by rendering — hardware, or `rofi -dmenu -theme <the built rasi>` + `hyprctl layers` for the real width — and notes checks.rofi-text-width guards the invariant that makes it fit, never how it looks. Declined the other half (fixing the guest's fontconfig/icon cache): the screenshots are useful as content checks, and a guest that lied convincingly about spacing would be worse than one that obviously cannot. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
177 lines
9.5 KiB
Markdown
177 lines
9.5 KiB
Markdown
# Testing Nomarchy
|
||
|
||
How to verify changes actually work — from cheap eval checks to booting the
|
||
full desktop. Adapted from the previous Nomarchy iteration's agent docs.
|
||
|
||
**The honesty rule (for humans and AI agents alike):** for Waybar, Hyprland,
|
||
or any visual change, the only reliable check is booting the live ISO. If
|
||
you can't boot it (e.g. you're on macOS or have no KVM), **say so** rather
|
||
than claiming success. "All Nix files parse" is not "the bar renders."
|
||
|
||
## 1. Cheap checks (always do these first)
|
||
|
||
```sh
|
||
nix flake check --no-build # full module-system evaluation, no builds
|
||
nix-instantiate --parse <file> # syntax-only, works even on macOS
|
||
git ls-files '*.py' | xargs python3 -m py_compile # all tracked Python
|
||
bash -n <script>.sh # shell syntax (tools/, installer bits)
|
||
```
|
||
|
||
`nix flake check` needs a Linux machine (or a Linux builder) since all
|
||
outputs are `x86_64-linux`. It catches type errors, missing options, and
|
||
bad merges — most breakage stops here. It also evaluates the downstream
|
||
template through `lib.mkFlake` (including a real nixos-hardware profile),
|
||
so template/wrapper drift fails fast too.
|
||
|
||
## 1b. CI (automatic on push)
|
||
|
||
Every push to `main`/`v1` runs `.gitea/workflows/check.yml`: the §1
|
||
cheap checks (flake eval, Python + shell syntax) on the Gitea instance.
|
||
That's the **eval tier only** — the runner is a docker container without
|
||
KVM, so the `checks.*` VM suite and real builds stay local (this file)
|
||
until a KVM-capable runner is registered; the workflow carries a
|
||
commented `vm-checks` job ready for that day. A green CI run is *not* "it
|
||
renders" (the honesty rule below still applies) — it means "nobody broke
|
||
evaluation".
|
||
|
||
## 2. Build and boot the live ISO
|
||
|
||
```sh
|
||
# Build + boot in QEMU, one command:
|
||
tools/test-live-iso.sh
|
||
|
||
# Or by hand:
|
||
nix build .#nixosConfigurations.nomarchy-live.config.system.build.isoImage
|
||
# → result/iso/*.iso — dd it to a USB stick for real hardware
|
||
```
|
||
|
||
The script prefers UEFI (OVMF) with a legacy-BIOS fallback, uses KVM when
|
||
`/dev/kvm` is readable, and boots with `virtio-vga-gl` + `gl=on` —
|
||
**Hyprland (and the desktop session) need real OpenGL in the guest**; without it the
|
||
session may not start.
|
||
|
||
### What the live environment gives you
|
||
|
||
- Auto-login as `nomarchy` (no password, passwordless sudo), greetd →
|
||
Hyprland via `initial_session`; logging out lands on tuigreet.
|
||
- The flake is seeded writable at `~/.nomarchy` (from the read-only
|
||
`/etc/nomarchy` copy) — `$NOMARCHY_PATH` already points there.
|
||
- Locked flake inputs are pinned into the ISO store, so a
|
||
`home-manager switch` for the **already-active / default theme** works
|
||
**without a network**. Switching to an arbitrary other preset can still
|
||
need downloads (that preset's HM generation is not in the pin) — the
|
||
tool should fail with an offline-oriented message (#113).
|
||
|
||
## 3. Verification checklist
|
||
|
||
Work through these in order; each one exercises a different layer.
|
||
|
||
| # | Check | Verifies |
|
||
|---|---|---|
|
||
| 1 | Boots to Hyprland with the default theme wallpaper visible (Boreal) | greetd autologin, awww, session start |
|
||
| 2 | Waybar shows at top, themed (blue accent on dark) | HM waybar module, palette baking |
|
||
| 3 | `SUPER+Return` opens Kitty with the default theme colors (Boreal) | terminal default, ANSI palette |
|
||
| 4 | `btop` in the terminal is themed | per-theme asset baking |
|
||
| 5 | `nomarchy-state-sync list` prints 24 presets | package, baked themes dir |
|
||
| 6 | `nomarchy-state-sync apply boreal` (or re-apply the **current** theme) offline → switch succeeds. Applying a *different* preset may need a network — the ISO pins the default theme's generation, not every preset (#113) | state write + offline rebuild contract |
|
||
| 7 | `SUPER+SHIFT+T` cycles wallpapers instantly (try `tokyo-night` for 4, or `boreal` for its set) | the runtime wallpaper path |
|
||
| 8 | `nomarchy-state-sync apply summer-night` → after the switch the bar has its own identity (light bar, different styling) | whole-swap waybar.css assets |
|
||
| 9 | Open a GTK app — dark theme matching the palette | Stylix layer |
|
||
| 10 | `home-manager generations` lists one generation per theme change; activating an older one rolls the theme back | atomicity / rollback story |
|
||
|
||
Items 6 and 8 are the big ones — they prove the all-Home-Manager theming
|
||
model end to end.
|
||
|
||
## 4. Testing the installer
|
||
|
||
One command runs the whole offline regression:
|
||
|
||
```sh
|
||
tools/test-install.sh
|
||
```
|
||
|
||
It builds the ISO, boots it in QEMU **with networking disabled**, runs an
|
||
unattended LUKS+swap install onto a blank disk via the installer's
|
||
`NOMARCHY_UNATTENDED=1` mode (config delivered on a small vfat disk —
|
||
typing long commands into a guest drops keystrokes), waits for the
|
||
installer's poweroff, then boots the installed disk, enters the
|
||
passphrase, and screenshots the first boot. The machine-checkable parts
|
||
(install completes, disk boots) fail the script; the visual verdict —
|
||
**themed desktop, no autogenerated-config banner, no login prompt** — is
|
||
yours, from `first-boot.png`. The helpers it builds on live in
|
||
`tools/vm/` (QMP key injection, GL-safe VNC screenshots) and work for any
|
||
manual VM poking; `tools/vm/gap-analysis.py` is the maintainer tool for
|
||
diagnosing offline installs that try to build from source.
|
||
|
||
To test by hand instead, replicate what `tools/test-install.sh` does: the
|
||
unattended env it uses is in the script, and the same flow works
|
||
interactively from the live terminal. If the desktop comes up unthemed,
|
||
read `/var/log/nomarchy-hm-preactivate.log` on the installed system.
|
||
|
||
`tools/plymouth-preview.sh` renders the **boot splash** — including the LUKS
|
||
password dialog — into X11 windows in about ten seconds: no reboot, no root,
|
||
and no DRM. It runs plymouthd inside `unshare -rm` (a user + mount namespace),
|
||
binds a writable dir over `/run/plymouth` (which is both the daemon's socket
|
||
dir *and* its compiled-in plugin path), masks `/dev/dri` so a preview can never
|
||
mode-set a real output, and sets `PLY_CREATE_FAKE_MULTI_HEAD_SETUP` for plymouth's
|
||
built-in fake dual head. Pass it a theme store path to iterate on the script
|
||
without a rebuild. Use it before touching `modules/nixos/plymouth/`: that script
|
||
draws the passphrase box, a failure there is silent (the plugin logs
|
||
`starting boot animation` and no error), and "type your LUKS password blind" is
|
||
a bad way to find out. Its limit: the fake heads exist from the start, so it
|
||
cannot reproduce a head arriving *mid-splash* (#137).
|
||
|
||
Two full-desktop VM harnesses (softGL Hyprland, too heavy for `checks.*`)
|
||
live next to the scripts: `tools/theme-shot.nix` (themed-desktop
|
||
screenshots) and `tools/monitor-fallback.nix` (KVM display transitions:
|
||
atomic dock handoff, internal-first undock, and low-level lid-inhibitor
|
||
lifecycle). The latter restores the DRM output before deleting QEMU's final
|
||
synthetic headless output; physical cable-removal timing is a V3 check because
|
||
Hyprland's headless backend otherwise aborts in a VM-only zero-output state.
|
||
|
||
## 5. VM-specific gotchas
|
||
|
||
- **No KVM** (e.g. nested without acceleration): everything works but
|
||
`home-manager switch` will be painfully slow. Don't read slowness as
|
||
breakage.
|
||
- **Black screen / session exits**: almost always missing guest GL. Use the
|
||
script's qemu flags; on real hardware check `journalctl -b -u greetd`.
|
||
- **Tiny resolution**: the live config forces `monitor = ,highres,auto,1`;
|
||
if QEMU still picks 1024×768, resize the window — Hyprland follows.
|
||
- **Display menu + QEMU**: System › Display mode list is sorted by **pixel
|
||
area** (highest first). After a pick, the toast shows hyprctl’s reported
|
||
size. In a **fixed-size** QEMU/viewer window, a higher guest mode still
|
||
*looks* smaller (more pixels in the same window) — that is viewer scaling,
|
||
not an inverted apply. Trust hyprctl / the toast numbers.
|
||
- **First theme switch is the slowest**: it evaluates the flake on a RAM
|
||
disk. Subsequent switches reuse the eval cache.
|
||
- **Menu geometry does not survive the guest**: the rofi picker draws with
|
||
icons at roughly a quarter of `ui.iconSize` and rows short enough that a
|
||
6-row root clips its last entry behind a scrollbar (`lines = 8` should
|
||
fit it) — the guest's font/icon environment, not a regression. Menu
|
||
screenshots are evidence for **which rows appear**, never for spacing,
|
||
icon size or label truncation.
|
||
**And do not "just measure" instead** — that is how #131 stayed wrong for
|
||
weeks. Measuring needs a width and a font, and both are per-theme: text
|
||
menus render through `themes/<slug>/rofi.rasi`, where boreal and
|
||
neon-glass pinned a fixed `620px` while the rest used `40%`, in fonts from
|
||
Inter 11 to JetBrainsMono 14. The item measured one combination that no
|
||
theme actually shipped, concluded "only truncates below 1920", and the
|
||
truncation was live on a 2560 panel. Since #131 the width is
|
||
`calc( 84ch min 65% )` — font-relative by design, so the arithmetic is
|
||
even less predictable from a spec sheet. **Judge it by rendering**: on
|
||
hardware, or with `rofi -dmenu -theme <the built rasi>` plus
|
||
`hyprctl layers` for the real width. `checks.rofi-text-width` guards the
|
||
invariant that makes it fit; nothing guards how it looks.
|
||
|
||
## 6. When something fails
|
||
|
||
- Session/login problems: `journalctl -b -u greetd`, then `journalctl
|
||
--user -b`.
|
||
- Theme switch failures: run `nomarchy-state-sync apply <x>` from a
|
||
terminal — the home-manager output streams there. The state file is
|
||
written *before* the rebuild, so after fixing you can just re-run
|
||
`home-manager switch --flake ~/.nomarchy`.
|
||
- Report results faithfully: what booted, what rendered, what you could
|
||
not verify and why.
|