-
Notifications
You must be signed in to change notification settings - Fork 0
148 lines (131 loc) · 5.6 KB
/
release.yml
File metadata and controls
148 lines (131 loc) · 5.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
name: Release
# Manually-triggered release workflow.
# Runs `npm run bump-version <version>` to update every manifest, then
# opens a one-shot release PR (because main is protected and rejects
# direct pushes — see GH013), squash-merges it, tags the merged commit,
# pushes the tag, and creates a GitHub release with auto-generated notes
# assembled from PRs since the previous tag.
#
# Usage: GitHub web UI → Actions → "Release" → "Run workflow" → enter the
# new semver (e.g. `1.0.1`). Or via CLI:
#
# gh workflow run release.yml -f version=1.0.1
on:
workflow_dispatch:
inputs:
version:
description: "New semver (e.g. 1.0.1) — must be greater than the current package.json version"
required: true
type: string
permissions:
contents: write # tag + release
pull-requests: write # open + merge the release PR (main is protected)
jobs:
release:
name: Bump, tag, and release
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Check out repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# Need full history so `gh release create --generate-notes` can
# diff against the previous tag.
fetch-depth: 0
# Use the default GITHUB_TOKEN to push the bump commit + tag.
token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Node.js
uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: 22
cache: npm
- name: Install dependencies
run: npm ci
- name: Validate the requested version is newer than the current one
run: |
set -euo pipefail
requested="${{ inputs.version }}"
current=$(node -p "require('./package.json').version")
echo "Current: $current"
echo "Requested: $requested"
if [ "$current" = "$requested" ]; then
echo "::error::Requested version $requested is the same as the current version. Pick a higher one."
exit 1
fi
# Lexicographic vs numeric: use sort -V (version sort) to make
# sure the requested version is strictly greater.
highest=$(printf '%s\n%s\n' "$current" "$requested" | sort -V | tail -n1)
if [ "$highest" != "$requested" ]; then
echo "::error::Requested version $requested is not greater than current $current."
exit 1
fi
- name: Bump version metadata
run: npm run bump-version -- "${{ inputs.version }}"
- name: Verify the bump landed in every manifest
run: npm run check-version
- name: Run tests against the bumped tree
run: npm test
- name: Configure git committer
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Push the bump on a release branch
run: |
set -euo pipefail
version="${{ inputs.version }}"
branch="release/v${version}"
git checkout -b "$branch"
git add package.json package-lock.json plugins/opencode/.claude-plugin/plugin.json .claude-plugin/marketplace.json
git commit -m "chore(release): v${version}"
git push origin "$branch"
- name: Open the release PR and merge it
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
set -euo pipefail
version="${{ inputs.version }}"
branch="release/v${version}"
# Direct pushes to main are blocked by the "Changes must be made
# through a pull request" branch protection rule, so the release
# workflow opens a one-shot PR and immediately squash-merges it.
# This still satisfies the rule (the change goes via a PR) while
# keeping the workflow fully automated.
pr_url=$(gh pr create \
--base main \
--head "$branch" \
--title "chore(release): v${version}" \
--body "Automated release PR generated by \`.github/workflows/release.yml\`. Bumps every version-bearing manifest to v${version}.")
echo "Created release PR: $pr_url"
# `gh pr create` returns synchronously but GraphQL's PR node
# is eventually consistent — the v1.0.4 release hit a race
# where `gh pr merge` fired ~500ms after create and got:
# "Could not resolve to a PullRequest with the number of N"
# Retry up to 5 times with a 2s backoff to cover propagation.
for attempt in 1 2 3 4 5; do
if gh pr merge "$pr_url" --squash --delete-branch; then
break
fi
if [ "$attempt" = "5" ]; then
echo "::error::gh pr merge still failing after 5 attempts"
exit 1
fi
echo "gh pr merge attempt $attempt failed (likely GraphQL lag); retrying in 2s..."
sleep 2
done
- name: Tag the merged commit on main
run: |
set -euo pipefail
version="${{ inputs.version }}"
# Pick up the squash-merge commit that gh pr merge just created.
git fetch origin main
git checkout main
git reset --hard origin/main
git tag -a "v${version}" -m "Release v${version}"
git push origin "v${version}"
- name: Create GitHub release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create "v${{ inputs.version }}" \
--title "v${{ inputs.version }}" \
--generate-notes