Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions .github/workflows/release-from-notes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ jobs:
"https://x-access-token@github.com/${GITHUB_REPOSITORY}.git" \
"HEAD:refs/heads/release/${VERSION}"

- name: Open and auto-merge release PR
- name: Open and merge release PR
id: pr
env:
GH_TOKEN: ${{ github.token }}
Expand All @@ -336,11 +336,17 @@ jobs:
--body "Automated release commit and next-development version bump for ${TAG}. This PR is required because main is protected; once merged, the release continues automatically.")"
PR_NUMBER="${PR_URL##*/}"
echo "pr_number=${PR_NUMBER}" >> "${GITHUB_OUTPUT}"
gh pr merge "${PR_NUMBER}" --merge --auto --delete-branch

# Waits for GitHub's native auto-merge to land the PR (e.g. once required
# status checks pass). Fails fast on a real conflict or on the PR being
# closed unmerged, instead of spinning for the full timeout.
# Merge immediately rather than requesting GitHub's native auto-merge
# (which requires the repo-level "Allow auto-merge" setting and
# would otherwise just fail with a GraphQL error). This works because
# required approvals is 0 and main has no required status checks that
# only run on pull_request events, so the PR is already mergeable.
gh pr merge "${PR_NUMBER}" --merge --delete-branch

# Safety net: the merge above is normally synchronous, but this confirms
# it actually landed (e.g. if a merge queue or delayed check deferred
# it) and fails fast on a real conflict or on the PR being closed
# unmerged, instead of spinning for the full timeout.
- name: Wait for release PR to merge
env:
GH_TOKEN: ${{ github.token }}
Expand Down
19 changes: 12 additions & 7 deletions docs/release-security.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,10 @@ derived release commit) through every job.

`prepare-release` does **not** push directly to `main`. It pushes the release
commit and the next-SNAPSHOT commit to a throwaway `release/<version>`
branch, opens a PR into `main`, enables GitHub's native auto-merge, and waits
for the PR to actually merge before pushing the tag. This is deliberate:
branch, opens a PR into `main`, and merges it immediately (required approvals
is 0 and `main` has no required status checks that only run on `pull_request`
events, so the PR is already mergeable — no dependency on the repo-level
"Allow auto-merge" setting) before pushing the tag. This is deliberate:
`main`'s ruleset requires a pull request, and the only way to push directly
despite that would be adding a bypass actor to the ruleset. Ruleset bypass is
granted to an **identity** (e.g. "GitHub Actions"), not to this one job —
Expand Down Expand Up @@ -227,8 +229,8 @@ required reviewers.
privileged identity to grant. If you also require status checks on `main`,
make sure at least one check actually runs on `pull_request` events (this
repo's CI currently only triggers on `push`/`workflow_dispatch`), otherwise
the release PR's auto-merge will wait until `prepare-release`'s own
15-minute timeout and fail.
the immediate merge in `prepare-release` will fail outright with an
unmergeable-PR error.
- **Tag ruleset `v*`**: block updates and deletions for everyone (immutable
tags). Tag **creation** does not need to be restricted to a bypass actor:
by the time `prepare-release` pushes a tag, the commit it points to has
Expand Down Expand Up @@ -296,9 +298,12 @@ Same procedures as zenwave-sdk (`docs/release-security.md` there):
2. **Snapshot credentials trust `develop`/`next`** — see snapshot section.
3. **npm publication trusts the environment approval + OIDC claims** — no
token exists, but an approved run of this workflow from `main` can publish.
4. **GitHub Actions app bypass on `main`** — narrowed by read-only default
workflow permissions and CODEOWNERS on `.github/`; use a dedicated GitHub
App to narrow further.
4. **No ruleset bypass actor exists on `main` or `v*` anymore.** The release
commit lands via an auto-merged PR instead, which was a deliberate change
from an earlier design that would have added a "GitHub Actions" bypass —
that approach was rejected because ruleset bypass is actor-based, not
job-based: it would have let *any* workflow in this repository with
`contents:write` push straight to `main`, not just `prepare-release`.
5. **Trust in `main` itself** — everything on `main` is by definition trusted;
branch protection and reviews are what make that hold.

Expand Down
Loading