Skip to content

feat(renovate): auto-refresh pinned Nix FOD hashes on dep bump (RIG-2727) - #626

Open
rigel-mintaka wants to merge 3 commits into
mainfrom
compass-repo/rig-2727-renovate-fod-hash-refresh
Open

feat(renovate): auto-refresh pinned Nix FOD hashes on dep bump (RIG-2727)#626
rigel-mintaka wants to merge 3 commits into
mainfrom
compass-repo/rig-2727-renovate-fod-hash-refresh

Conversation

@rigel-mintaka

Copy link
Copy Markdown
Contributor

A Renovate dependency bump moves a pinned Nix fixed-output-derivation (FOD) hash, but nothing regenerates it — so the bump PR fails CI on hash mismatch in fixed-output derivation and can't land without a manual hand-fix. PR #579 (golang.org/x/net → v0.56.0 security bump) hit exactly this: its Gates (moon + pgtest) job failed building compass-guestd-go-modules.drv because the gomod change moved the Go module set but left guest-image/default.nix's pinned vendorHash stale.

Compass pins exactly two FOD hashes outside the vendored forks/ trees:

  • Go vendorHash (buildGoModule compass-guestd) in guest-image/default.nix — moved by a go/go.mod | go/go.sum bump (gomod manager).
  • bun outputHash (recursive FOD of bun install) in agent-image/entrypoint.nix — moved by a bun.lock bump (bun/catalog manager).

Neither is a URL hash nix store prefetch-file can recompute (that is refresh-toolchain-hashes.ts's job for the vendored-binary pins). A vendorHash/outputHash is only knowable by realising the derivation. tools/renovate/refresh-fod-hashes.ts self-gates per FOD on its trigger manifest, fakes the pin to force the mismatch, builds the guest-image rootfs vehicle (which realises both FODs, failing fast at the FOD before the heavy compile/pack), parses the reported got: SRI for that derivation, and writes it back — so the bump PR lands green.

Wired at two sites to survive Renovate's winner-take-all per-branch task slot: top-level postUpgradeTasks (branch mode — gomod and bun/npm-first branches) and the catalog packageRule (update mode — catalog-first branches, where the collapsed branch config evicts the top-level branch task). One command string → one anchored allowedCommands entry. Guarded by config.test.ts (command↔allowlist coupling, both-site wiring, fileFilters coverage) and a refresh-fod-hashes.test.ts regression suite. Verified end-to-end against real nix: the shipped script recomputes both hashes to their exact committed values.

Spec-impact: none. Refs #579. Refs RIG-2727

Co-authored-by: Matt Wilkinson matt@rigel.build

…727)

A Renovate dependency bump moves a pinned Nix fixed-output-derivation (FOD) hash, but nothing regenerates it — so the bump PR fails CI on `hash mismatch in fixed-output derivation` and can't land without a manual hand-fix. PR #579 (`golang.org/x/net` → v0.56.0 security bump) hit exactly this: its `Gates (moon + pgtest)` job failed building `compass-guestd-go-modules.drv` because the gomod change moved the Go module set but left `guest-image/default.nix`'s pinned `vendorHash` stale.

Compass pins exactly two FOD hashes outside the vendored `forks/` trees:

- Go `vendorHash` (buildGoModule compass-guestd) in `guest-image/default.nix` — moved by a `go/go.mod` | `go/go.sum` bump (gomod manager).
- bun `outputHash` (recursive FOD of `bun install`) in `agent-image/entrypoint.nix` — moved by a `bun.lock` bump (bun/catalog manager).

Neither is a URL hash `nix store prefetch-file` can recompute (that is `refresh-toolchain-hashes.ts`'s job for the vendored-binary pins). A `vendorHash`/`outputHash` is only knowable by realising the derivation. `tools/renovate/refresh-fod-hashes.ts` self-gates per FOD on its trigger manifest, fakes the pin to force the mismatch, builds the guest-image rootfs vehicle (which realises both FODs, failing fast at the FOD before the heavy compile/pack), parses the reported `got:` SRI for that derivation, and writes it back — so the bump PR lands green.

Wired at two sites to survive Renovate's winner-take-all per-branch task slot: top-level `postUpgradeTasks` (branch mode — gomod and bun/npm-first branches) and the catalog `packageRule` (update mode — catalog-first branches, where the collapsed branch config evicts the top-level branch task). One command string → one anchored `allowedCommands` entry. Guarded by `config.test.ts` (command↔allowlist coupling, both-site wiring, fileFilters coverage) and a `refresh-fod-hashes.test.ts` regression suite. Verified end-to-end against real nix: the shipped script recomputes both hashes to their exact committed values.

Spec-impact: none. Refs #579. Refs RIG-2727

Co-authored-by: Matt Wilkinson <matt@rigel.build>
@linear-code

linear-code Bot commented Aug 25, 2026

Copy link
Copy Markdown

RIG-2727

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown

Compass engineering docs preview: https://compass-repo-rig-2727-renova.compass-eng-docs.pages.dev

Deployed from compass-repo/rig-2727-renovate-fod-hash-refresh at ea588d1.

rigel-mintaka and others added 2 commits August 25, 2026 17:43
Review-loop fixes on PR #626, additive over the submitted tip:

- Document the verified biome/devenv-channel FOD-refresh exemption. The
  devenv-nixpkgs lockstep branch rewrites bun.lock + the biome catalog pin
  but intentionally omits the FOD refresh; biome is a root-only devDependency
  absent from compass-agent's filtered `bun install --filter '@compass/agent'`
  tree (verified: not in node_modules nor .bun), so a channel bump cannot move
  agent-image/entrypoint.nix's outputHash. Pinned by an explicit config.test.ts
  guard so a future biome-in-compass-agent change forces a revisit.
- Correct the catalog-rule comment: the per-upgrade FOD refresh leg is
  idempotent but NOT free (it re-realises the node_modules FOD once per catalog
  upgrade). Kept last-write-wins; noted why a same-branch short-circuit is
  unsafe without relying on Renovate's grouped-update apply ordering.
- Make the got:-SRI parse robust: scan from the mismatch header to the next
  header instead of a fixed 5-line window, so a future nix that adds context
  lines before `got:` can't silently wedge every FOD build. Add window-boundary
  + no-cross-block-bleed tests.
- Write the recomputed SRI literally (function replacer) so a `$`-bearing value
  can never be interpreted as a replacement pattern. Add a literal-write test.

Co-authored-by: Matt Wilkinson <matt@rigel.build>
…IG-2727)

Round-2 review low finding: parseGotForFragment attributes a nix mismatch
block to a FOD by `drvName.includes(fragment)`. Safe for the current two
fragments ("go-modules"/"node-modules", mutually disjoint), but a future
FOD_ENTRIES edit adding an ambiguous fragment (e.g. a bare "modules") would
silently misattribute a got: SRI and write the wrong hash.

Add a module-load assertion that no drvFragment is a substring of another, so
such an edit fails loud at import rather than misattributing at runtime, plus a
test stating the invariant.

Co-authored-by: Matt Wilkinson <matt@rigel.build>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant