Repository navigation
ci(previews): deploy a live Studio preview for each PR - #2054
Draft
dawsontoth wants to merge 6 commits into
Draft
dawsontoth wants to merge 6 commits into
dawsontoth wants to merge 6 commits into
Conversation
Each PR into stage from a branch of this repository gets a live Studio at https://<branch>.app.<cluster domain>/, dropped when the PR closes. preview-build.yaml builds the PR's head commit in dev mode, without telemetry, in an unprivileged pull_request run and uploads web/. preview.yaml (pull_request_target, from stage) waits for that build, writes its own static config.yaml and deploys the payload as hdbms-pr-<N> with HarperFast/deploy-action v1.0.0 over OIDC. Fork PRs get no preview, and nothing in it checks out PR code. workflowPrivilege.test.ts now checks that every pull_request_target job has the fork guard and checks out nothing, since the cluster's trust policy pins the file's path, not its content. Co-Authored-By: Claude Opus 5.5 <[email protected]>
- The wait step also matches the build run's head branch, so another PR's run at the same commit is never picked. - The guard test requires the fork guard as a top-level && term, with string literals stripped and no ||, so a negated, quoted or bypassed guard fails it. - deploying.md says previews need the dev CM's CORS entry, sign in with email and password, and serve Harper's plain 404. - Workflow comments keep only the constraints. Co-Authored-By: Claude Opus 5.5 <[email protected]>
- preview-build.yaml names each run "Build PR Preview #<N>", and the wait step matches that title, so another PR's run of the same commit, even from the same branch, is never picked. - A failed runs listing no longer counts as "no build started" after ten minutes; the 30-minute limit still applies. - The fork-guard test refuses any condition with parentheses, braces or ||, so a guard inside a negated group or between two interpolations fails it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Contributor
There was a problem hiding this comment.
Code Review
This pull request adds documentation for PR previews in .github/deploying.md and introduces a test suite in src/lib/workflowPrivilege.test.ts to ensure that pull_request_target workflows are properly guarded and do not execute untrusted code. The feedback recommends enhancing the robustness of the tests by normalizing whitespace when verifying the fork guard condition and adding defensive checks to handle malformed workflow files without crashing.
Previews will live on the central manager's own cluster, where a preview is a worker thread in CM's Harper process. - Both workflows run only when a login in the HARPER_PREVIEW_AUTHORS repository variable (a JSON array) opened the PR and caused the event. Unset, nobody gets a preview: its JavaScript runs with the visitor's dev CM session. - The deploy passes branched_databases: false. Branching would fork every database on the node but system, CM's included; the payload runs no server code, so sharing touches nothing. - The fork-guard test splits the condition at parenthesis depth 0, so a function call such as contains(fromJSON(...)) passes while a guard inside a call, a negated group or a top-level || still fails. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Coverage Report
File CoverageNo changed files found. |
…shared - The cleanup step runs only when HARPER_PREVIEW_TARGET is set and the PR's author is listed, so an unset variable doesn't fail every merge. The gate sits on the step: any other term in the job's if: makes harper preview setup unable to verify that cleanup runs on close. - The setup command in the header and deploying.md now says dataMode=shared, which branched_databases: false needs. - The fork-guard test also refuses index expressions. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…est can't read The fork-guard check now compares with whitespace collapsed, so a guard split across lines still counts. A jobs, job, steps or step value of the wrong type is reported as a problem rather than crashing the test or being skipped, so it fails instead of passing unchecked. Co-Authored-By: Claude Opus 5.5 <[email protected]>
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.
⊙ Problem
A Studio PR has no running build to try: reviewers check out the branch, or wait for
dev. Harper 5.4 hosts a preview per PR (HarperFast/harper#3042), and Harper's own repos should dogfood it with HarperFast/deploy-action, as HarperFast/documentation#797 does for the docs site.🤔 Previews live on the central manager's own cluster
That is Dawson's plan: only the
hdbmsfront end gets previews, athttps://<branch>.app.dev.studio.harperfabric.com/, and they call the one dev CM. Each preview is a worker thread in CM's Harper process, not a sandbox, and the CM answers CORS only for origins it lists (http://localhost:5173today).💡 Solution
preview-build.yamlbuilds the PR's head in an unprivilegedpull_requestrun, in dev mode without telemetry: the bundle calls the dev CM, with no Datadog RUM, GTM or sourcemap upload. It uploadsweb/.preview.yamlis deploy-action's example plus documentation#797's wait: the deploy job waits for that build, writes its own staticconfig.yamlaround it, and deployshdbms-pr-<N>sharing the cluster's data. Nothing checks out PR code.🤔 Static files only, not the deploy template
The template is a fastify route plus
@fastify/static: server code the PR controls. On CM's cluster that code could read CM's deployed.env(its AWS keys) and reach the cluster's CA throughserver.operation, so the preview runs none;preview.yamlwrites the config. The cost: template changes aren't previewed, and previews lack the template's frame guard and cache headers.🤔 Trusted users, shared data
A preview's JavaScript runs with its visitor's dev CM session, so the
HARPER_PREVIEW_AUTHORSvariable lists who gets one; unset, nobody does. Previews share data because branching forks every database butsystem, CM's included. OAuth sign-in lands on dev Studio (AGENTS.md); use email and password on a preview.🤔 Things to look at
The gate, polling, conflicts, permissions, and what isn't here
if:makesharper preview setupunable to verify that cleanup runs on close. An author dropped from the list after deploying leaves a preview that expires on its own.pull_request_target, soworkflow_runcan't deploy. The deploy job polls for the build of its exact commit, by a run title naming the PR. Unlike documentation#797, another PR's build of the same commit can't be picked, and a failed API call isn't read as "no build started". It deploys nothing if the PR moves on or closes, and fails if the build fails, takes over 30 minutes, or never starts.pull_requestrun for it, so the deploy job fails after 10 minutes and says why.stageget one. Both workflows filter on it, the default branch the trust policy pins; PRs intodevorprodrun neither.actions: readon the deploy job, beyond the example, reads the build's runs and downloads its artifact.⚖️ Alternatives
Four weighed
pull_request_target.workflow_rundeploy instead of polling: the trust policy pinsevent_name: pull_request_target, so that run could not exchange a token.previewinstead of a list: every push would wait for an approval.config.yaml, so a PR could load server code into CM's process.📋 Before this merges (Dawson)
None of this has been done. This PR changes no cluster or repository settings.
Harper 5.4 on the CM cluster. Previews need
5.4.0-alpha.2or later (alpha.1predatesenable_previewsandharper preview setup). The CM-previews session's notes say dev and stage run harper-pro 5.3.2; I couldn't check (no credentials, and nothing public reports it).Make previews reachable. A preview's worker binds only a UDS mirror of the secure port, so the cluster needs
http.securePortandtls.unixDomainSockets, or deploys are refused.*.app.dev.studio.harperfabric.comneeds DNS (none today), a certificate, and routing to the preview's socket: the shared ports answer 503 for a preview host.threads.maxIsolated(default 8) caps live previews.Let previews call the CM. Add
https://*.app.dev.studio.harperfabric.comto the cluster'shttp.corsAccessList, keepinghttp://localhost:5173. Wildcard entries need Harper 5.4.0-alpha.1 or later; 5.3 matches exact origins only. The entry trusts every app previewed on that cluster, so keep its previews to Studio.harper preview setup app=hdbms dataMode=shared, signed in as asuper_user, from a checkout of this branch (setup reads.github/workflows/preview.yamland checks it). It creates thehdbms-ci-previewrole and user and both trust policies, enables previews forhdbms, and sets theHARPER_PREVIEW_TARGETrepository variable. WithoutdataMode=shared, the cluster refuses the workflow'sbranched_databases: false.The
HARPER_PREVIEW_AUTHORSrepository variable: a JSON array of the GitHub logins that get previews, such as["dawsontoth"]. Until it's set, nobody gets one.Environments (Settings > Environments): create
previewandpreview-cleanup(onlye2e-devexists) and allowstageunder Deployment branches and tags on both. No required reviewers: the list is the gate, and reviewers would hold every deploy.An Actions policy allowing
pull_request_targetfor.github/workflows/preview.yaml. Studio is public, and GitHub blocks the event in public repositories from December 2. The repository has no policies yet. From the action's README:🔧 Changes
.github/workflows/preview-build.yaml: the unprivileged build. It titles each runBuild PR Preview #<N>for the wait step to find, builds only for listed users, builds in dev mode with noVITE_TELEMETRY_ENABLED, drops the hidden sourcemaps and uploadsweb/..github/workflows/preview.yaml: the deploy job, gated on the list, which needsactions: readto find the build by SHA and title, doesn't read a failed listing as "no build", writesconfig.yamloutside the artifact, runs the pinned action and shares data; and the cleanup job.src/lib/workflowPrivilege.test.ts: everypull_request_targetjob must carry the fork guard as a top-level&&term, outside every parenthesis (string literals stripped, whitespace collapsed; no top-level||, index or second${{ }}), and use no checkout, local action or reusable workflow. A job or step it can’t read fails. The refused shapes are listed beside it. Arun:step that executes the artifact is not caught..github/deploying.md: a PR previews section: who gets one, why previews share data, and that a preview needs the CM's CORS entry and email sign-in.✅ Verification
checkPreviewWorkflowFilefrom harperorigin/main(907ff6bb1), for branchstage: no problems, nothing unverified, nothing unsafe. A PR-head checkout, a missing fork guard on either job, and branchmainare each refused. A list term on the cleanup job'sif:is reported unverified, hence the step gate.--mode dev, noVITE_TELEMETRY_ENABLED) callsdev.studio.harperfabric.comand holds no Datadog token, GTM loader or Reo script: 30 MB without sourcemaps.http://localhost:5173with credentials and answers nothing forhttps://x.app.dev.studio.harperfabric.com.tsc -b, oxlint, dprint, commitlint. Removing any of the new test's checks, or the cleanup job's fork guard, turns it red; so does adding a checkout.verify-pr.yamlruns: fix(auth): verify email with the sign-up browser proof or the password #2050's head found its run and wroterun-id; a commit the PR had left, and merged feat(previews): turn on and manage PR previews from Studio #2045, exited 0 with a notice; another title matched nothing; a failing listing ran to the time limit.Build PR Preview #2054run uploadedpr-2054-build(12 MB). It now skips, with the list unset.Review findings left open
cleanupshares neither the deploy's concurrency group nor itsby_ref. deploy-action re-reads the PR after deploying and drops its own preview if the PR closed (preview.ts), drops by app and PR number only (dropClosedPreview), and its README says to keep cleanup out of the deploy's group.hdbms-pr-*previews.run:step that executes the artifact.🤖 Generated with Claude Code
Related PRs: 1 independent
Complexity: medium
Review-Coverage: authored=claude; ran=cursor-composer,gemini,codex,cursor-grok; late=cursor-grok:adjudicated; adjudicated=domain; declined=muse; rounds=5; full=2 @ 5813c0c
Review-Attention: deep ~40m (sensitive: preview-build.yaml, preview.yaml; decisions: do-less-alternative, preview-in-cm-process, cleanup-gated-by-author-only, allowlist-repo-variable; raised: degraded review) @ 5813c0c