Skip to content

Commit e36a8d4

Browse files
authored
fix(ci): release-please auto-creates tag via commit-message detection (#38)
## Problem The previous workflow gated the `create-tag` job on `needs.release-please.outputs.release_created == 'true'`. But `release-please-config.json` sets `skip-github-release: true` (to avoid conflicting with GoReleaser's release flow), which means `release_created` is **always false**. Result: tags never get created. We caught this when v0.1.7 and v0.1.8 both shipped without tags, blocking subsequent release-please runs until tags were manually backfilled. ## Fix Collapse into a single job and detect release-PR merges by inspecting the merge commit's subject (the openclaw-operator pattern). When the subject matches `chore(main): release X.Y.Z`, create the `vX.Y.Z` tag. Otherwise no-op. Also adds a 'Fix stale autorelease labels' step so the `autorelease: pending` label gets flipped to `tagged` automatically, preventing the 'untagged merged release PRs outstanding' deadlock release-please throws when labels aren't reconciled. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
1 parent 8690453 commit e36a8d4

1 file changed

Lines changed: 64 additions & 44 deletions

File tree

.github/workflows/release-please.yaml

Lines changed: 64 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,6 @@ permissions:
1010
jobs:
1111
release-please:
1212
runs-on: ubuntu-latest
13-
outputs:
14-
release_created: ${{ steps.release.outputs.release_created }}
15-
tag_name: ${{ steps.release.outputs.tag_name }}
16-
sha: ${{ steps.release.outputs.sha }}
1713
steps:
1814
# Mint a short-lived token (~1h) from the paperclip-release-bot App.
1915
# Avoids long-lived PATs that the paperclipinc org policy blocks.
@@ -30,51 +26,75 @@ jobs:
3026
config-file: release-please-config.json
3127
manifest-file: .release-please-manifest.json
3228

33-
# Because skip-github-release is true in release-please-config.json,
34-
# release-please only creates the release PR and updates the manifest;
35-
# it does NOT push the tag. We push the tag manually here so downstream
36-
# GoReleaser/OLM jobs can key off it.
37-
create-tag:
38-
needs: release-please
39-
if: ${{ needs.release-please.outputs.release_created == 'true' }}
40-
runs-on: ubuntu-latest
41-
steps:
42-
- uses: actions/create-github-app-token@v1
43-
id: app-token
44-
with:
45-
app-id: ${{ vars.RELEASE_BOT_APP_ID }}
46-
private-key: ${{ secrets.RELEASE_BOT_PRIVATE_KEY }}
47-
48-
- uses: actions/checkout@v6
49-
with:
50-
fetch-depth: 0
51-
token: ${{ steps.app-token.outputs.token }}
52-
53-
- name: Create and push release tag
29+
# skip-github-release is true in release-please-config.json so that
30+
# release-please does NOT create a GitHub release (which would
31+
# conflict with GoReleaser's draft-then-publish flow on immutable
32+
# releases). However, this also makes release-please's
33+
# `release_created` output always false, so we cannot use it to gate
34+
# tag creation. Instead, we detect a release-PR merge by inspecting
35+
# the merge commit's subject line and create the tag manually.
36+
- name: Create release tag
5437
env:
55-
TAG: ${{ needs.release-please.outputs.tag_name }}
56-
SHA: ${{ needs.release-please.outputs.sha }}
5738
GH_TOKEN: ${{ steps.app-token.outputs.token }}
5839
run: |
59-
git config user.name "paperclip-release-bot[bot]"
60-
git config user.email "paperclip-release-bot[bot]@users.noreply.github.com"
61-
git tag -a "${TAG}" "${SHA}" -m "Release ${TAG}"
62-
git push origin "${TAG}"
40+
# Detect if the current push merged a release-please PR.
41+
# Check both the commit message (squash merge) and the merged
42+
# PR title (merge commit) to handle all merge strategies.
43+
COMMIT_MSG=$(gh api "repos/${{ github.repository }}/commits/${{ github.sha }}" \
44+
--jq '.commit.message' | head -1)
45+
46+
VERSION=""
47+
if [[ "$COMMIT_MSG" =~ ^chore\(main\):\ release\ ([0-9]+\.[0-9]+\.[0-9]+) ]]; then
48+
VERSION="${BASH_REMATCH[1]}"
49+
elif [[ "$COMMIT_MSG" =~ ^Merge\ pull\ request\ \#([0-9]+) ]]; then
50+
# Merge commit - check the PR title instead
51+
PR_NUM="${BASH_REMATCH[1]}"
52+
PR_TITLE=$(gh api "repos/${{ github.repository }}/pulls/${PR_NUM}" --jq '.title')
53+
if [[ "$PR_TITLE" =~ ^chore\(main\):\ release\ ([0-9]+\.[0-9]+\.[0-9]+) ]]; then
54+
VERSION="${BASH_REMATCH[1]}"
55+
fi
56+
fi
6357
64-
- name: Flip stale autorelease labels on older open PRs
58+
if [[ -n "$VERSION" ]]; then
59+
TAG="v${VERSION}"
60+
61+
# Only create if tag doesn't already exist
62+
if gh api "repos/${{ github.repository }}/git/refs/tags/${TAG}" &>/dev/null; then
63+
echo "Tag ${TAG} already exists, skipping"
64+
else
65+
echo "Creating tag ${TAG} at ${{ github.sha }}"
66+
gh api "repos/${{ github.repository }}/git/refs" \
67+
-f ref="refs/tags/${TAG}" \
68+
-f sha="${{ github.sha }}"
69+
echo "Tag ${TAG} created - release workflow will trigger"
70+
fi
71+
else
72+
echo "Not a release commit, skipping tag creation"
73+
fi
74+
75+
# Flip "autorelease: pending" to "autorelease: tagged" on any
76+
# release PR that still has the stale label. Covers both merged
77+
# and closed PRs -- closed-but-not-merged release PRs (e.g. a
78+
# superseded vX.Y.Z PR) also block release-please if the label
79+
# isn't cleaned up. Runs on every push to main so the label
80+
# gets fixed even if the tag step above was skipped or failed.
81+
- name: Fix stale autorelease labels
6582
env:
6683
GH_TOKEN: ${{ steps.app-token.outputs.token }}
67-
TAG: ${{ needs.release-please.outputs.tag_name }}
6884
run: |
69-
# Remove autorelease:pending / autorelease:tagged from any PRs that
70-
# are now superseded by this release.
71-
gh pr list \
72-
--label "autorelease: pending" \
73-
--json number \
74-
--jq '.[].number' \
75-
| while read -r pr; do
76-
gh pr edit "$pr" \
77-
--remove-label "autorelease: pending" \
78-
--add-label "autorelease: tagged" \
79-
|| true
85+
for STATE in merged closed; do
86+
STALE_PRS=$(gh pr list \
87+
--repo "${{ github.repository }}" \
88+
--state "$STATE" \
89+
--label "autorelease: pending" \
90+
--json number,title \
91+
--jq '.[].number')
92+
93+
for PR in $STALE_PRS; do
94+
echo "Fixing stale label on ${STATE} PR #${PR}"
95+
gh api "repos/${{ github.repository }}/issues/${PR}/labels/autorelease:%20pending" \
96+
-X DELETE || true
97+
gh api "repos/${{ github.repository }}/issues/${PR}/labels" \
98+
-f "labels[]=autorelease: tagged" || true
8099
done
100+
done

0 commit comments

Comments
 (0)