Skip to content

Commit b9f261b

Browse files
committed
chore(release): automate v1.1.2 releases
1 parent 41065b1 commit b9f261b

8 files changed

Lines changed: 374 additions & 5 deletions

File tree

.github/workflows/release.yml

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v*"
7+
8+
permissions:
9+
contents: write
10+
11+
concurrency:
12+
group: release-${{ github.ref }}
13+
cancel-in-progress: false
14+
15+
jobs:
16+
publish:
17+
name: Publish ${{ github.ref_name }}
18+
runs-on: ubuntu-latest
19+
env:
20+
CENTRAL_TOKEN_USERNAME: ${{ secrets.CENTRAL_TOKEN_USERNAME }}
21+
CENTRAL_TOKEN_PASSWORD: ${{ secrets.CENTRAL_TOKEN_PASSWORD }}
22+
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
23+
RELEASE_TAG: ${{ github.ref_name }}
24+
steps:
25+
- name: Checkout repository
26+
uses: actions/checkout@v6
27+
with:
28+
fetch-depth: 0
29+
30+
- name: Set up Temurin JDK 21
31+
uses: actions/setup-java@v5
32+
with:
33+
distribution: temurin
34+
java-version: "21"
35+
cache: maven
36+
server-id: central
37+
server-username: CENTRAL_TOKEN_USERNAME
38+
server-password: CENTRAL_TOKEN_PASSWORD
39+
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
40+
gpg-passphrase: GPG_PASSPHRASE
41+
42+
- name: Validate tag format and project version
43+
run: |
44+
set -euo pipefail
45+
46+
if ! printf '%s\n' "${RELEASE_TAG}" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$'; then
47+
echo "Release tag must match vX.Y.Z. Got: ${RELEASE_TAG}" >&2
48+
exit 1
49+
fi
50+
51+
release_version="${RELEASE_TAG#v}"
52+
pom_version="$(mvn -q -DforceStdout help:evaluate -Dexpression=project.version | tail -n 1)"
53+
54+
if [ "${pom_version}" != "${release_version}" ]; then
55+
echo "Tag version ${release_version} does not match pom.xml version ${pom_version}." >&2
56+
exit 1
57+
fi
58+
59+
echo "RELEASE_VERSION=${release_version}" >> "${GITHUB_ENV}"
60+
61+
- name: Ensure tag commit belongs to main or master
62+
run: |
63+
set -euo pipefail
64+
65+
git fetch --no-tags origin +refs/heads/main:refs/remotes/origin/main || true
66+
git fetch --no-tags origin +refs/heads/master:refs/remotes/origin/master || true
67+
68+
release_sha="$(git rev-list -n 1 "${RELEASE_TAG}")"
69+
release_branch=""
70+
71+
for branch in main master; do
72+
if git show-ref --verify --quiet "refs/remotes/origin/${branch}" && \
73+
git merge-base --is-ancestor "${release_sha}" "refs/remotes/origin/${branch}"; then
74+
release_branch="${branch}"
75+
break
76+
fi
77+
done
78+
79+
if [ -z "${release_branch}" ]; then
80+
echo "Release tag ${RELEASE_TAG} must point to a commit reachable from origin/main or origin/master." >&2
81+
exit 1
82+
fi
83+
84+
echo "RELEASE_BRANCH=${release_branch}" >> "${GITHUB_ENV}"
85+
86+
- name: Resolve imported GPG key id
87+
run: |
88+
set -euo pipefail
89+
90+
key_id="$(gpg --list-secret-keys --keyid-format LONG | awk '/^sec/{split($2, parts, "/"); print parts[2]; exit}')"
91+
if [ -z "${key_id}" ]; then
92+
echo "Unable to resolve imported GPG key id." >&2
93+
exit 1
94+
fi
95+
96+
echo "GPG_KEY_ID=${key_id}" >> "${GITHUB_ENV}"
97+
98+
- name: Verify release build
99+
run: mvn -B -ntp clean verify
100+
101+
- name: Generate release notes
102+
run: ./scripts/generate-release-notes.sh "${RELEASE_TAG}" "https://github.com/${GITHUB_REPOSITORY}" > release-notes.md
103+
104+
- name: Publish to Maven Central
105+
run: mvn -B -ntp -P release -DskipTests -Dgpg.keyname="${GPG_KEY_ID}" deploy
106+
107+
- name: Upload release artifacts for diagnostics
108+
if: always()
109+
uses: actions/upload-artifact@v4
110+
with:
111+
name: release-${{ github.ref_name }}
112+
path: |
113+
release-notes.md
114+
target/*.jar
115+
target/*.asc
116+
target/central-publishing/**
117+
if-no-files-found: warn
118+
119+
- name: Publish GitHub Release
120+
env:
121+
GH_TOKEN: ${{ github.token }}
122+
run: |
123+
set -euo pipefail
124+
shopt -s nullglob
125+
126+
assets=(target/*.jar target/*.asc)
127+
128+
gh release create "${RELEASE_TAG}" "${assets[@]}" \
129+
--verify-tag \
130+
--fail-on-no-commits \
131+
--latest \
132+
--title "${RELEASE_TAG}" \
133+
--notes-file release-notes.md

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@
44

55
规则:按时间倒序记录(最新版本在最上方)。
66

7+
## [1.1.2] - 2026-03-11
8+
9+
1. 新增 tag 驱动的 GitHub Actions `release.yml`,在 `vX.Y.Z` tag 推送后自动执行构建、测试、Maven Central 发布与 GitHub Release 创建。
10+
2. 新增 `scripts/generate-release-notes.sh`,基于 git log 自动生成 release notes,并按 `Features / Improvements / Fixes / Docs / Build / CI` 分类整理。
11+
3. README 与维护者文档补充 release 流程、tag 规则、必需 secrets 以及 commit message 分类约定。
12+
4. 统一仓库内公开文档版本标识到 `1.1.2`,为后续 `v1.1.2` tag 发版做准备。
13+
714
## [1.1.1] - 2026-03-11
815

916
1. 升级 `maven-compiler-plugin``maven-surefire-plugin``maven-javadoc-plugin``maven-gpg-plugin``central-publishing-maven-plugin`,并将 `junit-jupiter` 升级到更稳妥的小版本带。

README.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,14 +73,14 @@ Maven:
7373
<dependency>
7474
<groupId>pub.lighting</groupId>
7575
<artifactId>threadforge-core</artifactId>
76-
<version>1.1.1</version>
76+
<version>1.1.2</version>
7777
</dependency>
7878
```
7979

8080
Gradle:
8181

8282
```gradle
83-
implementation("pub.lighting:threadforge-core:1.1.1")
83+
implementation("pub.lighting:threadforge-core:1.1.2")
8484
```
8585

8686
## 兼容性与构建
@@ -467,9 +467,18 @@ mvn verify
467467

468468
项目已启用 JaCoCo 覆盖率门禁:`LINE >= 80%`
469469

470+
## Release 流程
471+
472+
- release tag 格式固定为 `vX.Y.Z`,例如 `v1.1.2`
473+
- `release.yml` 只接受 `main``master` 历史中的 tag commit,并要求 tag 版本与 `pom.xml` 中的 `project.version` 一致
474+
- push tag 后会自动执行:`clean verify``-P release deploy`、生成 release notes、创建 GitHub Release 并上传 jar 资产
475+
- 自动 release notes 基于 git commit 历史生成,分类为 `Features``Improvements``Fixes``Docs``Build / CI`
476+
- 维护者操作说明与 secrets 清单见 [`docs/releases.md`](./docs/releases.md)
477+
470478
## 更新日志
471479

472480
- 完整更新记录:[`CHANGELOG.md`](./CHANGELOG.md)
481+
- 维护者发版手册:[`docs/releases.md`](./docs/releases.md)
473482

474483

475484
## API 文档与 GitHub Wiki

docs/api/Home.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
这是 ThreadForge 的完整 API 调用文档首页。
99

10-
当前 Maven 坐标:`pub.lighting:threadforge-core:1.1.0`
10+
当前 Maven 坐标:`pub.lighting:threadforge-core:1.1.2`
1111

1212
## 快速导航
1313

docs/api/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
- `groupId`: `pub.lighting`
1313
- `artifactId`: `threadforge-core`
14-
- `version`: `1.1.0`
14+
- `version`: `1.1.2`
1515

1616
## 文档目标
1717

docs/releases.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Release Guide
2+
3+
本文档描述 ThreadForge 从 `1.1.2` 开始使用的自动发版流程。
4+
5+
## 触发规则
6+
7+
- tag 命名必须为 `vX.Y.Z`
8+
- tag 对应的 commit 必须可从 `main``master` 到达
9+
- `pom.xml` 中的 `project.version` 必须与 tag 去掉 `v` 之后的版本完全一致
10+
11+
## 自动执行内容
12+
13+
当维护者 push 一个符合规则的 tag 后,`.github/workflows/release.yml` 会自动执行:
14+
15+
1. checkout 全量历史与 tags
16+
2. 使用 JDK 21 执行 `mvn -B -ntp clean verify`
17+
3. 使用 `release` profile 执行 `mvn -B -ntp -P release -DskipTests deploy`
18+
4. 依据上一条 semver tag 到当前 tag 的 git commit 历史生成 release notes
19+
5. 创建 GitHub Release,并上传生成的 jar / sources jar / javadoc jar 等资产
20+
21+
## 必需 Secrets
22+
23+
- `CENTRAL_TOKEN_USERNAME`: Sonatype Central user token username
24+
- `CENTRAL_TOKEN_PASSWORD`: Sonatype Central user token password
25+
- `GPG_PRIVATE_KEY`: ASCII-armored private key
26+
- `GPG_PASSPHRASE`: 对应私钥的 passphrase
27+
28+
## Release Notes 分类规则
29+
30+
release notes 由 [`scripts/generate-release-notes.sh`](../scripts/generate-release-notes.sh) 生成,按 commit subject 前缀归类:
31+
32+
- `feat:` -> `Features`
33+
- `fix:` -> `Fixes`
34+
- `docs:` -> `Docs`
35+
- `build:` / `ci:` / `chore(build):` / `chore(ci):` / `chore(deps):` / `chore(release):` -> `Build / CI`
36+
- 其他提交 -> `Improvements`
37+
38+
如果提交信息不符合以上约定,仍会被收进 `Improvements`,但 release notes 的可读性会下降。
39+
40+
## 建议的提交前缀
41+
42+
- `feat`: 新功能或对外能力增强
43+
- `fix`: bug 修复
44+
- `docs`: 文档更新
45+
- `refactor`: 结构调整但不改变对外行为
46+
- `build`: Maven、依赖、发布链调整
47+
- `ci`: GitHub Actions、校验流程、缓存策略调整
48+
- `chore(release)`: 版本号、release 准备工作
49+
50+
## 维护者发版步骤
51+
52+
1. 在目标 release commit 中更新 `pom.xml``README.md``CHANGELOG.md` 等版本信息
53+
2. 将变更合入 `main`
54+
3. 创建并推送 tag,例如 `git tag v1.1.2 && git push origin v1.1.2`
55+
4. 等待 GitHub Actions `Release` workflow 完成
56+
5. 检查 GitHub Release 正文、附件和 Maven Central 发布结果
57+
58+
## 失败排查
59+
60+
- tag 校验失败:检查 tag 命名和 `pom.xml` 版本是否一致
61+
- branch 校验失败:确认 tag 指向的 commit 已经在 `main``master` 历史中
62+
- Central 发布失败:优先检查 token、GPG key、POM 元数据和 Sonatype Portal 返回日志
63+
- Release notes 分类不理想:统一后续提交前缀,必要时重新打一个修正后的 release commit

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<groupId>pub.lighting</groupId>
77
<artifactId>threadforge-core</artifactId>
8-
<version>1.1.1</version>
8+
<version>1.1.2</version>
99
<name>ThreadForge Core</name>
1010
<description>Structured concurrency framework for Java focused on reducing the cognitive load of concurrent programming.</description>
1111
<url>https://github.com/wuuJiawei/ThreadForge</url>

0 commit comments

Comments
 (0)