Skip to content

Commit 8dc6252

Browse files
TomOnTimetlimoncelli26claude
authored
CICD: Tag release directly instead of via a PR (#4518)
The dispatch release workflow created the empty "Release vX.Y.Z" commit by opening an auto-merged PR, but this org forbids GitHub Actions from creating pull requests ("GitHub Actions is not permitted to create or approve pull requests"), so the run failed at gh pr create. Since main is protected (PR required, enforced for admins) and a PAT is not an option, land the release without a PR: the release job now tags the tip of the selected branch directly and pushes the tag. Tag pushes are unaffected by branch protection (no tag protection rules), and the changelog stays correct because GORELEASER_PREVIOUS_TAG is computed by version, not commit ancestry. - Drop the empty-commit + gh pr create/merge steps; tag the tip instead. - Remove the now-unused pull-requests: write permission. - Update release-engineering.md to describe the direct-tag approach. The annotated tag still carries the "Release vX.Y.Z" message. preflight validation, the full longtest gate, skip_longtest, previous_tag override, release-from-any-branch, and the manual tag-push path are unchanged. Co-authored-by: Tom Limoncelli <tlimoncelli@coreweave.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent d2073a3 commit 8dc6252

2 files changed

Lines changed: 21 additions & 31 deletions

File tree

.github/workflows/release_draft.yml

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ jobs:
8787
full: true
8888
secrets: inherit
8989

90-
# release: create the release commit + tag (dispatch only) and run GoReleaser.
90+
# release: create the tag (dispatch only) and run GoReleaser.
9191
# Runs for a manual tag push (unchanged behavior) OR after a successful
9292
# dispatch+verify.
9393
release:
@@ -108,7 +108,6 @@ jobs:
108108
permissions:
109109
packages: write
110110
contents: write
111-
pull-requests: write
112111
steps:
113112
- name: Set up QEMU
114113
uses: docker/setup-qemu-action@v4
@@ -137,37 +136,27 @@ jobs:
137136
echo "Release \`${{ inputs.version }}\` was cut with **skip_longtest=true** — the full integration suite did NOT run."
138137
} >> "$GITHUB_STEP_SUMMARY"
139138
140-
# DISPATCH ONLY: main is protected (PRs required, enforced for admins), so
141-
# the empty "Release vX.Y.Z" commit is landed via an auto-merged PR (0
142-
# required reviews/checks make this possible with GITHUB_TOKEN), then
143-
# tagged. Pushing the tag with GITHUB_TOKEN intentionally does NOT
144-
# re-trigger this workflow's tag-push path, so there is no double release.
145-
- name: Create release commit and tag
139+
# DISPATCH ONLY: tag the tip of the selected branch and push the tag.
140+
#
141+
# We deliberately do NOT land an empty "Release vX.Y.Z" commit on the
142+
# branch: main is protected (PRs required, enforced for admins) and this
143+
# org forbids GitHub Actions from creating pull requests, so an
144+
# automated, PAT-free PR is impossible. Tagging is unaffected by branch
145+
# protection (no tag protection rules), and the changelog is still correct
146+
# because GORELEASER_PREVIOUS_TAG is computed by version below (it does
147+
# not rely on the tag sitting on its own commit). The annotated tag itself
148+
# carries the "Release vX.Y.Z" message.
149+
#
150+
# Pushing the tag with GITHUB_TOKEN intentionally does NOT re-trigger this
151+
# workflow's tag-push path, so there is no double release.
152+
- name: Create and push tag
146153
if: github.event_name == 'workflow_dispatch'
147154
env:
148-
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
149155
VERSION: ${{ inputs.version }}
150-
BRANCH: ${{ github.ref_name }}
151156
run: |
152157
set -eu
153158
git config user.name "github-actions[bot]"
154159
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
155-
156-
work="release/${VERSION}"
157-
git switch -c "$work"
158-
git commit --allow-empty -m "Release ${VERSION}"
159-
git push -u origin "$work"
160-
161-
gh pr create --base "$BRANCH" --head "$work" \
162-
--title "Release ${VERSION}" \
163-
--body "Automated empty commit marking release ${VERSION}."
164-
gh pr edit "$work" --add-label longtest || echo "WARNING: could not add 'longtest' label (continuing)."
165-
gh pr merge "$work" --squash --delete-branch
166-
167-
# Fast-forward the local checkout to the merged commit, then tag it.
168-
git fetch origin "$BRANCH"
169-
git checkout "$BRANCH"
170-
git reset --hard "origin/${BRANCH}"
171160
git tag -a "$VERSION" -m "Release ${VERSION}"
172161
git push origin "refs/tags/${VERSION}"
173162

documentation/release/release-engineering.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,13 @@ The workflow then, in order:
7878
1. **preflight** — validates the version; refuses if the tag already exists.
7979
2. **verify** — runs the **entire** `longtest` integration suite as a gate. If
8080
anything fails, nothing is tagged or published.
81-
3. **release** — creates the empty `Release <version>` commit via an
82-
auto-merged PR (labeled `longtest`), tags it, and runs GoReleaser to produce
83-
the **draft** release.
81+
3. **release** — tags the tip of the selected branch and runs GoReleaser to
82+
produce the **draft** release.
8483

85-
The empty commit guarantees each release/RC lands on its own commit, so the
86-
changelog range is computed correctly.
84+
The release is tagged directly (not via an empty `Release <version>` commit):
85+
this org forbids GitHub Actions from opening pull requests and `main` is
86+
protected, so a PAT-free automated PR is not possible. The changelog range is
87+
still correct because it is computed by version, not by commit ancestry.
8788

8889
### Manual (escape hatch)
8990

0 commit comments

Comments
 (0)