diff --git a/.changeset/2155-heartbeat-fork-pr-labels.md b/.changeset/2155-heartbeat-fork-pr-labels.md new file mode 100644 index 000000000..c67c00ece --- /dev/null +++ b/.changeset/2155-heartbeat-fork-pr-labels.md @@ -0,0 +1,10 @@ +--- +"@bradygaster/squad-cli": patch +"@bradygaster/squad-sdk": patch +--- + +Fix `squad-heartbeat.yml`'s "Repair orphaned squad labels" step 403ing when a fork PR closes. + +`GITHUB_TOKEN` is forced read-only for `pull_request`-triggered workflow runs originating from a fork, regardless of the workflow's declared `permissions:` block, so `issues.addLabels` was rejected with `403 Resource not accessible by integration`. The trigger is switched to `pull_request_target` across all 5 synced copies (`.github/workflows/`, `templates/workflows/`, `packages/squad-cli/templates/workflows/`, `packages/squad-sdk/templates/workflows/`, `.squad-templates/workflows/`), which runs with base-repo token permissions regardless of fork origin. + +This is safe because the `actions/checkout` step never overrides `ref:` (always checks out the base ref, never the fork head), and no step reads or executes anything from the triggering PR's content. diff --git a/.github/workflows/squad-heartbeat.yml b/.github/workflows/squad-heartbeat.yml index e2dea5bce..0ebffd832 100644 --- a/.github/workflows/squad-heartbeat.yml +++ b/.github/workflows/squad-heartbeat.yml @@ -19,7 +19,13 @@ on: # React to completed work or new squad work issues: types: [closed, labeled] - pull_request: + # pull_request_target (not pull_request): GITHUB_TOKEN is forced read-only + # for `pull_request` events triggered by a fork, so write steps below + # (addLabels/createComment) 403 on fork PR closes (#2080-class bug). + # Safe here because the checkout step below has no `ref:` override, so it + # always checks out the BASE ref — no fork code or fork-controlled input + # is ever read or executed by this job. + pull_request_target: types: [closed] # Manual trigger diff --git a/.squad-templates/workflows/squad-heartbeat.yml b/.squad-templates/workflows/squad-heartbeat.yml index 7fe3564c6..cc1cbe986 100644 --- a/.squad-templates/workflows/squad-heartbeat.yml +++ b/.squad-templates/workflows/squad-heartbeat.yml @@ -10,7 +10,13 @@ on: # React to completed work or new squad work issues: types: [closed, labeled] - pull_request: + # pull_request_target (not pull_request): GITHUB_TOKEN is forced read-only + # for `pull_request` events triggered by a fork, so write steps below + # (addLabels/createComment) 403 on fork PR closes. + # Safe here because the checkout step below has no `ref:` override, so it + # always checks out the BASE ref — no fork code or fork-controlled input + # is ever read or executed by this job. + pull_request_target: types: [closed] # Manual trigger diff --git a/packages/squad-cli/templates/workflows/squad-heartbeat.yml b/packages/squad-cli/templates/workflows/squad-heartbeat.yml index 7fe3564c6..cc1cbe986 100644 --- a/packages/squad-cli/templates/workflows/squad-heartbeat.yml +++ b/packages/squad-cli/templates/workflows/squad-heartbeat.yml @@ -10,7 +10,13 @@ on: # React to completed work or new squad work issues: types: [closed, labeled] - pull_request: + # pull_request_target (not pull_request): GITHUB_TOKEN is forced read-only + # for `pull_request` events triggered by a fork, so write steps below + # (addLabels/createComment) 403 on fork PR closes. + # Safe here because the checkout step below has no `ref:` override, so it + # always checks out the BASE ref — no fork code or fork-controlled input + # is ever read or executed by this job. + pull_request_target: types: [closed] # Manual trigger diff --git a/packages/squad-sdk/templates/workflows/squad-heartbeat.yml b/packages/squad-sdk/templates/workflows/squad-heartbeat.yml index 7fe3564c6..cc1cbe986 100644 --- a/packages/squad-sdk/templates/workflows/squad-heartbeat.yml +++ b/packages/squad-sdk/templates/workflows/squad-heartbeat.yml @@ -10,7 +10,13 @@ on: # React to completed work or new squad work issues: types: [closed, labeled] - pull_request: + # pull_request_target (not pull_request): GITHUB_TOKEN is forced read-only + # for `pull_request` events triggered by a fork, so write steps below + # (addLabels/createComment) 403 on fork PR closes. + # Safe here because the checkout step below has no `ref:` override, so it + # always checks out the BASE ref — no fork code or fork-controlled input + # is ever read or executed by this job. + pull_request_target: types: [closed] # Manual trigger diff --git a/templates/workflows/squad-heartbeat.yml b/templates/workflows/squad-heartbeat.yml index 7fe3564c6..cc1cbe986 100644 --- a/templates/workflows/squad-heartbeat.yml +++ b/templates/workflows/squad-heartbeat.yml @@ -10,7 +10,13 @@ on: # React to completed work or new squad work issues: types: [closed, labeled] - pull_request: + # pull_request_target (not pull_request): GITHUB_TOKEN is forced read-only + # for `pull_request` events triggered by a fork, so write steps below + # (addLabels/createComment) 403 on fork PR closes. + # Safe here because the checkout step below has no `ref:` override, so it + # always checks out the BASE ref — no fork code or fork-controlled input + # is ever read or executed by this job. + pull_request_target: types: [closed] # Manual trigger