diff --git a/.github/workflows/autolabeler.yaml b/.github/workflows/autolabeler.yaml index 964f95d..a1afe45 100644 --- a/.github/workflows/autolabeler.yaml +++ b/.github/workflows/autolabeler.yaml @@ -3,15 +3,27 @@ name: 🏷️ Autolabel Pull Requests # Applies release-drafter labels (feature/fix/chore) to incoming PRs based on # branch-name patterns from .github/release-drafter.yml. Labels feed into the # changelog categories that release-drafter.yaml later groups for the draft. +# +# `pull_request_target`, not `pull_request`: a `pull_request` run on a fork gets +# a read-only GITHUB_TOKEN whatever `permissions:` asks for - that block only +# drops privileges - so labelling failed on every outside contribution and left +# a red check nobody could clear. This runs from `main` with a token that can +# write, and reads the config from `main` too. +# +# That token is why: **never check out, install or execute pull request code +# here.** One checkout of the PR ref plus one `npm ci` hands write access to a +# stranger's postinstall script. This job talks to the API and nothing else. +# Anything that runs contributor code belongs in ci.yaml. on: - pull_request: + pull_request_target: types: - opened - reopened - synchronize permissions: + contents: read pull-requests: write jobs: @@ -19,7 +31,9 @@ jobs: name: 🏷️ Autolabel pull request runs-on: ubuntu-latest steps: + # Pinned to the commit behind v7: tags are mutable, and this one now + # resolves inside a job holding a write token. - name: 🏷️ Run autolabeler - uses: release-drafter/release-drafter/autolabeler@v7 + uses: release-drafter/release-drafter/autolabeler@34d80673e067bdc0c24568d3af899c216adcfaa9 # v7 with: config-name: release-drafter.yml diff --git a/AGENTS.md b/AGENTS.md index a971173..bbc2b37 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -479,6 +479,21 @@ adding packager `ignore` rules. - **`pages.yaml`** — publishes `docs/` on pushes to `main` touching `docs/**`. - **`autolabeler.yaml`** — applies changelog labels from branch-name patterns (`feat/`, `fix/`, `chore/`). Any other prefix needs labels set by hand. + On **`pull_request_target`**, and it must stay there: a `pull_request` run on + a fork gets a read-only token whatever `permissions:` asks for. The trade is + a write token, so **it must never check out, install or run pull request + code**, and its action is pinned to a commit, not a tag. + +**Pull requests from forks** get no secrets and a read-only token, by design. +`ci.yaml` runs contributor code, so it stays on `pull_request`, and `sign: +false` keeps the Developer ID out of a job that runs it. Lint, the end-to-end +tests and an unsigned six-row build are what a fork gets — enough to merge on. +Two repository settings would clear a red fork check by handing fork code the +keys: *send write tokens to* and *send secrets and variables to workflows from +fork pull requests*. Leave both off; a job needing a write token gets its own +checkout-free `pull_request_target` workflow instead. The separate *require +approval* gate, where a fork's run waits for a maintainer to press **Approve +and run** once per push, is expected, not a failure. **Version numbers come from PR labels, not `package.json`.** The release-drafter resolver reads `major`/`minor`/`patch` off merged PRs and