build(desktop): allow controlled unsigned v0.1.0 release
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (push) Has been cancelled
Client Quality Gates / Shared client and Web (pull_request) Has been cancelled
Client Quality Gates / macOS Desktop (pull_request) Has been cancelled
Storage Persistence Guard / storage-persistence-audit (pull_request) Has been cancelled

This commit is contained in:
Cheng Zhou
2026-07-17 10:52:20 +08:00
parent fa26c84bd2
commit c23a5d6a22
16 changed files with 647 additions and 83 deletions
@@ -28,14 +28,14 @@ npm run desktop:build:app
- [ ] `frontend/package.json``package-lock.json`、Tauri 配置、Cargo manifest/lock 版本一致。
- [ ] `VITE_BUILD_CHANNEL=release``VITE_BUILD_COMMIT=<release tag commit>` 由 CI 注入,且 `npm run release:env:check` 通过。
- [ ] 在正式 release tag 和签名环境中执行 `npm run desktop:release-readiness:check`,确认 tag、构建元数据、签名/公证变量、updater 私钥和生产 artifact HTTPS 基址齐备。
- [ ] macOS app 已签名和公证
- [ ] Windows 应用和 NSIS 安装器已使用组织证书签名并带有效 RFC 3161 时间戳,`Get-AuthenticodeSignature` 应用和安装器返回 `Valid`
- [ ] 在正式 release tag 环境中执行 `npm run desktop:release-readiness:check`,确认 tag、构建元数据、当前精确版本的平台签名策略、updater 私钥和生产 artifact HTTPS 基址齐备。
- [ ] macOS app 已完成 Apple 签名/公证;若当前精确版本获批平台签名例外,则已验证 `Signature=adhoc`、确认未公证并附带 Gatekeeper 风险说明
- [ ] Windows 应用和 NSIS 安装器已使用组织证书签名并带有效 RFC 3161 时间戳,`Get-AuthenticodeSignature` 返回 `Valid`;若当前精确版本获批例外,则应用和安装器必须返回 `NotSigned` 并附带 SmartScreen 风险说明
- [ ] updater `.sig` 使用组织 CI secret 或密钥库中的私钥生成,私钥未进入仓库。
- [ ] 设置 `TAURI_SIGNING_PRIVATE_KEY``TAURI_SIGNING_PRIVATE_KEY_PASSWORD`Apple 签名/公证变量后,以 `REQUIRE_DESKTOP_SIGNING=true` 再次执行 `npm run release:env:check`,随后执行 `npm run desktop:build:macos-release -- --ci`
- [ ] 设置 Windows PFX、密码、`WINDOWS_TIMESTAMP_URL` updater 私钥后,以 `REQUIRE_WINDOWS_SIGNING=true` 执行 readiness,再执行 `npm run desktop:build:windows-release -- --ci`
- [ ] 始终设置 `TAURI_SIGNING_PRIVATE_KEY``TAURI_SIGNING_PRIVATE_KEY_PASSWORD``REQUIRE_UPDATER_SIGNING=true`;默认 macOS 路径再设置 Apple 变量和 `REQUIRE_DESKTOP_SIGNING=true`,例外路径则设置 `DESKTOP_PLATFORM_SIGNING_MODE=unsigned-exception``ALLOW_UNSIGNED_PLATFORM_RELEASE=true`执行 `npm run desktop:build:macos-unsigned-release -- --ci`
- [ ] 默认 Windows 路径设置 PFX、密码、`WINDOWS_TIMESTAMP_URL``REQUIRE_WINDOWS_SIGNING=true`;例外路径不得伪装签名状态,构建后必须对应用和安装器验证 `NotSigned`
- [ ] 正式 updater feed 使用 `--artifact <CTMS.app.tar.gz>``--platform-artifact windows-x86_64=<CTMS.nsis.zip>` 汇总生成同一个 `latest.json``SHA256SUMS.txt`
- [ ] 正式 updater feed 执行 `npm run desktop:update-feed:check -- --feed <release-dir>/latest.json --artifacts-dir <release-dir> --require-platform windows-x86_64`,并确认两个平台的 updater artifact、`.sig`、安装包、checksum manifest 和 `latest.json` 均通过校验。
- [ ] 正式 updater feed 执行 `npm run desktop:update-feed:check -- --feed <release-dir>/latest.json --artifacts-dir <release-dir> --require-platform windows-x86_64 --require-provenance`,并确认两个平台的 updater artifact、`.sig`、安装包、provenance、checksum manifest 和 `latest.json` 均通过校验。
- [ ] 不可变制品先上传,`latest.json` 最后原子替换;若 feed 校验未通过,不替换线上 `latest.json`
- [ ] Web 与 Desktop 制品记录同一产品版本、Git 标签和完整提交 SHA。
@@ -70,7 +70,7 @@ npm run desktop:build:app
- [ ] updater capability 不直接暴露给 WebView,自动更新只走受控 Tauri command。
- [ ] 更新弹窗 release notes 过滤 URL、token 查询参数和 Authorization/Bearer 形态文本。
- [ ] CI release 候选 workflow 包含 version/runtime/desktop/ui/type/unit/build/desktop app smoke 门禁。
- [ ] signed Desktop release candidate workflow 只允许从 `vX.Y.Z` tag 运行;macOS 和 Windows 原生 job 分别完成签名验证,再由聚合 job 生成包含 `darwin-aarch64``darwin-x86_64``windows-x86_64` 的 feed、checksum 清单和 verified release directory。
- [ ] Desktop release candidate workflow 只允许从 `vX.Y.Z` tag 运行;先按精确版本解析平台签名策略,macOS 和 Windows 原生 job 分别验证实际签名状态,再由聚合 job 生成包含 `darwin-aarch64``darwin-x86_64``windows-x86_64` 的 feed、provenance、checksum 清单和 verified release directory。
人工复审还必须确认:
@@ -276,6 +276,19 @@ npm run desktop:build:app
创建正式 tag 前仍必须由发布负责人确认:
- GitHub Actions 已配置 updater、Apple 与 Windows 签名 secrets,以及 versioned artifact base URL 和 Windows timestamp URL。
- 组织 Windows 证书允许 PFX 导入 CI;若为硬件或云托管密钥,先将 workflow 切换为经审计的 Tauri `signCommand` provider。
- GitHub Actions 已配置 updater secrets versioned artifact base URL;默认平台签名路径还必须配置 Apple 与 Windows secrets 和 Windows timestamp URL。
- 若使用默认 Windows 签名路径,组织证书允许 PFX 导入 CI;若为硬件或云托管密钥,先将 workflow 切换为经审计的 Tauri `signCommand` provider。
- macOS/Windows 原生 candidate job、联合 feed 校验、真实安装/升级和生产下载源上传全部通过。
## 13. 2026-07-17 v0.1.0 平台签名例外记录
经发布负责人明确批准,v0.1.0 可在无法提供 Apple Developer 和 Windows 组织代码签名凭据时受控分发。该批准仅覆盖精确版本 `0.1.0`
- `frontend/desktop-release-policy.json` 默认仍为 `signed`,仅将 v0.1.0 列入 `unsignedPlatformExceptions`;后续版本不会继承本例外。
- macOS 使用 Tauri `signingIdentity: "-"` 生成 ad-hoc 签名 Universal app/DMG,必须通过 `codesign --verify` 并确认 `Signature=adhoc`,不执行或声称 Apple 公证。
- Windows 应用和 NSIS 安装器不做 Authenticode 签名,必须通过 `Get-AuthenticodeSignature` 确认 `NotSigned`,不执行或声称 RFC 3161 时间戳。
- 两端仍必须从同一 `v0.1.0` tag 和 SHA 构建,使用同一 updater 私钥生成 `.sig`,并通过联合 feed、checksum 和 provenance 校验。
- 制品与 Actions artifact 名称必须含 `_UNSIGNED``UNSIGNED-PLATFORM`,发布目录必须携带 `UNSIGNED-PLATFORM-RELEASE.txt``DESKTOP-RELEASE-PROVENANCE.json`
- 受控分发说明必须明确提示 macOS Gatekeeper 与 Windows SmartScreen 警告;平台未签名制品不得描述为 Apple/Microsoft 信任或已公证/已 Authenticode 签名。
本地实物验证已完成:使用当前 updater 私钥成功构建 Universal `x86_64 arm64` macOS app、DMG、`.app.tar.gz``.sig``codesign --verify --deep --strict` 通过,签名详情为 `Signature=adhoc``TeamIdentifier=not set`Gatekeeper 拒绝符合预期。Windows `NotSigned`、NSIS 和 updater `.sig` 仍须由 `windows-latest` 原生 job 在正式 tag 上验证。
+9
View File
@@ -305,9 +305,18 @@ Before promoting `main` to `release`, confirm:
- release checklist is complete
- production initialization, build, and smoke verification are complete
- Desktop updater signatures are present and verified
- platform signing is either complete or the exact version has an approved,
checked-in exception in `frontend/desktop-release-policy.json`; the current
v0.1.0 exception requires macOS ad-hoc signing, unsigned Windows artifacts,
controlled distribution, and explicit trust warnings
- rollback or remediation path is clear
- release tag has been prepared
Platform signing remains the default for versions without an exact approved
exception. An exception never permits disabling Tauri updater signatures or
building Web, macOS, and Windows from different tags or commits.
Reference:
- [docs/guides/release-checklist.md](guides/release-checklist.md)
+4 -3
View File
@@ -106,7 +106,7 @@ npm run desktop:build:app
npm run desktop:build:app
```
正式桌面发布构建必须从同一正式 tag 分别在 macOS 和 Windows 原生 CI job 执行。两端都必须设置 updater 签名私钥macOS 设置 Apple 签名/公证变量并以 `REQUIRE_DESKTOP_SIGNING=true` 执行 readiness 与 Universal 构建Windows 设置 PFX 证书、密码和 RFC 3161 时间戳变量并以 `REQUIRE_WINDOWS_SIGNING=true` 执行 readiness、签名 NSIS 构建和 Authenticode 校验。两个平台通过后才能汇总正式 updater feed。
正式桌面发布构建必须从同一正式 tag 分别在 macOS 和 Windows 原生 CI job 执行。两端都必须设置 updater 签名私钥,updater 签名不可关闭。平台签名默认路径仍要求 macOS 设置 Apple 签名/公证变量并以 `REQUIRE_DESKTOP_SIGNING=true` 执行,Windows 设置 PFX、密码和 RFC 3161 时间戳变量并以 `REQUIRE_WINDOWS_SIGNING=true` 执行。只有 `frontend/desktop-release-policy.json` 对当前精确版本存在已批准例外时,workflow 才可改为 macOS ad-hoc、Windows Authenticode 未签名路径;例外制品必须标注 `UNSIGNED-PLATFORM`、生成 provenance 和风险说明。两个平台与联合 feed 校验通过后才能汇总正式 updater feed。
后端改动应补充执行受影响模块的后端测试、迁移检查和接口回归。
@@ -206,7 +206,7 @@ dev -> main
- 前后端测试通过
- 网页端构建通过
- macOS 桌面端构建通过
- Windows x64 NSIS 兼容性构建通过;正式发布时签名和时间戳验证通过
- Windows x64 NSIS 兼容性构建通过;正式发布时平台签名和时间戳验证通过,或当前精确版本的已批准平台签名例外验证通过
- 数据库迁移经过验证
- 已知风险和回滚方式已记录
@@ -295,7 +295,7 @@ git tag -a v1.2.0 -m "CTMS v1.2.0"
git push origin v1.2.0
```
网页端、macOS 和 Windows 桌面端必须从同一个 `v1.2.0` 标签构建。不得从不同分支、不同提交或本地未提交状态构建正式制品。macOS 签名/公证Windows 代码签名/RFC 3161 时间戳以及两个平台的 updater 签名制品必须全部验证通过后,才能分发安装包并最后原子替换生产 `latest.json`
网页端、macOS 和 Windows 桌面端必须从同一个 `v1.2.0` 标签构建。不得从不同分支、不同提交或本地未提交状态构建正式制品。两个平台的 updater 签名制品必须始终验证通过;macOS 签名/公证Windows 代码签名/RFC 3161 时间戳必须验证通过,除非 `frontend/desktop-release-policy.json` 对该精确版本记录了已批准例外。采用例外时必须验证 macOS 确为 ad-hoc、Windows 确为 `NotSigned`,并随安装包分发 `UNSIGNED-PLATFORM` 风险说明、provenance 和 checksum,之后才能最后原子替换生产 `latest.json`
发布记录至少包含:
@@ -450,6 +450,7 @@ git log --oneline --decorate --graph --all -30
- [ ] `npm run build` 通过
- [ ] `npm run desktop:build:app` 通过
- [ ] 正式桌面发布构建已使用 updater 签名私钥执行
- [ ] 平台签名已验证,或当前精确版本已在 `frontend/desktop-release-policy.json` 获批例外且 `UNSIGNED-PLATFORM` 风险说明、provenance 和 checksum 齐备
- [ ] 数据库迁移与回滚方案确认
- [ ] 发布说明完成
- [ ] `main -> release` 合并完成
+54 -24
View File
@@ -87,7 +87,11 @@ npm run desktop:build:app
`release:env:check` verifies build channel and commit metadata, and becomes
platform-strict with `REQUIRE_DESKTOP_SIGNING=true` on macOS or
`REQUIRE_WINDOWS_SIGNING=true` on Windows.
`REQUIRE_WINDOWS_SIGNING=true` on Windows. Formal native jobs also set
`REQUIRE_UPDATER_SIGNING=true`, `DESKTOP_RELEASE_PLATFORM`, and the
version-derived `DESKTOP_PLATFORM_SIGNING_MODE`. The exact-version policy in
`frontend/desktop-release-policy.json` is checked with
`npm run desktop:release-policy:check`.
`desktop:release:check` statically verifies the Tauri bundle, updater public
key, CSP, capability scopes, command allowlist, query-token ban, generic system
notification boundary, CI gate coverage, and secure session token boundary. The
@@ -132,10 +136,10 @@ The release pipeline must:
1. build from the accepted release tag and commit;
2. build the Universal macOS app/DMG and Windows x64 NSIS installer from that tag;
3. sign and notarize macOS, and Authenticode-sign Windows with an RFC 3161 timestamp;
3. use Apple signing/notarization and Windows Authenticode/RFC 3161 signing by default, or use a checked-in exact-version exception that constrains macOS to ad-hoc signing and Windows to Authenticode-unsigned output;
4. produce macOS `.app.tar.gz` and Windows `.nsis.zip` updater artifacts plus their `.sig` files with the shared updater private key;
5. generate one combined `latest.json` and checksum manifest with `npm run desktop:update-feed:create`;
6. verify all macOS and Windows entries with `npm run desktop:update-feed:check -- --feed <release-dir>/latest.json --artifacts-dir <release-dir> --require-platform windows-x86_64`;
5. generate `DESKTOP-RELEASE-PROVENANCE.json`, one combined `latest.json`, and a checksum manifest;
6. verify all macOS and Windows entries with `npm run desktop:update-feed:check -- --feed <release-dir>/latest.json --artifacts-dir <release-dir> --require-platform windows-x86_64 --require-provenance`;
7. upload immutable artifacts first;
8. atomically replace `latest.json` last.
@@ -143,22 +147,33 @@ For Universal macOS artifacts, `latest.json` must provide both
`darwin-aarch64` and `darwin-x86_64` entries pointing at the same Universal
update package.
The same feed must also contain `windows-x86_64`, pointing to the signed NSIS
`.nsis.zip` updater package. The Windows `.exe` installer is distributed next
to the updater package but is not used as the updater URL.
The same feed must also contain `windows-x86_64`, pointing to the updater-signed
NSIS `.nsis.zip` package. The Windows `.exe` installer is distributed next to
the updater package but is not used as the updater URL. Tauri updater signing is
mandatory in both platform-signed and platform-signing-exception modes.
The signed release candidate workflow lives at
The formal release candidate workflow lives at
`.github/workflows/desktop-release-candidate.yml`. It must be run from a
matching `vX.Y.Z` tag and produces a verified release directory as a GitHub
artifact. That artifact is still only a release candidate; the release owner
must upload immutable files to the production download origin and replace
`latest.json` atomically after validation.
Platform signing defaults to `signed`. An exception is allowed only when
`frontend/desktop-release-policy.json` names the exact product version and
records release-owner approval. The current v0.1.0 exception uses macOS ad-hoc
signing and unsigned Windows application/installer binaries. Its artifact names
and release directory contain `UNSIGNED-PLATFORM`; the directory must include
`UNSIGNED-PLATFORM-RELEASE.txt`, `DESKTOP-RELEASE-PROVENANCE.json`, and
`SHA256SUMS.txt`. This does not establish Apple or Microsoft publisher trust,
and Gatekeeper or SmartScreen warnings are expected. Later versions return to
the signed default unless separately approved.
## Windows Release and Internal Validation
Windows x64 NSIS is an approved formal Desktop target. Formal Windows builds
run in `.github/workflows/desktop-release-candidate.yml` from the same exact
`vX.Y.Z` tag and SHA as Web and macOS. They must:
`vX.Y.Z` tag and SHA as Web and macOS. The default signed path must:
- import a Base64-encoded PFX from `WINDOWS_CERTIFICATE` using
`WINDOWS_CERTIFICATE_PASSWORD`;
@@ -170,6 +185,12 @@ run in `.github/workflows/desktop-release-candidate.yml` from the same exact
- publish the signed NSIS `.exe`, `.nsis.zip`, and `.nsis.zip.sig` into the
combined verified Desktop release directory.
For an approved exact-version unsigned-platform exception, the Windows job
must instead leave the application and installer Authenticode-unsigned, require
`Get-AuthenticodeSignature` to return `NotSigned`, append `_UNSIGNED` to the
installer and updater artifact names, and still create and verify the updater
`.sig`. A certificate secret and timestamp URL are not required in this mode.
Windows release validation also covers:
- WebView2 runtime prerequisite behavior;
@@ -186,19 +207,23 @@ only, runs on `windows-latest`, injects `VITE_BUILD_CHANNEL` and
and Desktop safety gates, builds an unsigned NSIS installer with updater
artifacts disabled, and uploads the `.exe` plus `SHA256SUMS.txt` as a GitHub
Actions artifact. This workflow is for internal compatibility verification
only; it must not generate `latest.json`, update feeds, signed release
only; it must not generate `latest.json`, update feeds, verified release
directories, or formal Windows release artifacts. A successful internal build
does not substitute for the signed tag-only release workflow.
does not substitute for the tag-only formal workflow, including when that
formal workflow uses an approved platform-signing exception.
The formal workflow requires these organization settings:
The formal workflow always requires these organization settings:
- secrets: `TAURI_SIGNING_PRIVATE_KEY`,
`TAURI_SIGNING_PRIVATE_KEY_PASSWORD`, `WINDOWS_CERTIFICATE`, and
`WINDOWS_CERTIFICATE_PASSWORD`;
- variable: `WINDOWS_TIMESTAMP_URL`;
- secrets: `TAURI_SIGNING_PRIVATE_KEY` and
`TAURI_SIGNING_PRIVATE_KEY_PASSWORD`;
- shared versioned HTTPS artifact prefix: `DESKTOP_UPDATE_BASE_URL` or the
manual workflow input.
The default signed path additionally requires `WINDOWS_CERTIFICATE`,
`WINDOWS_CERTIFICATE_PASSWORD`, and `WINDOWS_TIMESTAMP_URL`, plus the Apple
credentials documented by the release owner. The v0.1.0 exception does not
require those platform certificate settings.
The checked-in workflow implements the exportable PFX path. Confirm that the
organization's certificate policy permits an exportable CI certificate before
provisioning it. If the selected CA provides only hardware- or cloud-backed
@@ -224,16 +249,21 @@ npm run build
npm run desktop:build:app
export TAURI_SIGNING_PRIVATE_KEY="$UPDATER_PRIVATE_KEY"
export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="$UPDATER_PRIVATE_KEY_PASSWORD"
export REQUIRE_DESKTOP_SIGNING=true
export REQUIRE_UPDATER_SIGNING=true
export DESKTOP_RELEASE_PLATFORM=macos
export DESKTOP_PLATFORM_SIGNING_MODE=unsigned-exception
export ALLOW_UNSIGNED_PLATFORM_RELEASE=true
npm run release:env:check
npm run desktop:release-readiness:check
npm run desktop:build:macos-release -- --ci
npm run desktop:build:macos-unsigned-release -- --ci
npm run desktop:update-feed:create -- --artifact <CTMS.app.tar.gz> --platform-artifact windows-x86_64=<CTMS.nsis.zip> --include <CTMS.dmg> --include <CTMS-installer.exe> --base-url <versioned-https-artifact-prefix> --output-dir <release-dir>
npm run desktop:update-feed:check -- --feed <release-dir>/latest.json --artifacts-dir <release-dir> --require-platform windows-x86_64
npm run desktop:update-feed:check -- --feed <release-dir>/latest.json --artifacts-dir <release-dir> --require-platform windows-x86_64 --require-provenance
```
The macOS and Windows signed builds run in their native CI jobs. macOS requires
the Apple signing/notarization credentials defined by the release owner;
Windows requires the PFX certificate/password and timestamp URL above. Both use
the same updater signing key and are aggregated only after both native jobs
pass. Unsigned internal builds are not formal distributions.
The macOS and Windows builds run in their native CI jobs and always use the same
updater signing key. In the default path, macOS requires Apple
signing/notarization credentials and Windows requires the PFX/password and
timestamp URL. In the approved v0.1.0 exception, macOS must verify
`Signature=adhoc`, Windows must verify `NotSigned`, and both must carry explicit
platform-trust warnings. Native artifacts are aggregated only after both jobs
and the combined updater feed pass.