提交与发布
提交信息遵循 Conventional Commits 1.0.0,版本号遵循语义化版本 2.0.0。版本号、CHANGELOG.md 和 GitHub Release 都由提交信息生成,所以提交信息的格式直接决定发布说明。英文版 CHANGELOG.en.md 与 CHANGELOG.md 逐条对应,发布时由中文条目翻译而来。
持续集成
检查与正式发布由以下 GitHub Actions 工作流负责,另有独立的 SignPath 测试签名工作流。构建与测试在 Windows 上用 stable Rust(MSVC)运行;提交信息检查和标签核对只用 git 与 Node,在 Ubuntu 上运行。
| 工作流 | 触发 | 内容 |
|---|---|---|
check.yml |
拉取请求;手动运行(Actions 页面或 gh workflow run check.yml);发布时调用 |
提交信息格式(仅拉取请求,含标题);TypeScript、Biome、文案检查、前端单元测试、前端构建、rustfmt、Clippy、单元测试、第三方许可声明是否最新 |
release.yml |
推送 v* 标签 |
核对标签、版本号、CHANGELOG.md、已译完的 CHANGELOG.en.md 与更新公钥,确认标签在 main 上;运行上面的检查;构建并为应用内更新签名,再用仓库里的公钥校验签名和其中的版本号;为更新对话框显示的更新内容签名并校验;静默安装,核对卸载项及安装文件、运行副本、免安装下载的更新类型,再静默卸载;生成更新清单并发布 GitHub Release |
推送到分支不会触发检查,也没有 nightly 或定时构建。推送前在本地运行 start.bat check:它包含 check.yml 中的 TypeScript、Biome、文案检查、前端单元测试、rustfmt 和 Clippy,不含前端构建、Rust 单元测试和许可声明检查。
提交信息
<type>(<scope>)!: <描述>
<正文,可选>
<页脚,可选>
type与scope用英文小写,描述用中文,说明改了什么,结尾不加句号;首行不超过 100 个字符。- 首行与正文之间空一行。正文解释原因和影响,只补充有助于理解改动的实现细节。
- 常规检查、测试或构建是否通过不写入提交信息;性能验证以简短结论或关键对照数据概括,不罗列测试过程、原始资料存放位置或工作记录。
- 一个提交只做一类事。功能、顺手的重构和文档最好分开提交。
| type | 用途 | 进入发布说明 | 版本影响 |
|---|---|---|---|
feat |
新功能、用户可见的新行为 | 新功能 | 次版本号 |
fix |
缺陷修复(包括卡顿、视觉瑕疵) | 修复 | 修订号 |
perf |
性能改进 | 性能 | 修订号 |
revert |
回退之前的提交 | 回退 | 修订号 |
refactor |
不改变行为的重构 | — | 修订号 |
docs |
只改文档 | — | 修订号 |
style |
只改代码格式 | — | 修订号 |
test |
只改测试 | — | 修订号 |
build |
构建脚本、依赖、工具链 | — | 修订号 |
ci |
GitHub Actions 工作流 | — | 修订号 |
chore |
其他杂项,包括发布提交 | — | 修订号 |
scope 可省略,跨模块的改动不写。常用的范围:
| scope | 目录 |
|---|---|
core |
crates/argus-core |
service |
crates/argus-service |
host |
src-tauri |
ui |
ui |
deps |
依赖升级(配合 build) |
示例:
feat(ui): 进程页支持按列筛选
fix(host): 修复调整窗口大小时闪现的对角线白线
perf(core): 进程快照复用缓冲区
build(deps): 升级 Tauri
docs: 补充特权服务的验证步骤
破坏性变更
在冒号前加 !,并在页脚写 BREAKING CHANGE: <说明>,说明会单独列在发布说明的“破坏性变更”里。对 Argus 来说,破坏性变更是指升级后用户需要额外处理的改动,例如:设置或历史数据的格式不再兼容、特权服务的 IPC 协议需要同时升级、删除功能或快捷键、改变命令行参数。
feat(service)!: 特权服务改用新的 IPC 协议
BREAKING CHANGE: 升级后需要在设置中重新安装特权服务
检查提交信息
pnpm commitlint origin/main..HEAD # 检查尚未推送的提交
pnpm commitlint --message "feat(ui): 新功能" # 检查一条信息
版本号
格式为 主版本号.次版本号.修订号,可以带预发布后缀(如 1.0.0-beta.1)。不指定版本时,按上一个标签以来的提交推断:
- 有破坏性变更:主版本号加一;在 1.0.0 之前(初始开发阶段)改为次版本号加一。
- 有
feat:次版本号加一。 - 其他:修订号加一。
1.0.0 和预发布版本需要显式指定。版本号写在 Cargo.toml(工作区)、Cargo.lock、package.json 和 src-tauri/tauri.conf.json 四处,由发布脚本统一修改,不要手改。
发布步骤
-
切到
main,工作区干净,start.bat check通过。第一次发布前先配置更新签名密钥;tauri.conf.json里没有公钥时脚本会拒绝发布。 -
在 CI 上运行一遍检查,见推送标签前在 CI 上检查。
-
预览版本号与中英文发布说明:
pnpm release --dry-run。英文条目没有预先写好时,显示的是待译的草稿。 -
创建发布提交和标签:
pnpm release # 按提交推断版本 pnpm release minor # 或指定级别:major、minor、patch pnpm release 1.0.0-beta.1 # 或指定版本脚本修改四处版本号,把发布说明加到
CHANGELOG.md开头,把由它生成的英文草稿加到CHANGELOG.en.md开头(小节标题已是英文,条目仍是中文),提交chore(release): vX.Y.Z并创建附注标签vX.Y.Z,不会推送。仓库还没有任何标签时,默认发布当前版本号。 -
检查
CHANGELOG.md。润色只改正文:标题行## [X.Y.Z](比较链接) - YYYY-MM-DD由脚本生成,应用和发布工作流都按这个格式查找条目,日期后不能再有文字;##只用于版本条目的标题,条目内的小节用###。然后把CHANGELOG.en.md中的草稿译成英文:与中文条目逐条对应,标题行同样不改,用词与界面的英文文案(locales/en-US)一致;之后润色中文时同步修改英文。修改后git commit --amend,再用git tag -fa vX.Y.Z -m "Argus vX.Y.Z"把标签移到新提交上。也可以在运行pnpm release之前先把该版本的条目写进CHANGELOG.md和CHANGELOG.en.md并提交:脚本会沿用已有条目,只把标题换成带比较链接和日期的格式(首个版本的发布说明就是这样预先写好的)。两份文件中有不是条目标题的二级标题时,脚本不改动任何文件,列出这些行。 -
推送前在本地核对:
node scripts/release.mjs verify vX.Y.Z。它与发布工作流的第一步做同样的核对,读取条目的规则与应用相同:英文条目缺失、仍含中文(链接地址除外)或日期与中文条目不同时不通过;另外确认标签指向当前提交、版本文件和两份更新日志没有未提交的改动。node scripts/release.mjs notes X.Y.Z打印 GitHub Release 的正文:先是“简体中文”一节,后是“English”一节。 -
推送分支和标签:
git push --atomic origin main vX.Y.Z。release.yml随即开始检查和构建,完成后版本出现在 GitHub Releases。
推送前想撤销:git tag -d vX.Y.Z,然后 git reset --hard HEAD~1。
发布工作流失败时,修复后发布新的修订版本,不要移动已经推送的标签。
推送标签前在 CI 上检查
推送标签后,release.yml 先运行 check.yml,此时失败,该版本号即作废。start.bat check 不含 Rust 单元测试、前端构建和许可声明检查,也不在 CI 的环境里运行(英文 Windows、管理员账户、当时最新的 stable Rust,Clippy 与测试带 --locked),所以先推送 main,对它手动运行一次 check.yml,通过后再运行 pnpm release:
git push origin main
gh workflow run check.yml --ref main
gh run watch # 选择刚开始的 Check,等待结束
也可以在 Actions 页面选择 Check,对 main 运行。之后的发布提交只改版本号和 CHANGELOG.md,后者由第 6 步在本地核对。
release.yml 中构建与发布的步骤只在推送标签后运行,无法预先在 CI 上检查:用 Secrets 中的私钥构建并签名安装包、为更新内容签名、静默安装与卸载、生成清单、上传 Release。这些步骤新增或修改后,第一次运行就是正式发布,推送标签后留意它们的日志;发布完成后可从 releases/latest/download/latest.json 取得清单,核对版本号、argus.releaseNotes 与 argus.signature。
发布产物
| 文件 | 说明 |
|---|---|
Argus_<版本>_x64-setup.exe |
NSIS 安装程序:为所有用户安装到 Program Files(需要管理员授权)、按 Windows 显示语言使用简体中文或英文界面、不允许降级;单页安装界面,已安装旧版本时原地升级;卸载时询问是否一并卸载特权服务,并做任务管理器恢复检查。界面见安装程序 |
Argus_<版本>_x64.exe |
免安装版主程序(即 argus.exe)。与安装版使用相同的设置、历史和开机启动项,数据同样在 %APPDATA%、%LOCALAPPDATA%,不能应用内更新。放在标准用户可写的文件夹时,安装特权服务存在被替换提权的风险,见设置 → 权限 |
SHA256SUMS.txt |
上面两个文件的 SHA-256,可用 sha256sum -c SHA256SUMS.txt 校验 |
latest.json、beta.json |
应用内更新的清单,只在稳定版上,见更新通道 |
带预发布后缀的版本标记为 Pre-release。程序没有代码签名,首次运行时 Windows SmartScreen 会提示“未知发布者”。
更新签名密钥
Windows Authenticode 的测试接入见 Windows 代码签名。测试签名工作流独立于正式发布;以下密钥仍用于应用内更新验证,与 Windows 代码签名证书分开管理。
应用内更新只安装用这把密钥签名的安装包,签名同时覆盖版本号(插件的 requireSignedVersion)。这与代码签名无关,消除不了 SmartScreen 提示。
第一次配置时,在维护者本机生成密钥(会提示设置密码),把私钥和密码存进仓库的 Actions Secrets。PowerShell 默认禁止运行脚本,pnpm 会解析到被拦下的 pnpm.ps1,所以这里写 pnpm.cmd:
New-Item -ItemType Directory -Force "$env:USERPROFILE\.tauri" | Out-Null
pnpm.cmd tauri signer generate -w "$env:USERPROFILE\.tauri\argus-updater.key"
gh secret set TAURI_SIGNING_PRIVATE_KEY --repo Ray-lea/Argus --body (Get-Content -Raw "$env:USERPROFILE\.tauri\argus-updater.key").Trim()
gh secret set TAURI_SIGNING_PRIVATE_KEY_PASSWORD --repo Ray-lea/Argus
再把 argus-updater.key.pub 的内容填进 src-tauri/tauri.conf.json 的 plugins.updater.pubkey 并提交。私钥和密码要离线备份:丢失后只能换一把新密钥,而已安装的版本只认旧公钥,用户必须手动下载新版本重新安装一次。
本地构建(start.bat build installer)不生成更新签名,不需要私钥;发布工作流用 --config 打开 createUpdaterArtifacts,签名后用 node scripts/release.mjs check-signature 以 tauri.conf.json 中的公钥校验,Secrets 里的私钥与公钥不配对时在发布前失败。
更新对话框显示的更新内容同样签名:node scripts/release.mjs release-notes <版本> release-notes.json 写出该版本及之前最近 15 个版本的 CHANGELOG.md 条目(JSON),每个条目以 translations 附上 CHANGELOG.en.md 中同一版本的英文译文(0.3 之前的版本忽略这个字段),pnpm tauri signer sign --app-version <版本> 用同一私钥签名并把版本号写进可信注释,再以 check-signature 校验。signer sign 只接受私钥文本本身,TAURI_SIGNING_PRIVATE_KEY 须保存私钥内容而不是文件路径。应用只显示验签通过且版本号一致的更新内容,并按界面语言选用译文;没有译文的版本显示中文,列表下方加以说明。
更新通道
应用读取最新稳定版 Release 上的清单:稳定版通道读 releases/latest/download/latest.json,测试版通道读同一位置的 beta.json。
- 发布稳定版时,两份清单都指向这个版本,随 Release 一起上传。
- 发布预发布版本时,只把
beta.json改为指向它,覆盖上传到当前最新的稳定版 Release。仓库还没有稳定版时测试版通道收不到更新,工作流会给出警告。 - 清单由
node scripts/release.mjs manifest生成。notes取自CHANGELOG.md的对应条目,转成纯文本并去掉提交范围(core、ui等),供 0.2 之前的版本显示;argus.releaseNotes与argus.signature是上一节签名的更新内容原文及其签名,新版本的更新对话框只读这两项。
应用依次尝试 GitHub 与加速代理(见设置 → 更新)。代理只在 GitHub 地址前加前缀,清单和安装包都不变。
发布后发现严重问题时
已升级的用户无法回到旧版本:安装程序不允许降级,应用内更新只提供比当前版本新的版本。能做的是先停止推送,再尽快发布修订版本。
-
停止推送:把有问题的版本改为 Pre-release,并把上一个稳定版设为 Latest,然后确认应用读取的清单已回到上一个版本。可在 Release 说明开头写明问题。
gh release edit vX.Y.Z --prerelease gh release edit vX.Y.W --latest # 上一个稳定版 gh release view --json tagName # 应为上一个稳定版 curl -sL https://github.com/Ray-lea/Argus/releases/latest/download/latest.json # version 应为上一个版本GitHub 的最新稳定版不包括 Pre-release,
releases/latest/download/随之指向上一个稳定版的latest.json与beta.json,旧版本检查时得到“已是最新版本”。不要只删除这两份清单:地址会返回 404,检查失败后,0.2 及之后的版本保留已下载的更新,仍可安装;下载线路为“自动”时还会改用加速代理,可能取得代理缓存的旧清单。 -
已下载但未安装的更新在下一次检查成功、清单不再提供更新的版本时撤下。自动检查在启动约 1 分钟后进行一次,此后每天一次;在此之前仍可安装。
-
下载线路为“自动”时先连接 GitHub,GitHub 可访问时不经过代理。选择“仅使用加速代理”或无法访问 GitHub 的用户取得的是代理返回的清单,代理可能缓存旧的
latest.json,Argus 无法清除。签名只保证安装包未被改动,不保证清单是最新的:按旧清单仍会下载有问题的版本,因为它的安装包还在 Release 上。情况严重时可删除该安装包(gh release delete-asset vX.Y.Z Argus_X.Y.Z_x64-setup.exe),GitHub 随即对它返回 404,但代理同样可能缓存了安装包。 -
修复后按正常步骤发布修订版本。新的稳定版发布后成为 Latest,两个通道都指向它;有问题的版本保持 Pre-release,标签不动。
有问题的是测试版时,第 1 步不起作用:测试版本来就是 Pre-release,而 release.yml 发布它时已把它的清单覆盖到最新稳定版的 beta.json(稳定版原来的 beta.json 随之丢失)。改为从最新稳定版下载 latest.json(与该稳定版发布时的 beta.json 内容相同),以 beta.json 的名字传回,再确认测试版通道的清单已回到该稳定版:
gh release download vX.Y.W --pattern latest.json --dir rollback # 最新稳定版
mv rollback/latest.json rollback/beta.json
gh release upload vX.Y.W rollback/beta.json --clobber
curl -sL https://github.com/Ray-lea/Argus/releases/latest/download/beta.json # version 应为该稳定版
此后第 2、3 步同样适用于测试版通道的用户。修复后发布新的测试版或稳定版时,release.yml 会再次写入 beta.json。
安装程序
安装与卸载界面不使用 NSIS 的向导页面,由两部分组成:
src-tauri/installer-template.nsi:Tauri 的 NSIS 模板,使用 Argus 的安装与卸载页面,通过主程序的内部安装模式处理文件复制,并扩展卸载时的数据清理:先结束遗留的 WebView2 进程,删除后仍有残留时列出文件夹;改动处都标有Argus。它由node scripts/nsis-template.mjs按已安装的@tauri-apps/cli版本从 Tauri 仓库取得模板后生成,不要手改;升级 Tauri 后重新运行,模板在替换处有变化时脚本会停止,需要对照新模板调整脚本。src-tauri/installer-ui.nsh:页面本身。安装程序为“安装或升级”“进度”“完成”三页,卸载程序为“确认(可选删除应用数据)”“进度”“完成”三页;按 Windows 的应用颜色模式使用 Argus 的浅色或深色配色,中文用微软雅黑、英文用 Segoe UI,窗口大小在各语言下一致,并按显示缩放比例调整。文案按语言写在文件开头的LangString中。
已安装旧版本时,安装页显示“将从版本 X 升级到 Y”,覆盖安装而不先卸载(与应用内更新相同);已安装更新的版本时按钮不可用,需先卸载。被动模式(应用内更新)只显示进度页,静默安装(/S)不显示界面,命令行参数与 Tauri 的安装程序相同。
覆盖程序前,安装器从自身解出新版程序,以内部安装模式检查并复制主程序。检查通过进程路径和创建时间确认当前安装目录中的 Argus 实例,再单独请求其退出。其他程序阻止更新时,弹窗显示可查询到的占用进程及原始错误码。复制阶段的共享冲突和内存映射占用同样进入诊断与重试流程;Windows 未提供占用者信息时保留具体失败原因。释放占用后可单击“重试”;占用尚未解除时取消,原有程序文件保持不变。静默模式遇到占用时以非零错误码退出。
安装检查日志写入安装账户的 %TEMP%\Argus-installer-<随机标识>.log,弹窗给出具体路径。日志记录安装目标、失败 API、十进制与十六进制错误码、占用进程路径及重试结果。每次安装检查独立创建日志文件,等待重试时可直接读取;日志不记录进程命令行或窗口标题。跨账户提权时,根据交互窗口站的登录身份确定原桌面账户,仅为该新建日志授予读取权限,保留原有权限与父目录保护。权限设置失败时记录具体步骤和错误码,继续保留安装账户的日志。
NSIS 的 SetCtlColors 只接受编译时的颜色,界面使用 Argus 的默认强调色而不跟随 Windows 强调色。主按钮和次要操作支持 Tab/Shift+Tab 导航;主按钮具有焦点边框、悬停与按下反馈,可用空格或 Enter 激活。被禁用的降级按钮不参与键盘操作。
发布到 winget
第一个稳定版发布后,可以用 wingetcreate 向 microsoft/winget-pkgs 提交清单,安装包使用 Argus_<版本>_x64-setup.exe(支持 /S 静默安装)。之后的版本用 wingetcreate update 提交新地址。提交会创建公开的拉取请求,由维护者手动执行。