| name | hotplex-release |
| description | HotPlex Worker Gateway 标准化发布流程。**立即使用此 skill**:当需要发布新版本、创建 GitHub Release、管理版本号、生成 Changelog、收集变更或验证版本一致性时。自动化版本发布流程,确保完整的变更记录和跨所有组件的版本统一。无论是在 main 分支正式发布,还是在 feature 分支准备发布材料,此 skill 都能指导你完成正确的流程。 |
HotPlex 发布工作流
前置条件
gh CLI 已认证并有 repo 访问权限
- 已安装
make 和 go 1.26+
- 所有测试通过 (
make check)
- 工作目录干净(无未提交的更改)
分支保护策略
为什么分支保护很重要:在非 main 分支创建标签会导致发布混乱,因为:
- 标签应该指向稳定的、已合并到 main 的代码
- CI/CD 流程期望在 main 分支上触发 release workflow
- 避免在 feature 分支上意外发布不完整的版本
分支判断流程:
-
在工作流开始时,检查当前分支:
git branch --show-current
-
如果在 main 上:执行完整工作流(步骤 1–8),包括创建 tag 和 release。
-
如果不在 main 上(feature 分支、release prep 分支等):仅执行步骤 1–5(版本确定、变更收集、changelog 撰写、版本统一、验证)。然后:
- 将版本 bump + changelog 作为准备提交提交(例如
chore: prepare release vX.X.X)
- 不要创建 git tag — 标签只能在 main 上创建
- 不要推送 tag 或触发 GitHub Release — 这会在错误的分支上触发 CI
- 通知用户:"Release preparation committed on
<branch>。Tag and publish after merging to main."
-
合并到 main 后:fast-forward 或 checkout main,然后只执行步骤 6(tag)和步骤 7(推送 tag + GitHub Release)
步骤 1:确定下一个版本
为什么需要确定版本:语义化版本号帮助用户和依赖者理解变更的影响范围。错误的版本号会导致:
- 依赖者错过重要更新(将 major 标记为 minor)
- 或者遇到破坏性更改(将 breaking change 标记为 patch)
从 cmd/hotplex/main.go:16(version 变量)读取当前版本。
应用 语义化版本:
- Patch (
v1.1.0 → v1.1.1):Bug 修复、安全补丁、无新功能
- Minor (
v1.1.0 → v1.2.0):新功能、向后兼容的更改
- Major (
v1.1.0 → v2.0.0):破坏性更改
在继续之前与用户确认新版本。
步骤 2:收集变更
为什么收集变更很重要:完整的变更收集确保:
- Changelog 包含所有重要更改,不会遗漏任何修复或功能
- 可以准确评估版本级别(patch/minor/major)
- 为用户提供清晰的升级路径和影响分析
- 避免在发布后发现遗漏重要 commit
运行以下命令以收集自上次发布以来的所有变更:
LAST_TAG=$(git tag --sort=-version:refname | head -1)
echo "=== Changes since ${LAST_TAG} ==="
git log --oneline "${LAST_TAG}..HEAD" --no-merges
echo ""
echo "=== By Category ==="
echo "--- feat (Added) ---"
git log --oneline "${LAST_TAG}..HEAD" --no-merges --grep='^feat'
echo ""
echo "--- fix (Fixed) ---"
git log --oneline "${LAST_TAG}..HEAD" --no-merges --grep='^fix'
echo ""
echo "--- refactor / perf (Changed) ---"
git log --oneline "${LAST_TAG}..HEAD" --no-merges --grep='^refactor\|^perf'
echo ""
echo "--- chore / ci / docs (Infrastructure) ---"
git log --oneline "${LAST_TAG}..HEAD" --no-merges --grep='^chore\|^ci\|^docs\|^build'
echo ""
echo "--- Other ---"
git log --oneline "${LAST_TAG}..HEAD" --no-merges --invert-grep --grep='^feat\|^fix\|^refactor\|^perf\|^chore\|^ci\|^docs\|^build'
echo ""
echo "=== Full diffstat ==="
git diff --stat "${LAST_TAG}..HEAD"
在需要时审查每个 commit 的完整消息以获取上下文:
git log "${LAST_TAG}..HEAD" --no-merges --format="%h %s%n%b---"
范围分类映射
按其 scope 将 commit 分组到 changelog 部分:
| Conventional Commit | Changelog Section |
|---|
feat(...) | Added |
fix(...) | Fixed |
refactor(...), perf(...) | Changed |
chore(deps), build(...) | Changed(或 Dependencies 如果仅是 dep bump) |
| feat 带 breaking change 后缀或 BREAKING CHANGE footer | Changed + callout |
ci(...), docs(...) | 从 changelog 省略,除非面向用户 |
Scope → 显示组映射
编写 changelog 条目时,按功能区域分组:
| Commit Scope | Display Group |
|---|
gateway, session, hub, conn | Gateway Core |
worker, claude-code, opencode-server, ocs, codexcli, codex, acp | Worker |
slack, feishu, yuanxin, messaging, stt | Messaging |
webchat, ui, chat | WebChat UI |
config, agent-config | Configuration |
security, auth, ssrf | Security |
cli, onboard, doctor | CLI |
client, sdk, ts, python, java | SDK |
test, ci, build, makefile | Infrastructure |
步骤 3:撰写 Changelog
为什么 Changelog 很重要:
- 用户决策支持:帮助用户快速判断是否需要升级、升级风险和收益
- 问题追溯:当出现问题时,可以快速定位引入版本和变更内容
- 发布透明度:让团队和社区了解产品演进方向
- 自动化集成:CI/CD 使用 changelog 生成 release notes
按照 Keep a Changelog 格式更新 CHANGELOG.md。
模板
## [X.X.X] - YYYY-MM-DD
### Summary
1-3 句话概括本版本的核心主题和最重要变更。
- 提及版本定位(patch/minor/major)
- 点出 2-3 个最关键的 feature 或 fix
- 说明影响面(哪些模块受益,用户可感知的变化)
### Added
- **Display Group**: One-line description of the change. (#PR or commit SHA for significant changes)
### Changed
- **Display Group**: Description of what changed and why.
### Fixed
- **Display Group**: Description of what was broken and how it was fixed.
### Security
- Description of security-relevant changes (omit section if none).
撰写规则
为什么这些规则很重要:
-
Summary 必须有 — Summary 是面向用户的版本叙事,帮助读者在 10 秒内判断是否与自己相关。没有 Summary 的 changelog 只是 commit 列表,不是用户友好的发布说明。
-
Summary 写法 — 先说版本定位(patch/minor/major),再说核心变化(2-3 个关键点),最后说影响面(哪些模块受益,用户可感知的变化)。用自然语言而非条目列表,让 Summary 成为一个连贯的故事。
-
每个逻辑更改一个条目 — 将相关的 commit 合并为一个条目,而不是每个 commit 一行。这样避免 changelog 过于冗长,更关注功能而非实现细节。
-
现在时态,祈使语气 — "Add feature" 而非 "Added feature" 或 "Adds feature"。这与 Keep a Changelog 标准一致,使内容更简洁直接。
-
在每个条目开头加粗显示组 — 便于快速扫描,让用户能快速找到感兴趣的模块变更。
-
仅包含 PR 号或 commit SHA — 只用于重要/面向用户的更改。内部实现细节的引用对用户没有价值,只会增加噪音。
-
省略内部重构、CI 更改和仅文档更新 — 除非面向用户。这些变更对最终用户没有影响,不应该出现在发布说明中。
-
合并小修复 — 将多个小的 bugfix 合并到一个 "minor fixes" 条目中,避免 changelog 过于冗长。
-
按影响排序 — 各部分中的条目按重要性排序(最重要的在前),确保用户首先看到最相关的变更。
示例条目
## [1.2.0] - 2026-04-30
### Summary
v1.2.0 是一次 minor 版本更新,聚焦于 **可观测性与运维体验**。新增 Session Stats API 和 Conversation Store,
为 WebChat 和管理端提供会话级别的 token/延迟/成本统计。WebChat 经历了全面 UX 重构(暗色主题 + GenUI 工具组件 +
CommandMenu),Gateway Core 获得了连接稳定性修复(CAS race guard、fast reconnect、session ID 一致性)。
### Added
- **Gateway Core**: Session stats API — aggregated turn statistics from done events (`GET /api/sessions/{id}/stats`).
- **Session**: Conversation store — async batch writer for turn-level persistence (user input + assistant response with tools, tokens, cost, duration).
- **WebChat UI**: "Obsidian" dark theme with glassmorphism design system, GenUI tool rendering, and slash command palette.
### Changed
- **Session**: SQLite storage optimization — PRAGMA tuning, cascade delete, events TTL cleanup, automatic VACUUM.
- **Gateway Core**: Fast reconnect for idle sessions — skip terminate+resume cycle when worker is still alive.
### Fixed
- **Gateway Core**: Claude Code mapper silently discarded `EventSystem` and `EventSessionState` — payload type mismatch caused all state transitions to be dropped.
- **WebChat UI**: Connection stability — deterministic session IDs across REST/WS paths, browser console warnings eliminated.
步骤 4:版本统一
为什么版本统一很重要:
- 构建一致性:确保本地构建和 CI 构建使用相同的版本号
- 可追溯性:当用户报告问题时,可以从任何组件的版本信息追溯到完整发布
- 依赖管理:SDK 用户需要知道他们使用的是哪个 gateway 版本
- 避免混淆:版本不一致会导致调试困难和用户混淆
更新以下所有位置的版本字符串。对所有位置使用 semver 格式(例如,代码用 v1.2.0,包管理器用 1.2.0)。
4.1 核心 Gateway (Go)
| 文件 | 模式 | 示例 |
|---|
cmd/hotplex/main.go:16 | version = "v1.x.x" | v1.2.0 |
Makefile:24 | VERSION := v1.x.x(LDFLAGS 引用 $(VERSION)) | v1.2.0 |
cmd/hotplex/gateway_run.go | tracing.Init(ctx, log, "hotplex-gateway", versionString()) | version from LDFLAGS |
4.2 WebChat UI
| 文件 | 模式 | 示例 |
|---|
webchat/package.json | "version": "1.x.x" | 1.2.0 |
注意:页面状态栏通过 import { version } from "../../package.json" 读取版本号,显示为 v{version}-stable。
4.3 多语言 SDK
| 文件 | 模式 |
|---|
examples/typescript-client/package.json | "version": "1.x.x" |
examples/python-client/pyproject.toml | version = "1.x.x" |
examples/python-client/hotplex_client/__init__.py | __version__ = "1.x.x" |
examples/java-client/pom.xml | <version>1.x.x-SNAPSHOT</version> |
4.4 项目文档
| 文件 | 模式 | 示例 |
|---|
README.md | badge Version-vX.X.X | Version-v1.2.0 |
README_zh.md | badge Version-vX.X.X | Version-v1.2.0 |
AGENTS.md | 头部 **最后更新**: YYYY-MM-DD | **最后更新**: 2026-05-10 |
AGENTS.md | 头部 **版本**: vX.X.X | **版本**: v1.2.0 |
注意:CLAUDE.md 是 AGENTS.md 的符号链接,只需编辑 AGENTS.md(这是实际文件),CLAUDE.md 会自动同步。修改时请同时更新版本号和最后更新日期。
4.5 基础设施
| 文件 | 模式 |
|---|
Dockerfile | LABEL version="1.x.x" |
4.6 API 文档
| 文件 | 模式 | 示例 |
|---|
cmd/hotplex/doc.go | swaggo 注解 @version X.X.X | 1.2.0 |
docs/swagger/swagger.json | "version": "X.X.X" | 1.2.0 |
注意:swagger.json 由 swag init 从 doc.go 注解自动生成,但当前为手动提交。更新 doc.go 后需同步更新 swagger.json 中的 "version" 字段。
验证命令
更新后,验证所有位置都已更改:
git diff
grep -rn "1\.2\.0" cmd/hotplex/main.go Makefile webchat/package.json \
examples/typescript-client/package.json examples/python-client/pyproject.toml \
examples/python-client/hotplex_client/__init__.py examples/java-client/pom.xml \
Dockerfile cmd/hotplex/doc.go docs/swagger/swagger.json \
README.md README_zh.md AGENTS.md
步骤 5:验证
为什么需要全面验证:
- 代码质量:确保没有引入新的 lint 错误或格式问题
- 构建成功:验证所有平台都能成功构建,避免 CI 失败
- 版本注入:确认 ldflags 正确注入版本,二进制文件报告正确的版本号
- Changelog 格式:避免发布后发现格式错误或遗漏重要信息
- 干净 diff:确保只包含预期的版本和 changelog 更改,避免意外提交
按顺序运行:
make quality
make build
./bin/hotplex-$(go env GOOS)-$(go env GOARCH) version
head -50 CHANGELOG.md
git diff --stat
步骤 6:Git 提交和标签
为什么需要显式暂存和带注释的标签:
- 显式暂存:避免意外包含不相关的文件(如临时文件、调试更改)
- 带注释的标签:标签消息会在 GitHub Release 中显示,提供更多上下文
- 版本化提交:创建清晰的版本历史,方便回滚和追溯
git add \
cmd/hotplex/main.go \
Makefile \
webchat/package.json \
examples/typescript-client/package.json \
examples/python-client/pyproject.toml \
examples/python-client/hotplex_client/__init__.py \
examples/java-client/pom.xml \
Dockerfile \
cmd/hotplex/doc.go \
docs/swagger/swagger.json \
README.md \
README_zh.md \
AGENTS.md \
CHANGELOG.md
git commit -m "chore: release vX.X.X"
git tag -a vX.X.X -m "Release vX.X.X"
步骤 7:GitHub Release
为什么需要手动更新 Release Notes:
CI workflow (.github/workflows/release.yml) 在 tag push 时自动触发并:
- 为
darwin-amd64, darwin-arm64, linux-amd64, linux-arm64 构建二进制
- 计算 SHA-256 校验和
- 创建带有
generate_release_notes: true 的 GitHub Release
但是,CI 的 generate_release_notes: true 只生成 PR 级别摘要,缺少:
- Summary 部分:版本定位和影响面说明
- 完整的结构:Added/Changed/Fixed 的详细分组
- 用户友好的描述:技术 commit 消息到用户语言的转换
因此,每次 release 完成后都需要手动用 CHANGELOG.md 内容覆盖自动生成的注释。
git push origin main && git push origin vX.X.X
gh run list --workflow=release.yml --limit=1
gh run watch <RUN_ID> --exit-status
VERSION="1.2.0"
awk -v ver="[$VERSION]" '
/^## \[/ && index($0, ver) { found=1; next }
/^## \[/ && found { exit }
found { print }
' CHANGELOG.md | perl -ne 'BEGIN{$b=1} $b=0 if /\S/; if($b && /^\s*$/){next} print' > /tmp/release-notes.md
head -1 /tmp/release-notes.md | grep -q "^### " || { echo "ERROR: Release notes extraction failed — first line is not a changelog section"; exit 1; }
LINES=$(wc -l < /tmp/release-notes.md)
if [ "$LINES" -gt 100 ]; then
echo "ERROR: Release notes has ${LINES} lines — likely extracted multiple versions, expected < 100"
exit 1
fi
echo "Release notes: ${LINES} lines"
LAST_TAG=$(git tag --sort=-version:refname | sed -n '2p')
LOGIN_BLACKLIST="HotPlexBot|hotplex-ai"
CONTRIBUTORS=$(git log "${LAST_TAG}..v${VERSION}" --no-merges --format="%H" | while read sha; do
gh api "repos/{owner}/{repo}/commits/$sha" --jq '.author.login // empty' 2>/dev/null
done | sort -u | grep -vE "^($LOGIN_BLACKLIST)$")
if [ -n "$CONTRIBUTORS" ]; then
echo -e "\n### Contributors\n" >> /tmp/release-notes.md
echo "$CONTRIBUTORS" | while read login; do
echo -n "[](https://github.com/${login}) " >> /tmp/release-notes.md
done
echo "" >> /tmp/release-notes.md
fi
gh release edit vX.X.X --notes-file /tmp/release-notes.md
gh release view vX.X.X
步骤 8:发布后验证
为什么需要发布后验证:
- 确保用户体验完整:验证 release notes 显示完整内容,用户能获得准确的变更信息
- 构建产物完整:确认所有平台的二进制文件都已成功构建和上传
- 版本一致性:验证二进制文件的版本号与标签版本一致
- 清理工作区:避免临时文件污染后续工作
发布后检查清单:
- 验证 release notes 显示完整 CHANGELOG 内容和 Summary 部分(不仅仅是 PR 摘要):
gh release view vX.X.X
- 验证附加了所有 5 个 artifact:4 个平台二进制 +
checksums.txt
- 验证二进制版本:下载并运行
./hotplex-* version
- 清理临时文件 (
rm -f /tmp/release-notes.md)
常见陷阱和故障排除
常见陷阱(版本不一致、Tag 错误、Changelog 格式、Release Notes、CI 失败、遗漏补发)的诊断和解决方法见 references/troubleshooting.md。
关键提醒
同步检查:步骤 4 中的 14 处版本号必须全部匹配。CI workflow 通过 ldflags 从 git tag 覆盖 main.version,但源文件必须对本地构建一致。
CI 自动发布:.github/workflows/release.yml 在 tag push 时自动处理二进制构建、校验和和 release 创建。手动创建 release 仅用于 workflow_dispatch 或恢复场景。