feat(session): first-boot welcome toast (#81)
All checks were successful
Check / eval (push) Successful in 4m23s

One-shot user unit fires a dismissible "You're set" notification on the
first graphical session (SUPER+M / SUPER+T / SUPER+? + Network + Doctor),
then writes settings.firstBootShown into the flake checkout. Skips the
live ISO hostname (hosts/live.nix keeps its own toast). Toggle:
nomarchy.firstBootWelcome.enable. checks.first-boot covers the gate.

Verified: V2 — flake check --no-build; checks.first-boot green; local
shim smoke; downstream-template-home ships the unit.
This commit is contained in:
Bernardo Magri
2026-07-11 10:19:46 +01:00
parent 073adf743d
commit 8f720b1078
12 changed files with 180 additions and 11 deletions

View File

@@ -86,6 +86,7 @@
nomarchy-doctor = final.callPackage ./pkgs/nomarchy-doctor { };
nomarchy-control-center = final.callPackage ./pkgs/nomarchy-control-center { };
nomarchy-battery-notify = final.callPackage ./pkgs/nomarchy-battery-notify { };
nomarchy-first-boot = final.callPackage ./pkgs/nomarchy-first-boot { };
nomarchy-detect-hw = final.callPackage ./pkgs/nomarchy-detect-hw { };
# pull / rebuild / home (+ legacy aliases) — also on HM so a home
# switch can refresh a broken system-package nomarchy-pull.
@@ -124,6 +125,7 @@
nomarchy-doctor = pkgs.nomarchy-doctor;
nomarchy-control-center = pkgs.nomarchy-control-center;
nomarchy-battery-notify = pkgs.nomarchy-battery-notify;
nomarchy-first-boot = pkgs.nomarchy-first-boot;
nomarchy-detect-hw = pkgs.nomarchy-detect-hw;
default = pkgs.nomarchy-theme-sync;
};
@@ -680,6 +682,70 @@
'';
};
# First-session welcome toast gate (#81): fires once when
# settings.firstBootShown is unset, writes the marker via
# theme-sync, silent on re-run; skips hostname nomarchy-live.
# notify-send + theme-sync shimmed on PATH (same pattern as
# battery-notify). Real toast rendering stays session/V3.
first-boot = pkgs.testers.runNixOSTest {
name = "nomarchy-first-boot";
nodes.machine = { ... }: {
environment.systemPackages = [
pkgs.nomarchy-first-boot
pkgs.nomarchy-theme-sync
pkgs.jq
];
};
testScript = ''
machine.wait_for_unit("multi-user.target")
# Writable flake checkout with a valid theme-state (template).
machine.succeed(
"mkdir -p /tmp/fake-nomarchy && "
"cp ${./templates/downstream/theme-state.json} "
"/tmp/fake-nomarchy/theme-state.json"
)
machine.succeed(
"mkdir -p /shim && "
"printf '#!/bin/sh\\necho \"$*\" >> /tmp/notifications\\n' > /shim/notify-send && "
"chmod +x /shim/notify-send"
)
env = (
"PATH=/shim:/run/current-system/sw/bin "
"NOMARCHY_PATH=/tmp/fake-nomarchy "
"HOME=/root"
)
# First run: toast + marker.
machine.succeed(f"{env} nomarchy-first-boot")
out = machine.succeed("cat /tmp/notifications")
assert "You're set" in out, f"welcome toast missing:\n{out}"
assert "SUPER+M" in out and "SUPER+T" in out, f"key pointers missing:\n{out}"
assert "Network" in out or "Wi" in out, f"network pointer missing:\n{out}"
shown = machine.succeed(
f"{env} nomarchy-theme-sync get settings.firstBootShown"
).strip()
assert shown == "true", f"marker not written: {shown!r}"
# Second run: silent (no second toast).
machine.succeed("rm -f /tmp/notifications")
machine.succeed(f"{env} nomarchy-first-boot")
machine.fail("test -s /tmp/notifications")
# Live ISO hostname: skip even with marker cleared.
machine.succeed(
f"{env} nomarchy-theme-sync --quiet set settings.firstBootShown false --no-switch"
)
machine.succeed("rm -f /tmp/notifications")
machine.succeed(f"{env} hostname nomarchy-live || true")
# uname -n is the kernel nodename set via hostnamectl/hostname.
machine.succeed("hostname nomarchy-live")
machine.succeed(f"{env} nomarchy-first-boot")
machine.fail("test -s /tmp/notifications")
'';
};
# Memory-pressure protection (oom.nix): earlyoom must kill a
# runaway allocator BEFORE the kernel thrash point — and must not
# take anything else with it. A bystander unit survives the kill,