All checks were successful
Check / eval (push) Successful in 2m57s
- #14: first scheduled lock bump confirmed (8fded63 nomarchy-bump Mon
05:20 UTC 2026-07-06); remove queue item.
- #57 V2: checks.live-install-entry builds nomarchy-live HM gen and
asserts Install Nomarchy.desktop + installer bin + menu gate.
- #41: float/center hyprpolkitagent + pinentry-qt from package app-ids;
capture-float-classes.nix harness (softGL empty); residual portal
class stays NEXT [blocked:hw]. HARDWARE-QUEUE confirm steps.
Verified: live-install-entry + windowrule-syntax green.
125 lines
4.7 KiB
Nix
125 lines
4.7 KiB
Nix
# Capture window classes for BACKLOG #41 float candidates (hyprpolkitagent,
|
|
# pinentry-qt, optional portal). Headless softGL Hyprland; dumps hyprctl
|
|
# clients while dialogs are open into $out/classes.json + classes.txt.
|
|
#
|
|
# nix build --impure -f tools/capture-float-classes.nix --no-link --print-out-paths
|
|
#
|
|
# Maintainer harness — not a checks.* gate (needs interactive dialogs;
|
|
# softGL may drop some Qt windows).
|
|
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;
|
|
};
|
|
in
|
|
pkgs.testers.runNixOSTest {
|
|
name = "capture-float-classes";
|
|
node.pkgsReadOnly = false;
|
|
node.specialArgs = { username = "nomarchy"; };
|
|
nodes.machine = { pkgs, lib, ... }: {
|
|
imports = [ flake.nixosModules.nomarchy home-manager.nixosModules.home-manager ];
|
|
virtualisation.memorySize = 4096;
|
|
virtualisation.cores = 2;
|
|
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=1280,yres=800" ];
|
|
|
|
users.users.nomarchy = {
|
|
isNormalUser = true;
|
|
password = "test";
|
|
extraGroups = [ "wheel" "video" ];
|
|
};
|
|
security.sudo.wheelNeedsPassword = false;
|
|
# PolicyKit test action so pkexec can raise the agent.
|
|
security.polkit.enable = true;
|
|
|
|
nomarchy.system.stateFile = flake + "/theme-state.json";
|
|
systemd.tmpfiles.rules = [
|
|
"d /home/nomarchy/.nomarchy 0755 nomarchy users -"
|
|
"C /home/nomarchy/.nomarchy/theme-state.json 0644 nomarchy users - ${flake + "/theme-state.json"}"
|
|
];
|
|
services.greetd.settings.initial_session = {
|
|
command = "start-hyprland";
|
|
user = "nomarchy";
|
|
};
|
|
home-manager.useGlobalPkgs = true;
|
|
home-manager.users.nomarchy = {
|
|
imports = [ flake.homeModules.nomarchy ];
|
|
nomarchy.stateFile = flake + "/theme-state.json";
|
|
nomarchy.idle.enable = false;
|
|
# Ensure pinentry-qt is the active pinentry (keys.nix default).
|
|
};
|
|
};
|
|
testScript = ''
|
|
machine.wait_for_unit("multi-user.target")
|
|
machine.wait_for_file("/run/user/1000/hypr", 180)
|
|
machine.sleep(15)
|
|
|
|
hy_env = "XDG_RUNTIME_DIR=/run/user/1000 HYPRLAND_INSTANCE_SIGNATURE=$(ls /run/user/1000/hypr | head -1)"
|
|
hyctl = f"su - nomarchy -c '{hy_env} hyprctl"
|
|
|
|
def clients():
|
|
return machine.succeed(f"{hyctl} clients -j'").strip()
|
|
|
|
dumps = []
|
|
|
|
# ── pinentry-qt (keys.nix default) ───────────────────────────────
|
|
machine.execute(
|
|
f"su - nomarchy -c '{hy_env} timeout 8 pinentry-qt' >/tmp/pinentry.log 2>&1 &"
|
|
)
|
|
machine.sleep(3)
|
|
dumps.append(("pinentry", clients()))
|
|
machine.execute("pkill -x pinentry-qt >/dev/null 2>&1 || true")
|
|
machine.sleep(1)
|
|
|
|
# ── hyprpolkitagent via pkexec (passwordless sudo wheel — may still
|
|
# open agent depending on polkit rules; best-effort) ────────────
|
|
machine.execute(
|
|
f"su - nomarchy -c '{hy_env} timeout 8 pkexec true' >/tmp/pkexec.log 2>&1 &"
|
|
)
|
|
machine.sleep(3)
|
|
dumps.append(("pkexec", clients()))
|
|
machine.execute("pkill -x hyprpolkitagent >/dev/null 2>&1 || true")
|
|
machine.execute("pkill -f pkexec >/dev/null 2>&1 || true")
|
|
machine.sleep(1)
|
|
|
|
# ── Always dump current clients for residual ─────────────────────
|
|
dumps.append(("idle", clients()))
|
|
|
|
import json
|
|
out_path = machine.succeed("mktemp -d").strip()
|
|
summary = []
|
|
for label, raw in dumps:
|
|
path = f"{out_path}/{label}.json"
|
|
machine.succeed(f"cat > {path} <<'EOF'\n{raw}\nEOF")
|
|
try:
|
|
data = json.loads(raw) if raw else []
|
|
except Exception as e:
|
|
data = []
|
|
summary.append(f"{label}: parse error {e}")
|
|
classes = sorted({
|
|
c.get("class") or c.get("initialClass") or ""
|
|
for c in (data if isinstance(data, list) else [])
|
|
} - {""})
|
|
titles = sorted({
|
|
c.get("title") or c.get("initialTitle") or ""
|
|
for c in (data if isinstance(data, list) else [])
|
|
} - {""})
|
|
summary.append(f"{label}: classes={classes} titles={titles}")
|
|
|
|
machine.succeed(
|
|
"cat > /tmp/float-class-summary.txt <<'EOF'\n"
|
|
+ "\n".join(summary)
|
|
+ "\nEOF"
|
|
)
|
|
print("\n".join(summary))
|
|
# Copy into the test result via machine.copy_from_vm if available —
|
|
# at minimum assert pinentry produced *some* client dump JSON.
|
|
machine.succeed("test -s /tmp/float-class-summary.txt")
|
|
'';
|
|
}
|