All checks were successful
Check / eval (push) Successful in 3m51s
Verifying Bernardo's #107 upgrade path (legacy theme-state.json checkout → new code) turned up a footgun in state-read.nix's own help text. It said: • Still on theme-state.json? Rename it to state.json (…) On a git flake only tracked files exist, so a hand-rename leaves state.json untracked and therefore invisible — producing exactly the "state file is missing" error the tip hangs off. Point at the menu write (which stages the rename via git add --intent-to-add + git rm), name `git add state.json` for the manual path, and add a bullet for "file is on disk but eval says missing", which is otherwise a genuinely confusing dead end. Message verified by rendering the real failure, not by reading the source. The rename itself is safe and needs no code change: with identical content, theme-state.json and state.json produce byte-identical derivations (HM 2yqqy8h…, system 9qp9l3g…), so it cannot change what boots. Legacy eval, the migration write, and re-eval on a dirty/intent-to-add tree all pass. #133 filed: no check pins the compat shim, so it can rot before we drop it.
95 lines
3.0 KiB
Nix
95 lines
3.0 KiB
Nix
# Pure state.json loader — fail closed with a short, actionable
|
|
# message instead of a raw `readFile` / `fromJSON` stack buried in a
|
|
# consumer. Used by modules/home/theme.nix (required path), the NixOS
|
|
# stateFile consumers, and lib.mkFlake (early gate).
|
|
#
|
|
# Field-level schema checks stay in theme.nix (post-defaults). This file
|
|
# only gates *existence* and *JSON-shape* so the first failure the user
|
|
# sees points at the state file, not at nightlight.nix.
|
|
#
|
|
# Callers may still pass a legacy path (state.json); messages name
|
|
# whatever path they gave so a half-migrated checkout is still debuggable.
|
|
{ lib }:
|
|
|
|
path:
|
|
|
|
let
|
|
pathStr = toString path;
|
|
baseName = baseNameOf pathStr;
|
|
|
|
tip = ''
|
|
Fix:
|
|
• Missing file → copy the template next to your flake.nix:
|
|
templates/downstream/state.json
|
|
or regenerate from a preset:
|
|
nomarchy-state-sync apply boreal
|
|
• Bad syntax / wrong shape → edit ${baseName} (trailing commas
|
|
are the usual culprit) or reset with `apply` as above.
|
|
• Field-level schema (colors, ui, border, …):
|
|
nomarchy-state-sync validate
|
|
Eval-time field checks live in modules/home/theme.nix.
|
|
• Still on theme-state.json? Easiest is any menu write /
|
|
`nomarchy-state-sync set` — it migrates and stages the rename
|
|
for you. Renaming by hand needs `git add state.json` too.
|
|
• File IS on disk but eval still says missing? On a git flake only
|
|
*tracked* files exist — an untracked state.json is invisible:
|
|
git add state.json'';
|
|
|
|
missingMsg = ''
|
|
|
|
Nomarchy: state file is missing:
|
|
${pathStr}
|
|
|
|
This file is required (appearance + menu settings). Add state.json to
|
|
your flake checkout — git-tracked — so evaluation stays pure.
|
|
${tip}'';
|
|
|
|
emptyMsg = ''
|
|
|
|
Nomarchy: state file is empty:
|
|
${pathStr}
|
|
${tip}'';
|
|
|
|
notObjectMsg = ''
|
|
|
|
Nomarchy: state file must be a JSON object `{ ... }`:
|
|
${pathStr}
|
|
${tip}'';
|
|
|
|
# lib.trim / lib.strings.trim — strip leading/trailing whitespace so an
|
|
# all-whitespace file counts as empty and a BOM-less `{` is recognized.
|
|
strip = s:
|
|
let
|
|
# Prefer lib.trim when present (nixpkgs ≥ 23.11); fall back so a
|
|
# very old pin still gates missing/non-object.
|
|
trim =
|
|
if lib ? trim then lib.trim
|
|
else if lib.strings ? trim then lib.strings.trim
|
|
else (x: x);
|
|
in trim s;
|
|
|
|
in
|
|
if !(builtins.pathExists path) then
|
|
throw missingMsg
|
|
else
|
|
let
|
|
raw = builtins.readFile path;
|
|
stripped = strip raw;
|
|
in
|
|
if stripped == "" then
|
|
throw emptyMsg
|
|
# Reject non-objects before fromJSON where we can (null / array / string
|
|
# literals). Subtle syntax errors still surface from fromJSON itself —
|
|
# those messages already include line/column; the path tip above is the
|
|
# part a raw stack used to bury.
|
|
else if builtins.match "[[:space:]]*\\{.*" stripped == null then
|
|
throw notObjectMsg
|
|
else
|
|
let
|
|
value = builtins.fromJSON raw;
|
|
in
|
|
if !(builtins.isAttrs value) then
|
|
throw notObjectMsg
|
|
else
|
|
value
|