All checks were successful
Check / eval (push) Successful in 3m6s
Doctor tripwire was self-hiding under waybar (PATH miss / weak glyph). Call nomarchy-doctor by store path, strip ANSI tooltips, use + format + signal 10 (generated and whole-swap). theme-shot asserts class:bad, guest btop.theme, and pokes RTMIN+10 before desktop shots. V2: theme-shot green for rose-pine, summer-night, catppuccin, everforest, vantablack; bar crops show the red alert-circle. btop TUI visual is softGL-blocked (Ghostty) → HARDWARE-QUEUE. Closes #51 and #52.
106 lines
5.2 KiB
Nix
106 lines
5.2 KiB
Nix
# Themed-desktop capture harness (item 28 slice c) — boots the full
|
|
# desktop headlessly (software-GL Hyprland) at 1920x1080 and QMP-dumps
|
|
# screenshots into $out: desktop.png, menu.png (rofi root open), and
|
|
# btop.png (Ghostty running btop, when the terminal starts under softGL).
|
|
# Also asserts nomarchy-doctor-status JSON (class:bad) — the harness
|
|
# seeds an untracked theme-state.json, so doctor always reports ✖.
|
|
# Maintainer tool, NOT a checks.* gate (a full-desktop VM is far too
|
|
# heavy for CI). Impure: THEME env picks the theme slug.
|
|
# THEME=<slug> nix build --impure -f tools/theme-shot.nix --no-link --print-out-paths
|
|
# The guest gets a minimal ~/.nomarchy (just the theme JSON as
|
|
# theme-state.json), seeded via tmpfiles BEFORE greetd starts the
|
|
# session, so the session's own exec-once wallpaper paint works —
|
|
# assets come from the themesDir baked into the tool. Bar, menus AND
|
|
# wallpaper render.
|
|
let
|
|
flake = builtins.getFlake ("git+file://" + toString ../.);
|
|
inherit (flake.inputs) nixpkgs home-manager;
|
|
pkgs = import nixpkgs {
|
|
system = "x86_64-linux";
|
|
overlays = [ flake.overlays.default ];
|
|
config.allowUnfree = true;
|
|
};
|
|
slug = let s = builtins.getEnv "THEME"; in if s == "" then "boreal" else s;
|
|
in
|
|
pkgs.testers.runNixOSTest {
|
|
name = "theme-shot-${slug}";
|
|
node.pkgsReadOnly = false; # nomarchy sets nixpkgs.config
|
|
node.specialArgs = { username = "nomarchy"; };
|
|
nodes.machine = { pkgs, lib, ... }: {
|
|
imports = [ flake.nixosModules.nomarchy home-manager.nixosModules.home-manager ];
|
|
virtualisation.memorySize = 4096;
|
|
virtualisation.cores = 4;
|
|
# Software GL for Hyprland (GLES/aquamarine) without a host GPU.
|
|
hardware.graphics.enable = true;
|
|
environment.variables.LIBGL_ALWAYS_SOFTWARE = "1";
|
|
boot.initrd.availableKernelModules = [ "virtio_gpu" ];
|
|
virtualisation.qemu.options = [ "-vga none" "-device virtio-gpu-pci,xres=1920,yres=1080" ];
|
|
|
|
users.users.nomarchy = {
|
|
isNormalUser = true;
|
|
password = "test";
|
|
extraGroups = [ "wheel" "video" ];
|
|
};
|
|
nomarchy.system.stateFile = flake + "/themes/${slug}.json";
|
|
# Seed the runtime state (theme-sync reads $NOMARCHY_PATH, default
|
|
# ~/.nomarchy) before greetd auto-starts the session, so the
|
|
# exec-once `nomarchy-theme-sync wallpaper` finds it and paints.
|
|
# NixOS activation creates /home/nomarchy before systemd runs.
|
|
systemd.tmpfiles.rules = [
|
|
"d /home/nomarchy/.nomarchy 0755 nomarchy users -"
|
|
"C /home/nomarchy/.nomarchy/theme-state.json 0644 nomarchy users - ${flake + "/themes/${slug}.json"}"
|
|
];
|
|
services.greetd.settings.initial_session = {
|
|
command = "start-hyprland";
|
|
user = "nomarchy";
|
|
};
|
|
home-manager.useGlobalPkgs = true; # HM must see the overlay'd node pkgs
|
|
home-manager.users.nomarchy = {
|
|
imports = [ flake.homeModules.nomarchy ];
|
|
nomarchy.stateFile = flake + "/themes/${slug}.json";
|
|
nomarchy.idle.enable = false; # no lock/dpms during the shot
|
|
};
|
|
};
|
|
testScript = ''
|
|
machine.wait_for_unit("multi-user.target")
|
|
machine.wait_for_file("/run/user/1000/hypr", 180)
|
|
# hyprctl dispatch helper (session env resolved from the runtime dir).
|
|
hy = "su - nomarchy -c 'XDG_RUNTIME_DIR=/run/user/1000 HYPRLAND_INSTANCE_SIGNATURE=$(ls /run/user/1000/hypr | head -1) hyprctl dispatch"
|
|
# The session exec-once paints the wallpaper (softGL first paint at
|
|
# 1920x1080 is slow) — give it a generous settle.
|
|
machine.sleep(30)
|
|
# Doctor indicator contract: untracked theme-state → ✖ → status JSON
|
|
# with class:bad and the glyph (Waybar self-shows the module).
|
|
status = machine.succeed("su - nomarchy -c 'nomarchy-doctor-status'")
|
|
assert '"class":"bad"' in status, f"doctor-status not bad: {status!r}"
|
|
assert ("" in status or "" in status), f"doctor glyph missing: {status!r}"
|
|
# Baked btop theme must be present (asset or generated) for the slug.
|
|
machine.succeed("test -f /home/nomarchy/.config/btop/themes/nomarchy.theme")
|
|
machine.succeed("grep -q 'theme\\[main_bg\\]' /home/nomarchy/.config/btop/themes/nomarchy.theme")
|
|
# Poke custom/doctor (signal 10) so a first-poll miss at session start
|
|
# doesn't leave the tripwire blank for the full 300s interval.
|
|
machine.execute("su - nomarchy -c 'pkill -RTMIN+10 waybar' >/dev/null 2>&1 || true")
|
|
machine.sleep(3)
|
|
# Park the cursor in the bottom-right corner so it doesn't sit in the
|
|
# middle of the desktop shot (that frame doubles as the theme-picker
|
|
# preview.png source).
|
|
machine.execute(hy + " movecursor 1912 1075' >/dev/null 2>&1")
|
|
machine.sleep(1)
|
|
machine.screenshot("desktop")
|
|
# Open the root menu for the polish review.
|
|
machine.execute(hy + " exec nomarchy-menu' >/dev/null 2>&1")
|
|
machine.sleep(8)
|
|
machine.screenshot("menu")
|
|
# Close menu, launch btop in Ghostty for the per-theme btop.theme shot.
|
|
# softGL may fail to start Ghostty — btop.png is best-effort then; the
|
|
# file assertions above still prove the theme is baked into the guest.
|
|
machine.execute("su - nomarchy -c 'pkill -x rofi' >/dev/null 2>&1 || true")
|
|
machine.sleep(1)
|
|
machine.execute(
|
|
hy + " exec ghostty --gtk-single-instance=false -e btop' >/dev/null 2>&1"
|
|
)
|
|
machine.sleep(12)
|
|
machine.screenshot("btop")
|
|
'';
|
|
}
|