Skip to content

ci: 合并时收口 objectstack 里被 Fixes 声明的 issue(缺凭据时改为出声,不静默) - #3179

Merged
os-zhuang merged 4 commits into
mainfrom
claude/cross-repo-fixes-closer
Aug 2, 2026
Merged

ci: 合并时收口 objectstack 里被 Fixes 声明的 issue(缺凭据时改为出声,不静默)#3179
os-zhuang merged 4 commits into
mainfrom
claude/cross-repo-fixes-closer

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

GitHub 的关闭关键字只在同仓库内生效。本仓库一个写着 Fixes objectstack-ai/objectstack#4475 的 PR,在人眼里和同仓库关闭长得完全一样,合并后那个 issue 却会永远开着——而且目标 issue 页面上也不会出现这条 PR 的任何引用

对本仓库来说这不是假设:缺陷通常在 objectstack 被发现(验收在那边跑),在这里被修复。v17 验收批次一天之内就撞了两次——#3150 修了 objectstack#4475,#3163 修了 objectstack#4478,两个 framework 侧 issue 都是人工关掉的。这次是把那两次手工动作固化下来。

两种模式,都可见

有 CROSS_REPO_ISSUE_TOKEN  → 关闭目标 issue,并在其上留下指向本 PR 的说明
无该 token                 → 在本 PR 上留言,点名哪些 issue 仍需手工关闭

第二种模式是刻意设计的,不是降级凑合。

本仓库现有的 secret 只有 GITHUB_TOKENNPM_TOKENCODECOV_TOKEN,而 GITHUB_TOKEN 的写权限被限制在运行工作流的那个仓库内——它关不掉 objectstack 的 issue,这正是本问题的成因。

在管理员配置跨仓库凭据之前,这个工作流没有能力完成关闭动作,那它必须出声。一个因为 secret 从未配置而悄悄什么都不做的工作流,正是「声明了却从未生效」的那类缺陷——两个仓库今天都还在修同一形状的问题。缺失的凭据必须自己 announce,而不是让人以为已经接好了。

需要管理员做的事(本 PR 无法自带)

新增仓库 secret CROSS_REPO_ISSUE_TOKEN:对 objectstack-ai/objectstack 具备 issues: write 的 fine-grained PAT 或 GitHub App token。组织里已安装的 GitHub App 即可胜任。

实现要点

  • 只匹配限定形式 owner/repo#N。裸 #N 由 GitHub 原生处理,绝不触碰。已自测:Fixes #3134 不命中,Fixes objectstack-ai/objectstack#4475 命中。
  • 同仓库的限定写法会被过滤掉,避免在已被 GitHub 关闭的 issue 上留无意义评论。
  • 已 closed 的目标跳过单个目标失败不吞掉其余,且走 core.warning 而非静默。
  • pull_request_target 是因为 pull_request 对 fork 运行不下发 secret;其通常风险不成立——本 job 从不 checkout head ref,也从不执行 PR 里的任何东西,只读 PR 正文并调用 issues API。

验证

  • YAML 解析通过;内嵌脚本按 actions/github-script 的 async 包装做 node --check,语法通过。
  • 正则以真实样本自测:跨仓库引用命中,裸 Fixes #N 不命中。
  • 未做:端到端触发一次真实合并。该工作流只在 pull_request_target: closedmerged == true 时运行,本 PR 自身合并即是它的第一次真实执行——届时应当因缺 token 而留下提示评论,这本身就是可观察的验收信号

配套的同名工作流已在 objectstack 侧一并提交(方向相反)。


Generated by Claude Code

Defects are routinely found in objectstack (where verification runs) and fixed
here, but GitHub's closing keywords only act within a repository — so those
framework issues stay open with no reference to the PR that fixed them. v17
verification hit this twice in one day (#3150 → objectstack#4475, #3163 →
objectstack#4478); both were closed by hand.

The job has two modes and both are visible: with a cross-repo token it closes
the foreign issue and links the PR; without one it comments on the merged PR
naming what still needs closing. A silent no-op on a missing secret is the
'declared but never enforced' shape both repos keep having to fix, so the
absent credential announces itself instead.
@vercel

vercel Bot commented Aug 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectui Ignored Ignored Aug 2, 2026 8:23am

Request Review

Copy link
Copy Markdown
Contributor Author

更正:正文「验证」一节里有一句声明站不住

原文写的是:

本 PR 自身合并即是它的第一次真实执行——届时应当因缺 token 而留下提示评论,这本身就是可观察的验收信号

这句很可能不成立,不应作为验收依据。

pull_request_target 的工作流定义从 base 分支读取。本 PR 合并之前,main 上并不存在这个文件,所以它多半不会在自己的合并事件上运行。真正的首次执行,会是此后第一个正文带跨仓库关闭关键字的已合并 PR——在本仓库,那通常就是下一个修 objectstack 侧 issue 的 PR。

更正后的验证状态如实列一遍:

状态
YAML 解析 ✅ 已验证
内嵌脚本语法(按 actions/github-script 的 async 包装 node --check ✅ 已验证
关键字正则(跨仓库引用命中,裸 Fixes #N 不命中) ✅ 已验证
端到端触发一次真实合并 未实测

也就是说:这个工作流的运行时行为一次都没有被真正执行过。 静态检查只能保证它不会因语法错误而崩,保证不了它在真实事件下做对事。

合并后建议主动确认一次:下一个带跨仓库 Fixes 的 PR 合并时,去 Actions 里看 Cross-repo Issue Closer 是否触发、以及缺 token 时是否按预期留下提示评论。若始终不触发,第一嫌疑是 pull_request_target 的事件/权限配置,而不是脚本逻辑。

(本条更正由协调会话在合并前补记。正文那句话是我写的,属于「声称验证过、实际没有」的那一类——正是 objectstack#4482 批次反复在修的问题。)


Generated by Claude Code

…s `octokit`

`actions/github-script` injects an `octokit` binding into the script scope, so
`const octokit = ...` aborts the run before any logic executes:

    SyntaxError: Identifier 'octokit' has already been declared

Caught on objectstack#4553's own merge — the first time the workflow ever ran.
The pre-merge `node --check` missed it because the test wrapper declared only
{github, context, core, require}; a wrapper that omits an injected identifier
cannot see a collision with it. The wrapper now carries the full injected set,
and the old spelling fails that check.
@os-zhuang
os-zhuang marked this pull request as ready for review August 2, 2026 08:13

Copy link
Copy Markdown
Contributor Author

上一条更正也是错的——以本条为准

我在上一条评论里说「pull_request_target 从 base 分支读工作流定义,本 PR 合并前 main 上没有它,所以多半不会在自己的合并上运行」。

实测证明会运行。 objectstack 的同名 PR(objectstack-ai/objectstack#4553)已经合并,其工作流在自己的合并事件上触发了——run_number: 1event: pull_request_target。原因是合并把提交推进 main 之后 closed 事件才发出,所以事件触发时 base 分支已经含有该文件。

所以完整的时间线是:一句原始声明说「会跑」,一句更正说「不会跑」,而真实结果是第三种——跑了,然后崩了

SyntaxError: Identifier 'octokit' has already been declared

actions/github-script 本来就往脚本作用域注入 octokit,而原版脚本又 const octokit = ... 声明了一次,于是一行业务逻辑都没执行。objectstack 侧因此在每个 PR 合并上都挂了一个红检查,由 objectstack-ai/objectstack#4573 修复。

本 PR 已在合并前修掉同一个 bugcrossRepo 重命名,见 fix(ci): rename the cross-repo client 那个提交),所以本仓库不会经历那一轮红灯。

合并后请这样核验

本 PR 正文不含任何跨仓库关闭关键字,所以预期是:工作流触发 → 记录 No cross-repository closing keywords in this PR body. → 成功退出。

这是可核验的预期,不是验收结论。 请去 Actions 看 Cross-repo Issue Closer 的第一次运行;若它再次失败,以日志为准,不要采信本评论。

真正的功能验证要等下一个正文带 Fixes objectstack-ai/objectstack#N 的 PR 合并——那时它应当因缺 CROSS_REPO_ISSUE_TOKEN 而在该 PR 上留下提示评论。


Generated by Claude Code

Without this the job returns early whenever a PR body carries no cross-repo
reference, so a repository that has the secret and one that does not produce
byte-identical logs. Presence only; the value is never read into the log.
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 2, 2026
Merged via the queue into main with commit b200720 Aug 2, 2026
15 checks passed
@os-zhuang
os-zhuang deleted the claude/cross-repo-fixes-closer branch August 2, 2026 08:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants