From c7db1174185125b961dc4201590411d37e0ee4ce Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Sat, 13 Jun 2026 16:01:26 -0700 Subject: [PATCH] fix(ci): record a FAILED deployment when the preview deploy aborts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the deploy job fails after the build (artifact validation rejects the bundle, wrangler upload errors, etc.), it produced no preview and recorded no Deployment — so Reviewbot's before/after 'after' cell sat on an eternal 'Rendering preview…' spinner with no signal it would never come (exactly the .zip-allowlist regression). Add a failure step (if: failure() && CF creds present) that resolves the PR the same fork-safe way and records a 'failure' deployment_status. Reviewbot already maps a failed deployment_status to a terminal 'preview deploy failed' card, so the spinner now resolves to an honest terminal state on any deploy abort. --- .github/workflows/ui-preview-deploy.yml | 56 +++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/.github/workflows/ui-preview-deploy.yml b/.github/workflows/ui-preview-deploy.yml index 5414420b68..38bf95fcd8 100644 --- a/.github/workflows/ui-preview-deploy.yml +++ b/.github/workflows/ui-preview-deploy.yml @@ -239,3 +239,59 @@ jobs: description: "Preview ready", }); core.notice(`Preview deployment recorded for PR #${prNumber}: ${url}`); + + - name: Record FAILED deployment for Reviewbot + # Runs when an earlier step in this job failed (artifact validation rejected the bundle, the + # wrangler upload errored, etc.) — i.e. a deploy was attempted but never produced a preview. + # Record a `failure` deployment_status so Reviewbot flips the "after" cell from an eternal + # spinner to a terminal "preview deploy failed" card, instead of waiting forever for a success + # event that will never come. Gated on CF creds so a credential-less skip records nothing. + if: failure() && steps.cfg.outputs.ready == 'true' + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const sha = context.payload.workflow_run.head_sha; // GitHub-set; never fork-supplied + const slug = `${context.repo.owner}/${context.repo.repo}`; + // Same fork-safe PR resolution as the success step above. + let prNumber = context.payload.workflow_run.pull_requests?.[0]?.number; + if (!prNumber) { + const assoc = await github.rest.repos.listPullRequestsAssociatedWithCommit({ + owner: context.repo.owner, + repo: context.repo.repo, + commit_sha: sha, + }); + prNumber = assoc.data.find((p) => p.state === "open" && p.base.repo.full_name === slug)?.number; + } + if (!prNumber) { + const openPrs = await github.paginate(github.rest.pulls.list, { + owner: context.repo.owner, + repo: context.repo.repo, + state: "open", + per_page: 100, + }); + prNumber = openPrs.find((p) => p.head.sha === sha)?.number; + } + if (!prNumber) { + core.warning(`Preview deploy failed but could not resolve a PR for ${sha} — no failure status recorded.`); + return; + } + const deployment = await github.rest.repos.createDeployment({ + owner: context.repo.owner, + repo: context.repo.repo, + ref: sha, + environment: `preview/pr-${prNumber}`, + auto_merge: false, + required_contexts: [], + transient_environment: true, + description: "Gittensory UI preview (failed)", + payload: JSON.stringify({ pr: prNumber, head_sha: sha }), + }); + await github.rest.repos.createDeploymentStatus({ + owner: context.repo.owner, + repo: context.repo.repo, + deployment_id: deployment.data.id, + state: "failure", + environment: `preview/pr-${prNumber}`, + description: "Preview deploy failed", + }); + core.notice(`Recorded FAILED preview deployment for PR #${prNumber}.`);