跳转至

发布 VoiceStudio

Electron 是唯一受维护的桌面发行版。Tauri 止步于 v0.5.3;其签名的更新器 feed 和安装程序是不可变的兼容性资产。

共享商店输入与平台交接在 STORE-PUBLISHING.md 中定义。Microsoft Store、Snap Store 和 Mac App Store 在各自完成首次经核验的生产发布之前,均不属于受维护的发布渠道。

Snap Store 打包通过 .github/workflows/snap-store.yml 执行。每个 v* 标签都会构建严格(strict)的 Snap,并使用 snap-store 环境中的 SNAPCRAFT_STORE_CREDENTIALS 将其上传到 Store 的 candidate 渠道。Stable 渠道绝不自动发布:在运行手册(runbook)检查通过后,由仓库所有者以 promote_revision 派发工作流。请遵循 Snap Store 运行手册 进行限制(confinement)检查、候选版本验证、证据与回滚。在其 stable 列表经独立安装验证之前,请勿将 Snap 添加到下方受维护渠道表中。

Microsoft Store EXE 准备使用由所有者派发的 microsoft-store.yml 工作流和 Microsoft Store 运行手册。它构建一个独立的 Store 安装程序,而不改变直接下载产物。暂存其不可变版本化 URL 需要所有者批准;Partner Center 的提交和发布仍是所有者的手动操作。

Mac App Store 准备使用手动、私有的 mac-app-store.yml 工作流和 Mac App Store 运行手册。在捆绑运行时合规门禁(bundled-runtime compliance gate)未解决之前,该工作流无法构建,并且永远不会上传到 App Store Connect。MAS 软件包使用 App Store 更新;直接分发的 macOS 软件包保留现有应用更新器与 Developer ID 公证(notarization)流程。

凭据

Electron 发布使用以下 GitHub Actions 机密(secrets):

  • ELECTRON_MACOS_CSC_LINK 和 ELECTRON_MACOS_CSC_KEY_PASSWORD,用于 Developer ID Application .p12 证书及其密码。
  • ELECTRON_MAC_APP_STORE_CSC_LINK、ELECTRON_MAC_APP_STORE_CSC_KEY_PASSWORD、ELECTRON_MAC_APP_STORE_INSTALLER_LINK、ELECTRON_MAC_APP_STORE_INSTALLER_KEY_PASSWORD 和 ELECTRON_MAC_APP_STORE_PROVISIONING_PROFILE,用于私有 MAS 打包。这些与 Developer ID 凭据相互独立,且不授权上传或发布。
  • APPLE_ID、APPLE_APP_SPECIFIC_PASSWORD(不是 Apple ID 登录密码)和 APPLE_TEAM_ID,用于 macOS 公证与装订(stapling)。
  • ELECTRON_WINDOWS_CSC_LINK 和 ELECTRON_WINDOWS_CSC_KEY_PASSWORD,用于 Windows Authenticode 签名。macOS 证书无法签署 Windows 应用。
  • app-store-connect 环境中的 APP_STORE_CONNECT_KEY_ID、APP_STORE_CONNECT_ISSUER_ID 和 APP_STORE_CONNECT_API_KEY,用于 iOS TestFlight 上传(mobile.md)。TestFlight 不是发布渠道。
  • google-play 环境中的 ANDROID_UPLOAD_KEYSTORE、ANDROID_UPLOAD_KEYSTORE_PASSWORD、ANDROID_UPLOAD_KEY_ALIAS、ANDROID_UPLOAD_KEY_PASSWORD 和 GOOGLE_PLAY_SERVICE_ACCOUNT_JSON,用于 Play 内部测试上传(mobile.md)。Play 内部测试同样不是发布渠道。
  • 仓库的 GITHUB_TOKEN,用于创建草稿和上传资产。
  • DOCKERHUB_USERNAME 和 DOCKERHUB_TOKEN,用于 Docker Hub 镜像。
  • POSTHOG_PROJECT_TOKEN(机密)和 POSTHOG_HOST(仓库变量)会作为分析令牌与主机内置到打包构建中。它们覆盖仓库内的可发布令牌;即使用户未在首次运行同意提示中选择“是”,分析功能也不会发送任何数据。

归档的 TAURI_SIGNING_PRIVATE_KEY* 机密仅被保留,以便最终 Tauri 版本可被审计。Tauri 发布工作流已移除;每个新版本都通过 electron-release.yml 发布。TAURI_SUNSET_TAG 必须继续标识 v0.5.3,以便 Electron 发布可以原样复制最终签名的 latest.json 和 latest-user.json feed。这些 feed 必须始终指向不可变的 v0.5.3 Tauri 资产;旧的 Tauri 客户端绝不能收到 Electron 安装程序。

版本管理

根目录的 package.json 是受维护应用版本的单一事实来源。Electron Builder 直接读取它。请保持以下活跃镜像一致:

  • pyproject.toml
  • backend/core/version.py(_FALLBACK_VERSION)

归档的 Tauri 清单冻结在其最终发布版本。不要提升或重新构建它们。tests/test_app_version.py 强制执行活跃版本契约。版本提升是手动的,需要所有者批准。在请求提升之前,main 可保持在最新发布版本。发布时,应同时提升规范版本和两个受维护镜像,且只有在验证通过后才能为该确切版本打标签。

打标签之前

  1. 仅在所有必需的 CI 和审查均为绿色后合并。
  2. 运行仅产物的 Electron 预演 .github/workflows/electron-build.yml,并检查全部五个输出:Linux x64、Windows x64、Windows ARM64、macOS arm64、macOS x64。Windows ARM64 是实验性的:某条任务失败不会阻塞其他四条。该工作流首先检查设置界面,然后在 Linux、Windows 和 Apple Silicon 上于单独的临时配置文件中安装并启动托管的 Python 运行时。Intel Mac 保留其现有仅 UI/远程契约下的打包/设置检查。本地运行时测试会下载运行时软件包、保持模型下载禁用、验证实时后端连接与干净关闭,并移除测试配置文件。仅检查设置界面不能证明运行时安装有效。
  3. 在受影响的平台上验证打包启动和托管后端的启动。
  4. 将 changelog.d/ 片段合并到 ## [Unreleased](scripts/release.py 会完成此操作并删除它们;手动方式是:uv run python scripts/changelog_fragments.py),然后将 CHANGELOG.md 中的 ## [Unreleased] 重命名为 ## [X.Y.Z] — YYYY-MM-DD。以最大的用户可见变更开头,将 Highlights 控制在 3–5 条要点,在相关时包含迁移步骤和真实截图,并根据标签对比和所包含的 PR 核实人类贡献者与 bug 报告者。发布工作流会自行向 GitHub Release 说明添加 ### Contributors 区块:包括自最新稳定发布以来所有提交过代码的人,以及该时间段内合并的 PR 作者(按提交数排序;机器人、AI 代理和未关联的占位符绝不列出)。它不会查找 bug 报告者,并且当变更日志部分已有 ### Contributors 标题时会跳过该区块,因此当你希望为角色或报告者署名时,请手动编写一个。
  5. 运行 uv run pytest tests/test_app_version.py tests/test_changelog_style.py -q。
  6. 审计贡献者协议:git fetch origin cla-signatures,然后运行 GH_TOKEN=... python scripts/cla_audit.py。对其中列出的每位未签署贡献者进行跟进,请他们签署 .github/CLA-1.0.md(或由其雇主签署 Corporate CLA)。此步骤在 CLA 工作流和 scripts/cla_audit.py 进入 main 后适用。如果 Git 无法读取任何文件的历史记录,审计将中止;请恢复缺失对象的访问权限并重新运行,然后再将其贡献者列表视为完整。
  7. 确认版本与预期标签一致。

构建发布草稿

git tag vX.Y.Z
git push origin vX.Y.Z

该标签会触发 .github/workflows/electron-release.yml。它构建相同的五个构建目标(失败的实验性 Windows ARM64 环节不会阻塞其他环节,且其 feed 仅在已发布时才会被检查),验证打包后的启动和更新器元数据,创建或更新草稿,并上传:

  • 名为 VoiceStudio-<version>-<os>-<arch>.<ext> 的 Electron 安装程序(v0.5.7-beta.3 之前的版本使用 VoiceStudio-Electron-*),以及各平台特定的 electron-stable-*.yml feed;feed 文件名从不改变,每个 feed 都指向自己的安装程序,因此旧客户端可以跨越这次重命名完成更新
  • 各平台的 store-artifact-manifest-*.json 溯源记录
  • SHA256SUMS.txt
  • 编写的 CHANGELOG 发布说明
  • 为旧客户端保留的最终 Tauri 更新器 feed 的不可变副本

标签推送永远不会发布 Electron GitHub Release,也不会构建 Docker 镜像(上面的 Snap 工作流仅将标签上传到 candidate)。请先检查草稿和已下载的安装程序。

如果打标签后工作流本身需要修复,请保持标签不可变。在 main 上合并并验证工作流修复,然后从 main 以 release_tag=vX.Y.Z 触发 electron-release.yml。每次构建仍会检出确切的标签;只有工作流定义来自 main。

发布

以 publish=true 为确切标签触发 electron-release.yml。默认情况下需要签名和公证。所有者可以显式设置 allow_unsigned=true。未经所有者决定,切勿选择该例外。每次构建(无论是否发布)都会验证每个平台环节实际交付的内容,发布说明以基于这些检查生成的 Installer signing(安装程序签名)表格开头(✅ 已签名并公证,⚠️ 未签名或 ad hoc,同时标明用户会看到什么)。签名无效的 macOS 捆绑包始终会失败;已配置签名凭据的平台,如果其安装程序未使用这些凭据签名,也会失败,即使设置了 allow_unsigned=true。

所有者可以委托此权限:UNSIGNED_RELEASE_PUBLISHERS Actions 变量中列出的账户(逗号分隔的登录名,Settings → Secrets and variables → Actions → Variables;只有管理员可以编辑)也可以触发或重新运行未签名发布。删除某个登录名即撤销其权限;变量为空表示仅所有者可操作。

发布 GitHub Release 会触发该标签的 Docker 工作流。Docker 发布标签永远不会由标签推送构建,且 :latest 只跟踪 main;发布不会移动它。

发布后,请验证每个受维护的渠道:

渠道 工作流 验证
GitHub Release electron-release.yml Linux x64、Windows x64(在其环节通过时还包括 ARM64)、macOS arm64 和 x64 安装程序、更新器 feed、校验和、CHANGELOG 说明、保留的 v0.5.3 Tauri feed
手机下载 electron-release.yml → mobile-release.yml 来自同一标签的已签名 Android APK/AAB 和未签名 iOS 侧载 IPA
GHCR CUDA docker.yml(已发布) :X.Y.Z、:X.Y、:stable 清单
GHCR ROCm docker.yml(已发布) :X.Y.Z-rocm、:X.Y-rocm、:stable-rocm 清单
GHCR CUDA arm64 docker.yml(已发布) :X.Y.Z-cuda-arm64、:X.Y-cuda-arm64、:stable-cuda-arm64 清单(linux/arm64)
Docker Hub docker.yml(已发布) 对应的 CUDA/ROCm/CUDA arm64 标签
Docker Hub 概览 docker.yml 阅读 Update Docker Hub description 步骤日志;该步骤在遇到 403 后仍可能继续
滚动容器 仅 main 推送时的 docker.yml :latest、:main、:rocm 和 :cuda-arm64 的时间戳会更新;发布永远不会更新它们

渠道缺失属于发布缺陷。没有 RC 标签;唯一的预发布版本是选择加入的 Beta 渠道(见下文)。Beta 之间的预览版本是来自 main 的构建(electron-build.yml 产物或 Docker :latest),绝不是侧分支。不维护任何 Tauri 构建。

发布助手

make release(或 uv run python scripts/release.py)可以从任何分支发布一个版本。它会显示上一个版本和 main 上的版本,然后询问 stable 或 beta,并建议新标签(v0.5.7,或 beta 版本的 v0.5.7-beta.1、-beta.2……;beta 后缀始终是 -beta.N)。如果 package.json 已经指定了即将发布的版本,则使用该版本;如果该版本已被打标签,则跳到下一个补丁版本。选项是方向键菜单(上/下或 j/k,Enter 确认;Esc 取消),在没有终端时会回退到手动输入答案。然后它会询问 draft 或 publish,打印计划(包括切换到 main 并快进它),并且只有在你输入标签名称后才会继续。

  • 需要运行两次,因为 main 只能通过拉取请求更改。 第一次运行会同步提升三个版本文件,运行 uv lock,从 [Unreleased] 生成新的 CHANGELOG.md 章节(稳定版会重命名它;beta 会复制它),运行版本守卫,在 release/vX.Y.Z[-beta.N] 上提交并打开其拉取请求。合并后,再次以相同版本运行助手:main 已经包含该版本,因此它不会更改任何内容,只会打标签。
  • Draft(草稿): 标签推送会构建草稿发布;不会公开任何内容。
  • Publish(发布): 先推送标签,然后以 publish=true 触发 electron-release.yml。它会列出哪些平台具有签名密钥(macOS 需要 Developer ID 和公证配置,Windows 需要 ELECTRON_WINDOWS_CSC_LINK 及其密码),对于任何没有这些密钥的平台,除非你明确接受以未签名方式交付该平台,否则会停止。dispatch 的 allow_unsigned 标志只接受没有凭据的平台:已配置签名的平台仍必须生成已签名的安装程序,并且发布说明中的 Installer signing 表格会显示每个平台的实测结果。
  • --publish vX.Y.Z[-beta.N](make release-publish TAG=…)稍后发布现有的草稿构建。
  • --dry-run(make release-dry)打印计划且不更改任何内容。

它不能替代下面的事后发布验证;每次都要执行。

移动应用

手机应用与桌面端一样,从相同的 vX.Y.Z 或 vX.Y.Z-beta.N 标签构建。electron-release.yml 调用 mobile-release.yml,并在创建或发布 GitHub Release 之前,等待签名的 Android APK/AAB 和未签名的 iOS 侧载 IPA。所有下载共享其版本和 SHA256SUMS.txt。手机构建失败会阻止发布;请勿手动发布仅包含桌面端的版本。发布后,Docker CUDA、ROCm 和 CUDA arm64 构建会针对同一标签被触发;在宣布发布完成之前,请确认它们已完成。

App Store 签名和商店推广仍与 GitHub 下载分开。未签名的 IPA 需要使用用户自己的 Apple ID 签名才能侧载;它不是 TestFlight 构建。不能因为 Apple 证书的可用性而省略此工件。手动运行 mobile-release 时,可以将 release_ref 设置为现有标签,并将 signed_ios=false,以便在不更改标签的情况下恢复手机工件。旧版 mobile-v* 标签仅用于支持历史性的独立构建。

Beta 频道

Beta 是从 main 切出的第二个发布频道,供在 设置 → 更新 → 更新频道(用户指南)中选择加入的测试人员使用。它没有观察期、没有侧分支、也没有 cherry-pick。

Stable Beta
标签和 package.json 版本 vX.Y.Z vX.Y.Z-beta.N
GitHub Release 完整发布,成为 latest 预发布,永远不会是 latest
更新源 electron-stable-<os>-<arch> electron-beta-<os>-<arch>
Docker :X.Y.Z、:X.Y、:stable(以及 -rocm、-cuda-arm64) :beta、:X.Y.Z-beta.N(以及 -rocm、-cuda-arm64)
Snap / Microsoft Store / Mac App Store 是 否(仅 Stable)
Tauri 停用更新源 复制进发布 不复制

切出 Beta:经所有者批准版本号后,将 package.json、pyproject.toml 和 _FALLBACK_VERSION 一并提升到 X.Y.Z-beta.N,运行 uv lock(它会记录 PEP 440 形式的 X.Y.ZbN;lockstep 测试比较的是版本,而不是字符串),在 CHANGELOG.md 中添加 ## [X.Y.Z-beta.N] 部分,从 main 打标签,然后运行带 publish 参数的 electron-release.yml。该工作流从版本中推导频道(prepare_electron_release.py --print-channel),将其写入每个平台的更新源,使用 --prerelease --latest=false 发布,并以 promote_stable=false 触发 Docker 构建。任何其他预发布形式(-rc、-alpha)都会被拒绝。

提升到 Stable 是从 main 分支进行的常规 vX.Y.Z 发布。Beta 用户会自动升级到它,因为更新器会比较 semver(X.Y.Z-beta.N 排序低于 X.Y.Z)。上方的验证表适用于 Stable;对于 beta,还需确认 GitHub Release 被标记为 Pre-release,releases/latest 仍指向最后一个稳定版,且 :stable 和 :X.Y 没有移动。

软件包要求

Linux 软件包必须在 resources/native/lib 下携带原生辅助程序的非 glibc 库。打包检查会拒绝缺失的库或由宿主机解析的库。AppImage 使用静态运行时,不需要宿主机上的 libfuse2;请在干净的宿主机上测试下载的镜像,因为构建运行器上的库可能会掩盖重定位错误。

除 Intel macOS 外,所有直接的桌面安装程序都在 resources/bin/ 下附带 OmniVoice GGUF 运行时(#2780)。electron-release.yml 和打包演练调用 build-omnivoice-tts.yml(desktop: true),并将每个平台的工件暂存到 electron/build/omnivoice-tts/;Windows ARM64 附带 x64 构建。签名会重写二进制文件,因此构建会在签名后刷新 bin/checksums.sha256(electron/omnivoice-tts-bundle.mjs;在 macOS 上需在 bundle 密封之前完成)。如果安装程序缺少运行时、清单不再与发布的字节匹配,或(macOS)运行时未签名,packaging-contract.mjs 会使发布失败。运行时构建失败会导致发布失败;没有不带它发布的回退方案。

Linux 发布版 AppImage 包含 gh-releases-zsync 更新信息,以及一个带版本号的 .AppImage.zsync 资源,供 AppImageUpdate/AppImageLauncher 使用。发布版镜像跟随最新的稳定版 GitHub Release。构建使用 readelf 和 zsyncmake(Ubuntu 软件包 zsync)在重新生成 electron-updater 的 blockmap 和校验和之前嵌入这些信息;现有的应用内更新器仍使用其独立的频道清单。v0.5.7-beta.3 及更早的镜像会查找 VoiceStudio-Electron-*-linux-x64.AppImage.zsync,因此 AppImageUpdate 无法将它们移动到更名后的发布;它们通过应用内更新器进行更新。请针对最终下载的工件(而不是发布前构建)验证两条更新路径。

Electron 更新清单使用平台和架构频道。确认每个清单都指定了已上传的安装程序、报告了带标签的版本,并与工件字节匹配。在发布之前,至少执行一次已安装版本的更新跳转。

签名的 Electron macOS 发布需要付费的 Apple Developer 身份和上述机密。在没有凭据的情况下,草稿构建可以保持未签名;常规发布会拒绝缺失的签名/公证凭据,并同时验证签名和装订票。不要仅仅为了关闭签名报告而接受 allow_unsigned。Tauri 签名密钥不能用于签名 Electron 软件包。现有的工件名称、应用 ID、数据路径和更新器频道都属于兼容性契约。

重试与回滚

如有可能,请在同一个工作流运行中重新运行失败的作业。发布作业只替换当前草稿中的 Electron 资源。它拒绝覆盖已发布的 Release。

客户端只接受更高的版本。要回滚,请修复或还原代码,经所有者批准选择一个更高的补丁版本,进行测试,然后发布该版本。不要移动或重新创建现有标签。

合并队列(维护者)

在启用“要求分支保持最新”的情况下,每次合并到 main 都会让所有其他打开的 PR 落后,并且每个 PR 都需要更新并运行一次完整的 CI。合并队列取代了这种方式:通过自身检查的 PR 会加入队列,GitHub 会构建一个由 main 加上已排队 PR 组成的临时提交,对其运行一次 CI,然后合并。

工作流已就绪:ci.yml 在 merge_group 上运行,而 merge-queue-checks.yml 在队列的提交上报告 PR 级别的必需检查(“CLA gate”、“Commit identities”、“CLA”、“Commit identity policy”),因为这些检查无法在那里运行。tests/test_merge_queue.py 使这些名称与规则集的必需列表保持一致;更改时请同步更新两者。

要启用它(Settings → Rules → Rulesets → main):

  1. 添加 Require merge queue:方法 Merge commit,构建并发数 1–3,最小分组大小 1,等待时间 0–5 分钟,状态检查超时 60 分钟。
  2. 在 Require status checks to pass 下,取消勾选 Require branches to be up to date before merging;队列使这一项变得不再必要。
  3. 使用 Merge when ready(队列按钮)合并 PR,而不是 Merge。一旦队列启用,gh pr merge N --merge 就会加入队列。

先在一个 PR 上试试。如果某个已加入队列的 PR 卡住,说明规则集中的必需检查名称不再与队列提交所报告的任务或状态匹配:将规则集与 tests/test_merge_queue.py 进行比对。

本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 om/VoiceStudio