Skip to content

ci(catalog): fail build if catalog.generated.ts is stale vs source JSON (issue #19) - #21

Merged
johnnyhuy merged 2 commits into
mainfrom
feat/catalog-stale-ci
Jul 30, 2026
Merged

ci(catalog): fail build if catalog.generated.ts is stale vs source JSON (issue #19)#21
johnnyhuy merged 2 commits into
mainfrom
feat/catalog-stale-ci

Conversation

@johnnyhuy

Copy link
Copy Markdown
Contributor

Closes #19.

Problem

The catalog flow is:

  1. Edit src/main/{providers,gateways}/catalog.source.json
  2. Run npm run gen:catalog
  3. Push

Step 2 is easy to forget. Without it the app and CLI ship a stale catalog — types drift silently, new entries never reach the wizard.

The prebuild hook covers local builds but:

  • Doesn't run for npm run typecheck / npm run lint
  • Doesn't fail loudly if gen-catalog.mjs itself errors
  • Doesn't surface the issue in CI without a build step succeeding first

Solution

A direct, byte-for-byte comparison runs in CI before typecheck. If the source JSON has changed but the generated TS hasn't, CI fails with a one-line fix.

Changes

scripts/gen-catalog.mjs (refactor)

  • Exports SOURCES array and buildForSource(source) for reuse by the checker
  • Computes a stable SHA-256 fingerprint (16-hex prefix) of the entries (sorted by id, then keys sorted within objects) and writes it into the file header:
    /**
     * GENERATED FILE — DO NOT EDIT BY HAND.
     * ...
     * fingerprint: 3d35ef2c7f6a58e0  (entries: 18)
     */
    
  • The grep-friendly fingerprint lets future tools do cheap "is this stale?" checks without re-running the generator

scripts/check-catalog-stale.mjs (new)

  • For each source in SOURCES:
    • Read on-disk catalog.generated.ts (or detect ENOENT separately)
    • Build the expected body via buildForSource(source)
    • Compare; if different, log path + heading + actionable hint
  • Exit 0 if everything matches, 1 otherwise
  • Distinct messages for "missing file" vs "out of sync"

npm + CI wiring

  • package.json adds catalog:check script
  • .github/workflows/ci.yml runs npm run catalog:check immediately after npm ci, before typecheck. The prebuild hook remains as a developer convenience.

Verification

$ node scripts/check-catalog-stale.mjs
✓ src/main/providers/catalog.generated.ts
✓ src/main/gateways/catalog.generated.ts

all catalog.generated.ts files are in sync with their sources
exit=0

# mutate JSON without regen
$ node scripts/check-catalog-stale.mjs
✗ .../src/main/providers/catalog.generated.ts
    catalog.generated.ts is stale vs src/main/providers/catalog.source.json.
    Run:  npm run gen:catalog

1 catalog(s) out of date.
exit=1

# delete a generated file
$ node scripts/check-catalog-stale.mjs
✗ .../src/main/gateways/catalog.generated.ts
    catalog.generated.ts is missing. Run: npm run gen:catalog

1 catalog(s) out of date.
exit=1

Local CI:

Out of scope

  • Pinning the fingerprint as a separate CI assertion step (the == comparison catches any drift; the comment is for humans/git-diffs)
  • Auto-running gen:catalog from CI (intentional — keep developer step explicit so the diff is reviewable)

Closes #19.

- scripts/gen-catalog.mjs:
  - Export buildForSource() so the stale-check can re-emit the expected
    body without running the full regen.
  - Stable stringify: sort entries by id, sort object keys, sha256-fingerprint
    the result, write as a comment header line.
  - Entry point at the bottom still produces the on-disk files when run
    directly.

- scripts/check-catalog-stale.mjs:
  - For each source: rebuild the expected body via buildForSource(), compare
    byte-for-byte against the on-disk catalog.generated.ts.
  - Exits 0 if everything is in sync, 1 otherwise.
  - Reports each stale source with its heading and an actionable hint:
    "Run: npm run gen:catalog".
  - Distinguishes "missing file" from "out of sync" cases.

- Regenerated catalog.generated.ts files now carry
  "fingerprint: <sha256-prefix>  (entries: N)" in the header. Commit-time
  diff stays friendly (git diff shows the new entries + the new fingerprint
  line).
- package.json: catalog:check script
- .github/workflows/ci.yml: app job runs catalog:check immediately after
  npm ci, before typecheck. This is the failing-fast layer for the
  fingerprint loop above — if a PR edits catalog.source.json without
  running npm run gen:catalog, CI surfaces it with a direct error
  message and the fix in one line.

The prebuild hook remains for developer convenience (so local builds
still auto-regen), but the CI check is authoritative.
@johnnyhuy
johnnyhuy merged commit 0373941 into main Jul 30, 2026
2 checks passed
@johnnyhuy
johnnyhuy deleted the feat/catalog-stale-ci branch July 30, 2026 20:40
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.

CI: fail build if catalog.generated.ts is stale vs catalog.source.json

1 participant