Repository navigation
fix(autofix): skip the PAT-backed jobs when the run has no secrets - #8671
Conversation
yiliang114
left a comment
There was a problem hiding this comment.
LGTM, no blockers. Correct fix for the fork-PR CI noise: GitHub withholds secrets from runs tied to a fork PR's own events, so CI_DEV_BOT_PAT arrives empty and the PAT-backed jobs reddened while doing nothing. Since a job if: can't read the secrets context, route derives 'secretless' from the event shape that produces the empty secret (pull_request/pull_request_review + head is a fork), and review-scan/takeover-ack gate on it; base-context events (issue_comment/schedule/dispatch) correctly stay secret-holding so the comment lane still serves fork PRs. The deleted-fork (null head.repo) case stays secretless, the concurrency group picks up the secretless conjunct so a skipped fork review can't hold the shared per-PR slot, and the review-scan empty-token backstop reds loudly (targets=[], has_targets=false) instead of scanning an empty fleet as healthy. Tests execute the real decide/guard scripts under bash across all event shapes. No P0/P1.
Code Coverage Summary
CLI Package - Full Text ReportCore Package - Full Text ReportFor detailed HTML reports, please see the 'coverage-reports-22.x-ubuntu-latest' artifact from the main CI run. |
Code review — 10 verified findingsThis PR adds a 1.
|
…an that cannot authenticate GitHub hands no repository secrets to a workflow run tied to a pull request whose head lives in a fork — the run header reads `Secret source: None` — so `secrets.CI_DEV_BOT_PAT` arrives empty and every `gh` call made with it is unauthenticated. Route admitted those PRs anyway. The real-time fork branch spent two API reads deciding it, then review-scan spent three more failing and exited 1 on `metadata_fetch_failed` — a reason whose blocked comment promises "a later scheduled scan will retry", true for a 5xx and false for a credential the run was never handed. Every review of a fork PR reddened the workflow while changing nothing, and that noise buried the failures that do need a human. The branch's comment explained the admission by saying the event "runs in BASE-repo context". That holds for the workflow FILE, which is read from base, but not for the credentials. So decline in route, exactly as the `pull_request` label branch already does for the same reason. Nothing that functioned is lost: the admitted path could never authenticate, and the scheduled scan admits fork takeover PRs in repo context on its own. Real-time pickup for fork PRs needs a credentialed lane, which a `workflow_run` bridge provides separately. Chosen over gating the PAT-backed jobs on a route output: that reached the same outcome through a new output, two job conditions and a concurrency expression that hand-mirrored them, while route already declines this class of event one branch away and the two API reads still got paid for. Also add a hard guard on an empty PAT at the top of review-scan. No job `if:` can read the `secrets` context, so a deleted or renamed secret — or a lane nobody has modelled — is invisible until the step looks, and it must stop there: unauthenticated reads answer as if the repository held no PRs, which the scan would report as a healthy fleet of zero and stay green while the loop is dead. Tests: the fork-admission replay becomes a fork-decline replay over the same shapes (including the two the removed branch existed to admit), and pins that the decline costs no API call; the new guard is replayed under bash with a present-PAT negative control.
a889883 to
d0f9f3c
Compare
|
Please do not rebase or force-push to an active PR as it invalidates existing review comments. Note for future reference, the bots always squash all changes into a single commit automatically as part of the integration. 中文请勿对活跃的 PR 执行 rebase 或 force-push,因为这会使已有的评审评论失效。另外,供日后参考:作为集成流程的一部分,机器人始终会自动将所有改动压缩(squash)为单个提交。 |
|
Thanks — this changed the shape of the PR. Findings 4/5/7 showed most of what I added was unreachable, and 9 showed the reachable part belonged one branch away. I rebuilt it around that instead of patching the original. The Per finding:
Net on the workflow is now +38/−24, most of it the replaced comment block. One thing the PR now states plainly rather than glosses: this removes real-time fork pickup rather than fixing it. It never worked, so nothing regresses, but the 40-70min it was meant to avoid comes back. A Two measurements from that work that are worth recording here, since both contradict what the code assumed:
Verification: 121/121 on the autofix suite, 56/56 across the sibling suites that read this workflow, and five mutations all caught — restore the fork admission, decline but still call the API, reword the decline, drop the guard, make the guard exit 0. 🤖 Generated with Claude Code — Claude Opus 5 (1M context) |
Code reviewNo runtime correctness defect survived verification — the routing change itself is sound. What did survive are accuracy problems: the new decline message overpromises, and three of the added/adjacent comments describe paths that don't exist (or no longer exist). Plus two test-harness issues. 1.
|
Summary
GitHub hands no repository secrets to a workflow run tied to a pull request whose head lives in a fork. The run header says so:
secrets.CI_DEV_BOT_PATtherefore arrives empty and everyghcall made with it is unauthenticated.Route admitted those PRs to the real-time review lane anyway. The fork branch spent two API reads deciding it, then
review-scanspent three more failing and exited 1:metadata_fetch_failedpromises "a later scheduled scan will retry the API" — true for a 5xx, false for a credential the run was never handed. Everypull_request_reviewon a fork PR reddened this workflow while changing nothing.The admitting branch explained itself with "This event runs in BASE-repo context". That holds for the workflow file, which is read from base — verified: run
31152873061executed a version ofqwen-autofix.ymlits own PR branch does not contain — but not for the credentials.Measured, from one morning's runs:
QwenLM/qwen-codeActionsNonemetadata_fetch_failedIt is the event, not the PR and not the author.
issue_commenton the very same fork PRs (#8365, #8418, #8425, #8525, #8639) readsSecret source: Actions, because that run is not bound to the PR head. #8436's author holdsadminhere and the run still gotNone.The condition is not new; #8410 only made it loud. Before it, the unauthenticated
gh pr viewreturned empty and the scan soft-rejected with a misleading reason and exited 0 — run31123457895, PR #8619, same empty PAT:Changes
Route declines fork PRs at the
pull_request_reviewtrigger, mirroring thepull_requestlabel branch one screen below, which already refuses forks for exactly this reason.pr_is_managedstays false, sodo_reviewstays false and the existing job condition and concurrency expression skipreview-scanwith nothing new added. The two API reads that funded the old verdict go away with it.review-scangains a hard guard on an empty PAT. No jobif:can read thesecretscontext, so a deleted or renamed secret — or a lane nobody has modelled — is invisible until the step looks. It stops there: unauthenticatedgh pr listanswers as if the repository held no PRs, and the scan would read that as a healthy fleet of zero and stay green while the loop is dead.What is lost, stated plainly
Real-time pickup for fork PRs is removed, not fixed. It never worked — the admitted PR reached a scan that could not authenticate — so no behaviour regresses, but the intent (spare a takeover PR the 40-70 minutes the throttled
*/10schedule really takes) is not served here. Restoring it needs a credentialed lane; aworkflow_runbridge does that in a separate PR. Until then fork PRs are handled by the scheduled scan, which already admits them:Rejected alternative
Reading the metadata with
github.tokendoes not help.review-addressstill needs the PAT to push and comment, and it is empty for the whole run — the round would fail later, after a checkout, annpm ci, a build and an agent run.Verification
npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/qwen-autofix-workflow.test.js— 121/121 passqwen-resolve-workflow,qwen-fleet-shepherd-workflow,package-scripts— 56/56 pass;node --test .github/scripts/classify-release-notes.test.mjs— 3/3 passbashwith a present-PAT negative control.中文说明
摘要
GitHub 不会把仓库 secrets 发给「绑定在 fork PR head 上」的 workflow run,run 日志头部直接写明:
因此
secrets.CI_DEV_BOT_PAT是空的,用它发出的每个gh调用都未认证。而 route 仍然把这些 PR 放进了实时评审车道。fork 分支先花两次 API 读取做判定,
review-scan再连烧三次后 exit 1:metadata_fetch_failed的承诺是「稍后的定时扫描会重试 API」——对 5xx 成立,对一个从未下发给这个 run 的凭据不成立。于是每次 review 一个 fork PR 都把工作流刷红,却什么也没改变。那段放行逻辑的注释理由是 "This event runs in BASE-repo context"。这对 workflow 文件成立(文件确实从 base 读取——已验证:run
31152873061执行的qwen-autofix.yml版本并不存在于它自己的 PR 分支上),但对凭据不成立。一个上午的实测:
QwenLM/qwen-codeActionsNonemetadata_fetch_failed决定因素是事件,既不是 PR 也不是作者。
issue_comment作用在同一批 fork PR(#8365、#8418、#8425、#8525、#8639)上读到的是Secret source: Actions,因为那种 run 不绑定 PR head。#8436 的作者在本仓是admin,run 依然是None。这个状况并非新出现,#8410 只是让它变响了。在那之前,未认证的
gh pr view返回空,扫描以一个误导性理由软拒绝并 exit 0 —— run31123457895,PR #8619,同样是空 PAT:变更
route 在
pull_request_review触发处直接拒绝 fork PR,与下方pull_request标签分支的写法一致——那里早已出于同样原因拒绝 fork。pr_is_managed保持 false,于是do_review为 false,现有的 job 条件与 concurrency 表达式自然跳过review-scan,无需新增任何东西。原先为那个判定支付的两次 API 读取也一并消失。review-scan新增一个空 PAT 硬闸门。 jobif:读不到secrets上下文,所以被删除或改名的 secret(以及任何尚未建模的车道)在步骤真正去看之前都是不可见的。它必须在此停下:未认证的gh pr list会表现得像仓库里一个 PR 都没有,扫描会把它读成「零 PR 的健康车队」,在整个循环已死的情况下永远显示绿色。明确说明失去了什么
fork PR 的实时接管是被移除,不是被修复。它从来没能工作过——被放行的 PR 进入的是一个无法认证的扫描——所以没有行为回归,但它的本意(让接管中的 PR 不必等那个实测 40-70 分钟的
*/10定时任务)在本 PR 中没有被满足。要恢复它需要一条有凭据的车道,workflow_run桥接会在另一个 PR 中提供。在那之前,fork PR 由定时扫描处理,而它本来就在接管这些 PR:被否决的方案
改用
github.token读元数据没有用。review-address仍然需要 PAT 来 push 和发评论,而整个 run 里它都是空的——只会把失败推迟到 checkout、npm ci、构建和跑完 agent 之后。验证
npx vitest run --config ./scripts/tests/vitest.config.ts scripts/tests/qwen-autofix-workflow.test.js—— 121/121 通过qwen-resolve-workflow、qwen-fleet-shepherd-workflow、package-scripts—— 56/56 通过;node --test .github/scripts/classify-release-notes.test.mjs—— 3/3 通过bash真实回放,并带一个「PAT 存在」的负对照。