From 3557a3f495926ce6c6d58046cf5bba38f4b0e553 Mon Sep 17 00:00:00 2001 From: Kenny Van de Maele Date: Mon, 8 Jun 2026 00:26:30 +0200 Subject: [PATCH] fix(ci): restore pull-requests:write for PR label/comment writes (#3367) #3336 reduced the PR-checks workflow to pull-requests:read on the assumption that PR labels/comments only need issues:write (the REST path is /issues/{n}/...). They do not: modifying a pull request's labels or comments requires the pull-requests scope, so issues:write alone returns 403 and crashed the description check on every PR. Restore pull-requests:write, and fail soft in swapLabel so a label-permission error can never mask the description verdict. --- .github/scripts/check-pr-description.js | 11 +++++++++-- .github/workflows/pr-description-check.yml | 10 ++++++---- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/.github/scripts/check-pr-description.js b/.github/scripts/check-pr-description.js index 2a06c2b36..f5dabea5d 100644 --- a/.github/scripts/check-pr-description.js +++ b/.github/scripts/check-pr-description.js @@ -103,14 +103,21 @@ module.exports = async ({ github, context, core }) => { async function swapLabel(num, add, remove) { if (await labelExists(add)) { - await github.rest.issues.addLabels({ owner, repo, issue_number: num, labels: [add] }); + try { + await github.rest.issues.addLabels({ owner, repo, issue_number: num, labels: [add] }); + } catch (e) { + // Fail soft on a token that can't write labels so a label permission + // problem never masks the actual description verdict. + if (e.status !== 403) throw e; + core.warning(`Could not add "${add}" — token lacks label write here; skipping.`); + } } else { core.warning(`Label "${add}" does not exist in the repo — skipping. Create it once to enable labelling.`); } try { await github.rest.issues.removeLabel({ owner, repo, issue_number: num, name: remove }); } catch (e) { - if (e.status !== 404 && e.status !== 410) throw e; + if (e.status !== 404 && e.status !== 410 && e.status !== 403) throw e; } } diff --git a/.github/workflows/pr-description-check.yml b/.github/workflows/pr-description-check.yml index d527d6938..899d74c7a 100644 --- a/.github/workflows/pr-description-check.yml +++ b/.github/workflows/pr-description-check.yml @@ -7,12 +7,14 @@ on: # pull_request_target runs in the base-repo context (has secrets). # The checkout below pins to the base branch so no fork code is executed. # The script only reads context.payload and calls the GitHub API. -# Least privilege: contents:read for the base-ref checkout, pull-requests:read -# for pulls.get (mergeability), issues:write for label + comment management -# (PR labels and comments both go through the issues API). +# Least privilege: contents:read for the base-ref checkout, pull-requests:write +# to add/remove labels and post comments on PRs, and issues:write for the same +# on real issues. NOTE: modifying a *pull request's* labels/comments needs the +# `pull-requests` scope even though the REST path is under `/issues/{n}/...`; +# `issues:write` alone returns 403 on PRs. permissions: contents: read - pull-requests: read + pull-requests: write issues: write jobs: