File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 4343 });
4444 }
4545
46+ // Only a `pull_request`/`pull_request_review` run that was itself triggered
47+ // FROM a fork gets a read-only GITHUB_TOKEN — label mutations there 403 with
48+ // "Resource not accessible by integration". `schedule` and `workflow_dispatch`
49+ // runs always execute in the base repo's context with a read/write token, even
50+ // when the PR they're reconciling happens to come from a fork, so they must NOT
51+ // be skipped or fork PRs would never get stale labels cleaned up.
52+ // See: https://docs.github.com/en/actions/concepts/security/github_token
53+ const isReadOnlyRun = Boolean(context.payload.pull_request) &&
54+ context.payload.pull_request.head?.repo?.owner?.login !== owner;
55+
56+ function isForkPR(pr) {
57+ return pr.head?.repo?.owner?.login && pr.head.repo.owner.login !== owner;
58+ }
59+
4660 // Strips stateLabels from a PR, optionally keeping one.
4761 // Also removes stale-awaiting-author when not keeping awaiting-author.
62+ // Only skipped when this run's own token is read-only (see isReadOnlyRun) —
63+ // schedule/workflow_dispatch runs reconcile fork PRs normally.
4864 async function reconcileLabels(pr, desiredLabel) {
65+ if (isReadOnlyRun && isForkPR(pr)) {
66+ core.info(`PR #${pr.number}: fork PR on a read-only run — skipping label mutation`);
67+ return;
68+ }
4969 const currentLabels = new Set(pr.labels.map(l => l.name));
5070 for (const label of stateLabels) {
5171 if (label !== desiredLabel && currentLabels.has(label)) {
You can’t perform that action at this time.
0 commit comments