diff --git a/.moon/workspace.yml b/.moon/workspace.yml index 773d9d18a..a26561461 100644 --- a/.moon/workspace.yml +++ b/.moon/workspace.yml @@ -175,6 +175,13 @@ projects: # (runInCI:false) — the shell compile itself is covered by the gtk4 e2e + # darwin CI lanes. compass-app-dev: 'tools/compass-app-dev' + # The release-notes generator (compass-distribution §18): assembles the + # Release body appendix + the machine-readable nix-outputs.json manifest from + # the built asset names, the resolved container image and the pinned nix + # toolchain store paths. Registered so its typecheck + unit tests ride the + # moon-driven CI sweep — it was unregistered until RIG-3731, so the release + # lane depended on a tool whose 22 tests never ran in CI. + release-notes: 'tools/release-notes' # File hashing uses moon's in-process native walker (the v2.5+ default), NOT the # git shell-out. This is load-bearing: the git hasher (`git ls-files` then `git diff --git a/tools/release-notes/index.ts b/tools/release-notes/index.ts index a58a36be4..597e20f8d 100644 --- a/tools/release-notes/index.ts +++ b/tools/release-notes/index.ts @@ -282,8 +282,14 @@ async function gatherImage(sha: string): Promise { }); } -/** The `nix path-info --json` record shape (the fields the manifest reads). */ +/** + * The `nix path-info --json` record shape (the fields the manifest reads). + * The array form carries its own `path`; the keyed form does not — there the + * store path IS the object key, so it is modelled separately rather than + * pretending the value carries one. + */ type PathInfoEntry = { path: string; narHash?: string }; +type KeyedPathInfo = Omit; /** * Resolve the toolchain `langs` set to store paths and run `nix path-info` over @@ -305,11 +311,12 @@ async function gatherNixOutputs(): Promise { const infoJson = await $`nix path-info --json ${store}`.quiet().text(); const info = JSON.parse(infoJson) as | PathInfoEntry[] - | Record; - // nix path-info emits an array (newer nix) or an object keyed by path. + | Record; + // nix path-info emits an array (newer nix) or an object keyed by path; in + // the keyed form the key is the authoritative store path. const entries: PathInfoEntry[] = Array.isArray(info) ? info - : Object.entries(info).map(([path, v]) => ({ path, ...v })); + : Object.entries(info).map(([path, v]) => ({ ...v, path })); // A single-store-path query returns exactly one entry; anything else means // `store` did not resolve to one output path and picking [0] would record // an arbitrary identity — fail loud rather than ship a wrong manifest entry.