Skip to content

feat(review): GitHub-Actions build-and-serve visual-capture fallback#4131

Merged
JSONbored merged 2 commits into
mainfrom
feat/gh-actions-fallback-visual-4112-v2
Jul 8, 2026
Merged

feat(review): GitHub-Actions build-and-serve visual-capture fallback#4131
JSONbored merged 2 commits into
mainfrom
feat/gh-actions-fallback-visual-4112-v2

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

  • Adds a GitHub-Actions build-and-serve fallback for the visual-capture pipeline (feat(review): GitHub-Actions build-and-serve fallback for repos with no CI preview deploy #4112, part of the Epic: extend automated visual-capture convergence (#577) to self-host + metagraphed #3607 convergence epic): when preview-url.ts's existing discovery chain (Deployments API → commit checks → cloudflare-bot PR comment → an explicit preview.url_template) finds no CI-produced preview at all for a PR, and the new opt-in review.visual.actions_fallback config 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.
  • The dispatched job never holds or needs a credential: its network reach never leaves 127.0.0.1 inside its own sandboxed runner. It uploads its captured PNGs as a GitHub Actions artifact and stops. On completion, a new workflow_run webhook handler (maybeCaptureOnActionsFallbackWorkflowRun in src/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 (isGithubArtifactStorageUrl in src/review/visual/actions-fallback.ts) for GitHub's Actions artifact-storage hosts, then stores the extracted PNGs in R2 for buildCapture to serve.
  • Dispatch is always pinned to the repo's default branch (workflow_dispatch against ref: <default-branch>), so a contributor can never smuggle a modified workflow definition through their own PR branch.
  • Fully config-as-code and off by default: 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.
  • Rebased onto origin/main after 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 (see capture.ts/focus-manifest.ts).

Scope

  • The PR title follows type(scope): short summary Conventional Commit format, for example fix(api): restore profile access checks.
  • This PR is focused and does not mix unrelated backend, UI, MCP, docs, dependency, and deploy changes.
  • This follows CONTRIBUTING.md and does not reintroduce GitHub Pages, VitePress, site/, or CNAME.
  • I linked a currently open issue this PR resolves (e.g. Closes #123) — a linked open issue is required for every contributor PR.

Validation

  • git diff --check
  • npm run actionlint
  • npm run typecheck
  • npm run test:coverage locally — 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 new src/review/visual/actions-fallback.ts module is 100% lines/branches/functions (verified via coverage/lcov.info).
  • npm run test:workers
  • npm run build:mcp
  • npm run test:mcp-pack
  • npm run ui:openapi:check
  • npm run ui:lint
  • npm run ui:typecheck
  • npm run ui:build
  • npm audit --audit-level=moderate — 0 vulnerabilities
  • New or changed behavior has unit/integration tests for new branches, fallback paths, and sanitizer boundaries — test/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 through processJob), plus new cases in test/unit/visual-capture.test.ts and test/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

  • No secrets, wallet details, hotkeys, coldkeys, user PATs, private keys, raw trust scores, private rankings, or private maintainer evidence are exposed.
  • Public GitHub text stays sanitized, low-noise, and does not imply compensation guarantees or optimization tactics.
  • Auth, cookie, CORS, GitHub App, Cloudflare, or session changes include negative-path tests — N/A here (no auth/session surface touched), but the new trust boundary (untrusted job → artifact → trusted download) is covered: isGithubArtifactStorageUrl rejects 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.
  • API/OpenAPI/MCP behavior is updated and tested where needed — no public API/OpenAPI/MCP surface changed.
  • UI changes use live API data or real empty/error/loading states, not production mock/demo fallbacks. — N/A, no UI changes.
  • Visible UI changes include a UI Evidence section below — N/A, backend/CI-only change, no visible UI surface.
  • Public docs/changelogs are updated where needed; changelogs are only edited for release-prep PRs — .gittensory.yml.example / config/examples/gittensory.full.yml document the new actions_fallback field; CHANGELOG.md untouched.

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

  • This ships the discovery/trust-boundary/config primitives for the fallback end-to-end; per-route pixel-diff and scroll-GIF support are intentionally not wired for fallback-sourced shots in this first cut (a fallback-sourced "after" has no raw bytes threaded back for diffing) — consistent with this same pipeline's other documented first-cut scope narrowings (e.g. hosted pixel-diff, mobile scroll-GIF).
  • Self-hosted target repos must copy .github/workflows/visual-capture-fallback.yml into their own .github/workflows/ at that exact path/name (documented in the workflow's own header) before setting review.visual.actions_fallback: true — this is not needed for gittensory-ui/metagraphed, which already have their own preview-deploy pipeline.

Closes #4112

…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.
@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

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-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 8, 2026
@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.43750% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 93.71%. Comparing base (470d5fe) to head (4c5f771).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/queue/processors.ts 96.77% 0 Missing and 1 partial ⚠️
src/review/visual/actions-fallback.ts 99.30% 0 Missing and 1 partial ⚠️
src/review/visual/capture.ts 92.85% 0 Missing and 1 partial ⚠️
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     
Files with missing lines Coverage Δ
packages/gittensory-engine/src/focus-manifest.ts 99.15% <100.00%> (+<0.01%) ⬆️
src/queue/processors.ts 95.18% <96.77%> (+0.01%) ⬆️
src/review/visual/actions-fallback.ts 99.30% <99.30%> (ø)
src/review/visual/capture.ts 90.84% <92.85%> (+0.76%) ⬆️
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@loopover-orb

loopover-orb Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Caution

🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥🟥

🛑 Gittensory review result - fixes required

Review updated: 2026-07-08 06:53:34 UTC

13 files · 1 blocker · readiness 100/100 · CI failing · unstable

🛑 Suggested Action - Manual Review

  • Possible leaked secret in the diff (private_key_block) — Remove the secret from the diff, rotate the exposed credential, then re-run the gate.
Nits — 1 non-blocking
  • PR author also opened the linked issue — Link an issue that was opened by a different contributor, or provide a rationale for why this self-authored issue represents genuine discovery work.

Why this is blocked

  • Possible leaked secret in the diff (private_key_block) — Remove the secret from the diff, rotate the exposed credential, then re-run the gate.

CI checks failing

  • codecov/patch — 98.43% of diff hit (target 99.00%)
Signal Result Evidence
Code review ❌ 1 blocker No AI review summary
Linked issue ✅ Linked #4112
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 52 registered-repo PR(s), 43 merged, 496 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 52 PR(s), 496 issue(s).
Gate result ❌ Blocking Repo-configured hard blocker found.
Linked issue satisfaction

Addressed
The PR implements a workflow_dispatch-based fallback pinned to the default branch, a contents:read/no-secrets job, a workflow_run handoff using the bot's own installation token (never a token from the untrusted job), an SSRF allowlist extension (isGithubArtifactStorageUrl) with dedicated tests, and documents activation conditions in the config examples — covering essentially every listed deliverab

Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: Python, TypeScript, JavaScript, Ruby, Go, Kotlin, MDX, Shell
  • Official Gittensor activity: 52 PR(s), 496 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Treat this as maintainer-lane context rather than normal contributor-lane activity.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.

🟩 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.

  • Re-run Gittensory review

@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 8, 2026
…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. manual-review Gittensor contributor context

Development

Successfully merging this pull request may close these issues.

feat(review): GitHub-Actions build-and-serve fallback for repos with no CI preview deploy

1 participant