Skip to content

Commit 3124338

Browse files
committed
CI: add release workflow
1 parent 3dbc6b2 commit 3124338

File tree

2 files changed

+241
-0
lines changed

2 files changed

+241
-0
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
name: Create Release PR
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
version:
7+
description: 'Version type'
8+
required: true
9+
type: choice
10+
options:
11+
- patch
12+
- minor
13+
- major
14+
15+
jobs:
16+
create-release-pr:
17+
runs-on: ubuntu-latest
18+
permissions:
19+
contents: write
20+
pull-requests: write
21+
steps:
22+
- name: Checkout
23+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
24+
with:
25+
fetch-depth: 0
26+
persist-credentials: false
27+
28+
- name: Setup Node.js
29+
uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1
30+
with:
31+
node-version: 22
32+
33+
- name: Configure Git
34+
run: |
35+
git config user.name "github-actions[bot]"
36+
git config user.email "github-actions[bot]@users.noreply.github.com"
37+
38+
- name: Version bump
39+
id: version
40+
run: |
41+
npm version "$VERSION_TYPE" --no-git-tag-version
42+
VERSION=$(node -p "require('./package.json').version")
43+
echo "version=$VERSION" >> $GITHUB_OUTPUT
44+
env:
45+
VERSION_TYPE: ${{ github.event.inputs.version }}
46+
47+
- name: Get release notes
48+
id: release-notes
49+
run: |
50+
# Get the default branch
51+
DEFAULT_BRANCH=$(gh api "repos/$GITHUB_REPOSITORY" --jq '.default_branch')
52+
53+
# Get the last tag
54+
LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")
55+
if [ -z "$LAST_TAG" ]; then
56+
LAST_TAG=$(git rev-list --max-parents=0 HEAD)
57+
fi
58+
59+
# Generate release notes
60+
NOTES=$(gh api \
61+
--method POST \
62+
-H "Accept: application/vnd.github+json" \
63+
"/repos/$GITHUB_REPOSITORY/releases/generate-notes" \
64+
-f tag_name="v$VERSION" \
65+
-f target_commitish="$DEFAULT_BRANCH" \
66+
-f previous_tag_name="${LAST_TAG}" | jq -r '.body')
67+
68+
# Save to file to handle multiline content
69+
echo "$NOTES" > release-notes.md
70+
env:
71+
GH_TOKEN: ${{ github.token }}
72+
VERSION: ${{ steps.version.outputs.version }}
73+
GITHUB_REPOSITORY: ${{ github.repository }}
74+
75+
- name: Create Pull Request
76+
uses: peter-evans/create-pull-request@153407881ec5c347639a548ade7d8ad1d6740e38 # v5.0.2
77+
with:
78+
branch: release/v${{ steps.version.outputs.version }}
79+
delete-branch: true
80+
title: "Release v${{ steps.version.outputs.version }}"
81+
body-path: release-notes.md
82+
commit-message: "chore: release v${{ steps.version.outputs.version }}"
83+
labels: |
84+
Type: Release
85+
assignees: ${{ github.actor }}

.github/workflows/release.yml

Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
name: Release
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- master
7+
- main
8+
types:
9+
- closed
10+
workflow_dispatch:
11+
inputs:
12+
version:
13+
description: 'Version to publish (e.g., 1.2.3)'
14+
required: false
15+
type: string
16+
17+
jobs:
18+
release:
19+
if: |
20+
(github.event_name == 'pull_request' &&
21+
github.event.pull_request.merged == true &&
22+
contains(github.event.pull_request.labels.*.name, 'Type: Release')) ||
23+
github.event_name == 'workflow_dispatch'
24+
runs-on: ubuntu-latest
25+
permissions:
26+
contents: write
27+
id-token: write # OIDC
28+
pull-requests: write # PR comment
29+
steps:
30+
- name: Checkout
31+
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
32+
with:
33+
fetch-depth: 0
34+
persist-credentials: false
35+
36+
- name: Get package info
37+
id: package
38+
run: |
39+
if [ "$EVENT_NAME" = "workflow_dispatch" ] && [ -n "$INPUT_VERSION" ]; then
40+
VERSION="$INPUT_VERSION"
41+
else
42+
VERSION=$(node -p "require('./package.json').version")
43+
fi
44+
PACKAGE_NAME=$(node -p "require('./package.json').name")
45+
echo "version=$VERSION" >> $GITHUB_OUTPUT
46+
echo "name=$PACKAGE_NAME" >> $GITHUB_OUTPUT
47+
env:
48+
EVENT_NAME: ${{ github.event_name }}
49+
INPUT_VERSION: ${{ github.event.inputs.version }}
50+
51+
- name: Check if tag exists
52+
id: tag-check
53+
run: |
54+
if git rev-parse "v$VERSION" >/dev/null 2>&1; then
55+
echo "exists=true" >> $GITHUB_OUTPUT
56+
else
57+
echo "exists=false" >> $GITHUB_OUTPUT
58+
fi
59+
env:
60+
VERSION: ${{ steps.package.outputs.version }}
61+
62+
- name: Setup Node.js
63+
if: steps.tag-check.outputs.exists == 'false'
64+
uses: actions/setup-node@3235b876344d2a9aa001b8d1453c930bba69e610 # v3.9.1
65+
with:
66+
node-version: 22
67+
registry-url: 'https://registry.npmjs.org'
68+
69+
- name: Ensure npm 11.5.1 or later is installed
70+
if: steps.tag-check.outputs.exists == 'false'
71+
run: |
72+
NPM_VERSION=$(npm -v)
73+
echo "Current npm version: $NPM_VERSION"
74+
if ! npx semver -r ">=11.5.1" "$NPM_VERSION"; then
75+
echo "npm version $NPM_VERSION is too old. Installing latest npm..."
76+
npm install -g npm@latest
77+
echo "Updated npm version: $(npm -v)"
78+
fi
79+
80+
- name: Install dependencies
81+
if: steps.tag-check.outputs.exists == 'false'
82+
run: npm ci
83+
84+
- name: Build package
85+
if: steps.tag-check.outputs.exists == 'false'
86+
run: npm run build || true
87+
88+
- name: Publish to npm with provenance
89+
if: steps.tag-check.outputs.exists == 'false'
90+
run: npm publish --provenance --access public
91+
92+
- name: Create GitHub Release with tag
93+
id: create-release
94+
if: steps.tag-check.outputs.exists == 'false'
95+
run: |
96+
if [ "$EVENT_NAME" = "workflow_dispatch" ]; then
97+
RELEASE_URL=$(gh release create "v$VERSION" \
98+
--title "v$VERSION" \
99+
--target "$SHA" \
100+
--generate-notes)
101+
else
102+
RELEASE_URL=$(gh release create "v$VERSION" \
103+
--title "v$VERSION" \
104+
--target "$SHA" \
105+
--notes "$PR_BODY")
106+
fi
107+
echo "url=$RELEASE_URL" >> $GITHUB_OUTPUT
108+
env:
109+
GH_TOKEN: ${{ github.token }}
110+
VERSION: ${{ steps.package.outputs.version }}
111+
SHA: ${{ github.sha }}
112+
EVENT_NAME: ${{ github.event_name }}
113+
PR_BODY: ${{ github.event.pull_request.body }}
114+
115+
- name: Comment on PR - Success
116+
if: |
117+
always() &&
118+
github.event_name == 'pull_request' &&
119+
steps.tag-check.outputs.exists == 'false' &&
120+
success()
121+
run: |
122+
gh pr comment "$PR_NUMBER" \
123+
--body "✅ **Release v$VERSION completed successfully!**
124+
125+
- 📦 npm package: https://www.npmjs.com/package/$PACKAGE_NAME/v/$VERSION
126+
- 🏷️ GitHub Release: $RELEASE_URL
127+
- 🔗 Workflow run: $SERVER_URL/$REPOSITORY/actions/runs/$RUN_ID"
128+
env:
129+
GH_TOKEN: ${{ github.token }}
130+
PR_NUMBER: ${{ github.event.pull_request.number }}
131+
VERSION: ${{ steps.package.outputs.version }}
132+
PACKAGE_NAME: ${{ steps.package.outputs.name }}
133+
RELEASE_URL: ${{ steps.create-release.outputs.url }}
134+
SERVER_URL: ${{ github.server_url }}
135+
REPOSITORY: ${{ github.repository }}
136+
RUN_ID: ${{ github.run_id }}
137+
138+
- name: Comment on PR - Failure
139+
if: |
140+
always() &&
141+
github.event_name == 'pull_request' &&
142+
steps.tag-check.outputs.exists == 'false' &&
143+
failure()
144+
run: |
145+
gh pr comment "$PR_NUMBER" \
146+
--body "❌ **Release v$VERSION failed**
147+
148+
Please check the workflow logs for details.
149+
🔗 Workflow run: $SERVER_URL/$REPOSITORY/actions/runs/$RUN_ID"
150+
env:
151+
GH_TOKEN: ${{ github.token }}
152+
PR_NUMBER: ${{ github.event.pull_request.number }}
153+
VERSION: ${{ steps.package.outputs.version }}
154+
SERVER_URL: ${{ github.server_url }}
155+
REPOSITORY: ${{ github.repository }}
156+
RUN_ID: ${{ github.run_id }}

0 commit comments

Comments
 (0)