Wave Terminal 发布流程全解:从版本号管理、CI 构建到多渠道分发与自动更新
2026/9/13 11:29:05 网站建设 项目流程

Wave Terminal 发布流程全解:从版本号管理、CI 构建到多渠道分发与自动更新

【免费下载链接】wavetermAn open-source, AI-integrated, cross-platform terminal for seamless workflows项目地址: https://gitcode.com/GitHub_Trending/wa/waveterm

本篇技术指南以 Wave Terminal(Wave)开源仓库的 RELEASES.md 为骨架,完整还原该项目从"版本号提升"到"用户终端自动更新"的整套发布工程体系:SemVer 版本管理、GitHub Actions 三个核心工作流、electron-builder 打包配置、S3 产物中转、包管理器分发以及基于 electron-updater 的双通道自动更新。读完本文,你将能复现 Wave 的发布流水线,并理解其每一个环节在仓库源码中的具体落地实现,可直接迁移到自己的 Electron + Go 混合架构项目中。

发布流水线总览

Wave Terminal 的发布过程由一条高度自动化的流水线驱动,整体分为五个阶段:

  1. 版本提升(Bump Version):手动触发工作流,按语义化版本规则修改package.json中的版本号,并打上对应的 Git tag。
  2. 构建产物(Build Helper):tag 推送后自动触发跨平台构建矩阵,编译 Go 后端与 Electron 前端,签名、公证并生成各平台安装包。
  3. 草稿发布(Draft Release):构建完成后自动在 GitHub 创建草稿 Release,上传全部产物。
  4. 测试与发布(Publish):维护者下载草稿产物本地测试,通过后编辑 Release 说明并点击 Publish。
  5. 分发(Distribution):发布动作触发 Publish Release 工作流,将产物从 staging S3 桶拷贝到 release 桶,同时向 Homebrew、WinGet、Chocolatey、Snap 等渠道推送。

三个阶段之间通过 Git tag 与 GitHub Release 事件解耦,任何一步失败都可以在对应环节单独重试,这正是这套流水线稳定性的关键设计。

版本号体系:SemVer 与-beta预发布标识

Wave 的版本号遵循语义化版本(SemVer)规范,形如0.14.50.11.1-beta.0。当前仓库 package.json 中记录的版本为0.14.5,但版本号只是入口,真正的版本计算逻辑集中在 version.cjs 中。

version.cjs 的版本计算规则

version.cjs 是一个可独立执行的 Node 脚本,其核心逻辑位于 version.cjs#L33-L67:

  • 无参数执行:直接输出当前版本,供构建脚本读取。
  • none:不改变主版本号。若同时传入0/false且当前版本带-beta后缀,则执行semver.inc(VERSION, "patch")0.11.1-beta.1这类版本提升为0.11.1(先去掉预发布标识,再补一次 patch 递增,避免与已存在的正式版冲突,见 version.cjs#L44-L46)。
  • patch/minor/major:调用semver.inc递增对应段位;当标记为预发布时,会自动加上pre前缀与beta标识,得到0.11.2-beta.0这类版本。
  • true/1:仅递增预发布号,例如0.11.1-beta.00.11.1-beta.1,对应 version.cjs#L63 的semver.inc(VERSION, "prerelease", null, "beta")

脚本执行后会直接改写package.json并输出新版本号。在本地你可以通过 Task 命令复现这一逻辑:

# 输出当前版本 task version # 将版本提升为下一个 patch 预发布版(如 0.14.5 -> 0.14.6-beta.0) task version -- patch true # 把预发布版提升为正式版(如 0.14.6-beta.0 -> 0.14.6) task version -- none false

Bump Version 工作流的两个输入

.github/workflows/bump-version.yml 定义了名为Bump Version的手动触发(workflow_dispatch)工作流,提供两个输入:

输入项可选值默认值含义
SemVer Bumpnone/patch/minor/majornone按语义化版本规则递增主版本号
Is Prereleasetrue/falsetrue是否作为预发布版;为true时追加-beta.X后缀,为false时移除该后缀

工作流内部通过task version -- <bump> <is-prerelease>计算新版本(见 bump-version.yml#L60-L63),随后使用 GitHub App 签发的 Token 调用 GitHub API 将package.json的版本变更直接提交到受保护分支,并创建v<新版本>的 Git tag。注意这里的提交与 tag 是签名的,且绕过了分支保护——这是通过actions/create-github-app-token换取专用机器人身份实现的(见 bump-version.yml#L31-L39)。

三种典型发布场景

结合 RELEASES.md 的官方说明,版本提升的组合策略如下:

  • 正式版发布后开启新一轮预发布:SemVer Bump 设为预期的版本段位(patch/minor/major),Is Prerelease 设为true。例如从0.14.5得到0.15.0-beta.0
  • 在同一版本号下推进预发布:SemVer Bump 设为none,Is Prerelease 设为true,将0.11.1-beta.0递增为0.11.1-beta.1
  • 将预发布版提升为正式版:SemVer Bump 设为none,Is Prerelease 设为false,移除-beta.X后缀。

Build Helper:跨平台构建、签名与产物上传

tag 创建后,Build Helper工作流(.github/workflows/build-helper.yml)会自动启动。它监听v[0-9]+.[0-9]+.[0-9]+*格式的 tag 推送(见 build-helper.yml#L8-L10),并同时支持手动触发。

平台构建矩阵

工作流使用 GitHub Actions matrix 策略并行构建四个目标(见 build-helper.yml#L20-L33):

平台Runner产物
darwinmacos-latestmacOS arm64 + x64(zip、dmg)
linux (x64)ubuntu-latestdeb、rpm、snap、AppImage、pacman、zip
linux (arm64)ubuntu-24.04-arm上述 Linux 格式的 arm64 版本
windowswindows-latestnsis、msi、zip

构建环境使用 Go 1.25.6 与 Node 22(见 build-helper.yml#L13-L14),Linux 与 Windows 平台还会额外安装 Zig 编译器用于 CGO 静态链接。

构建链:task package

每个平台最终执行的构建入口是 Taskfile.yml 中的package任务(见 Taskfile.yml#L124-L132),其内部执行链为:

clean → npm:install → build:backend → build:tsunamiscaffold → npm run build:prod → npm exec electron-builder
  • build:backend:编译两个 Go 组件——wavesrv(终端后端服务)与wsh(Wave Shell 客户端)。wsh会为 darwin/linux/windows 三平台共 8 种架构组合并行产出(见 Taskfile.yml#L298-L332)。
  • build:tsunamiscaffold:构建 Tsunami 前端脚手架并复制到dist/tsunamiscaffold
  • npm run build:prod:通过 electron-vite 以生产模式构建 Electron 主进程与前端代码。
  • npm exec electron-builder:调用 electron-builder.config.cjs 生成各平台可分发安装包。

签名与公证

RELEASES.md 明确指出,Build Helper 会对 macOS 包进行签名与公证(notarization),并对 Windows 包进行代码签名。具体到工作流实现:

  • macOS:通过CSC_LINKAPPLE_IDAPPLE_APP_SPECIFIC_PASSWORDAPPLE_TEAM_ID等环境变量驱动 electron-builder 完成签名与公证,且由于公证可能偶发失败,Darwin 构建被包在nick-fields/retry中最多重试 3 次(见 build-helper.yml#L135-L150)。
  • Windows:使用 DigiCert Signing Manager(Keylocker)方案,先安装 Keylocker KSP 工具链,再通过SM_CODE_SIGNING_CERT_SHA1_HASH等变量配置签名(见 build-helper.yml#L98-L125)。这一变量同时被 electron-builder.config.cjs#L6-L7 读取,决定是否启用 Windows 签名。

产物去向

构建完成后,产物被上传到两个位置(见 build-helper.yml#L161-L173):

  1. S3 staging 桶s3://waveterm-github-artifacts/staging-w2/<version>,对应 Taskfile.yml 中ARTIFACTS_BUCKET: waveterm-github-artifacts/staging-w2(见 Taskfile.yml#L13)与artifacts:upload任务。
  2. GitHub Actions Artifact:作为create-release作业的输入,用于组装草稿 Release。

create-release作业随后将所有平台的 zip、dmg、exe、msi、rpm、deb、pacman、snap、AppImage 等产物挂载到一个draft(草稿)Release上,并依据 tag 是否包含-beta标记该 Release 为预发布(见 build-helper.yml#L192-L198)。

本地复现打包:task package与构建前置条件

如果你希望在本地复现 Build Helper 的打包过程,可以参考 BUILD.md 准备环境并执行:

# 首次克隆后安装依赖(npm install + go mod tidy + docs 依赖) task init # 生产构建并打包,产物输出到 make/ 目录 task package

需要注意的本地构建约束:

  • Linux:需要安装zip、Zig 编译器;若要在 ARM64 上打包,还需通过 Gem 安装fpm(因为 electron-builder 内置的 FPM 版本不含 Linux ARM 目标)。对应命令为USE_SYSTEM_FPM=1 task package
  • Windows:同样需要 Zig 编译器用于 CGO 静态链接。
  • NodeJS:要求 22 LTS;Task 通过arduino/setup-task在 CI 中安装,本地则需自行安装 Task 命令行工具。

package任务在 CI 中还额外设置了USE_SYSTEM_FPM: trueSNAPCRAFT_BUILD_ENVIRONMENT: host环境变量,以使用系统级 FPM 与宿主环境构建 Snap(见 build-helper.yml#L128-L133)。

测试新版本:草稿 Release 与本地下载

Build Helper 完成时会在 GitHub Releases 页面生成草稿,维护者可以据此下载产物进行本地测试。除从网页下载外,仓库还提供了 Task 任务从 S3 staging 桶拉取全部产物:

# 下载指定版本的产物到 artifacts/<version>/ task artifacts:download:<version> -- --profile <aws-profile>

该任务需要本地配置一个对 S3 桶具有读写权限的 AWS CLI profile。其实现位于 Taskfile.yml#L391-L399,会先将本地artifacts/<version>目录清空,再执行aws s3 cp s3://waveterm-github-artifacts/staging-w2/<version>/ ... --recursive

发布正式版本:Publish Release 工作流

测试通过后,维护者需要:

  1. 在 Releases 页面找到对应版本的草稿;
  2. 点击右上角铅笔图标编辑草稿,完善 Release 说明与变更日志;
  3. 滚动到底部点击Publish发布。

Publish 动作会触发 .github/workflows/publish-release.yml(监听release: [published]事件),此后一切自动化、无需人工介入:

  • 发布到 release 桶:执行task artifacts:publish:<version>,将产物从staging-w2/<version>拷贝到dl.waveterm.dev/releases-w2(RELEASES_BUCKET,见 Taskfile.yml#L14)。release 桶正是 electron-updater 与各包管理器读取的官方发布源。
  • 发布 Snap:分别对 amd64 与 arm64 的.snap文件执行snapcraft upload。预发布版仅发布到beta频道,正式版同时发布到betastable频道(频道判定逻辑见 Taskfile.yml#L421)。
  • 提交 WinGet 更新:仅对正式版执行wingetcreate update CommandLine.Wave,向 WinGet 仓库提交版本升级 PR(见 publish-release.yml#L77-L96)。

自动更新机制:electron-updater 与双频道发布

Wave 使用electron-updater,其默认依赖已声明在 package.json 的electron-updater条目中。

更新元数据与检查频率

RELEASES.md 说明:每次发布都会生成指向当前频道最新版本的YAML 文件(包含文件大小与校验和),应用每小时检查一次这些文件。源码实现印证了这一点:

  • electron-builder.config.cjs#L17 设置generateUpdatesFilesForAllChannels: true,为每个频道生成更新元数据文件。
  • electron-builder.config.cjs#L120-L123 将publish.provider配置为generic,URL 指向https://dl.waveterm.dev/releases-w2——即 release S3 桶。
  • emain/updater.ts#L126-L134 中,start()每隔 600000ms(10 分钟)检查一次定时器是否到期,真正执行检查时再校验autoupdate:intervalms(默认 3600000ms,即 1 小时)是否已过去,兼顾了系统休眠导致定时器失效的场景。

更新渠道与防降级

Wave 的 beta / stable 双渠道完全由版本号驱动:带-beta.X后缀的版本进入beta渠道,正式版本进入latest渠道。渠道解析逻辑位于 emain/updater.ts#L19-L36:

  1. 读取应用资源目录下的app-update.yml(由 electron-builder 在打包时生成)获取内置渠道;
  2. 读取用户配置autoupdate:channel
  3. 若用户尚未设置该配置,或用户配置为latest但实际安装的是beta构建,则自动将配置写为beta——这是为了防止用户从 beta 版降级回稳定版而丢失数据。

用户侧配置项

RELEASES.md 提到用户可通过autoupdate:channel设置选择更新渠道。完整的相关配置项记录在 docs/docs/config.mdx 的配置总表中:

配置项类型默认值说明
autoupdate:enabledbooltrue是否启用更新检查(需重启应用生效)
autoupdate:intervalmsfloat643600000两次更新检查之间的等待毫秒数(需重启应用生效)
autoupdate:installonquitbooltrue退出时是否自动安装已下载的更新(需重启应用生效)
autoupdate:channelstring"latest"更新渠道:"latest"(稳定版)或"beta"(更新更频繁)

emain/updater.ts的构造函数完整消费了这四个配置:intervalms控制检查频率、autoCheckEnabled控制总开关、autoInstallOnAppQuit传给autoUpdater(见 emain/updater.ts#L47-L62)。下载完成后应用会弹出系统通知,点击后弹出"Restart / Later"对话框,用户确认重启即完成安装(见 emain/updater.ts#L181-L210)。

支持自动更新的包格式

自动更新仅对特定分发格式生效:DMG、AppImage、RPM、DEB,而 Windows 的所有分发格式(nsis、msi 等)均支持自动更新。这与 electron-builder.config.cjs 中配置的构建目标一一对应(mac 产 dmg/zip、linux 产 deb/rpm/snap/AppImage/pacman、win 产 nsis/msi/zip)。

包管理器多渠道分发

除应用内更新外,Wave 还将正式版同步到主流包管理器,形成多渠道分发矩阵:

Homebrew(macOS)

Homebrew 维护着一个Autobump 机器人,会定期检查 Wave 的发布源,发现新的正式版后自动更新 Cask 配方。RELEASES.md 说明项目通过在 autobump 名单中登记来启用这一机制,Cask 配方为wave.rb

WinGet(Windows)

WinGet 通过 PR 管理包版本。Wave 在 Publish Release 工作流中调用微软提供的wingetcreate工具(对应 Taskfile.yml#L430-L437 的artifacts:winget:publish:*任务),以专用机器人账号向 WinGet 仓库提交升级 PR,通常一天内被合并。该任务对预发布版直接跳过(通过 status 退出码控制),仅正式版才会提交。

Chocolatey(Windows)

Chocolatey 社区提供了一套 PowerShell 模块用于发布。Wave 的做法是维护一个独立的wavetermdev/chocolatey仓库,其中包含发布脚本与每日运行的工作流:每天检查新版本、校验 SHA、验证安装可行性,然后推送新版本并回写更新后的包清单。由于社区审核周期较长,更新通常需要数周才会被接受。

Snap(Linux / macOS)

Snap 通过snapcraft构建并发布到 Snap Store。Publish Release 工作流对所有 beta 与正式版执行该步骤(见 .github/workflows/publish-release.yml 中的publish-snap-amd64publish-snap-arm64作业):beta 版只发布到beta频道,正式版发布到beta+stable两个频道,且即时生效。Snap 的打包参数(core22基础、classic 限制模式、原生 Wayland 支持)定义在 electron-builder.config.cjs#L110-L115。

electron-builder 配置深度解读

electron-builder.config.cjs 是打包的最终裁决者,RELEASES.md 特别强调了两处非常规配置:

Go 二进制必须排除出 ASAR

ASAR 归档内的文件对 NodeJS 而言不是真正的"文件",无法通过 Shell 命令直接执行。由于 Wave 的wavesrvwsh是需要在运行时被 Electron 主进程以子进程方式调用的 Go 原生二进制,它们必须留在 ASAR 之外。配置如下(见 electron-builder.config.cjs#L43-L46):

asarUnpack: [ "dist/bin/**/*", // wavesrv 和 wsh 二进制 "dist/schema/**/*", // Monaco 编辑器使用的 schema 文件 ],

同时files过滤规则也做了配合:dist/bin目录只打包当前架构的wavesrv.${arch}*与所有wsh*,排除掉其他架构的冗余二进制(见 electron-builder.config.cjs#L21-L26)。

node_modules 整体排除

由于 Vite 已在构建期将前端依赖全部打包进产物,electron-builder.config.cjs通过"!node_modules"将整个node_modules排除在打包之外(见 electron-builder.config.cjs#L32),同时关闭了npmRebuildnodeGypRebuild。唯一例外是monaco-editor——它体积庞大且包含 web worker,需要保留以便按需加载。

其他关键配置

  • 产物命名${productName}-${platform}-${arch}-${version}.${ext},Linux 与 Snap 使用单独的命名模板(见 electron-builder.config.cjs#L16、#L79、#L114)。
  • macOS 目标:zip + dmg,arm64 与 x64 双架构;最低系统版本 10.15.0;通过extendInfo声明了联系人、相机、麦克风、日历、位置等隐私权限用途文案(见 electron-builder.config.cjs#L47-L77)。
  • Linux 目标:zip、deb、rpm、snap、AppImage、pacman 六种格式;desktop 条目关键词为developer;terminal;emulator;;并通过--enable-features UseOzonePlatform --ozone-platform-hint auto参数启用原生 Wayland 支持(见 electron-builder.config.cjs#L78-L94)。
  • RPM 构建细节:通过 fpm 参数--rpm-rpmbuild-define _build_id_links none移除/usr/lib/.build-id/链接,避免与其他 Electron 应用(如 Slack)产生冲突(见 electron-builder.config.cjs#L116-L119)。
  • macOS 通用二进制权限修复afterPack钩子会在打包 macOS universal 二进制后,将app.asar.unpacked/dist/bin下所有wavesrv*文件的权限恢复为0o755(rwxr-xr-x),规避通用二进制打包导致的权限丢失问题(见 electron-builder.config.cjs#L124-L140)。
  • extraResources:将 Tsunami 脚手架(dist/tsunamiscaffold)作为额外资源随应用分发(见 electron-builder.config.cjs#L34-L39)。

结语

Wave Terminal 的发布体系是一个"小团队 + 强自动化"的工程范本:版本号由单一脚本(version.cjs)驱动、构建由单一入口(task package)驱动、分发由 GitHub Release 事件驱动,S3 staging/release 双桶隔离了"构建验证"与"对外发布"两个阶段,而双频道更新机制则让 beta 用户与稳定版用户各取所需。如果你正在为自己的 Electron 项目设计发布流水线,不妨以 RELEASES.md、Taskfile.yml、electron-builder.config.cjs 三份文件为起点,逐段对照本文复刻一套属于你自己的版本发布闭环。

【免费下载链接】wavetermAn open-source, AI-integrated, cross-platform terminal for seamless workflows项目地址: https://gitcode.com/GitHub_Trending/wa/waveterm

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询