fix(ci): grant status access and probe the real Actions token

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
cryppadottaandPaperclip committed 2026-11-06 16:13:39 +00:00
1 parent 715cee88ce
commit c3c84394b7
4 files changed
+63

No files matched your search

@@ -243,9 +243,21 @@ test("workflow uses read permissions, a protected environment, trusted checkout,
assert.match(workflow, /ref: \$\{\{ github.sha \}\}/);
assert.match(workflow, /persist-credentials: false/);
assert.match(workflow, /continue-on-error: true/);
for (const permission of ["actions", "checks", "contents", "pull-requests", "statuses"]) {
assert.ok(workflow.includes(` ${permission}: read`), `Missing ${permission}: read`);
}
assert.doesNotMatch(workflow, /: write|download-artifact|restore@|pull_request_target:/);
for (const name of ["PAPERCLIP_PR_REVIEW_WEBHOOK_URL", "PAPERCLIP_PR_REVIEW_WEBHOOK_TOKEN"]) {
assert.ok(workflow.includes(`secrets.${name}`));
assert.ok(!workflow.includes(`vars.${name}`));
}
});
test("API probe uses the same read permissions without private webhook access", () => {
const probe = readFileSync(".github/workflows/pr-review-api-access.yml", "utf8");
for (const permission of ["actions", "checks", "contents", "pull-requests", "statuses"]) {
assert.ok(probe.includes(` ${permission}: read`));
}
assert.match(probe, /snapshot\(Number\(process.env.PR_NUMBER\)\)/);
assert.doesNotMatch(probe, /\$\{\{ secrets\.|environment:|: write|deliver\(/);
});
+2
View File
@@ -13,6 +13,7 @@ on:
- CI work benchmark
- Incremental CI qualification
- Backup checkpoint qualification
- PR review API access
types: [completed]
# External check providers and legacy commit statuses can finish after CI.
check_run:
@@ -24,6 +25,7 @@ permissions:
checks: read
contents: read
pull-requests: read
statuses: read
jobs:
notify:
@@ -0,0 +1,43 @@
name: PR review API access
on:
pull_request:
paths:
- .github/scripts/notify-pr-merge-review.mjs
- .github/scripts/tests/notify-pr-merge-review.test.mjs
- .github/workflows/pr-merge-review.yml
- .github/workflows/pr-review-api-access.yml
permissions:
actions: read
checks: read
contents: read
pull-requests: read
statuses: read
jobs:
probe:
if: github.repository == 'paperclipai/paperclip'
runs-on: ubuntu-latest
timeout-minutes: 5
# This job has no environment or private webhook secrets. It proves that
# the notifier's status query works with the actual read-only Actions token.
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
with:
persist-credentials: false
sparse-checkout: .github/scripts
- name: Read the PR status rollup with the Actions token
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
PR_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
node --input-type=module <<'JS'
import assert from 'node:assert/strict';
import { snapshot } from './.github/scripts/notify-pr-merge-review.mjs';
const pr = snapshot(Number(process.env.PR_NUMBER));
assert.equal(pr.headRefOid, process.env.PR_HEAD_SHA);
assert.ok(pr.statusCheckRollup.length > 0);
console.log('Actions token can read the complete PR status rollup.');
JS
+6
View File
@@ -34,6 +34,12 @@ PR code or downloads PR artifacts or caches. It reacts to the repository's PR
workflow completions, external check completions, and legacy commit statuses.
When adding another PR verification workflow, add its name to `workflow_run`.
The separate `PR review API access` workflow checks notifier changes using
the real read-only Actions token. It has no private environment or webhook
credentials and sends no webhook. Both workflows explicitly grant
`actions`, `checks`, `contents`, `pull-requests`, and `statuses` read access.
The GraphQL status rollup needs both check and commit-status permissions.
The notifier reads the current PR's status rollup and branch rules. Both
`ci / verify` and `ci / e2e`, plus any additional required checks, must succeed.
Other checks must succeed or finish as skipped or neutral. Optional skipped or