aidog 发版
一次发版 = 改 .version → 同步 manifest → commit+push master → CI 出包 → 补 Release 说明。
行号漂移,定位以文件名 / 符号名为准。
0. 架构铁律(动手前必读)
- 版本唯一可信源 = 根目录
.version(单行 semver)。package.json/src-tauri/tauri.conf.json/src-tauri/Cargo.toml/docs/package.json全是派生物,只能由node scripts/sync-version.mjs写。手改 manifest = 漂移,CI 的Verify version sync步骤(sync-version.mjs --check)直接 exit 1。 - CI 触发条件 = push master 且 diff 含
.version或release.yml(.github/workflows/release.yml:5-9)。只改 manifest 不改.version→ 不触发;改了.version但没跑 sync → 触发后在门禁步骤挂。 - tag
v<版本>已存在时 tauri-action 失败。 正常 push 路径假定新版本无碰撞;重发同版本必须走workflow_dispatch(cleanup job 会gh release delete --cleanup-tag)。 - Release 正文由 workflow 的
releaseBody模板生成(下载表 / macOS quarantine / 自动更新 / 文档链接)。gh release edit写进去的 changelog 会被同版本重发(workflow_dispatch)整体重建冲掉 —— 所以正文的可信副本永远是仓库里的.github/release-notes/v<版本>.md,每次 dispatch 重发完成后立刻重跑 §4 的gh release edit,这条闭环是发版流程的一部分,不是可选收尾。 - 禁主 agent
sleep轮询 CI。 监控脚本以run_in_background起,结果由 harness 通知回注(memoryno-sleep-polling)。
1. 更新版本号
默认 patch 位 +1(用户没特别说明时)。
CUR=$(tr -d '[:space:]' < .version)
NEW=$(echo "$CUR" | awk -F. '{printf "%d.%d.%d", $1, $2, $3+1}')
echo "$NEW" > .version
node scripts/sync-version.mjs # 写入 4 个 manifest
node scripts/sync-version.mjs --check # 必须 ✓
git add .version package.json src-tauri/tauri.conf.json src-tauri/Cargo.toml docs/package.json
git commit -m "chore(release): bump version to $NEW"
漂移修复同理:先决定目标版本写进
.version,再 sync,不要反向把.version改成 manifest 的值(除非确认那个版本还没发过 tag)。
2. push 触发 CI
git push origin master(本仓 push 需用户明确授权 —— 项目 CLAUDE.md 禁自动 push)。
远端有两个 remote:origin = https://github.com/lazygophers/aidog.git(真发版目标),no-mistakes 是本地镜像,push 它不触发任何 CI。
3. 后台监控 CI
bash .claude/skills/aidog-release/scripts/watch-release.sh
用 Bash(run_in_background: true) 起,日志落 /tmp/aidog-release-<版本>.log。脚本按本地 HEAD sha 匹配 run,不会盯错上一次的 run;失败时自动追加 gh run view --log-failed 尾部日志到同一文件。
matrix 两个 job:macos-latest(--target universal-apple-darwin,Apple Silicon + Intel 合一 dmg)、windows-latest(x64 exe / msi)。fail-fast: false,一个平台挂另一个照跑。
常见失败与处置:
| 症状 | 根因 | 处置 |
|---|---|---|
Verify version sync exit 1 |
manifest 被手改 / 漏跑 sync | 跑 node scripts/sync-version.mjs,commit 后重推 |
tauri-action tag already exists |
同版本重发 | 走 gh workflow run release.yml(cleanup job 删旧 tag+release),跑完重跑 §4 的 gh release edit(§0-4) |
| 仅 windows 或仅 macos 挂 | 平台特有编译/签名问题 | gh run view <id> --log-failed 看真实报错。修完代码后版本号不用动 —— 但 push 只在 diff 含 .version/release.yml 时才触发,所以同版本重跑一律走 gh workflow run release.yml,同样跑完重跑 §4 |
| 签名相关报错 | TAURI_SIGNING_PRIVATE_KEY(_PASSWORD) secret 缺失/过期 |
仓库 secrets 修,非代码问题 |
4. 更新 Release 说明
CI 产出的正文只有通用模板,没有本次改了什么。
完整性铁律:changelog 的范围必须是「上一个 release tag → 本次 tag」的全部非 merge commit,一条不漏。
注意 tag 不连续(v0.1.9 之后直接是 v0.1.11,没有 v0.1.10),所以上一版必须按 tag 排序取,不能按版本号 -1 推算。
V=$(tr -d '[:space:]' < .version)
PREV=$(git tag --sort=-v:refname | grep -v "^v$V$" | head -1) # 上一个真实存在的 tag
git rev-list --count --no-merges "$PREV..HEAD" # 总数,写完要对得上
git log --reverse --pretty="%s" --no-merges "$PREV..HEAD" | grep -E "^(feat|fix|style|perf|i18n)" # 用户可见
git log --reverse --pretty="%s" --no-merges "$PREV..HEAD" | grep -Ev "^(feat|fix|style|perf|i18n)" # 内部(refactor/chore/docs/test)
正文草稿放 .github/release-notes/v<版本>.md(随代码入库,发版前就能写好、可 review、可 diff;注意 .scratch/ 被全局 gitignore,放那里不进版本库),CI 跑完直接:
gh release edit "v$V" --notes-file .github/release-notes/v$V.md
写法:
- 分四段 ——
新增/修复/内部(不影响使用)/ 保留模板四节(下载表 / macOS quarantine / 自动更新 / 文档),只加不删。 - 用户可见的 commit 按功能域合并成一条人话(同一 feature 的多个 tracer-bullet 票合并,别一票一行),内部 commit 允许整段概括,但 refactor/chore/docs 不能整类丢弃 —— 大规模拆 crate、覆盖率、文档重做这些用户会问「这版到底动了什么」。
- 不贴 commit hash,不留
feat(scope):前缀。 - 收尾核对:每一条 commit 都能在正文里找到归属(合并进某条也算),数量对得上
git rev-list --count。
5. 验证门禁
node scripts/sync-version.mjs --check # 版本一致
gh run list --workflow=release.yml -L 1 # 状态 completed / success
gh release view "v$(tr -d '[:space:]' < .version)" --json assets -q '.assets[].name'
v0.1.11 实际资产清单(8 个,作为基线比对):
AiDog_<版本>_universal.dmg # macOS 安装包(无 .sig,dmg 不参与 updater)
AiDog_universal.app.tar.gz(+.sig) # macOS updater 包 —— 自动更新真正下载的是它
AiDog_<版本>_x64-setup.exe(+.sig) # Windows NSIS(推荐)
AiDog_<版本>_x64_en-US.msi(+.sig) # Windows MSI
latest.json # updater 清单(includeUpdaterJson: true),缺它客户端「关于」页自动更新会瞎
收尾自检:
-
.version是唯一手改的版本文件,4 个 manifest 由脚本写。 - push 前
--check绿。 - CI 两个平台 job 都 success。
- Release 资产 8 个齐(dmg / app.tar.gz+sig / exe+sig / msi+sig / latest.json)。
- Release 正文补了本次 changelog,模板四节没被删;若期间 dispatch 重发过,
gh release edit已重跑一次。
反例黑名单(§0 铁律未覆盖的独有坑)
- ❌ 未经用户授权
git push—— 项目 CLAUDE.md 明令禁止,发版这步永远由用户拍板。 - ❌ 按版本号 -1 推算上一个版本 —— tag 不连续(
v0.1.9后直接v0.1.11),只能git tag --sort=-v:refname取。 - ❌ 把正文草稿放
.scratch/—— 全局 gitignore,不进版本库,dispatch 重发后就没得恢复。 - ❌ changelog 只写 feat/fix,整类丢掉 refactor/chore/docs —— 拆 crate、覆盖率、文档重做这些用户会问「这版到底动了什么」。
相关
- workflow:
.github/workflows/release.yml - 同步器:
scripts/sync-version.mjs(--check= CI 门禁) - 监控脚本:
.claude/skills/aidog-release/scripts/watch-release.sh - memory
no-sleep-polling - 主要涉及文件:
.version、scripts/sync-version.mjs、.github/workflows/release.yml