Files

14 KiB
Raw Permalink Blame History

发布指南

当前你要做的操作(v2.1.1 macOS)

本机已打出 Tauri 窗口布局 的 Developer ID 签名 DMG(不是手搓 hdiutil,Finder 里是大图标 + 拖到 Applications):

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 专用密码发到聊天。终端执行其一:
APPLE_ID='1024way@gmail.com' ./scripts/macos-notarize.sh
# 或
bash ~/Documents/apple签名/公证OpsXShell.sh
  1. 弹出框里填 App 专用密码(appleid.apple.com → 登录与安全 → App 专用密码,标签如 app notary)。
  2. 脚本会 notarytool submit --wait --timeout 15m。第一次可能很久;--wait 卡住时等它超时,再跑同一条命令——已 Accepted 的不会重新上传,只会 staple。不要对同一份未变化的 DMG 连续第三次 submit。
  3. 成功标志:xcrun stapler validate 通过,且 codesign -dv 能看到钉票。把 这一份 DMG 发出去(尽量不要走微信,隔离属性会更烦)。
  4. 改代码后再 ./scripts/build.sh mac-arm64 --quick 会得到新哈希,必须重新公证。

Windows Authenticode、自动更新仍未做,不要对外写「已公证」直到第 6 步完成。

构建命令

# 推荐:当前平台完整门禁、打包、验证和 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 标准目录:

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

首次准备:

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 后执行:

# 只检查附件,不访问 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 位于:

src-tauri/target/aarch64-apple-darwin/release/bundle/macos/OpsXShell.app

未指定 --target 时,产物在 src-tauri/target/release/bundle/。开发态 Dock 图标差异见 架构说明。

临时 DMG

Tauri 调用 hdiutil 制作 DMG 时,可能在异常中断后留下:

rw.<进程号>.OpsXShell_<版本>_aarch64.dmg

这些 rw.*.dmg 是可写临时镜像,不是发布包。确认没有正在运行的 Tauri/hdiutil 打包任务后可以删除。正式包没有 rw.<数字>. 前缀。

如果 DMG 脚本报错 resource busy,先检查是否正从临时挂载目录运行 OpsXShell:

hdiutil info
lsof +D /Volumes/<临时卷名>

先正常退出该 OpsXShell 进程,再卸载对应的临时卷并重新构建。不要在应用仍从卷内运行时直接删除 rw.*.dmg。

签名、公证和自动更新

申请 Developer ID 的说明见 Apple 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 示例:

{
  "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。

# 先弹出已挂载的安装盘,再:
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 的进度页,用终端或:

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 也只能标开发包。

产物验证

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. 保留上一稳定版本及回滚说明。