You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: lead CI check with unprivileged pull_request, harden fork-comment workflow for checkout@v7
Invert the PR-check recommendation: the default is now a single `ci check`
step in the existing pull_request workflow (forks get the check but no
comment), with pull_request_target demoted to an opt-in "Commenting on fork
PRs" section.
Apply the actions/checkout@v7 fork-checkout change (enforced now, backported
to floating tags on July 16, 2026): bump checkouts to @v7 and add
allow-unsafe-pr-checkout: true on the read-only ./pr checkout in both the
recommended workflow and our own dogfooding bumpy-check.yaml. Without this the
fork-comment workflow would start failing once the backport lands.
- docs/github-actions.md: restructure + flag fix + 3rd security rule
- docs/prereleases.md: generalize the now-non-default check trigger note
- .github/workflows/bumpy-check.yaml: @v7 + flag on the fork job
Copy file name to clipboardExpand all lines: docs/github-actions.md
+24-17Lines changed: 24 additions & 17 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -16,9 +16,23 @@ These commands facilitate the following:
16
16
>
17
17
> The version-resolution shell snippets work as-is regardless of package manager — they only depend on `jq` and `git`, both preinstalled on GitHub-hosted runners.
18
18
19
-
## PR check workflow
19
+
## PR check
20
20
21
-
Posts/updates the release-plan comment on every PR, including PRs from forks. **Copy it as-is and you're covered** — it's structured so nothing in a fork PR can influence how bumpy is fetched or run. (If you change `main` to your own base branch, that's the only edit most repos need. See the security notes below before restructuring it.)
21
+
`bumpy ci check` confirms every PR carries a bump file and posts a release-plan comment showing what will be released. The simplest setup is one step in your existing PR workflow (or a new one triggered `on: pull_request`):
22
+
23
+
```yaml
24
+
- run: bunx @varlock/bumpy ci check
25
+
env:
26
+
GH_TOKEN: ${{ github.token }}
27
+
```
28
+
29
+
Give the job `permissions: pull-requests: write`. This runs in the ordinary `pull_request` context — the same trust level as the rest of your CI — so none of the privileged-workflow precautions in the next section apply: you can `bun install` and run bumpy from your devDeps like any other CLI. (If the job already ran `bun install`, `bunx` picks up your pinned version from `node_modules`; otherwise it fetches the latest.)
30
+
31
+
**Fork PRs get the check, but not the comment.** GitHub hands `pull_request` runs from forks a **read-only token and no secrets**, so the comment can't be posted there. `ci check` still runs and fails the job (red ✗) on a missing bump file, with the explanation in the job logs — forks stay gated correctly, you just don't get the rendered comment. For most repos that's the right trade, and it keeps you off `pull_request_target` entirely. If you do want the comment on fork PRs too, set up the dedicated workflow below.
32
+
33
+
## Commenting on fork PRs
34
+
35
+
Posting to a PR needs a **write** token, and GitHub only grants one to a fork PR through a privileged trigger — `pull_request_target`. That trigger runs with your write token and secrets **even though the PR is code you don't control**, so the workflow must be structured so nothing in the fork PR can influence what gets fetched or run. The one below does that — copy it as-is (changing only `main` if your base branch differs), and read the security notes before restructuring it.
22
36
23
37
```yaml
24
38
# .github/workflows/bumpy-check.yaml
@@ -38,17 +52,21 @@ jobs:
38
52
# here, so the package-manager config in effect (bunfig.toml, .npmrc)
39
53
# is YOURS, not the PR's. Hardcoded "main" (the PR controls its own
40
54
# base ref); change it to your base branch.
41
-
- uses: actions/checkout@v6
55
+
- uses: actions/checkout@v7
42
56
with:
43
57
ref: main
44
58
persist-credentials: false
45
59
46
60
# 2. UNTRUSTED PR head into ./pr — only READ from here, never run it.
47
-
- uses: actions/checkout@v6
61
+
# actions/checkout@v7 refuses to fetch fork PR code under
62
+
# pull_request_target by default; `allow-unsafe-pr-checkout` opts back
63
+
# in. It's safe HERE only because ./pr is never installed or run.
64
+
- uses: actions/checkout@v7
48
65
with:
49
66
ref: ${{ github.event.pull_request.head.sha }}
50
67
path: pr
51
68
persist-credentials: false
69
+
allow-unsafe-pr-checkout: true
52
70
53
71
- uses: oven-sh/setup-bun@v2
54
72
@@ -69,10 +87,11 @@ jobs:
69
87
70
88
### ⚠️ Security essentials
71
89
72
-
`pull_request_target` carries a **write token and secrets even on fork PRs** — that's what lets it comment on forks, and why a PR author must never be able to influence what runs. The workflow above handles this; two rules to preserve if you adapt it:
90
+
`pull_request_target` carries a **write token and secrets even on fork PRs** — that's what lets it comment on forks, and why a PR author must never be able to influence what runs. The workflow above handles this; three rules to preserve if you adapt it:
73
91
74
92
- **Never execute PR code** — no `bun install` / `npm install` (postinstall scripts run), no `bun run <script>` / `npm test`, no building from the PR tree.
75
93
- **Fetch and run bumpy from the trusted base checkout; only _read_ the PR tree** via `--cwd ./pr`. Keep `persist-credentials: false` on both checkouts.
94
+
- **Keep `allow-unsafe-pr-checkout: true` on the `./pr` checkout only.** Since [June 2026](https://github.blog/changelog/2026-06-18-safer-pull_request_target-defaults-for-github-actions-checkout/), `actions/checkout` refuses to fetch fork PR code under `pull_request_target` unless you set this deliberately-conspicuous flag (enforced on `@v7` now, and backported to floating tags like `@v6` on July 16, 2026). It's correct here _because_ `./pr` is read-only data — never add it to a checkout whose code you then install or run.
76
95
77
96
As a guardrail, `bumpy ci check` **fails** if it runs under `pull_request_target` without an explicit `--cwd` — so an outdated single-checkout workflow surfaces loudly instead of silently staying exploitable. If the working directory is genuinely trusted (e.g. a same-repo, non-fork PR), pass `--cwd .` to acknowledge it. (This is a migration nudge, not a security boundary: a PR that actually hijacked resolution would be running its own bumpy. The fix is the two-checkout layout above.)
78
97
@@ -118,18 +137,6 @@ You can also pin the version directly (`bunx @varlock/bumpy@1.2.3 ci check --cwd
118
137
119
138
</details>
120
139
121
-
### Don't need fork PR support?
122
-
123
-
If you don't care about posting comments on external/fork PRs (private repo, internal-only contributors, etc.), you can skip the separate workflow entirely. Just add a step to your existing `pull_request` CI workflow:
124
-
125
-
```yaml
126
-
- run: bunx @varlock/bumpy ci check
127
-
env:
128
-
GH_TOKEN: ${{ github.token }}
129
-
```
130
-
131
-
Make sure the job has `permissions: pull-requests: write`. Since `pull_request` runs in a non-privileged context, all the "no installs / no PR scripts" rules above don't apply — you can `bun install` and run bumpy from your devDeps like any other CLI. The trade-off: fork PRs won't get a comment (the check still runs and fails red on missing bump files, just without the helpful explanation).
132
-
133
140
## Release workflow (recommended: split jobs)
134
141
135
142
The recommended release workflow splits version-PR maintenance from publishing into separate jobs. Only the publish job carries `id-token: write` and npm credentials, and it runs inside a GitHub Environment — so a rogue workflow elsewhere in the repo can't request an OIDC token that npm will accept.
Copy file name to clipboardExpand all lines: docs/prereleases.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -141,7 +141,7 @@ That's the only workflow change. `bumpy ci release` reads the current branch, lo
141
141
142
142
> Make sure the checkout step uses `fetch-depth: 0` (the [release workflow](github-actions.md) already requires this) — the channel publish trigger diffs the triggering push to detect release PR merges.
143
143
144
-
> The PR check workflow (`bumpy-check.yaml`) needs no changes — it runs on `pull_request_target` and handles any base branch.
144
+
> The PR check needs no changes for channels — `bumpy ci check` detects the PR's base branch automatically, whether it runs as a step in your normal `pull_request` workflow or in the dedicated `pull_request_target` workflow for [fork-PR comments](github-actions.md#commenting-on-fork-prs).
0 commit comments