From 2cd82ffb72a6161c7d7184848adceff258a867ab Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Fri, 24 Jul 2026 10:45:03 -0700 Subject: [PATCH 1/2] fix(pr-size): mint bot token with pull-requests:write and degrade on runtime 403 (BE-4385) The comment job's sticky-comment upsert 403s on every PR because the app token is minted with only issues:write, but the issue-comments endpoints on a pull request require the app's Pull requests permission (GitHub's shared issues/PR endpoint model). Mint with pull-requests:write instead, and harden the upsert step (continue-on-error + id) so a runtime permission failure degrades to status + step summary rather than reddening the check. --- .github/workflows/pr-size.yml | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/.github/workflows/pr-size.yml b/.github/workflows/pr-size.yml index b7eb1f3..0219720 100644 --- a/.github/workflows/pr-size.yml +++ b/.github/workflows/pr-size.yml @@ -281,13 +281,25 @@ jobs: app-id: ${{ inputs.bot_app_id }} private-key: ${{ secrets.BOT_APP_PRIVATE_KEY }} # Scope the token to exactly what the upsert uses: the sticky comment - # rides the issues comments API, so issues:write is the only permission - # required. Requesting more (e.g. pull-requests:write) makes the mint - # 422 on an installation not granted it, for no gain. - permission-issues: write + # rides the issues-comments API on a PULL REQUEST, and for GitHub Apps + # that endpoint checks the app's Pull requests permission (not Issues) — + # so pull-requests:write is the one scope required (read is implied for + # the list-comments GET). Requesting an ungranted extra permission (e.g. + # issues:write) makes the mint 422 on an installation not granted it, + # for no gain — this workflow only ever comments on pull requests. + permission-pull-requests: write - name: Upsert sticky size comment if: steps.bot.outcome == 'success' + id: upsert + # Best-effort, same contract as the mint step: the size verdict lives in + # the pr-size job and the report is always in that job's step summary, so + # the comment path must degrade to status + summary, never redden the + # check. A runtime permission failure here (e.g. a 403 because the bot + # app lacks Pull requests on this installation — proven live on Jul-24) + # would otherwise escape the mint-only guard and fail the job, so tolerate + # it and let "Note degraded mode" report it. + continue-on-error: true env: GH_TOKEN: ${{ steps.bot.outputs.token }} REPO: ${{ github.repository }} @@ -321,12 +333,16 @@ jobs: # Covers every path where no comment is posted so the job never goes # green silently: "download failed" (dl errored under continue-on-error, # e.g. the size tool crashed before uploading the report), "creds absent" - # (bot step skipped) and "mint failed" (bot step errored). Whenever the - # report artifact exists it still lives in the pr-size job's step summary. - if: steps.dl.outcome != 'success' || steps.bot.outcome != 'success' + # (bot step skipped), "mint failed" (bot step errored) and "post failed" + # (upsert ran but 403'd at runtime — e.g. the app lacks Pull requests on + # this installation). Whenever the report artifact exists it still lives + # in the pr-size job's step summary. + if: steps.dl.outcome != 'success' || steps.bot.outcome != 'success' || steps.upsert.outcome == 'failure' run: | if [ "${{ steps.dl.outcome }}" != "success" ]; then echo "No bot comment posted: the size report artifact could not be downloaded (the pr-size job likely failed before uploading it). Check the pr-size job logs." + elif [ "${{ steps.upsert.outcome }}" = "failure" ]; then + echo "No bot comment posted: the sticky comment could not be posted — the bot app likely lacks the Pull requests permission on this installation (the issue-comments API on a PR checks it). The full report is in the pr-size job's step summary." else echo "No bot comment posted (credentials absent, or the app token could not be minted for this repo). The full report is in the pr-size job's step summary." fi From 7454a688b8e08e67eeb6adb8b232be526ad53c21 Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Fri, 24 Jul 2026 10:55:47 -0700 Subject: [PATCH 2/2] fix(pr-size): soften post-failed diagnosis to point at upsert log (BE-4385) The Note degraded mode post-failed branch hard-coded a single cause (the bot app lacks Pull requests permission), but this PR's mint now requests pull-requests:write, making that 403 the least-likely cause; continue-on- error also swallows network/5xx, rate-limit, and malformed-body failures. Point operators at the Upsert step log for the real error instead of asserting a permissions cause. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/pr-size.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/pr-size.yml b/.github/workflows/pr-size.yml index 0219720..b1a21cb 100644 --- a/.github/workflows/pr-size.yml +++ b/.github/workflows/pr-size.yml @@ -334,15 +334,16 @@ jobs: # green silently: "download failed" (dl errored under continue-on-error, # e.g. the size tool crashed before uploading the report), "creds absent" # (bot step skipped), "mint failed" (bot step errored) and "post failed" - # (upsert ran but 403'd at runtime — e.g. the app lacks Pull requests on - # this installation). Whenever the report artifact exists it still lives + # (upsert ran but failed at runtime — any of network/5xx, rate limit, a + # malformed body, or a residual permission 403; the upsert step log has + # the real error). Whenever the report artifact exists it still lives # in the pr-size job's step summary. if: steps.dl.outcome != 'success' || steps.bot.outcome != 'success' || steps.upsert.outcome == 'failure' run: | if [ "${{ steps.dl.outcome }}" != "success" ]; then echo "No bot comment posted: the size report artifact could not be downloaded (the pr-size job likely failed before uploading it). Check the pr-size job logs." elif [ "${{ steps.upsert.outcome }}" = "failure" ]; then - echo "No bot comment posted: the sticky comment could not be posted — the bot app likely lacks the Pull requests permission on this installation (the issue-comments API on a PR checks it). The full report is in the pr-size job's step summary." + echo "No bot comment posted: the sticky comment upsert failed at runtime — see the 'Upsert sticky size comment' step log above for the actual error (e.g. a network/5xx failure, GitHub rate limit, or — now that the mint requests pull-requests:write — a residual permission 403 on an installation that has not granted it). The full report is in the pr-size job's step summary." else echo "No bot comment posted (credentials absent, or the app token could not be minted for this repo). The full report is in the pr-size job's step summary." fi