feat(review): GitHub-Actions build-and-serve visual-capture fallback#4131
Conversation
…4112) Add an opt-in review.visual.actions_fallback that dispatches .github/workflows/visual-capture-fallback.yml (contents: read, zero secrets) when the existing preview discovery chain finds no CI-produced preview at all. The dispatched job builds, serves, and screenshots the PR's own code inside its own ephemeral runner using its preinstalled headless Chrome, then uploads the PNGs as a GitHub Actions artifact -- it never holds or needs a credential. On completion, a workflow_run webhook handler downloads that artifact using gittensory's own already-trusted installation token (never a token from the untrusted job), validated against an SSRF allowlist extension for the GitHub Actions artifact-storage hosts, and stores the shots in R2 for buildCapture to serve as the after render.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
gittensory-ui | 1b4b7d1 | Commit Preview URL Branch Preview URL |
Jul 08 2026, 06:10 AM |
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4131 +/- ##
==========================================
+ Coverage 93.68% 93.71% +0.02%
==========================================
Files 384 385 +1
Lines 36007 36195 +188
Branches 13213 13263 +50
==========================================
+ Hits 33733 33919 +186
Misses 1618 1618
- Partials 656 658 +2
🚀 New features to boost your workflow:
|
|
Caution 🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥 🛑 Gittensory review result - fixes requiredReview updated: 2026-07-08 06:53:34 UTC
🛑 Suggested Action - Manual Review
Nits — 1 non-blocking
Why this is blocked
CI checks failing
Linked issue satisfactionAddressed Review context
Contributor next steps
Signal definitions
🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
…back dispatch The fallback workflow runs with concurrency.cancel-in-progress: true per head SHA, so a recapture-poll retry firing before a slower build finishes would dispatch a second run, cancel the first, and repeat forever. Check for an already-queued/in-progress run for the same (PR, head SHA) before dispatching.
Summary
preview-url.ts's existing discovery chain (Deployments API → commit checks → cloudflare-bot PR comment → an explicitpreview.url_template) finds no CI-produced preview at all for a PR, and the new opt-inreview.visual.actions_fallbackconfig is enabled, gittensory dispatches.github/workflows/visual-capture-fallback.yml— a fork-safe,contents: read-only, zero-secrets workflow that builds, serves, and screenshots the PR's own code inside its own ephemeral GitHub Actions runner using the runner's preinstalled headless Chrome — and uses the result as the "after" shot.127.0.0.1inside its own sandboxed runner. It uploads its captured PNGs as a GitHub Actions artifact and stops. On completion, a newworkflow_runwebhook handler (maybeCaptureOnActionsFallbackWorkflowRuninsrc/queue/processors.ts) downloads that artifact using gittensory's own already-trusted installation token — never a token that passed through the untrusted job — validated against a new SSRF allowlist extension (isGithubArtifactStorageUrlinsrc/review/visual/actions-fallback.ts) for GitHub's Actions artifact-storage hosts, then stores the extracted PNGs in R2 forbuildCaptureto serve.workflow_dispatchagainstref: <default-branch>), so a contributor can never smuggle a modified workflow definition through their own PR branch.review.visual.actions_fallback: false(documented in.gittensory.yml.example/config/examples/gittensory.full.yml) means byte-identical behavior to today; it only activates when a repo has both opted in AND has no other preview source.origin/mainafter two sibling PRs in this same epic landed (fix(review): verify + fix theme-forcing and viewport matrix in visual capture #4109 localStorage theme-forcing, feat(review): let a bot-captured before/after satisfy the screenshot-table gate #4128 bot-capture-satisfies-screenshot-gate); both are preserved and composed with this change (seecapture.ts/focus-manifest.ts).Scope
type(scope): short summaryConventional Commit format, for examplefix(api): restore profile access checks.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Closes #123) — a linked open issue is required for every contributor PR.Validation
git diff --checknpm run actionlintnpm run typechecknpm run test:coveragelocally — full unsharded run: 582 files / 11829 tests (11822 passed, 7 pre-existing skips), 93.91% statements / 92.92% branches / 93.14% functions / 94.52% lines overall; the newsrc/review/visual/actions-fallback.tsmodule is 100% lines/branches/functions (verified viacoverage/lcov.info).npm run test:workersnpm run build:mcpnpm run test:mcp-packnpm run ui:openapi:checknpm run ui:lintnpm run ui:typechecknpm run ui:buildnpm audit --audit-level=moderate— 0 vulnerabilitiestest/unit/actions-fallback.test.ts(47 tests, incl. a hand-rolled ZIP reader exercised against real STORED/DEFLATE fixtures),test/unit/actions-fallback-webhook.test.ts(13 tests, end-to-end throughprocessJob), plus new cases intest/unit/visual-capture.test.tsandtest/unit/focus-manifest.test.ts.Also ran (not in the template but part of this repo's full
test:ci):npm run db:migrations:check,npm run db:schema-drift:check,npm run selfhost:env-reference:check,npm run selfhost:validate-observability,npm run cf-typegen:check,npm run test:engine-parity,npm run build:miner,npm run test:miner-pack,npm run rees:test,npm run ui:openapi:settings-parity,npm run ui:version-audit,npm run docs:drift-check,npm run manifest:drift-check,npm run command-reference:check,npm run ui:test— all green.Safety
isGithubArtifactStorageUrlrejects non-allowlisted hosts, private/loopback hosts, and non-https; the webhook handler is tested for name/trigger-type mismatch, failed runs, and unresolvable correlation, all of which must be silently ignored.UI Evidencesection below — N/A, backend/CI-only change, no visible UI surface..gittensory.yml.example/config/examples/gittensory.full.ymldocument the newactions_fallbackfield;CHANGELOG.mduntouched.UI Evidence
N/A — this PR has no visible UI/frontend surface (a new GitHub Actions workflow, a queue/webhook handler, and config plumbing only).
Notes
.github/workflows/visual-capture-fallback.ymlinto their own.github/workflows/at that exact path/name (documented in the workflow's own header) before settingreview.visual.actions_fallback: true— this is not needed forgittensory-ui/metagraphed, which already have their own preview-deploy pipeline.Closes #4112