1–5 分钟交付

专属 macOS Runner
跳过公共队列

$21.1 / 天起 · 物理机独享
配置云端 Mac
完整 Xcode SSH 接入

云端 Mac 跑 Xcode CI/CD:GitHub Actions 与 Jenkins 实战指南

团队里没有常驻 Mac,却要在每次合并后自动跑 Archive 和 TestFlight? 这篇笔记记录在 PixVPS 租用的独享 Mac mini M4 云端节点上,从 Xcode 环境基线、证书导入, 到挂载 GitHub Actions 自托管 Runner 与 Jenkins Agent 的完整接线过程—— 附我们在真实 SwiftUI 工程上的耗时数据与钥匙串踩坑备忘。

没有 Mac 时,iOS 流水线会在哪一步断掉

很多团队先用 Linux 跑单元测试和静态分析,以为「CI 已经搭好了」——直到要出 TestFlight 包才发现: xcodebuildcodesignnotarytool 和 App Store Connect API 全部要求运行在 Apple 签过名的 macOS 上。这不是装个 Docker 镜像能绕开的事: Archive 证书链、Swift 编译器对 Apple Silicon 的优化路径,都假定你脚下是一台真实 Mac。

因此每个有 iOS 产品的团队,迟早要面对「谁来长期提供这台 Mac」。 常见选项有三条:GitHub 托管 macOS Runner、办公室自购 Mac mini、租用云端 Mac 物理机。 它们没有绝对优劣,差别在于你对排队时间的容忍度、月构建频率,以及是否有人专职维护系统与证书。 本文聚焦第三条路的技术落地,编译耗时参考可参阅Xcode 编译速度对比实测

本次实测环境

硬件:Mac mini M4 · 10 核 CPU · 16 GB 统一内存 · 256 GB NVMe · 1 Gbps 独享带宽(PixVPS 日本节点)。
系统:macOS 15 Sequoia,Xcode 16.4。样例工程:SwiftUI 中型 App(约 11.8 万行、含 3 个 Extension target)。
CI:GitHub Actions self-hosted runner 2.323.0;Jenkins 2.479 LTS + macOS agent。
签名:Apple Distribution 证书 + App Store Connect API Key(Issuer ID + Key ID + .p8)。

托管 Runner、自建机房与云端独享:怎么选

把一次完整 iOS 构建拆成三段——等机器编译链接签名上传—— 你会发现不同方案的瓶颈完全不同。我们在同一 commit 上对比了 GitHub 托管 macos-14 Runner 与 PixVPS M4 独享节点:托管 Runner 在 UTC 13:00–17:00 高峰平均排队 22 分钟才开始执行 job, 实际 xcodebuild archive 耗时 5 分 38 秒;独享 M4 无排队,Archive 全量构建 3 分 52 秒, 16 GB 统一内存在 clean build 时未触发 swap。

3m 52s M4 节点 Archive 耗时
22m 托管 Runner 高峰排队
1–5 min PixVPS 节点开通
16 GB 统一内存(实测无 swap)
构建路径 典型月成本 排队 / 并发 更合适的场景
GitHub 托管 macOS Runner 按分钟计费(约 $0.08/min 起) 共享池,高峰明显排队 月构建 < 500 分钟、可接受等待
自购 Mac mini 放机房 硬件一次性 + 电费运维 独占,需自行维护系统升级 有固定办公场所、全年高频构建
云端独享 Mac(按天租用) PixVPS $21.1/天起,无合同 物理机独占,付款后即时可用 中小团队、发版周集中构建、远程协作

若你的痛点是「push 之后半小时才知道编译有没有过」,瓶颈往往在排队而非 Xcode 本身。 把 Runner 固定到一台随时在线的独享 Mac,是让反馈回路缩短的最直接手段。

远程 Mac 节点环境初始化四步清单

PixVPS 交付的 Mac mini 已预装完整 macOS 并授予管理员权限。开通后通过 SSH 或浏览器 VNC 登录, 建议按固定目录规范存放证书与描述文件,避免不同 Runner 脚本各自找路径。 以下四步是我们每次新节点初始化时的标准清单。

  1. 01
    安装 Xcode 并接受许可

    从 App Store 安装 Xcode 16.x,执行 sudo xcodebuild -license acceptxcodebuild -runFirstLaunch。验证:xcodebuild -version 输出预期版本号。

  2. 02
    导入 Distribution 证书与描述文件

    将 .p12 放到 ~/certs/,用 security import 写入专用钥匙串 ~/Library/Keychains/ci.keychain-db;.mobileprovision 放入 ~/Library/MobileDevice/Provisioning Profiles/

  3. 03
    配置 App Store Connect API Key

    在 Apple Developer 后台创建 API Key,将 AuthKey_XXXXXX.p8 存至 ~/private_keys/,上传 TestFlight 时用 altool 或 Fastlane pilot upload,绕开交互式双因素认证。

  4. 04
    首次全量 Archive 并保留 DerivedData

    克隆仓库后本地跑通一次 Release Archive,确认签名链路无报错。保留 DerivedData 可使后续增量构建节省约 30–45% 时间。

路径环境变量建议

~/.zprofile 或 Runner 启动脚本里统一导出 KEYCHAIN_PATHP8_KEY_PATHDEVELOPER_DIR, 让 GitHub Actions 与 Jenkins 共用同一套引用,减少「本地能编、CI 找不到证书」的来回排查。

GitHub Actions 自托管 Runner:从注册到 workflow

自托管 Runner 注册完成后,workflow 通过 label 把 job 精确派发到这台云端 Mac。 操作路径:仓库 Settings → Actions → Runners → New self-hosted runner, 选择 macOS ARM64,按页面提示下载 actions-runner 包后执行:

./config.sh --url https://github.com/YOUR_ORG/YOUR_REPO --token RUNNER_TOKEN --labels macos-m4,pixvps,ios-build --unattended

注册成功后安装为系统服务:sudo ./svc.sh installsudo ./svc.sh start。 workflow YAML 中指定 runs-on: [self-hosted, macos-m4]。 典型 iOS job 步骤链:检出代码 → 解锁 CI 钥匙串 → xcodebuild archivexcodebuild -exportArchive → Fastlane upload_to_testflight。 我们在 M4 节点上从 push 触发到 TestFlight 处理完成平均 约 10 分钟,本地构建与上传仅占 5–6 分钟。

自托管 Runner 安全边界

Runner 能访问仓库源码与签名密钥,务必限制 collaborator 权限、定期轮换 registration token, 避免在 fork 来的 PR 上自动触发带密钥的 workflow。 多项目共用一台节点时,可为不同仓库注册不同 Runner,或配合 OpenClaw 沙箱限制 Agent 的文件系统访问范围。

Jenkins macOS Agent 弹性接入

若团队已有 Jenkins 控制器(可运行在 Linux 上),macOS 构建能力通过 Agent 节点接入。 在云端 Mac 上安装 JDK 17,下载 agent.jar,以 LaunchDaemon 方式常驻, 控制器通过 SSH 或 JNLP 下发构建任务。

Jenkins 的优势在于可视化流水线与插件生态:Credentials Binding 注入钥匙串密码、 AnsiColor 日志着色、构建产物归档到 Artifactory 等。 典型 Pipeline 在 stage('Archive') 调用 sh 'xcodebuild ...', 在 stage('Upload') 调用 Fastlane。 与 GitHub Actions 相比,Jenkins 更适合多分支、多环境、需要人工审批闸门的企业内部流程。

云端 Mac 按天租用可当作「弹性 Agent」:发版周开通节点挂载 Jenkins,淡季释放, 不必为全年 365 天运维一台机房 Mac。固定 DEVELOPER_DIR 避免多版本 Xcode 切换混乱, 是 Jenkins 环境最容易被忽视的稳定性细节。

Archive、Export、TestFlight 全命令行管线

无论 GitHub Actions 还是 Jenkins,最终产物链路一致:Archive 生成 .xcarchive → Export 生成 .ipa → 上传 App Store Connect。 命令行方式是 CI 的标准做法,不依赖 Xcode GUI。

Archive 示例(Release、指定 scheme):

xcodebuild archive -workspace MyApp.xcworkspace -scheme MyApp -configuration Release -archivePath build/MyApp.xcarchive CODE_SIGN_STYLE=Manual PROVISIONING_PROFILE_SPECIFIER="MyApp AppStore"

Export 需配套 ExportOptions.plist(method 设为 app-store):

xcodebuild -exportArchive -archivePath build/MyApp.xcarchive -exportPath build/export -exportOptionsPlist ExportOptions.plist

上传 TestFlight(API Key 方式,适合无人值守):

xcrun altool --upload-app -f build/export/MyApp.ipa -t ios --apiKey KEY_ID --apiIssuer ISSUER_ID

M4 的 10 核 CPU 使 Swift 并发编译明显快于旧款 Intel CI 机; 若工程含大量 Swift Package 依赖,建议在 workflow 里缓存 ~/Library/Developer/Xcode/DerivedData 与 SourcePackages 目录, 第二次构建起耗时可再降三分之一左右。

无人值守签名:钥匙串排错手册

SSH 或无头 Runner 上 codesign 失败,绝大多数情况与钥匙串有关,而非证书本身过期。 构建脚本开头建议固定执行解锁与授权:

security unlock-keychain -p "$KEYCHAIN_PASSWORD" ~/Library/Keychains/ci.keychain-db

security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" ~/Library/Keychains/ci.keychain-db

密码通过 GitHub Secrets 或 Jenkins Credentials 注入,切勿明文写入仓库。 若报错 errSecInternalComponent,先检查钥匙串是否设为默认、codesign 是否被加入信任列表。 若 altool 上传卡在认证,核对 API Key 的 Issuer ID 与 .p8 文件名是否匹配。

另一个常见坑是 Provisioning Profile 与 Bundle ID 不一致——Archive 阶段不报错, Export 阶段才失败。建议在 CI 里加一步 security cms -D -i profile.mobileprovision 打印 UUID, 与工程里的 PROVISIONING_PROFILE_SPECIFIER 交叉核对。

按发版节奏租用云端构建机

独立开发者和小团队常陷入两难:需要 macOS 构建,却不想为了一台 CI 机租办公室、拉专线、处理断电与系统升级。 公有云 Linux 虚拟机无法满足(没有完整 macOS 与 Apple 签名链); 把 Mac mini 放家里又面临上行带宽不稳、IP 变动、误关机等风险。

PixVPS 提供独享物理 Mac mini M4:不做虚拟化、不超售, 每台机器 16 GB 内存与 1 Gbps 独享带宽,付款后 1–5 分钟自动开通。 五地节点——新加坡、日本、韩国、中国香港、美国东部——可按用户分布选择; 面向日本市场的 App 可选东京节点,缩短上传 TestFlight 时的跨境延迟。

计费按天 $21.1、按周 $57.1、按月 $105.7,无长期合同。 发版密集的一周开通节点挂载 Runner,日常维护期释放,往往比全年自购机器加电费更划算。 需要多机并行 Archive 时,可选购 Thunderbolt 5 并联服务组成 80 Gbps 集群,适合大型 monorepo 或多 App 矩阵。

  1. 01
    在 PixVPS 选择节点并开通

    登录控制台选区域与租期,付款后 SSH 凭据与 VNC 入口自动下发。接入教程见帮助中心

  2. 02
    按第三节完成 Xcode 与签名基线

    把钥匙串路径、API Key 路径写入环境变量,供 GitHub Actions / Jenkins 统一读取。

  3. 03
    注册 Runner 并跑通首次 pipeline

    先 Debug 构建验证编译,再切 Release Archive + TestFlight,把 Fastlane lane 固化进仓库。

团队形态 建议做法 云端 Mac 角色
独立开发者,月发版 1–2 次 发版日按天租用 + 手动 Archive 临时构建机,用完即释
5–15 人,每天多次 push 常驻 self-hosted Runner 按月租用,独占不排队
已有 Jenkins,缺 macOS Agent 云端 Mac 作弹性 Agent 峰值扩容,避免新购硬件
多 App 矩阵 + 夜间批量构建 TB5 集群并联 多台 M4 并行 Archive

iOS CI/CD 的门槛不在 Xcode 菜单栏,而在稳定可预期的 macOS 算力 + 单一可信的签名环境。 公共 Runner 适合低频构建;自建机房适合有运维能力的成熟团队; 云端独享 Mac 填补了「不想排队、又不想买机器」的中间地带。 把编译搬到云上之后,本地 MacBook 可以专心写代码,而不是在深夜被 CI 任务抢走风扇与内存。

物理机独享 · 1–5 分钟交付

给你的 iOS 流水线一台不排队的 macOS 构建机

PixVPS Mac mini M4 独享节点:完整 macOS 与 Xcode、16 GB 统一内存、 SSH / VNC 接入,可挂载 GitHub Actions 与 Jenkins Runner,按天 $21.1 起。

标准配置
芯片Apple M4 · 38 TOPS
CPU10 核独享
内存16 GB 统一
带宽1 Gbps 独享
SLA99.9%
交付1–5 分钟