diff --git a/.changeset/fix-cross-repo-closer-octokit.md b/.changeset/fix-cross-repo-closer-octokit.md new file mode 100644 index 0000000000..2800e195f5 --- /dev/null +++ b/.changeset/fix-cross-repo-closer-octokit.md @@ -0,0 +1,23 @@ +--- +--- + +fix(ci): rename the cross-repo client — `github-script` already declares `octokit` + +Release-nothing: touches `.github/workflows/cross-repo-issue-closer.yml` only. + +`actions/github-script` injects an `octokit` binding into the script scope, so +the `const octokit = require('@actions/github').getOctokit(token)` added in +#4553 aborted the run before a single line of logic executed: + + SyntaxError: Identifier 'octokit' has already been declared + +This surfaced on #4553's own merge — the first time the workflow ever ran, and +the first time any of its runtime behaviour was exercised. Until this lands, +every merged pull request in this repository carries one red check. + +The pre-merge validation missed it for an instructive reason. The script was +syntax-checked against a hand-written wrapper declaring `{github, context, +core, require}` — and a wrapper that omits an injected identifier cannot +possibly see a collision with it. The check reported clean because it was +asking a narrower question than the runtime asks. The wrapper now carries the +full injected set, and the previous spelling fails it. diff --git a/.github/workflows/cross-repo-issue-closer.yml b/.github/workflows/cross-repo-issue-closer.yml index 8f6ebb957d..dadaaf4282 100644 --- a/.github/workflows/cross-repo-issue-closer.yml +++ b/.github/workflows/cross-repo-issue-closer.yml @@ -60,6 +60,16 @@ jobs: 'gi', ); + // Report credential state on EVERY run, before any early return. + // Otherwise a repository with the secret and one without look + // identical until a cross-repo reference happens to show up — + // which can be days — and "is it configured?" stays unanswerable. + // Presence only; the value is never read into the log. + const token = process.env.CROSS_REPO_TOKEN; + core.info( + `CROSS_REPO_ISSUE_TOKEN: ${token ? 'configured' : 'ABSENT — cross-repo closes will be reported, not performed'}`, + ); + const targets = new Map(); for (const [, owner, repo, number] of body.matchAll(pattern)) { const key = `${owner}/${repo}#${number}`; @@ -75,8 +85,6 @@ jobs: } core.info(`Cross-repo targets: ${[...targets.keys()].join(', ')}`); - const token = process.env.CROSS_REPO_TOKEN; - if (!token) { // Degrade VISIBLY. Someone has to close these by hand, and this // comment is the only thing that will tell them so. @@ -103,25 +111,25 @@ jobs: // A second client: `github` is bound to GITHUB_TOKEN, which has no // write access outside this repository. - const octokit = require('@actions/github').getOctokit(token); + const crossRepo = require('@actions/github').getOctokit(token); for (const [key, t] of targets) { try { - const { data: issue } = await octokit.rest.issues.get({ + const { data: issue } = await crossRepo.rest.issues.get({ owner: t.owner, repo: t.repo, issue_number: t.number, }); if (issue.state === 'closed') { core.info(`${key} is already closed — skipping.`); continue; } - await octokit.rest.issues.createComment({ + await crossRepo.rest.issues.createComment({ owner: t.owner, repo: t.repo, issue_number: t.number, body: `已由 ${thisRepo} 的 ${prUrl} 修复并合并。\n\n` + `(跨仓库的关闭关键字不会自动生效,本条由 \`cross-repo-issue-closer\` 工作流代为收口。)\n\n` + `---\n_Generated by [Claude Code](https://claude.ai/code)_`, }); - await octokit.rest.issues.update({ + await crossRepo.rest.issues.update({ owner: t.owner, repo: t.repo, issue_number: t.number, state: 'closed', state_reason: 'completed', });