Release — STranslate 版本发布
为 STranslate 项目准备一次正式发布:校验 tag、汇总自上一个 tag 以来的改动、按既定风格生成 CHANGELOG.md,并给出打 tag 推送以触发 GitHub Actions 自动打包发布的步骤。
背景事实(项目现状,开工前已核实)
- 发版触发:推送形如
v2.0.x(v+ 三段语义化版本号,x为占位符)的 tag →.github/workflows/dotnet.yml在 windows-latest 上构建,并用CHANGELOG.md作为 GitHub Release 的 body(body_path: CHANGELOG.md),产物发布到publish/*。无需 Token,permissions: contents: write。 - 版本号来源:tag 名即版本号。
build.ps1会去掉v前缀并以/p:Version=传入 dotnet 构建,同时改写src/SolutionAssemblyInfo.cs后用git restore还原。不要手动改任何版本号文件。 - tag 形式:历史 tag 为带注释的 tag(
git cat-file -t v2.0.8→tag),用git tag -a创建。 CHANGELOG.md是整文件覆盖式发布说明,每次发版前重写为本次内容(GitHub Release body 就是它)。- 插件 NuGet 包版本独立于应用 tag 版本:存在
src/STranslate.Plugin/STranslate.Plugin.csproj的<PackageVersion>,仅当src/STranslate.Plugin/下公共 API 有变更时升 patch;版本记录累积在同目录README.md顶部。
发布流程
1. 确定版本号(严格校验)
- 取最新 tag:
git describe --tags --abbrev=0(取最新vX.Y.Ztag)。 - tag 格式严格为
v+ 三段语义化版本号,即v主.次.修订,例如v2.0.9、v2.1.1、v3.0.9均合法。v前缀不可省略,必须是恰好三段数字。- 默认下一版本 = 当前 patch + 1(如
v2.0.8→v2.0.9),用于纯 bugfix / 小幅优化发版。 - minor / major 跳级是允许的:若本次含较大新功能可由用户指定
v2.1.0,跨大版本重构可指定v3.0.0。版本号段由用户决定,技能不替用户判断该跳 minor 还是 patch——但格式必须合规。 - 严禁:去掉
v前缀、用v2.0.x这种字面占位符、四段号或带构建号后缀(如v2.0.9.1、v2.0.9-beta)、只有两段(如v2.0)。 - 历史上早期 tag 曾是
2.0.x(无v)或1.5.x.x形式,但 v2.0.0 起统一为v主.次.修订,新发版一律沿用此格式。
- 默认下一版本 = 当前 patch + 1(如
- 如果用户给定版本号,先校验:必须匹配正则
^v\d+\.\d+\.\d+$,否则停下来告知用户格式不符并给出正确建议,不要自行假设。
2. 汇总改动(核心)
- 获取自上个 tag 以来的提交:
合并提交(Merge)默认用git log <上一个tag>..HEAD --pretty=format:"%h %s" --no-merges--no-merges过滤掉,因为它们不携带语义信息。 - 过滤规则(这是本技能的关键约束):
- 忽略纯发版/版本号提交:
chore: update version to v...、docs: update changelog and publish ...、chore: bump version、c1deb830这类仅改 CHANGELOG / 版本号的提交一律不计入更新说明。 - 忽略新增功能的修复类提交不进 CHANGELOG:用户明确要求"忽略新增功能的修复 commit"。即:
- 修复的是本次开发周期内新增、尚未发布的特性所引入的 bug(提交信息通常是
fix(compact): ...、fix(placement): ...、fix(image-translate): ...等,且该特性在同周期内才由feat引入)→ 不单独列条目,其效果并入对应特性的"添加/优化"条目即可,不单写"修复:xxx"。 - 修复的是既有功能、面向用户历史版本的回归 → 正常列入"修复:"。
- 判断不确定时,看该
fix涉及的模块在<上一个tag>是否已存在:不存在→属于新功能自修,忽略;存在→面向用户的回归修复,列入。
- 修复的是本次开发周期内新增、尚未发布的特性所引入的 bug(提交信息通常是
- 忽略纯
chore:(除版本号外如chore: revert ...、chore)、纯docs:(设计文档、实现计划、规格说明)、纯内部refactor:无用户可见行为变化的提交——除非 refactor 带来了用户可感知的优化(则归入"优化:")。 feat:/perf:/ 有用户可见效果的refactor:/ 面向用户的fix:→ 进入更新说明。
- 忽略纯发版/版本号提交:
- 中文提交信息(如"更新软件&文档描述")按其真实语义归类,拿不准语义的琐碎提交宁可略去,保持更新说明清爽。
- 检测插件契约变更:用
git diff <上一个tag>..HEAD --stat -- src/STranslate.Plugin/看插件项目是否有改动;若有,进一步确认是否为公共 API 变更(判断标准见第 3 步)。这是是否执行第 3 步的依据。
3. 同步插件版本号与 README(仅当插件契约有变更)
第 2 步判定 src/STranslate.Plugin/ 下公共 API 有变更时执行本步;无变更则跳过。
什么是"插件契约变更":面向插件作者的公共契约发生变化——IOcrPlugin/ITranslatePlugin/ITtsPlugin/IVocabularyPlugin/IPluginContext 等接口、Service/PluginMetaData 等公共类、LangEnum 等枚举的公共成员被新增/修改/移除。纯 InternalsVisibleTo、纯 internal/private 实现、纯 .csproj 包引用调整不算。拿不准时用 git diff <上一个tag>..HEAD -- src/STranslate.Plugin/<文件>.cs 核实。
升版本号:改 src/STranslate.Plugin/STranslate.Plugin.csproj 的 <PackageVersion>,patch + 1(如 1.0.12 → 1.0.13)。插件 NuGet 包版本独立于应用 tag 版本,不跟 v2.0.x 走,按自身 patch 递增;仅当出现破坏性 API 变更才升 minor/major(罕见,需用户确认)。
迭代 README:在 src/STranslate.Plugin/README.md 顶部(首行介绍之后)新增一条版本记录,格式与历史一致:
## vX.Y.Z - YYYY-MM-DD
- <对插件作者可见的 API 变更,中文,一条一行>
日期取发版当日;变更说明聚焦插件作者视角的契约变化,不写应用层 UI/功能;只追加到顶部,不改历史记录。
本步确定的新插件版本号供第 5 步 CHANGELOG 的 ## 插件开发 区块引用。
4. 分类整理为 CHANGELOG 条目
按项目既有风格,统一用中文动词前缀,不要直接搬运 commit message:
添加:——新功能、新插件、新配置项、新语种等用户能用上的新东西。优化:——改进既有行为、UI 调整、性能优化、逻辑完善、策略变更。修复:——仅限面向用户历史版本的回归修复(见上方过滤规则)。插件开发:——涉及STranslate.Plugin契约变更(新增接口方法、新增枚举、参数变更、版本号提升)时使用。可作为## 更新下的内联条目(- 插件开发:...),也可在改动较多时单独成## 插件开发区块。判断依据:是否改了src/STranslate.Plugin/下的公共 API 或提升其版本。版本号由第 3 步提升,区块内首条用「STranslate.Plugin更新至1.0.x」开头,再列具体 API 变更。
条目写法要点:
- 一条 commit 可能合并/拆分;以用户视角的变更为粒度,而非 commit 粒度。多个相关 commit 合成一条,描述最终效果。
- 用词贴近既有 CHANGELOG(参见下方"格式模板"),避免出现"重构了代码内部结构"这类用户不关心的实现细节。
- 涉及贡献者在 commit 中提及的(如
@sean908、#711),保留@用户名 #PR号的引用写法。 - 涉及具体插件/服务名时用全称或与历史一致的简称(如"腾讯翻译(Transmart)"、"DeepL 插件")。
5. 写入 CHANGELOG.md
整文件覆盖写入,结构严格如下(顺序、标题层级、链接不可变):
## 更新
- 添加:...
- 添加:...
- 优化:...
- 修复:...
## 插件开发
- `STranslate.Plugin` 更新至 `1.0.x`
- ...
## 其他
- [插件市场](https://stranslate.zggsong.com/plugins.html)
- [使用说明](https://stranslate.zggsong.com/docs/)
- [集成调用](https://stranslate.zggsong.com/docs/invoke.html)
- [安装卸载](https://stranslate.zggsong.com/docs/(un)install.html)
- [FAQ](https://stranslate.zggsong.com/docs/faq.html)
**完整更新日志:** [v2.0.7...v2.0.8](https://github.com/STranslate/STranslate/compare/v2.0.7...v2.0.8)
规则:
## 更新永远是第一行(文件开头无# 标题、无空白)。条目内顺序:先添加:再优化:再修复:,每类内按重要性/相关性排列。## 插件开发区块仅当本次确有插件 API / 插件版本变更时才写;没有则整个区块省略,不要留空区块。## 其他区块及其五条链接逐字照抄,永远不变。- 最后一行
**完整更新日志:**的链接:前一个 tag 和本次 tag 分别替换。仓庘认定为STranslate/STranslate。 - 若某个分类本次没有任何条目,则该分类整段省略(例如纯 bugfix 版本可能只有
修复:)。
6. 提交并打 tag 发版
- 提交(沿用历史 commit 风格
chore: update version to vX.Y.Z)。除CHANGELOG.md外,若第 3 步执行了插件版本/README 更新,则一并git add:
未改插件时只git add CHANGELOG.md src/STranslate.Plugin/STranslate.Plugin.csproj src/STranslate.Plugin/README.md git commit -m "chore: update version to vX.Y.Z"git add CHANGELOG.md。 - 创建带注释的 tag(与历史一致):
git tag -a vX.Y.Z -m "vX.Y.Z" - 推送触发自动打包发布(外向操作,推送前向用户确认):
tag 推送后git push origin main git push origin vX.Y.Z.github/workflows/dotnet.yml会自动构建并发布 GitHub Release,无需手动跑build.ps1。
自检清单(写完 CHANGELOG、打 tag 前过一遍)
- 版本号匹配
^v\d+\.\d+\.\d+$(三段数字 +v前缀)。若为默认递增则 = 上一 tag patch + 1;若用户指定了 minor/major 跳级,确认格式合规即可。 - 改动来源是
<上一个tag>..HEAD,已用--no-merges。 - 已剔除版本号/changelog 维护提交。
- "修复:"条目均为面向用户历史版本的回归,不含本周期新功能的自修 fix。
- 纯 chore / docs / 无用户可见效果的 refactor 已剔除。
-
## 更新在文件第一行;## 其他五条链接逐字一致。 - 末尾 compare 链接的两个 tag 正确(前一个 tag ... 本次 tag)。
-
## 插件开发仅在确有插件 API/版本变更时存在。 - 若插件契约有变更:
<PackageVersion>已升 patch、README.md顶部已新增对应版本记录,且三文件一并提交;若无变更则二者均未改动。 - tag 用
git tag -a创建(带注释),非轻量 tag。
边界情况
- 当前 HEAD 已是某个 tag(即没有新提交):告诉用户自上个 tag 以来无改动,没有发版内容,不要硬凑条目。
- 跨多个特性分支合并:以最终合入 main 的 squash/merge 结果为准,按用户可见变更去重汇总,不要把同一特性的多步 commit 拆成多条。
- 用户只想预览不想发版:只整理 CHANGELOG 草稿展示给用户,不写文件、不打 tag、不推送,等用户确认。
- CHANGELOG 与 release body 的关系:
body_path: CHANGELOG.md意味着发版时 CHANGELOG 当前内容会原样成为 Release 说明。所以每次发版前必须把 CHANGELOG 重写为本次的内容,历史版本的说明在 GitHub Releases 页面各自留存,无需在文件里累积。