ci: build the pinned objectui SHA on the PR that moves it (#4290) - #4310
Merged
Conversation
`.objectui-sha` is the single source of truth for the vendored Console SPA: release.yml reads it, clones objectui at that commit, builds `@object-ui/console` and copies the dist into `packages/console/`. Editing that one line replaces the entire frontend the platform ships — and nothing on the PR side ever built it. The pin appeared in exactly two workflows, neither a gate: release.yml (post-merge, on the main push) and showcase-smoke.yml (manual + nightly, whose own header says "it never gates PRs"). It is also a root dotfile, so it matched neither of ci.yml's `core` and `docs` filters — #4288 moved the pin 76 commits with six of fourteen checks skipped, including Build Core, Test Core, Build Docs and the Dogfood gate. A SHA that cannot build reached `main` green and would have surfaced at publish time; the only thing between the two was the author remembering to run `scripts/build-console.sh` locally. Adds ci.yml's `Console Pin Gate` — a `console` paths filter over `.objectui-sha`, `scripts/build-console.sh` and the two files that gate them, plus a job that builds `@object-ui/console` at the pin against this tree's `@objectstack/client` and then runs `pnpm check:console-sha`. Two details that keep it honest rather than merely green: - It restores `packages/console/dist` under release.yml's existing cache key (`hashFiles('.objectui-sha', 'scripts/build-console.sh')`), so only a pin the repo has never built misses — and a miss is exactly when the gate has work to do. Watching the workflow and the drift guard therefore costs about a minute, not a full vite build. - The restore/save pair is split, where release.yml uses the combined action, because the combined post-step saves even when the job failed. `build-console.sh` writes the SHA stamp before it asserts the bundle canary, so a canary failure leaves a stamped-but-broken dist that would be cached, restored, then waved through the stamp check. Saving only once every assertion is green is what lets a restored dist carry the same guarantees as a freshly built one. A separate step asserts a real dist is on disk first, since `check:console-sha` deliberately exits 0 when there is no dist at all. Scope, stated plainly: this proves the pinned SHA builds. It does not cover a `packages/client` change breaking the injected-client bundle — that input lives under `packages/**`, and watching it here would rebuild the console on a large fraction of every PR. release.yml remains the only check for that direction. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0165VP1PnZDTvBrKwjC7jm3D
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 8 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #4290.
The gap
.objectui-shais the single source of truth for the vendored Console SPA:release.ymlreads it, clones objectui at that commit, builds@object-ui/consoleand copies the dist intopackages/console/. Editing that one line replaces the entire frontend the platform ships — and nothing on the PR side ever built it.Confirmed the issue's report against
main:release.yml(post-merge, on the main push) andshowcase-smoke.yml(manual + nightly, whose own header says "it never gates PRs").ci.yml'score(packages/**,examples/**,apps/!(docs)/**,package.json,pnpm-lock.yaml,tsconfig.json,.github/workflows/ci.yml) nordocsfilter. A pin-only diff therefore skipped Build Core, Test Core, Build Docs and the Dogfood gate as well — chore(console): refresh the vendored objectui pin to bebaebd39ace #4288 merged a 76-commit pin bump with six of fourteen checks skipped.A SHA that cannot build — a typo, a commit that was force-pushed away, a genuinely broken objectui, a dead bundle canary — reached
maingreen and would have surfaced at publish time. The only thing between the two was the author remembering to runscripts/build-console.shlocally: discipline, not a gate.The change
ci.ymlgains aconsolepaths filter (.objectui-sha,scripts/build-console.sh,scripts/check-console-sha.mjs,.github/workflows/ci.yml) and aConsole Pin Gatejob that builds@object-ui/consoleat the pin against this tree's@objectstack/client, then runspnpm check:console-sha.Two details that keep it honest rather than merely green:
hashFiles('.objectui-sha', 'scripts/build-console.sh')). Actions caches are repo-scoped, so a PR reads whatevermainalready built for this pin — only a pin the repo has never built misses, and a miss is exactly when the gate has work to do. Watching the workflow and the drift guard therefore costs ~a minute, not a full vite build.build-console.shwrites the SHA stamp (line 175) before it asserts the bundle canary (line 178), so a canary failure leaves a stamped-but-broken dist that would be cached, restored by the next run, and waved through the stamp check. Saving only once every assertion is green is what lets a restored dist carry the same guarantees as a freshly built one.Assert a real Console dist is in placestep runs before the drift check, becausecheck:console-shadeliberately exits 0 when there is no dist at all and only warns when a dist carries no stamp. Both are correct for that script and both would be vacuous passes here, where producing a dist is the entire point.Scope, stated plainly
This proves the pinned SHA builds. It does not cover a
packages/clientchange breaking the injected-client bundle — that input lives underpackages/**, and watching it here would rebuild the console on a large fraction of every PR.release.ymlremains the only check for that direction; the job comment says so rather than overclaiming.Cost
Only a moved pin pays the full clone + vite build, and #4288's 76 commits of staleness are the measure of how rare that is. The two self-trigger entries (this workflow, the drift guard) leave the pin untouched and hit the dist cache.
The
consolefilter follows the "a change to the guard runs the guard" rule the repo applies to every other filtered gate — deliberately, given #4291's finding that hand-maintained filters drift and go dormant (#2584, #3855). Unlike thatgeneratedfilter, this one's input set is a single pin file plus its builder, which is literally release.yml's existing cache key rather than a duplicated ledger; a cross-reference comment now points each at the other.The job lives in
ci.ymlrather than a standalone workflow on purpose: apaths:-filtered workflow publishes no check context when it doesn't match, which is the #3622 merge-deadlock. Gated on thefilterjob instead, the job publishes askippedcontext on every PR and can safely be made required.Verification
Both of the gate's paths were exercised, not just the one this PR happens to take.
Restore path — this PR's own CI run (job). The pin is unchanged here, so the key matched what
main's Release job had already cached, proving the cross-workflow key reuse works:48 seconds end to end (05:17:18 → 05:18:06), which is the "watching this workflow is nearly free" claim measured rather than asserted. All 16 checks on the PR are green.
Build path — run locally from a clean clone at the current pin, since CI could only take the cache branch:
(45932 KB matches the ~45900 KB reported in #4290. The restored dist's
dufigure differs because it is measured on a different filesystem; provenance is what the stamp check pins down, and that agrees.)Also checked:
console-pinresolves to the 14 steps intended, withif: needs.filter.outputs.console == 'true'matching thebuild-core/test/dogfoodpattern already in the file. Thefilterjob correctly setconsole=truefor this PR and dispatched the job.pnpm exec turbo run build --filter=@objectstack/client...resolves to client + its 26-package dependency closure and producespackages/client/dist/index.mjs— the artifactbuild-console.shprobes. It is pre-built rather than left to the script's own fallback, because that fallback is a barepnpm buildinpackages/clientthat tsup cannot finish without@objectstack/core/@objectstack/specbuilt; both existing callers pre-build for the same reason.bash -eacross four states — empty restore, healthy dist, dist-without-stamp (the vacuous-pass case), and emptyassets/+ zero-byteindex.html— failing on three and passing on one, with noset -eshort-circuit in the healthy path.Empty changeset — CI and docs only, releases nothing.
Follow-up for a maintainer
Console Pin Gatestill needs adding to branch protection's required checks. Until then it reports but does not block. The job comment flags that its name is the contract, the same trap the dogfood shards note.