Repository navigation
fix(runner): rebuild PR-pinned demos when the PR gets a new commit (DEV-3338) - #416
Merged
Merged
Conversation
…EV-3338) A demo pinned to a PR number kept serving the PR's first build: the build cache key held the number, which never changes while the commit behind it does, and builds are cached forever. PR builds are now keyed on, and install, the commit pkg.pr.new serves for the PR (x-commit-key on a HEAD). Opening such a demo compares that commit with the one its artifact was built from; when the PR has moved on, one view claims a rebuild in D1 (atomic, never twice for the same commit, so a broken commit costs one container) and every viewer gets the self-refreshing wait page until it lands. Builds look the commit up fresh; views use a 60s KV copy and confirm with pkg.pr.new before claiming, so a stale colo cannot rebuild a demo backwards, and the PR version itself is read from D1 rather than the cached row. Every build path records the commit it starts as attempted. Migration 0010 adds ht_built_sha and ht_attempt_sha. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- A PR build whose commit lookup failed fell back to the bare-number build cache key, the one that held the stale PR builds; it now skips the cache. - A view that lost the claim to a concurrent view served the old build; it now waits for the same rebuild. - A first build still running showed the "newer commit" page; the refresh is only considered once there is an artifact to replace. - A refresh check that throws (D1, KV write limit) serves the current build instead of a 500. - Undoing a claim whose job could not be scheduled restores updated_at too, so a reclaimed stale row is not held for another 30 minutes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit bca52eb. Configure here.
Checking after the read served the document read before a rebuild that finished in between, and never ran for a demo with no artifact, so a demo whose first build failed was never rebuilt for the PR's next commit. The check now runs first; with no artifact to replace, a claimed refresh shows the existing still-building page. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 7, 2026
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.

Context
A demo built against an open PR (
ht_version= the PR number) kept serving the PR's first build after the PR got new commits, afterupdate_demo, and even for a brand-new demo with the same files. The cause is the build cache: the key wasv2:<framework>:<PR number>:<filesHash>and builds are cached forever, but a PR number names whatever commit the PR has now. This blocks the common flow where a PR's demo link should show the PR's current behavior after review changes.What this PR does:
x-commit-keyheader of aHEAD https://pkg.pr.new/handsontable@<pr>. The cache key becomes…:<pr>@<sha>:…, and the copy ofpackage.jsonsent to the build container pins everypkg.pr.new/<pkg>@<pr>dependency to@<sha>. The stored source keeps the PR number; pinning the sha there would freeze the demo, because the version resolver ranks thepackage.jsonpin above the stored ref./d/<id>/or/embed/<id>/, the worker compares the PR's current commit with the one the artifact was built from (newht_built_shacolumn). If the PR has moved on, one view claims a rebuild through the existing BuildJob alarm, and every viewer (including one that lost the claim race) gets a self-refreshing "Updating to the latest commit (abc1234)" wait page (503) until the new build lands. Released versions and nightlies are untouched and never contact pkg.pr.new. A first build still running keeps its existing "still building" page.UPDATE, so two viewers cannot start two builds. A commit that was already attempted is never claimed again (newht_attempt_shacolumn), so a commit that fails to build costs one container, not one per view. A refresh stuck past the 30-minute stale window can be reclaimed, matching PATCH. The PR version itself is also read from D1, so a cached row cannot refresh a demo its owner already moved off the PR.budgetGateas an anonymous caller.update_demowith unchanged files now picks up a PR's new commit, with no hot-mcp change.0010_pr_build_sha.sqladdsht_built_shaandht_attempt_sha. CI applies migrations before the API deploy (master.yml).Out of scope, still tracked on DEV-3338:
ht_versionis still refused by the second validation (item 2).parcelenvironment (/edit,/share, and the javascript/typescript/react/react-js "Open live demo" links in the handsontable PR comment; vue runs onvue-cliand works) fails on pkg.pr.new builds. Measured locally, the cause is different from the ticket's: the identical?./??line works from registry 18.1.1 but fails when handsontable comes from a URL dependency, so wideningHOT_BLOCKING_SYNTAXwould not fix it (about 330 files would need downleveling).What to expect after deploy:
d/:id/embed/:idis already excluded from theapi-5xx-ratealert.Types of changes
runner/) changeHow was this verified?
runner/pipeline/pr-demo-refresh.test.mjs(22 tests) covers:package.jsonpinned to the sha;ht_built_shawithout contacting pkg.pr.new;updated_at), and a throwing refresh check serving the current build.share.ts/index.ts/snapshot-jobs.ts, the behavioral tests fail and the guard tests pass.UPDATEwith all migrations (0001–0010) applied innode:sqlite. First claim: 1 row. A second claim while building: 0. The same commit after a failure: 0. A new commit: 1.pnpm testinrunner/passes 2717/2717. I traced the run to confirm no test makes a real request to pkg.pr.new; the one existing spec that started to is now stubbed.tsc --noEmitonworkers/apipasses, and so doespnpm -r run typecheckafterpnpm build.curl -I: a HEAD returnsx-commit-keywith the full sha andaccess-control-allow-origin: *.@13766,@f66dd0dand@<full sha>return the same etag. The sha URL resolves forhandsontable,@handsontable/react-wrapper,@handsontable/vue3and@handsontable/angular-wrapper.meta.changes, the outbound HEAD from a Worker, and the DO path. Post-deploy check:wrangler d1 execute handsontable-demos --remote --command "SELECT build_status, ht_built_sha, ht_attempt_sha FROM demos WHERE id='1g4g311j4o'".Checklist
runner/config/frameworks.json(see CONTRIBUTING.md); otherwise it won't appear on demos.handsontable.com (n/a, no example changed)pnpm build(andpnpm dev) in the affected example/server-example locally (runner packages built;pnpm devnot run for this change)Related issue(s):
Note
Medium Risk
Changes the public demo view and build-cache paths, triggers container rebuilds from anonymous views, and depends on pkg.pr.new HEAD; mitigated by atomic D1 claims, graceful degradation, and broad pipeline tests.
Overview
Fixes DEV-3338: demos pinned to a PR number (
ht_version= PR id) no longer stay on the first commit forever because the build cache keyed only on the PR number.Build path: PR builds resolve the current commit via
HEADto pkg.pr.new (x-commit-key), use cache keys like13766@<sha>, pinpackage.jsondeps to that sha in the container, and persistht_built_sha/ht_attempt_sha(migration0010). Unknown commits skip cache read/write so stale bare-number entries are not reused. MCP create/rebuild and snapshot jobs passprShathrough the same logic.View path: Document loads on
/d/and/embed/callrefreshPrDemo, which compares live PR commit toht_built_sha, atomically claims one rebuild in D1 (no double-schedule, no retry of failed attempts), schedules BuildJob, and returns a 503 “Updating to the latest commit” page—or keeps serving the last good build on budget/pkg.pr.new/DB failures.Tests: New
pr-demo-refresh.test.mjsplus harness stubs for pkg.pr.new HEAD and PR-refreshUPDATEsemantics.Reviewed by Cursor Bugbot for commit 09cede9. Bugbot is set up for automated code reviews on this repo. Configure here.