From 9933017e0c51f4b7179f62115824164e5c2782b5 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Tue, 28 Jul 2026 03:19:25 +0900 Subject: [PATCH 1/2] fix(ci): let the PR labeler actually write labels --- .github/workflows/pr-labeler.yml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-labeler.yml b/.github/workflows/pr-labeler.yml index 8bc529b51f..348620fca7 100644 --- a/.github/workflows/pr-labeler.yml +++ b/.github/workflows/pr-labeler.yml @@ -17,8 +17,14 @@ concurrency: permissions: contents: read - # pulls.get only needs read; label mutations use the issues API. - pull-requests: read + # pulls.get only needs read, but the issues label endpoints are shared with + # pull requests: adding or removing a label on a PR number is rejected with + # "Resource not accessible by integration" unless the token also carries + # pull_requests=write (the API reports `issues=write; pull_requests=write` + # in x-accepted-github-permissions). Read-only here silently worked while + # every run happened to be a no-op sync, and failed on the first PR that + # actually needed a label written. + pull-requests: write issues: write jobs: From 8efa677eddf77aab54fc909f7a753982b6934260 Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Tue, 28 Jul 2026 03:24:37 +0900 Subject: [PATCH 2/2] test(ci): pin the labeler to the permissions the API demands --- .github/scripts/pr-labeler.test.cjs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/scripts/pr-labeler.test.cjs b/.github/scripts/pr-labeler.test.cjs index 71afd7836c..1c61937542 100644 --- a/.github/scripts/pr-labeler.test.cjs +++ b/.github/scripts/pr-labeler.test.cjs @@ -165,8 +165,15 @@ describe("pr-labeler workflow", () => { it("keeps trusted default-branch checkout, concurrency cancel, and minimal permissions", () => { assert.match(workflow, /ref:\s*\$\{\{\s*github\.event\.repository\.default_branch\s*\}\}/); assert.match(workflow, /cancel-in-progress:\s*true/); - assert.match(workflow, /pull-requests:\s*read/); assert.match(workflow, /issues:\s*write/); - assert.doesNotMatch(workflow, /pull-requests:\s*write/); + // The issues label endpoints are shared with pull requests: writing a label + // onto a PR number needs pull_requests=write alongside issues=write, which + // GitHub reports as `issues=write; pull_requests=write`. Pinning this to + // read made the workflow fail closed on the first PR that actually needed a + // label applied (#565), so write is the minimum here, not an escalation. + assert.match(workflow, /pull-requests:\s*write/); + // contents stays read — the labeler never pushes. + assert.match(workflow, /contents:\s*read/); + assert.doesNotMatch(workflow, /contents:\s*write/); }); });