保留流水线,替换执行机器

这些流水线,已经能在一台云端 Mac 上跑通

把必须依赖 macOS 的构建与测试段移到独享物理 Mac mini。原生 iOS、Flutter、Unity 与 MLX 各有不同的资源瓶颈:先看命令和前提,再按内存、缓存与任务并发选档。这里是可落地的工作流,不是耗时跑分。

独享物理机,非虚拟机。按天起租,入口价 $20.5/天;可用状态以控制台实时返回为准。

按工作负载定位

共 5 个场景,全部展开。选择分类会高亮并定位相关场景,不隐藏其他内容。

从触发条件到验收产物

找到你的执行段。

以下命令在节点内运行。先安装项目依赖,再设置对应环境变量;路径指向你自己的工程与配置文件。复制仅复制命令,不会执行任务。

原生 iOS · 夜间构建

让 Git 推送触发 archive,把上传留给 fastlane。

将云端 Mac 注册为 GitHub Actions self-hosted runner、GitLab Runner 或 Jenkins agent。推送到发布分支后,先检出固定提交,再生成 Release archive 与 IPA。上传 TestFlight 是独立步骤,便于把构建失败和上传失败分开重试。

先准备这三件事

  • 设置 WORKSPACE、SCHEME 与 EXPORT_OPTIONS,分别指向工作区、共享 scheme 和导出配置。
  • 将签名证书与描述文件配置到当前构建用户;可用 fastlane match 管理,解密凭据通过 CI 密钥注入。
  • 设置 ASC_API_KEY_JSON,指向 fastlane 使用的 App Store Connect API 密钥 JSON。不要把密钥放进代码仓库。

验收时看什么

归档 build/App.xcarchive、导出的 IPA、提交标识与 Xcode 版本。确认 TestFlight 接收了对应版本;构建命令退出成功不等于上传已经完成。

选型理由:Host M4 Plus 的 24GB 统一内存适合多 target 日常构建。先串行跑通,再根据内存压力和 DerivedData 占用决定是否增加并发。

archive → export → upload
set -euo pipefail
: "${WORKSPACE:?Set WORKSPACE}"
: "${SCHEME:?Set SCHEME}"
: "${EXPORT_OPTIONS:?Set EXPORT_OPTIONS}"
: "${ASC_API_KEY_JSON:?Set ASC_API_KEY_JSON}"

mkdir -p build
xcodebuild -version
xcodebuild \
  -workspace "$WORKSPACE" -scheme "$SCHEME" \
  -configuration Release \
  -destination 'generic/platform=iOS' \
  -archivePath build/App.xcarchive archive

xcodebuild -exportArchive \
  -archivePath build/App.xcarchive \
  -exportPath build/export \
  -exportOptionsPlist "$EXPORT_OPTIONS"

IPA_PATH="$(find build/export -type f -name '*.ipa' -print -quit)"
test -n "$IPA_PATH"
bundle exec fastlane pilot upload \
  --ipa "$IPA_PATH" \
  --api_key_path "$ASC_API_KEY_JSON"
所用档位:Host M4 Plus · M4 / 24GB / 512GB 建议节点:日本(东京)
租用 Host M4 Plus

跨平台 · Flutter

Android 继续在 Linux 跑,只迁移 iOS 这一段。

不必把整条双端流水线搬家。Linux runner 保留 Android 任务,云端 Mac 负责 CocoaPods、Xcode 编译与 IPA 导出。两端使用同一个提交标识,最终汇总产物;某一端失败时,只重跑该端任务。

缓存要有失效条件

  • 固定 Flutter、Ruby、CocoaPods 与 Xcode 版本,保留 pubspec.lock、Podfile.lock 和 Gemfile.lock。
  • 缓存 Pub 下载与 CocoaPods 下载目录;缓存键至少包含工具链版本和依赖锁文件摘要。
  • 不要让多个并发任务写入同一工程目录。DerivedData 与输出目录按任务隔离,避免缓存互相污染。

验收时看什么

保存 build/ios/ipa 中的签名 IPA、构建日志与依赖锁文件。先用设备安装或后续测试任务验证产物,再把构建结果标记为可发布。

选型理由:Host M4 Lite 适合单任务起步。若同时打开模拟器、运行测试和打包,先测峰值内存,再考虑 Host M4 Plus,而不是直接堆并发。

Flutter · iOS job
set -euo pipefail
: "${EXPORT_OPTIONS:?Set EXPORT_OPTIONS}"

flutter --version
xcodebuild -version
flutter doctor -v
bundle check || bundle install
flutter pub get

(
  cd ios
  bundle exec pod install --deployment
)

flutter build ipa \
  --release \
  --export-options-plist="$EXPORT_OPTIONS"

find build/ios/ipa -type f -name '*.ipa' -print
所用档位:Host M4 Lite · M4 / 16GB / 256GB 建议节点:新加坡
租用 Host M4 Lite

游戏 · iOS 包体

接住 Unity 导出的工程,再做签名出包与体积检查。

Unity 资源处理与 Xcode 编译是两个阶段。先用项目自己的导出流程得到 Xcode 工程,再在云端 Mac 执行 archive 与 export。用同一套资源设置比较包体,才能判断变化来自资源、编译选项还是导出方式。

把导出边界写清楚

  • UNITY_IOS_DIR 指向已经导出的 iOS 工程目录。下面命令从 Xcode 阶段开始,不替代项目的 Unity 构建脚本。
  • 若插件生成了 workspace,设置 UNITY_WORKSPACE;否则使用 Unity-iPhone.xcodeproj。两种入口不要混用。
  • 准备签名与 EXPORT_OPTIONS。需要 App Thinning 报告时,在导出配置中启用相应报告选项。

验收时看什么

检查 IPA 文件大小、导出日志与资源变更。IPA 压缩体积不等于用户实际下载或安装体积;发布决策还要结合导出报告与分发处理结果。

选型理由:Host M4 Pro 提供 64GB 统一内存与 2TB SSD,适合较大的资源工程和归档缓存。先记录资源导入、编译和导出各阶段用量,再安排任务队列。

Unity export → Xcode
set -euo pipefail
: "${UNITY_IOS_DIR:?Set UNITY_IOS_DIR}"
: "${EXPORT_OPTIONS:?Set EXPORT_OPTIONS}"

mkdir -p build
if [ -n "${UNITY_WORKSPACE:-}" ]; then
  xcodebuild -workspace "$UNITY_WORKSPACE" \
    -scheme Unity-iPhone -configuration Release \
    -destination 'generic/platform=iOS' \
    -archivePath build/Game.xcarchive archive
else
  xcodebuild \
    -project "$UNITY_IOS_DIR/Unity-iPhone.xcodeproj" \
    -scheme Unity-iPhone -configuration Release \
    -destination 'generic/platform=iOS' \
    -archivePath build/Game.xcarchive archive
fi

xcodebuild -exportArchive \
  -archivePath build/Game.xcarchive \
  -exportPath build/game-export \
  -exportOptionsPlist "$EXPORT_OPTIONS"

find build/game-export -type f -name '*.ipa' \
  -exec du -h {} \;
所用档位:Host M4 Pro · M4 Pro / 64GB / 2TB 建议节点:韩国(首尔)
租用 Host M4 Pro

原生 iOS · 适配回归

固定 Xcode,再把模拟器矩阵一项项跑完。

新系统适配不只是编译通过。把核心导航、权限弹窗、深色模式和关键布局写进测试;按 Xcode 版本与模拟器运行时保存结果。先让矩阵串行可复现,再决定哪些测试值得并发。

版本和设备都要可追溯

  • 可用 xcodes 管理多版本 Xcode。通过每个任务的 DEVELOPER_DIR 选择版本,避免并发修改整机默认工具链。
  • 先查看当前 Xcode 下可用的模拟器,为 SIM_A 与 SIM_B 设置不同设备的 UDID;运行时需要事先安装。
  • 保留 .xcresult,不要只保存终端最后一行。UI 截图、失败用例与重试记录要能对应到同一个提交。

验收时看什么

分别核对每个目标的测试退出码和结果包。模拟器回归不能替代真机的外设、性能与分发验证;这些任务应单独安排。

选型理由:Host M4 Plus 可作为日常回归起点。模拟器数量由实际内存压力决定,24GB 不是无限并发额度。

Xcode · simulator matrix
set -euo pipefail
: "${DEVELOPER_DIR:?Set DEVELOPER_DIR}"
: "${WORKSPACE:?Set WORKSPACE}"
: "${SCHEME:?Set SCHEME}"
: "${SIM_A:?Set SIM_A}"
: "${SIM_B:?Set SIM_B}"
export DEVELOPER_DIR

mkdir -p build
xcodebuild -version
xcrun simctl list devices available

RUN_ID="$(date +%s)-$$"
for DEVICE_ID in "$SIM_A" "$SIM_B"; do
  xcodebuild test \
    -workspace "$WORKSPACE" \
    -scheme "$SCHEME" \
    -destination "platform=iOS Simulator,id=$DEVICE_ID" \
    -parallel-testing-enabled NO \
    -resultBundlePath \
      "build/$RUN_ID-$DEVICE_ID.xcresult"
done
所用档位:Host M4 Plus · M4 / 24GB / 512GB 建议节点:香港
租用 Host M4 Plus

AI · MLX 推理实验

把模型放进统一内存,先测能否稳定运行。

在 Apple Silicon 物理节点上,用 MLX 加载兼容模型,记录生成速度、峰值内存和上下文长度。先验证单请求,再测批处理;构建任务和推理实验共享同一台机器的资源,不应未经测量就同时满载。

先确认模型与资源边界

  • MODEL_DIR 指向已准备好的 MLX 兼容模型目录。确认模型来源、使用许可、量化格式和依赖版本。
  • 64GB 是整机统一内存,系统、模型权重、缓存和其他进程都会占用。能否加载取决于模型与上下文配置。
  • 固定提示词与生成长度,记录冷启动和后续请求。不要把模型下载时间计入推理吞吐。

验收时看什么

保留模型版本、量化配置、MLX 环境和运行日志。用 /usr/bin/time -l 辅助观察进程用量,再结合系统内存压力判断是否存在换页。

选型理由:Host M4 Pro 的 64GB 统一内存适合需要更大内存空间的实验。2TB SSD 用于模型文件和实验产物,不会增加模型可用内存。

MLX · local inference
set -euo pipefail
: "${MODEL_DIR:?Set MODEL_DIR}"

python3 -m venv .venv
. .venv/bin/activate
python -m pip install mlx-lm
mkdir -p build

python -m pip freeze > build/mlx-environment.txt
/usr/bin/time -l \
  python -m mlx_lm.generate \
    --model "$MODEL_DIR" \
    --prompt "Explain reproducible builds." \
    --max-tokens 128 \
    > build/mlx-output.txt \
    2> build/mlx-metrics.txt

cat build/mlx-output.txt
cat build/mlx-metrics.txt
所用档位:Host M4 Pro · M4 Pro / 64GB / 2TB 建议节点:美国西部
租用 Host M4 Pro

命令复制后,请在自己的工程目录中核对变量与签名配置,再执行。

建议不是性能承诺

用一次完整任务校准配置。

上面的节点按团队协作时区给出建议,不代表已测得的网络延迟。代码仓库、产物存储与操作人员的位置同样影响体验。

01

记录资源峰值

固定提交与依赖版本,执行一次完整任务。分别记录内存压力、缓存体积和产物体积,避免只凭单次编译耗时选档。

02

分开排队与执行

先使用单执行器。确认任务本身稳定后,再增加并发;等待 runner 的时间、依赖下载时间与编译时间应分别记录。

03

选离协作链路近的节点

可选新加坡、日本(东京)、韩国(首尔)、香港、美国西部,共 5 个节点。先验证工程同步与交互连接,再固定团队的执行位置。

把验收标准说清楚

你希望明早看到什么?

以下是三类角色的场景化使用目标,不是客户评价或实测记录。把目标写进流水线验收,比只追求一次更快的构建更有用。

“我希望早上拿到对应提交的归档、测试结果和失败日志,不再先去检查办公室那台打包机。”
iOS 负责人 · 夜间构建目标
“我只迁移 Flutter 的 iOS 段。双端对齐同一个提交,失败时能单独重跑,不把整条流水线推倒重来。”
移动端架构师 · 流水线拆分目标
“我需要把模型版本、内存占用和生成结果一起留下。实验能复现,下一次选机器才有依据。”
算法工程师 · 推理实验目标

选好执行段,再选机器。

单任务起步看 Host M4 Lite,多 target 与日常回归看 Host M4 Plus,大型工程和内存密集实验看 Host M4 Pro。到方案页核对三档配置、四种租期与附加项,再提交订单。

全部以美元结算。仅支持 USDT-TRC20 与 Visa / Mastercard / Amex(经 Stripe);实际可用网关以后端接口返回为准。