Tart和GitHub Actions的CI/CD方案

引子

onevcat on Twitter / X

大家的 iOS 项目都是怎么做 CI 的?Xcode Cloud 不够用啊(太贵了)

看你的提交频率了…我自己的话低配就够了。另外就是找一款合适的虚拟机,尽量别自己折腾环境和升级啥的 挺烦的…我是tart+GitHub Action 还挺满意

Xcode 的 Test 结果可以直接显示在 GitHub 里吗?如果再能把截图都同步过去就完美了。

https://github.com/kishikawakatsumi/xcresulttool

Fastlane

不知道有没有帮助,可以参考下,

如何用极狐GitLab 为 iOS App 创建自动化CI/CD?详细教程来了

💡 GitLab Actions + Fastlane,GitLab Actions仅为触发器。

Tart

https://github.com/cirruslabs/tart

直接下载

下载带Xcode的完整镜像

1
2
3
brew install cirruslabs/cli/tart
tart clone ghcr.io/cirruslabs/macos-sonoma-xcode:latest sonoma-xcode
tart run sonoma-xcode

从头开始创建 macOS 虚拟机映像

Tart 可以从 *.ipsw 文件创建虚拟机。你可以在这里下载特定的 *.ipsw 文件,也可以使用 latest 代替 *.ipsw 的路径来下载最新的可用版本:

1
2
tart create --from-ipsw=latest sonoma-vanilla
tart run sonoma-vanilla

自托管 CI

创建 .cirrus.yml 文件

1
2
3
4
5
6
7
8
9
10
11
task:
name: hello
macos_instance:
# can be a remote or a local virtual machine
# image: ghcr.io/cirruslabs/macos-sonoma-base:latest
image: sonoma-xcode
hello_script:
- echo "Hello from within a Tart VM!"
- echo "Here is my CPU info:"
- sysctl -n machdep.cpu.brand_string
- sleep 15

运行该task

将上述 .cirrus.yml 文件放入存储库的根目录中,并使用以下命令运行它:

1
2
brew install cirruslabs/cli/cirrus
cirrus run

从 Tart VM 中检索工件

在许多情况下,需要从 Tart 虚拟机中检索特定文件或文件夹。例如,以下 .cirrus.yml 配置定义了一个任务,该任务构建二进制 tart 文件并通过 artifacts 指令公开它:

1
2
3
4
5
6
7
task:
name: Build
macos_instance:
image: ghcr.io/cirruslabs/macos-sonoma-xcode:latest
build_script: swift build --product tart
binary_artifacts:
path: .build/debug/tart

运行 Cirrus CLI 时,将 --artifacts-dir 定义 artifacts 写入主机上提供的本地目录:

1
cirrus run --artifacts-dir artifacts

请注意,所有检索到的工件都将以关联的任务名称和 artifacts 指令名称为前缀。对于上面的示例, tart 二进制文件将保存到 $PWD/artifacts/Build/binary/.build/debug/tart .

整合方案

第一步:GitHub Actions

GitHub Actions 触发 cirrus run

1
2
3
4
5
6
7
8
9
10
11
12
name: learn-github-actions
run-name: ${{ github.actor }} is learning GitHub Actions
on: [push]
jobs:
check-bats-version:
runs-on: self-hosted
steps:
- run: cd ~/git/ci
- run: git stash
- run: git checkout main
- run: git pull
- run: cirrus run --artifacts-dir artifacts

第二步:cirrus

这一步使用tart来管理并隔离运行环境。

cirrustart虚拟机中执行脚本: xcodebuild 打包或者测试。fastlane最好。

1
2
3
4
5
6
7
8
9
10
11
12
13
task:
name: xcode test
macos_instance:
# can be a remote or a local virtual machine
# image: ghcr.io/cirruslabs/macos-sonoma-base:latest
image: sonoma-xcode
hello_script:
- echo "Hello from within a Tart VM!"
- echo "Here is my CPU info:"
- sysctl -n machdep.cpu.brand_string
build_script: xcodebuild -scheme DemoApp -destination 'platform=iOS Simulator,name=iPhone 15,OS=17.2' clean test
binary_artifacts:
path: .build/debug/tart

GitHub Actions 部分log

1
2
3
4
5
6
7
8
9
10
Run cirrus run
'xcode test' task
pull virtual machine
clone virtual machine
boot virtual machine
syncing working directory
'hello' script
'build' script
'binary' artifacts
'xcode test' task succeeded in 03:17!

第三步:Fastlane

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
default_platform(:ios)

ipa_dir = "fastlane_build/"
ipa_name = "AppName" + Time.new.strftime("%Y-%m-%d_%H:%M:%S")

commit = last_git_commit
message = commit[:message]
short_hash = commit[:abbreviated_commit_hash]

# 更新内容
changelog = %(by Will
git branch: #{git_branch}
git short_hash: #{short_hash}
git message: #{message}
)

before_all do
app_store_connect_api_key(
key_id: "",
issuer_id: "",
key_filepath: "",
duration: 1200,
in_house: false,
)
end

platform :ios do
desc "Push a new beta build to TestFlight"
lane :beta do
increment_build_number(
build_number: latest_testflight_build_number + 1,
)
build_app(
scheme: "AppName",
# Debug、Release
configuration: "Release",
clean: true,
# 导出方式 app-store、ad-hoc、enterprise、development
export_method: "app-store",
export_xcargs: "-allowProvisioningUpdates", # enable automatic signing
# ipa的存放目录
output_directory: ipa_dir,
# 输出ipa的文件名为当前的build号
output_name: ipa_name,
)
upload_to_testflight(
skip_waiting_for_build_processing: true,
changelog: changelog,
)
end
end

总结

总的流程就是

  1. GitHub Actions触发
  2. cirrus管理Tart
  3. Fastlane执行打包或者测试

相比Jenkins,全局都是配置文件,更干净简洁,没有繁琐的UI。当然也稍微增加了门槛,上手就要求熟悉配置文件。