diff --git a/flake.nix b/flake.nix index b5d6940..7936d57 100644 --- a/flake.nix +++ b/flake.nix @@ -2204,10 +2204,14 @@ # `home-manager switch` is stubbed via NOMARCHY_REBUILD (that # generic apply path is exercised by every manual theme change); # this isolates the auto decision + apply + rebuild-trigger. + # The state dir is a git repo with autoCommit on, so every set/apply + # also exercises auto_commit against a repo that has ONLY state.json + # — the post-#107 shape whose dead legacy pathspecs once aborted + # every commit silently. auto-theme = pkgs.testers.runNixOSTest { name = "nomarchy-auto-theme"; nodes.machine = { pkgs, ... }: { - environment.systemPackages = [ pkgs.nomarchy-state-sync ]; + environment.systemPackages = [ pkgs.nomarchy-state-sync pkgs.git ]; time.timeZone = "UTC"; # naive HH:MM == the clock we set }; testScript = '' @@ -2220,6 +2224,14 @@ machine.succeed( "echo '{\"slug\":\"summer-day\"}' > /root/.nomarchy/state.json" ) + # Git-tracked like a real downstream flake — no legacy + # theme-state.json/theme.json anywhere in its history. + machine.succeed( + "git -C /root/.nomarchy init -q" + " && git -C /root/.nomarchy add state.json" + " && git -C /root/.nomarchy -c user.name=t -c user.email=t@t" + " commit -qm init" + ) machine.succeed( "printf '#!/bin/sh\\necho switch >> /root/rebuilds\\n' > /root/rebuild" " && chmod +x /root/rebuild" @@ -2229,14 +2241,24 @@ "NOMARCHY_DEFAULT_THEMES=${pkgs.nomarchy-default-themes} " "NOMARCHY_REBUILD=/root/rebuild ") - # Pair + schedule (setup writes only, no rebuild). - for kv in ["settings.autoTheme.enable true", + # Pair + schedule (setup writes only, no rebuild). autoCommit + # first, so every following write must land as its own commit. + for kv in ["settings.autoCommit true", + "settings.autoTheme.enable true", "settings.autoTheme.day summer-day", "settings.autoTheme.night summer-night", "settings.autoTheme.sunrise 07:00", "settings.autoTheme.sunset 20:00"]: machine.succeed(env + "nomarchy-state-sync --quiet set " + kv + " --no-switch") + # Every `set` above auto-committed (the regression: dead legacy + # pathspecs made `git commit` abort, skipping ALL of these). + machine.succeed( + "git -C /root/.nomarchy log --format=%s" + " | grep -qF 'set settings.autoTheme.sunset'" + ) + machine.succeed("git -C /root/.nomarchy diff --quiet HEAD -- state.json") + def slug(): return machine.succeed(env + "nomarchy-state-sync get slug").strip() def rebuilds(): @@ -2253,6 +2275,11 @@ machine.succeed(env + "nomarchy-state-sync auto") assert slug() == "summer-night", f"post-sunset should be night, got {slug()}" assert rebuilds() == 1, f"sunset should trigger one rebuild, got {rebuilds()}" + # The switch auto-committed too — nothing left dirty behind it. + machine.succeed( + "git -C /root/.nomarchy log --format=%s | grep -qF 'apply theme'" + ) + machine.succeed("git -C /root/.nomarchy diff --quiet HEAD -- state.json") # Another tick still after sunset → idempotent, no extra rebuild. machine.succeed("date -s '2026-01-02 20:06:00'") diff --git a/pkgs/nomarchy-state-sync/default.nix b/pkgs/nomarchy-state-sync/default.nix index 3a58de9..eadc837 100644 --- a/pkgs/nomarchy-state-sync/default.nix +++ b/pkgs/nomarchy-state-sync/default.nix @@ -16,7 +16,7 @@ stdenvNoCC.mkDerivation { pname = "nomarchy-state-sync"; - version = "0.5.0"; + version = "0.5.1"; src = ./.; diff --git a/pkgs/nomarchy-state-sync/nomarchy-state-sync.py b/pkgs/nomarchy-state-sync/nomarchy-state-sync.py index fea207c..a338938 100644 --- a/pkgs/nomarchy-state-sync/nomarchy-state-sync.py +++ b/pkgs/nomarchy-state-sync/nomarchy-state-sync.py @@ -189,8 +189,17 @@ def auto_commit(message: str) -> None: if not (FLAKE_DIR / ".git").exists() or shutil.which("git") is None: return git = ["git", "-C", str(FLAKE_DIR)] - # Pathspec: preferred name plus any leftover legacy file still staged. - paths = [STATE_NAME, *LEGACY_STATE_NAMES] + # Pathspec: preferred name plus any legacy file git still knows — in the + # index or in HEAD (a migration's staged `git rm` needs the pathspec to + # ride along). Names known to neither MUST be dropped: unlike `git diff`, + # `git commit` aborts on a pathspec that matches nothing, which silently + # killed every auto-commit once the #107 rename removed the legacy files. + def known(name: str) -> bool: + return subprocess.run(git + ["ls-files", "--error-unmatch", "--", name], + capture_output=True).returncode == 0 \ + or subprocess.run(git + ["cat-file", "-e", f"HEAD:{name}"], + capture_output=True).returncode == 0 + paths = [STATE_NAME] + [n for n in LEGACY_STATE_NAMES if known(n)] # No-op when the file already matches HEAD (a `set` to the same value). # On a repo with no commits yet this diff errors — then just commit. if subprocess.run(git + ["diff", "--quiet", "HEAD", "--"] + paths,