跳到内容

发布

当变更合并到主分支时,Tuist 会自动发布新版本。CLI、服务器和 Kura 缓存服务器都通过发布渠道(canary、候选版本、稳定版)进行交付,以确保推荐的安装方式保持稳定且更新节奏平缓;生成候选版本并将其提升为稳定版是每周有计划地执行的步骤。应用持续发布(iOS 应用仅持续部署到 TestFlight,详见此处)。

概览#

我们发布以下主要组件:

  • Tuist CLI - 命令行工具,通过 canary、候选版本和稳定渠道交付
  • Tuist Server - 后端服务,采用与 CLI 相同的渠道模型交付
  • Kura - 缓存服务器,采用与 CLI 相同的渠道模型交付
  • Tuist App - macOS 和 iOS 应用,持续发布(iOS 应用仅持续部署到 TestFlight,详见此处)

每次推送到 main 都会为 CLI、服务器和 Kura 发布 canary 预发布版本。只有当维护者将经过充分测试的候选版本提升为稳定版时,才会为这些组件中的每一个切割稳定版本,并且每周的计划会同步触发所有三个发布流程(参见发布渠道)。应用在每次推送到 main 时都会发布稳定版本。

工作原理#

1. 提交规范#

我们使用约定式提交来组织提交信息。这使得我们的工具能够理解变更的性质、确定版本升级并生成相应的变更日志。

格式:type(scope): description

提交类型及其影响#

类型描述版本影响示例
feat新功能或能力次版本号升级 (x.Y.z)feat(cli): add support for Swift 6
fix错误修复修订版本号升级 (x.y.Z)fix(app): resolve crash when opening projects
docs文档变更不发布docs: update installation guide
style代码风格变更不发布style: format code with swiftformat
refactor代码重构不发布refactor(server): simplify auth logic
perf性能改进修订版本号升级perf(cli): optimize dependency resolution
test测试新增/变更不发布test: add unit tests for cache
chore维护任务不发布chore: update dependencies
ciCI/CD 变更不发布ci: add workflow for releases

对于 CLI,这些升级决定了 canary 版本和变更日志分组。稳定的 CLI 版本是手动切割的次版本线 (X.Y.0),而稳定修订版 (X.Y.Z) 仅来自向后移植,因此 feat 或 fix 会通过下方的渠道流程触达 CLI 用户,而不是立即作为稳定版本发布。

破坏性变更#

破坏性变更会触发主版本号升级 (X.0.0),并应在提交正文中注明:

plaintext
feat(cli): change default cache location
BREAKING CHANGE: The cache is now stored in ~/.tuist/cache instead of .tuist-cache.
Users will need to clear their old cache directory.

2. 变更检测#

每个组件都使用git cliff来:

  • 分析自上次发布以来的提交
  • 按范围过滤提交(cli、app、server)
  • 确定是否存在可发布的变更
  • 自动生成变更日志

3. 发布流水线#

当检测到可发布的变更时:

  1. 版本计算:流水线确定下一个版本号
  2. 变更日志生成:git cliff 根据提交信息创建变更日志
  3. 构建流程:构建并测试组件
  4. 制品生成:生成特定于发布的资产,例如 CLI 捆绑包、校验和以及通过tuist --experimental-dump-help生成的tuist.spec.jsonCLI 规范
  5. 创建发布:创建包含制品的 GitHub 发布
  6. 分发:将更新推送到包管理器(例如,CLI 的 Homebrew)

对于应用,每次合并都会产生一个稳定版本。对于 CLI、服务器和 Kura,同样的自动流水线仅生成canary预发布版本;候选版本和稳定版本在每周的发布列车上切割(参见发布渠道)。

4. 范围过滤#

每个组件仅在存在相关变更时才发布:

  • CLI:带有(cli)范围或无范围的提交
  • App:带有(app)范围的提交
  • Server:带有(server)范围的提交

发布渠道#

CLI、服务器和 Kura 不会在每次推送到main时都提升为稳定版本。每个组件都通过三个渠道交付,以确保推荐的安装保持稳定且更新缓慢,而早期采用者则保留每次提交的构建。面向用户的文档位于CLI 发布渠道和服务器发布渠道;本节是维护者操作手册。

每个组件都使用相同的三个渠道:

渠道版本切割方式
CanaryX.Y.0-canary.N自动执行,在每次影响组件的推送到main时触发(<component>-release.yml;对于 Kura,当kura/变更时由server-production-deployment.yml调用)
候选版本X.Y.0-rc.N每周一,或手动在releases/<component>-<major>.<minor>.x分支上执行(<component>-rc.yml)
稳定版X.Y.Z每周一,或手动通过提升经过充分测试的候选版本(<component>-promote.yml);补丁通过<component>-backport.yml

Canary 和 RC 构建作为 GitHub 预发布版本发布(从不标记为“Latest”,从不移动 GHCR 上的:latest,从不推送到 CLI 的 Homebrew),因此包管理器和镜像解析器仅在明确选择加入时才会获取它们。Canary 始终针对下一个未发布的次版本:一旦切割了 RC 线,main的 canary 就会推进到下一个次版本。

托管的 Tuist 不等待稳定列车:服务器按提交部署,托管的 canary 和生产环境运行最新的 Kura canary。部署级联在部署之前会先发布该 canary 本身,因此kura-release.yml没有自己的触发器。仅对 Kura 发布工具进行的变更(mise/tasks/kura/**、kura-*.yml工作流)不会切割 canary。

你永远不会手动挑选版本号。每个渠道的下一个版本都由mise/tasks/<component>/release/channel-version.sh从现有的 git 标签派生而来,下面的工作流会调用它。

发布分支按组件进行命名空间隔离(releases/server-X.Y.x、releases/kura-X.Y.x、CLI 的releases/X.Y.x),因此这三个组件的线路绝不会在同一主版本上冲突。

每周计划#

每周一 UTC 时间 06:00,三个组件的提升作业同步运行:CLI 提升至稳定版、服务器提升至稳定版和Kura 提升至稳定版。每个作业都会提升尚未稳定的最新 RC 线路,通常是前一周一切割的线路。如果没有这样的线路,则该作业不发布任何内容。

当提升完成时,无论结果如何,其匹配的候选版本工作流(cli-rc.yml、server-rc.yml、kura-rc.yml)都会通过workflow_run触发,并从main切割下一条线路。

如果任何预定运行失败或被取消,都会向 Slack 发送消息。当发布分支在其最新 RC 之后有提交时,提升会失败。要发布该线路,请在其上切割一个新的 RC 并手动提升。否则,下周一将提升更新的线路。

切割候选版本#

要在每周计划之外切割新线路(请替换下方组件的工作流名称):

  1. 使用空的branch输入运行 <组件>候选版本工作流(例如server-rc.yml)。
  2. 它会发布X.Y.0-rc.1,并在成功后在构建的提交处创建受保护的releases/<component>-X.Y.x分支。

要将关键修复或回归引入正在测试的线路,请通过 PR 将其拣选到发布分支上(与回退流程相同;CI 从不执行拣选),然后迭代 RC:

  1. 从releases/<component>-X.Y.x分出分支,拣选修复,向发布分支打开 PR,解决其中的任何冲突并合并。
  2. 再次使用branch=releases/<component>-X.Y.x运行候选版本工作流。它将从分支 HEAD 发布X.Y.0-rc.(N+1)。

测试线路处于功能冻结状态:只有关键修复和回归才会进入该线路。

提升至稳定版#

要在每周计划之外提升线路,需在其 RC 经过充分测试后:

  1. 使用branch=releases/<component>-X.Y.x运行 <组件>提升至稳定版工作流(例如server-promote.yml)。
  2. 它会使用make_latest=true发布带有语义化版本标签的镜像,移动 GHCR 的:latest标签和 GitHub 的“Latest”指针。CLI 还会更新 Homebrew 公式。

除非分支 HEAD 正好是最新 RC 指向的提交,否则提升操作将拒绝运行。因此,如果在最后一个 RC 之后有任何修复合并到该线路上,你必须切割一个新的 RC 并让其经过测试,然后才能作为稳定版发布。

将修复回退到稳定线路#

一旦线路稳定,就使用仅限 CLI 的回退发布工作流(cli-backport.yml)来发布补丁:通过 PR 将修复拣选到releases/X.Y.x上,然后使用该分支运行工作流以切割X.Y.(Z+1)。回退操作永远不会移动“Latest”指针或 Homebrew 公式。同时维护两条线路:当前线路接受回归和安全修复,前一条线路仅接受关键和安全修复。其他错误修复不会回退,而是随下一个次版本一起发布。

对于服务器和 Kura,请使用服务器回退发布(server-backport.yml)或Kura 回退发布(kura-backport.yml)。提升操作仅发布 RC 的X.Y.0;它无法修补已稳定的线路。

  1. 从所选X.Y线路上最新发布的稳定组件标签开始,并向其现有的releases/server-X.Y.x或releases/kura-X.Y.x分支打开一个精准修复 PR。较新的次版本或主版本发布不会阻止对维护中线路的修补。请勿包含无关的主分支变更。
  2. 审查并合并源 PR。获取远程发布分支并确认其最终合并尖端,包括组件支持的平台以及 TLS 正/负控制。通过预合并头并不等同于对其他压缩或合并提交的确认。
  3. 在获得单独的发布批准后,从 main分派匹配的回退工作流,将source_sha设置为该确切确认的合并尖端,并将base_tag设置为当前稳定标签,例如[email protected]或[email protected]。工作流会派生出X.Y.(Z+1);操作人员不选择次版本升级或提升 canary 内容。

规划会刷新确切的远程分支和基础标签,要求所选源等于分支尖端,拒绝未更改的源或组件之外的变更、其信任策略输入和验证,并锁定解析后的基础提交。源自 main 的控制会在版本化镜像和 GitHub 发布之前立即重复这些检查。如果源分支、稳定基础或可用版本发生更改,请停止并重新确认,而不是替换为新源。

回退使用现有的多平台发布器,而不移动 GitHub 的“Latest”指针或 GHCR :latest。发布与托管部署是分开的:这些工作流不会提升托管服务器或 Kura 集群,不会绕过 canary 验收,也不会确认已安装的运行时。构建助手和 Dockerfile 仍然来自所选的稳定源。这些控制是发布关卡,而非无需凭据的构建作业;构建可以在最终关卡之前上传未标记的镜像摘要。镜像和 GitHub 发布不是原子的:如果在发布镜像标签后后续关卡失败,请检查部分发布并保留其原始源和工件。切勿替换现有发布或在其标签下使用不同的字节。

编写良好的提交消息#

由于提交消息直接影响发布说明,因此编写清晰、描述性的消息非常重要:

建议:#

  • 使用现在时:“add feature”而非“added feature”
  • 简洁但具有描述性
  • 当变更特定于组件时,包含范围
  • 在适用时引用问题:fix(cli): resolve build cache issue (#1234)

避免:#

  • 使用模糊的消息,如“fix bug”或“update code”
  • 在一次提交中混合多个不相关的变更
  • 忘记包含破坏性变更信息

破坏性变更#

对于破坏性变更,请在提交正文中包含BREAKING CHANGE::

plaintext
feat(cli): change cache directory structure
BREAKING CHANGE: Cache files are now stored in a new directory structure.
Users need to clear their cache after updating.

发布工作流#

CLI、服务器和 Kura 各自拥有一套渠道工作流,遵循相同的结构:

  • <component>-release.yml - 在每次触及组件的推送到main时发布 canary(Kura 的是一个可重用工作流,当kura/变更时由server-production-deployment.yml调用)
  • <component>-rc.yml - 每周一切割一个候选版本(通过提升后的workflow_run触发),或手动切割或迭代一个
  • <component>-promote.yml - 每周一 UTC 时间 06:00 将待处理的 RC 提升至稳定版,或手动提升经过测试的 RC
  • <component>-build-publish.yml - 上述三个工作流调用的共享构建和发布流水线

CLI、服务器和 Kura 还拥有<component>-backport.yml,用于在稳定线路上切割补丁。组件的所有工作流都通过一个<component>-publish并发组进行序列化,因此版本解析和标记永远不会竞争。Kura 是个例外:其 canary 使用kura-canary-publish,而其 RC、提升和回退使用kura-stable-publish,因此长时间的稳定构建永远不会阻碍部署级联。标签命名空间是不相交的,因此这两个组不会冲突。

Helm Chart 和基础设施镜像(CAPI operator、runners controller、xcresult processor image、linux runner image 等)仍通过.github/workflows/server-production-deployment.yml发布。它在推送到main时运行,并为托管部署执行 canary → 验收测试 → 生产环境的级联流程,同时在这些基础设施组件变更时发布它们。App、缓存、Gradle 插件、skills、Noora 以及独立的基础设施控制器各自拥有专用的*-release.yml工作流。它们共享变更检测机制:mise/tasks/release/components.json声明每个组件的标签前缀和包含路径,git cliff 将匹配的提交转换为发布说明。

CLI 构建(cli-build-publish.yml)还会生成并发布一个tuist.spec.json工件(由tuist --experimental-dump-help生成),以便下游工具可以使用该命令接口。

监控发布#

你可以通过以下方式监控发布:

  • GitHub Releases 页面
  • GitHub Actions 选项卡中的工作流运行记录
  • 每个组件目录中的变更日志文件

优势#

这种持续发布方法提供:

  • 快速交付:变更在合并后立即触达用户
  • 减少瓶颈:无需等待手动发布
  • 清晰沟通:根据提交消息自动生成变更日志
  • 流程一致:所有组件采用相同的发布流程
  • 质量保证:仅发布经过测试的变更

故障排除#

如果发布失败:

  1. 检查失败工作流的 GitHub Actions 日志
  2. 确保你的提交消息遵循约定格式
  3. 验证所有测试是否通过
  4. 检查组件是否成功构建

对于需要立即发布的紧急修复:

  1. 确保你的提交具有明确的作用域
  2. 合并后,监控发布工作流
  3. 对于服务器或 App,修复将在下次推送时交付;如有必要,触发手动发布
  4. 对于 CLI,修复会自动进入 canary 渠道。若要将其交付给稳定版用户,请将其回退到维护中的稳定线路(参见将修复回退到稳定线路)

App Store 发布#

虽然 CLI 和服务器遵循上述持续发布流程,但由于 Apple 的 App Store 审核流程,iOS App是个例外:

  • 手动发布:iOS App 发布需要手动提交至 App Store
  • 审核延迟:每次发布都必须经过 Apple 的审核流程,可能需要 1-7 天
  • 批量变更:多次变更通常打包在一次 iOS 发布中
  • TestFlight:Beta 版本可能在 App Store 发布之前通过 TestFlight 分发
  • 发布说明:必须根据 App Store 指南专门编写

iOS App 仍然遵循相同的提交约定并使用 git cliff 生成变更日志,但实际向用户发布的频率较低且为手动安排。