Files

252 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 发布指南
<a id="macos-next-steps"></a>
## 当前你要做的操作(v2.1.1 macOS)
本机已打出 **Tauri 窗口布局** 的 Developer ID 签名 DMG(不是手搓 `hdiutil`,Finder 里是大图标 + 拖到 Applications):
```text
src-tauri/target/aarch64-apple-darwin/release/bundle/dmg/OpsXShell_2.1.1_aarch64.dmg
```
这份包**还没公证、没 staple**。别人下载后仍可能被 Gatekeeper 拦。你这边只差公证:
1. Finder 里弹出 `/Volumes/OpsXShell`(挂载中无法 staple)。
2. 开 Clash:`proxy-on`(公证走 HTTPS;时间戳构建阶段已经用 HTTP 回退签过了)。
3. 不要把 App 专用密码发到聊天。终端执行其一:
```bash
APPLE_ID='1024way@gmail.com' ./scripts/macos-notarize.sh
# 或
bash ~/Documents/apple签名/公证OpsXShell.sh
```
4. 弹出框里填 **App 专用密码**(appleid.apple.com → 登录与安全 → App 专用密码,标签如 `app notary`)。
5. 脚本会 `notarytool submit --wait --timeout 15m`。第一次可能很久;`--wait` 卡住时等它超时,再跑同一条命令——**已 Accepted 的不会重新上传,只会 staple**。不要对同一份未变化的 DMG 连续第三次 `submit`。
6. 成功标志:`xcrun stapler validate` 通过,且 `codesign -dv` 能看到钉票。把 **这一份** DMG 发出去(尽量不要走微信,隔离属性会更烦)。
7. 改代码后再 `./scripts/build.sh mac-arm64 --quick` 会得到新哈希,必须重新公证。
Windows Authenticode、自动更新仍未做,不要对外写「已公证」直到第 6 步完成。
## 构建命令
```bash
# 推荐:当前平台完整门禁、打包、验证和 SHA-256
./scripts/build.sh
# macOS Apple Silicon 指定版本
./scripts/build.sh v2.1.1 mac-arm64
# 构建完成后公证并钉票(新 DMG 必须再跑;已 staple 会直接退出)
proxy-on # 本机 Clash;codesign 时间戳另见下文 HTTP 回退
APPLE_ID='你的开发者邮箱' ./scripts/macos-notarize.sh
# Windows x86_64(推荐在 Windows CMD/PowerShell 中执行)
scripts\build.bat v2.1.1 win-x64
# 本地重复构建时缩短门禁;正式发布不要跳过
./scripts/build.sh v2.1.1 mac-arm64 --quick
```
`scripts/build.sh` 与 `scripts/build.bat` 面向 v2.1.x,会检查 Node、npm、Rust target 和五处版本元数据。显式传入不同于当前源码的新版本时,脚本会同步版本文件;它们只负责构建,不会自动创建 tag 或推送远程仓库。安装包和 `SHA256SUMS.txt` 留在 Tauri 2 标准目录:
```text
src-tauri/target/aarch64-apple-darwin/release/bundle/ # macOS arm64:dmg/ + macos/*.app
src-tauri/target/x86_64-pc-windows-msvc/release/bundle/ # Windows:nsis/ + msi/
```
平台安装包必须在目标系统构建和验证。macOS 不能替代 Windows x86_64 实机打包。
### Windows 构建环境
建议使用 Windows 10/11 x64,并安装:
- Node.js 18+ 与 npm
- Rust stable 的 `x86_64-pc-windows-msvc` toolchain
- Visual Studio 2022 Build Tools,勾选 **Desktop development with C++**
- Windows 10/11 SDK
- Microsoft Edge WebView2 Runtime
首次准备:
```bat
rustup default stable-x86_64-pc-windows-msvc
rustup target add x86_64-pc-windows-msvc
npm ci
scripts\build.bat v2.1.1 win-x64
```
默认执行完整门禁;`--quick` 适合开发期重复构建,正式发布不要使用 `--skip-checks`。输出位于 `src-tauri\target\x86_64-pc-windows-msvc\release\bundle\`。没有代码签名证书时可以生成开发安装包,但 Windows SmartScreen 可能提示未知发布者。
## Gitee Release
构建、提交并推送对应 tag 后执行:
```bash
# 只检查附件,不访问 Gitee API
./scripts/release.py v2.1.1 --dry-run
# 创建或更新 Release,上传安装包和 SHA256SUMS.txt,并核验远程附件
./scripts/release.py v2.1.1
```
发布凭据从 `GITEE_TOKEN` 或 `~/.netrc` 的 `gitee.com` 条目读取,脚本不会输出 Token。脚本只读取 `src-tauri/target/<triple>/release/bundle/` 下的 `.dmg`、`.exe`、`.msi` 和 `SHA256SUMS.txt`,上传成功后保留本地文件。远程已有同名同大小附件时会跳过;同名但大小不同时停止,避免静默覆盖已发布二进制。不要再使用仓库根 `release/`。
macOS App 位于:
```text
src-tauri/target/aarch64-apple-darwin/release/bundle/macos/OpsXShell.app
```
未指定 `--target` 时,产物在 `src-tauri/target/release/bundle/`。开发态 Dock 图标差异见 [架构说明](./architecture.md#开发态-dock-图标已知限制)。
## 临时 DMG
Tauri 调用 `hdiutil` 制作 DMG 时,可能在异常中断后留下:
```text
rw.<进程号>.OpsXShell_<版本>_aarch64.dmg
```
这些 `rw.*.dmg` 是可写临时镜像,不是发布包。确认没有正在运行的 Tauri/hdiutil 打包任务后可以删除。正式包没有 `rw.<数字>.` 前缀。
如果 DMG 脚本报错 `resource busy`,先检查是否正从临时挂载目录运行 OpsXShell:
```bash
hdiutil info
lsof +D /Volumes/<临时卷名>
```
先正常退出该 OpsXShell 进程,再卸载对应的临时卷并重新构建。不要在应用仍从卷内运行时直接删除 `rw.*.dmg`。
<a id="签名公证和自动更新未完成"></a>
## 签名、公证和自动更新
申请 Developer ID 的说明见 [Apple Developer ID](https://developer.apple.com/developer-id/);本仓签名配置与公证方式如下。
**当前状态(本机可签 macOS;这份 DMG 待公证;自动更新仍未做):**
- 仓库 `tauri.conf.json` 的 `signingIdentity` 仍是 `"-"`,没证书的环境还能打 ad-hoc 开发包。
- 本机若已导入 **Developer ID Application**,把身份写进 **gitignore** 的 `src-tauri/tauri.signing.conf.json`。`./scripts/build.sh` 发现该文件就会按 Developer ID 签名。
- DMG 必须走 Tauri bundler(`bundle.macOS.dmg`:窗口 720×440,应用图标与 Applications 分列)。不要用手搓 `hdiutil create`,Finder 图标会变小。
- `timestamp.apple.com:443` 不通时,构建会用 `scripts/macos-codesign-wrapper.sh` 走 HTTP 时间戳。可设 `CODESIGN_USE_SYSTEM_TIMESTAMP=1` 关闭。
- **每次新打的 DMG 都必须再公证并 staple**:`APPLE_ID='…' ./scripts/macos-notarize.sh`。已钉票会直接退出。`--wait` 卡住时脚本会超时后尝试 staple。
- 证书、CSR、私钥放本机文稿 `~/Documents/apple签名/`,不要进 git。Team 内其他 Mac 桌面应用可复用同一张 Developer ID。
- Windows Authenticode、minisign 自动更新仍未接线。没有凭据、或尚未 staple 时,不得在 README / 界面 / CI 上写「已签名」「已公证」「支持自动更新」。
门禁:
- `REQUIRE_SIGNING=true` 时,`scripts/release-preflight.mjs` 检查环境变量齐全,`scripts/verify-release.mjs` 拒绝 macOS ad-hoc、要求 Windows Authenticode `Valid`。
- 未设该变量时,`./scripts/build.sh` 仍可打开发包;若存在 `tauri.signing.conf.json` 则会签 Developer ID。
- CI `.github/workflows/tauri-release.yml` 在 Secrets 齐全时写入 `tauri.signing.conf.json`。本机用同一文件名即可,不要提交。
### 1. 凭据
| 平台 | 要什么 | 放到哪里 |
| -------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| macOS | Apple Developer Program;**Developer ID Application**(不是 Development);App 专用密码 | 本机钥匙串 + gitignore 的 `tauri.signing.conf.json`;CI Secrets:`APPLE_CERTIFICATE`(p12 的 base64)、`APPLE_CERTIFICATE_PASSWORD`、`APPLE_SIGNING_IDENTITY`、`APPLE_ID`、`APPLE_PASSWORD`、`APPLE_TEAM_ID` |
| Windows | 代码签名证书(OV/EV PFX) | `WINDOWS_CERTIFICATE_BASE64`、`WINDOWS_CERTIFICATE_PASSWORD` |
| 自动更新 | 单独的 **minisign** 密钥对,与 Apple/Authenticode 不是同一把 | 私钥只进 CI Secrets;**公钥**写进仓库的 updater 配置 |
`src-tauri/tauri.signing.conf.json` 必须进 `.gitignore`,不得提交。macOS 示例:
```json
{
"bundle": {
"macOS": {
"signingIdentity": "Developer ID Application: Logan Li (B8DVUUUF6W)"
}
}
}
```
### 2. 本地与 CI 同源签名
1. 不要改仓库里的 `tauri.conf.json` 默认 `"-"`。
2. `REQUIRE_SIGNING=true` 时 `verify-release.mjs` 必须失败于 ad-hoc。
3. 正式标签才设 `REQUIRE_SIGNING=true`。日常 PR 保持未签名。
### 3. macOS 公证(notarytool + staple)
签名成功 ≠ 用户能打开。必须公证并钉票。不要对同一份未变化的 DMG 连续多次 `submit`。
```bash
# 先弹出已挂载的安装盘,再:
APPLE_ID='开发者邮箱' ./scripts/macos-notarize.sh
# 可选:指定 DMG 路径
APPLE_ID='开发者邮箱' ./scripts/macos-notarize.sh src-tauri/target/aarch64-apple-darwin/release/bundle/dmg/OpsXShell_2.1.1_aarch64.dmg
```
脚本会:已 staple 则退出;否则先 staple;没有票据才要 App 专用密码并 `notarytool submit --wait --timeout 15m`,超时仍 staple。开发者后台网页没有这份 DMG 的进度页,用终端或:
```bash
xcrun notarytool info <submission-id> --apple-id "$APPLE_ID" --team-id "$APPLE_TEAM_ID"
xcrun notarytool log <submission-id> --apple-id "$APPLE_ID" --team-id "$APPLE_TEAM_ID"
```
验收(必须在**另一台未关 SIP、未关 Gatekeeper 的 Mac**上):
1. 下载 **staple 之后** 的 DMG,双击打开,拖到「应用程序」,不要右键「打开」、不要 `xattr -d`。
2. 能启动、能连 SSH,菜单里没有 DevTools。
3. `codesign --verify --deep --strict` 且 `codesign -dv` 含 `Notarization Ticket=stapled`,不是 `Signature=adhoc`。
没做完 staple 之前,对外说明不要写「已公证」。
### 4. Windows Authenticode
CI 已有 PFX 导入和 `Get-AuthenticodeSignature` 门禁。本机应对齐:
1. 导入 PFX,把 thumbprint 写入 `tauri.signing.conf.json`(`digestAlgorithm: sha256`,时间戳如 `http://timestamp.digicert.com`)。
2. 对 `.exe` / `.msi` 都验 `Status -eq Valid`,不要只签主程序。
3. 在一台干净 Windows 上安装:SmartScreen 不应再是「未知发布者」。EV 证书对 SmartScreen 更友好,OV 可能仍有一段时间的声誉积累——如实写,不要标「已通过 SmartScreen」。
### 5. 自动更新(有公钥、HTTPS、回滚才开)
架构规则已经写死:没有签名公钥、HTTPS 更新源和回滚策略,更新必须保持关闭。接入顺序:
1. 生成 minisign 密钥;公钥进 `tauri.conf.json` 的 `plugins.updater.pubkey`;私钥只进 Secrets。
2. 依赖 `tauri-plugin-updater`,capabilities 只开 `updater:allow-check` 与 `updater:allow-download-and-install`。
3. 构建打开 `createUpdaterArtifacts`,每个安装包旁有 `.sig`。
4. 发布时生成 `latest.json`(版本、各平台 URL、签名),放到 HTTPS(Gitee/GitHub Release 附件或静态镜像,建议双 endpoint)。
5. 前端「检查更新」:**公钥和 endpoint 未进正式包之前不要画这个入口**。
6. 回滚:保留上一份 `latest.json`;更新失败或健康检查失败时指回上一版本;文档写清如何撤回错误版本(覆盖 manifest,而不是删掉用户已装的包)。
7. `release.py` 除 `.dmg/.exe/.msi` 外还要上传 `.sig` 与 `latest.json`。
未完成前,设置页、欢迎页、菜单都不要出现「自动更新」或「已是最新」。
### 6. 发布工作流补齐(与签名独立,但同一闭环)
`.github/workflows/tauri-release.yml` 现在只 `upload-artifact`(约 90 天过期),`permissions.contents: read`,不能建 GitHub Release。有凭据并公证之后再改:
- tag 与现有 Gitee 流程对齐(不要两套版本号)。
- `contents: write` 后创建 Release,附 SHA256SUMS、安装包、`.sig`、`latest.json`。
- 公证步骤放在「Verify native package」之前或之中;未 staple 不得当正式附件。
GitHub Release 不是「已公证」的替代;没有 notary/Authenticode 时,Release 也只能标开发包。
## 产物验证
```bash
npm run release:preflight:mac
npm run release:verify:mac
npm run release:preflight:win
npm run release:verify:win
```
验证脚本检查架构、体积和安装包存在性。正式标签还会验证 macOS 签名/Gatekeeper 或 Windows Authenticode 签名。
安装包还必须包含 `ssh://` 协议注册。macOS 检查 `CFBundleURLTypes`,Windows 安装后检查当前用户 `Software\\Classes\\ssh` 并实际执行一次协议唤起。只验证配置文件不足以证明单实例事件交付正常。
## 发布前检查
1. `package.json`、`package-lock.json`、`src-tauri/Cargo.toml`、`Cargo.lock` 和 `tauri.conf.json` 版本一致。
2. `npm run acceptance:full` 通过。
3. 目标平台构建和签名通过。
4. 安装后完成输入框复制/粘贴、SFTP 双向拖拽、普通区域右键菜单、SSH/SFTP/代理/跳板和 `ssh://` 单实例唤起验收。
5. 大模型新配置必须先测试后保存;Agent CLI 必须固定当前 SSH 目标,每条命令确认后才执行,critical 命令不得放行。
6. 检查安装包尺寸,没有上传 `rw.*.dmg`、日志、测试密钥或用户数据。
7. 保留上一稳定版本及回滚说明。