Skip to content

Commit 334f607

Browse files
authored
Merge pull request #5017 from VisActor/feat/release-work-flow
feat: optimiz workflows for release
2 parents 6a69ff1 + 79d20fc commit 334f607

4 files changed

Lines changed: 313 additions & 36 deletions

File tree

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Dispatch develop-synced after release
2+
3+
on:
4+
push:
5+
branches:
6+
- develop
7+
8+
jobs:
9+
dispatch_develop_synced:
10+
if: contains(github.event.head_commit.message, 'prelease version')
11+
runs-on: ubuntu-latest
12+
permissions:
13+
contents: write
14+
15+
steps:
16+
- name: Checkout develop
17+
uses: actions/checkout@v4
18+
with:
19+
ref: develop
20+
21+
- name: Extract version from commit message
22+
id: meta
23+
env:
24+
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
25+
run: |
26+
set -euo pipefail
27+
echo "Commit message: ${COMMIT_MESSAGE}"
28+
VERSION_WITH_V="$(echo "${COMMIT_MESSAGE}" | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' || true)"
29+
if [ -n "${VERSION_WITH_V}" ]; then
30+
VERSION="${VERSION_WITH_V#v}"
31+
else
32+
VERSION="$(echo "${COMMIT_MESSAGE}" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1 || true)"
33+
fi
34+
if [ -z "${VERSION}" ]; then
35+
echo "Failed to parse version from commit message" >&2
36+
exit 1
37+
fi
38+
echo "Parsed version: ${VERSION}"
39+
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
40+
41+
- name: Send develop-synced repository_dispatch
42+
uses: actions/github-script@v7
43+
env:
44+
VERSION: ${{ steps.meta.outputs.version }}
45+
with:
46+
github-token: ${{ secrets.GITHUB_TOKEN }}
47+
script: |
48+
const version = process.env.VERSION;
49+
if (!version) {
50+
core.setFailed('VERSION env is not set');
51+
return;
52+
}
53+
core.info(`Sending repository_dispatch develop-synced for version ${version}`);
54+
await github.rest.repos.createDispatchEvent({
55+
owner: context.repo.owner,
56+
repo: context.repo.repo,
57+
event_type: 'develop-synced',
58+
client_payload: { version }
59+
});

.github/workflows/post-release.yml

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
name: Post release after develop synced
2+
3+
on:
4+
repository_dispatch:
5+
types: [develop-synced]
6+
7+
jobs:
8+
post_release:
9+
runs-on: ubuntu-latest
10+
11+
permissions:
12+
contents: write
13+
14+
steps:
15+
- name: Read version from repository_dispatch payload
16+
id: meta
17+
env:
18+
PAYLOAD_VERSION: ${{ github.event.client_payload.version }}
19+
run: |
20+
set -euo pipefail
21+
if [ -z "${PAYLOAD_VERSION}" ]; then
22+
echo "No version in client_payload, skip post-release."
23+
echo "skip=true" >> "$GITHUB_OUTPUT"
24+
exit 0
25+
fi
26+
echo "Using version from payload: ${PAYLOAD_VERSION}"
27+
echo "version=${PAYLOAD_VERSION}" >> "$GITHUB_OUTPUT"
28+
echo "skip=false" >> "$GITHUB_OUTPUT"
29+
30+
- name: Checkout main
31+
if: steps.meta.outputs.skip != 'true'
32+
uses: actions/checkout@v4
33+
with:
34+
ref: main
35+
fetch-depth: 0
36+
persist-credentials: false
37+
38+
- name: Configure git remote to use PAT
39+
if: steps.meta.outputs.skip != 'true'
40+
env:
41+
GH_PAT: ${{ secrets.CREATE_TAG_RELEASE_TOKEN }}
42+
run: |
43+
set -euo pipefail
44+
git remote set-url origin "https://x-access-token:${GH_PAT}@github.com/${GITHUB_REPOSITORY}.git"
45+
git remote -v
46+
47+
- name: Fetch tags
48+
if: steps.meta.outputs.skip != 'true'
49+
run: |
50+
set -euo pipefail
51+
git fetch --tags --force
52+
53+
- name: Check existing tag and release
54+
id: exist
55+
if: steps.meta.outputs.skip != 'true'
56+
env:
57+
VERSION: ${{ steps.meta.outputs.version }}
58+
GH_TOKEN: ${{ secrets.CREATE_TAG_RELEASE_TOKEN }}
59+
run: |
60+
set -euo pipefail
61+
TAG="v${VERSION}"
62+
63+
if git rev-parse "refs/tags/${TAG}" >/dev/null 2>&1; then
64+
echo "Tag ${TAG} already exists, skip post-release."
65+
echo "skip=true" >> "$GITHUB_OUTPUT"
66+
exit 0
67+
fi
68+
69+
if gh release view "${TAG}" >/dev/null 2>&1; then
70+
echo "Release ${TAG} already exists, skip post-release."
71+
echo "skip=true" >> "$GITHUB_OUTPUT"
72+
exit 0
73+
fi
74+
75+
echo "skip=false" >> "$GITHUB_OUTPUT"
76+
77+
- name: Ensure main changelog exists
78+
if: steps.meta.outputs.skip != 'true' && steps.exist.outputs.skip != 'true'
79+
run: |
80+
set -euo pipefail
81+
if [ ! -f "docs/assets/changelog/en/release.md" ]; then
82+
echo "Error: docs/assets/changelog/en/release.md not found in main."
83+
exit 1
84+
fi
85+
86+
- name: Extract release body from main changelog
87+
id: body
88+
if: steps.meta.outputs.skip != 'true' && steps.exist.outputs.skip != 'true'
89+
env:
90+
VERSION: ${{ steps.meta.outputs.version }}
91+
run: |
92+
set -euo pipefail
93+
node <<'NODE'
94+
const fs = require('fs');
95+
96+
const version = process.env.VERSION;
97+
const changelogPath = 'docs/assets/changelog/en/release.md';
98+
const content = fs.readFileSync(changelogPath, 'utf8');
99+
100+
function escapeRegExp(str) {
101+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
102+
}
103+
104+
const headerPattern = new RegExp('^#\\s*v?' + escapeRegExp(version) + '\\b', 'm');
105+
const match = headerPattern.exec(content);
106+
107+
if (!match) {
108+
console.error('No changelog block for version', version, 'found in main changelog.');
109+
process.exit(1);
110+
}
111+
112+
const startIndex = match.index;
113+
const rest = content.slice(startIndex);
114+
115+
// Find the next release header after the current one.
116+
const nextHeaderPattern = /^#\s*v?\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?[^\n]*$/gm;
117+
let nextIndex = rest.length;
118+
let m;
119+
while ((m = nextHeaderPattern.exec(rest)) !== null) {
120+
if (m.index > 0) {
121+
nextIndex = m.index;
122+
break;
123+
}
124+
}
125+
126+
const block = rest.slice(0, nextIndex).trimEnd() + '\n';
127+
if (!block.trim()) {
128+
console.error('Extracted changelog block is empty for version', version);
129+
process.exit(1);
130+
}
131+
fs.writeFileSync('release-body.md', block, 'utf8');
132+
NODE
133+
echo "has_body=true" >> "$GITHUB_OUTPUT"
134+
135+
- name: Validate extracted release body
136+
if: steps.meta.outputs.skip != 'true' && steps.exist.outputs.skip != 'true' && steps.body.outputs.has_body == 'true'
137+
env:
138+
VERSION: ${{ steps.meta.outputs.version }}
139+
run: |
140+
set -euo pipefail
141+
if [ ! -s "release-body.md" ]; then
142+
echo "Error: release-body.md is missing or empty."
143+
exit 1
144+
fi
145+
146+
if ! grep -Eq "^#\\s*v?${VERSION}\\b" release-body.md; then
147+
echo "Error: release-body.md does not start with expected version header v${VERSION}."
148+
exit 1
149+
fi
150+
151+
HEADER_COUNT="$(grep -Ec '^#\s*v?[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.]+)?\b' release-body.md || true)"
152+
if [ "${HEADER_COUNT}" -ne 1 ]; then
153+
echo "Error: extracted release body contains ${HEADER_COUNT} release headers, expected exactly 1."
154+
exit 1
155+
fi
156+
157+
- name: Verify gh identity
158+
if: steps.meta.outputs.skip != 'true' && steps.exist.outputs.skip != 'true' && steps.body.outputs.has_body == 'true'
159+
env:
160+
GH_TOKEN: ${{ secrets.CREATE_TAG_RELEASE_TOKEN }}
161+
run: |
162+
set -euo pipefail
163+
gh api user -q '.login'
164+
165+
- name: Diagnose PAT repository permission
166+
if: steps.meta.outputs.skip != 'true' && steps.exist.outputs.skip != 'true' && steps.body.outputs.has_body == 'true'
167+
env:
168+
GH_TOKEN: ${{ secrets.CREATE_TAG_RELEASE_TOKEN }}
169+
run: |
170+
set -euo pipefail
171+
LOGIN=$(gh api user -q '.login')
172+
echo "PAT user: $LOGIN"
173+
OWNER=${GITHUB_REPOSITORY%%/*}
174+
REPO=${GITHUB_REPOSITORY#*/}
175+
RESP=$(gh api "/repos/$OWNER/$REPO/collaborators/$LOGIN/permission" 2>&1 || true)
176+
echo "$RESP"
177+
if echo "$RESP" | grep -q '404'; then
178+
echo "Not a collaborator (404), repository access may be restricted or PAT not authorized to org"
179+
elif echo "$RESP" | grep -q '"permission":"write"'; then
180+
echo "permission ok: write"
181+
elif echo "$RESP" | grep -q '"permission":"admin"'; then
182+
echo "permission ok: admin"
183+
else
184+
echo "permission insufficient: $RESP"
185+
fi
186+
187+
- name: Create tag and GitHub Release
188+
if: steps.meta.outputs.skip != 'true' && steps.exist.outputs.skip != 'true' && steps.body.outputs.has_body == 'true'
189+
env:
190+
VERSION: ${{ steps.meta.outputs.version }}
191+
GH_TOKEN: ${{ secrets.CREATE_TAG_RELEASE_TOKEN }}
192+
run: |
193+
set -euo pipefail
194+
TAG="v${VERSION}"
195+
196+
git fetch origin main:refs/remotes/origin/main --depth=1
197+
MAIN_SHA="$(git rev-parse origin/main)"
198+
199+
echo "Creating tag ${TAG} at ${MAIN_SHA}"
200+
git tag "${TAG}" "${MAIN_SHA}"
201+
git push origin "${TAG}"
202+
203+
echo "Creating GitHub Release ${TAG}"
204+
gh release create "${TAG}" \
205+
--target "main" \
206+
--title "${TAG}" \
207+
--notes-file "release-body.md"

.github/workflows/release.yml

Lines changed: 15 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,21 @@ on:
77

88
jobs:
99
build:
10-
runs-on: macOS-latest # 如果用了electron,记得改成 macOS-latest
10+
runs-on: macOS-latest
1111
permissions:
1212
contents: write
1313
pull-requests: write
1414

1515
strategy:
1616
matrix:
1717
node-version: [18.x]
18-
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
1918

2019
steps:
21-
- uses: actions/checkout@v3
22-
- run: |
20+
- name: Checkout
21+
uses: actions/checkout@v3
22+
23+
- name: Configure git user
24+
run: |
2325
git config user.name ${{ github.actor }}
2426
git config user.email ${{ github.actor }}@users.noreply.github.com
2527
@@ -30,7 +32,6 @@ jobs:
3032
cache: 'npm'
3133
cache-dependency-path: './common/config/rush/pnpm-lock.yaml'
3234

33-
# Install rush
3435
- name: Install rush
3536
run: node common/scripts/install-run-rush.js install --bypass-policy
3637

@@ -40,9 +41,9 @@ jobs:
4041
with:
4142
path: packages/vtable
4243
semver_string: ${{ github.ref_name }}
43-
semver_pattern: '^release/(.*)$' # ^v?(.*)$ by default
44+
semver_pattern: '^release/(.*)$'
4445

45-
- name: update nextBump of version policies
46+
- name: Update nextBump of version policies
4647
uses: xile611/set-next-bump-of-rush@main
4748
with:
4849
release_version: ${{ steps.semver_parser.outputs.full }}
@@ -53,33 +54,24 @@ jobs:
5354

5455
- name: Build packages
5556
env:
56-
NODE_OPTIONS: "--max_old_space_size=4096"
57-
NO_EMIT_ON_ERROR: "true"
57+
NODE_OPTIONS: '--max_old_space_size=4096'
58+
NO_EMIT_ON_ERROR: 'true'
5859
run: |
59-
# 设置环境变量确保错误信息完整输出
6060
export NODE_OPTIONS="--max_old_space_size=4096"
6161
export NO_EMIT_ON_ERROR="true"
62-
# 运行构建,并将输出保存到文件
6362
node common/scripts/install-run-rush.js build --only tag:package 2>&1 | tee build.log || {
6463
echo "=== Build failed, showing last 1000 lines of build.log ==="
6564
tail -n 1000 build.log
6665
echo "=== Full error details ==="
67-
# 尝试从日志中提取错误信息
6866
grep -A 50 "TypeScript Compilation Errors" build.log || true
6967
grep -A 50 "Build Error" build.log || true
7068
exit 1
7169
}
7270
73-
# - name: Run Bugserver
74-
# working-directory: ./packages/vtable
75-
# env:
76-
# BUG_SERVER_TOKEN: ${{ secrets.BUG_SERVER_TOKEN }}
77-
# run: node ../../common/scripts/install-run-rushx.js ci
78-
7971
- name: Publish to npm
8072
env:
81-
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
82-
NPM_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
73+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
74+
NPM_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
8375
run: node common/scripts/install-run-rush.js publish --publish --include-all
8476

8577
- name: Update shrinkwrap
@@ -91,7 +83,7 @@ jobs:
9183
with:
9284
path: packages/vtable
9385

94-
- name: Commit & Push changes
86+
- name: Commit and push changes
9587
uses: actions-js/push@master
9688
with:
9789
github_token: ${{ secrets.GITHUB_TOKEN }}
@@ -104,27 +96,14 @@ jobs:
10496
with:
10597
version: ${{ steps.package-version.outputs.current_version }}
10698

107-
- name: Create Release for Tag
108-
id: release_tag
109-
uses: ncipollo/release-action@v1.12.0
110-
env:
111-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
112-
with:
113-
tag: v${{ steps.package-version.outputs.current_version }}
114-
commit: main
115-
prerelease: false
116-
body: |
117-
${{ steps.changelog.outputs.markdown }}
118-
draft: true #
119-
12099
- name: Create Pull Request
121100
uses: dustinirving/create-pr@v1.0.2
122101
with:
123102
token: ${{ secrets.GITHUB_TOKEN }}
124103
title: '[Auto release] release ${{ steps.package-version.outputs.current_version }}'
125104
base: main
126105
head: ${{ github.ref_name }}
127-
labels: release # default labels, the action will throw error if not specified
128-
reviewers: fangsmile,Rui-Sun # default reviewers, the action will throw error if not specified
106+
labels: release
107+
reviewers: fangsmile,Rui-Sun
129108
body: |
130109
${{ steps.changelog.outputs.markdown }}

0 commit comments

Comments
 (0)