昨天我们刚讨论过一件事:越来越多开发者把 Claude Code、CodeGraph 索引和 Ollama 迁到远程 macOS 节点(见Cloud Mac vs 本地 Mac)。很多人以为「上云之后,push 就会自动绿」——结果往往是:终端里 Agent 改得很欢,git push 之后 GitHub Actions 仍在 ubuntu-latest 上跑测试,xcodebuild 直接失败,或 macOS job 在队列里等半小时。
这篇不是「GitHub Runner 是什么」的百科,也不包含 actions/runner 注册步骤(那留给下一篇教程)。我们要建立的也不是单点卖点的「租一台 Mac」,而是一套可延展的方法论:Cloud Mac AI Stack——其中 L1 回答:为什么必须先有执行引擎,而不是把 Cloud Mac 当成「能跑 Claude Code 的远程电脑」就结束?
Cloud Mac AI Stack · 系列 Slogan
Claude Code 生产 Diff,GitHub Runner 生产 Fact。
别人讲 Agent / Tool / CI / Automation;我们讲 Context → Diff → Fact → Workflow。本篇是这套语言的 L1 入口。
L1 在 Stack 里的职责
Claude Code(L3)解决「怎么改代码」;GitHub Runner(L1)解决「改完之后能不能被组织验收、签名并交付」。 Agent 提出答案;Runner 在 CI 里证明答案成立。没有 L1,L3 的 Diff 不会自动变成团队愿意 merge 的 Fact。
Stack 语言:Context → Diff → Fact → Workflow
现在大量内容在讲 Claude Code、Cursor、MCP、OpenHands,但很少讲:Agent 改完代码之后,谁负责成为组织认可的结果? 业界常用词是 Agent、Tool、CI、Automation——在 Cloud Mac AI Stack 里,我们用四个产出物串起来(L2 推理层单独见下表):
记忆链(与调用顺序无关 · 见下文五层结构图)
Context → Diff → Fact → Workflow
(MCP) (Claude Code) (Runner) (OpenHands)
| 层 | 职责层级 | 组件 | Stack 产出 |
|---|---|---|---|
| L0 | 基础设施 | Cloud Mac | 可运行面(macOS 节点) |
| L1 | 执行层 | GitHub Runner | Fact |
| L2 | 推理层 | Ollama | Inference(私有 token,可选) |
| L3 | 编码层 | Claude Code | Diff |
| L4 | 工具连接层 | MCP | Context |
| L5 | 自主执行层 | OpenHands | Workflow |
问题不在「AI 够不够聪明」,而在L1 是否独立存在。没有 Fact 层,Context 和 Diff 再漂亮,也不会变成 merge 按钮上的绿勾。
编码层与执行层:为什么「上了 Cloud Mac」CI 仍可能断档
在执行层拆分里,我们把「模型推理」和「Agent 执行」分开了:API 在厂商云端,shell / Git / 测试在 macOS 上跑。再往下拆一层,很多团队会忽略第二条执行链——不是 Agent 在终端里跑的那次 pnpm test,而是 push 之后由 CI 定义的、可重复、可审计的构建。
| 层级 | 典型组件 | 触发方式 | 回答的问题 |
|---|---|---|---|
| 编码 / Agent 层 | Claude Code、Cursor Agent | 你在终端或 IDE 里下指令 | 「帮我把这个 PR 改完」 |
| 执行 / CI 层 | GitHub Actions + self-hosted Runner | git push、PR、定时任务 |
「这次提交在干净环境里能构建、能测、能归档吗」 |
断层出现在这里:你把 Claude Code 放在 Cloud Mac 上,SSH 里跑测试全绿;但 workflow 仍指向 Linux runner,仓库的「官方真相」仍是红的。对 iOS 团队来说,这还不是尴尬而已——TestFlight 流水线根本不会在 Linux 上启动。执行层必须独立存在,且往往必须和同一类 macOS 环境对齐。
典型误判:Claude Code 说「测试全过」,PR 却是红的
下面是我们多次在客户仓库里见到的同一类事故——比架构图更好记:
- 开发者在 Cloud Mac 的 SSH 里跑 Claude Code,Agent 回复:「所有测试已经通过。」
- 开发者
git push,信心满满去开会。 - GitHub Actions 触发,workflow 里仍是
runs-on: ubuntu-latest。 - 约 10 分钟后,PR checks 红了;日志里常见第一句:
xcodebuild: command not found(或根本没有 iOS job,只有 Node 测试在 Linux 上绿着)。
问题不在 Claude Code,而在 Claude Code 的运行环境 ≠ CI 的运行环境。 Agent 在 macOS 上说的「通过」,没有被组织认可的 Runner 在同一条 GitHub Actions 流水线里复现。Runner 的价值,就是把「会话里的结论」变成「每次 push 可审计的 Fact」——必要时与 Claude Code 落在同一台 GitHub Actions self-hosted runner macOS 节点上。
Runner 不是「又租一台 Mac」
三个常见误解,我们直接划掉:
- 不是 Cloud Mac 产品介绍——租机器是 L0 底座;Runner 是在底座上常驻监听 GitHub 任务的软件与策略(label、并发、工作区清理)。
- 不是 GitHub 官方
macos-latest的说明书——托管 runner 是另一套计费、队列与隔离模型;self-hosted 是你把 job 调度到自己的节点。 - 不是 OpenClaw / OpenHands——OpenClaw 手记讲的是编排与回执(谁先触发、命令序列、审计日志);Runner 是机器上真正执行
xcodebuild、fastlane的那双脚。
一句话:Cloud Mac 提供 macOS 算力与出口;Runner 把这份算力接进 GitHub 的事件模型。
没有 Runner,Cloud Mac 只是远程电脑;有了 Runner,它才成为研发基础设施。 若只能记一句系列 Slogan,请记:Diff → Fact(上框蓝底引文)。
Cloud Mac AI Stack 五层结构图(系列共用 · 回链请用 #stack-map)
后续 L2–L5 文章请统一回链:「见 Cloud Mac AI Stack 五层结构图」(链到本篇 #stack-map)。这是在积累方法论 + 命名体系,而不只是单篇 SEO。
重要:Stack ≠ 调用顺序
结构图表示的是组织里的职责层级,不是运行时谁先调谁。 因此:
- Claude Code 不必依赖 Ollama——多数团队 L3 走 Claude API,L2 是可选私有推理。
- MCP 在图上高于 Claude Code,指的是 Context 层为编码提供工具上下文,不是说 MCP Server 一定比 CLI 先启动。
- 落地顺序(先 L0→L1 再叠 AI)见 § 接入顺序,与图的自下而上「承重关系」不是一回事。
Cloud Mac AI Stack 五层结构图(职责层级 · 自下而上)
┌──────────────┐
│ OpenHands │ L5 · Workflow
└──────┬───────┘
│
┌──────▼───────┐
│ MCP │ L4 · Context
└──────┬───────┘
│
┌──────▼───────┐
│ Claude Code │ L3 · Diff
└──────┬───────┘
│
┌──────▼───────┐
│ Ollama │ L2 · Inference(可选)
└──────┬───────┘
│
┌──────▼───────┐
│ GitHub Runner│ L1 · Fact ← 本篇
└──────┬───────┘
│
┌──────▼───────┐
│ Cloud Mac │ L0 · 基础设施
└──────────────┘
读图方式:L0 托住一切算力;L1 托住一切「组织敢不敢信」的 Fact;其上才是 Diff、Context、Workflow。 Ollama 在 L2 表示「私有推理可叠在底座上」,与 L3 可并行,不必理解为「必须先跑 Ollama 才能开 Claude Code」。
为什么叫「执行引擎」:职责与真实链路
在 Cloud Mac AI Stack 五层模型里,Runner 固定占 L1。它不是和 Claude Code 抢「谁更聪明」,而是承担三件可重复执行的事:
- 承接仓库事件——
on: push、pull_request、workflow_dispatch把 job 派发到带 label 的 self-hosted runner。 - 跑 macOS 原生工具链——
xcodebuild、swift test、notarytool、签名与归档;这是 Linux runner 无法替代的硬需求。 - 与 AI 层解耦——Agent 改代码 ≠ 自动通过 CI;Runner 把「改完」变成「artifact + 测试报告 + 可部署包」。
下面这条链路是我们给 iOS / Flutter(iOS 目标)团队画的真实交付路径——不是抽象的 push → build,而是从 AI 编码到 TestFlight 上发生了什么:
Cloud Mac AI Stack · L1 执行链(概念)
Claude Code(L3 编码层,SSH / 终端)
│
│ git commit & push
▼
GitHub(webhook / Actions 调度)
│
▼
GitHub Actions workflow
│
▼
GitHub Runner(L1 · self-hosted · macOS · ARM64)
│
├── xcodebuild(Debug / Release)
├── 单元测试 / UI 测试
├── archive → .ipa
└── fastlane → TestFlight / 内测分发
这张图的价值在于:别人可以抄「Runner 是什么」的定义,但很难抄你整条 Stack 的层级关系。 编码发生在 L3;真正把二进制送出去的是 L1。中间缺任何一环,都会出现「Agent 说完成了,App Store Connect 没包」的落差。
workflow 里你只会看到 Runner 的「标签」,而不是注册细节。下面片段用于说明 GitHub Actions iOS build 如何落到 macOS ARM64 self-hosted runner(token、launchd 等请等 L1-Q02):
# .github/workflows/ios-ci.yml(片段) jobs: build-ios: runs-on: [self-hosted, macOS, ARM64, cloud-mac] steps: - uses: actions/checkout@v4 - name: Build & Test run: xcodebuild -scheme MyApp -destination 'platform=iOS Simulator,name=iPhone 16' test
很多人搜的不是「GitHub Runner」,而是 iOS CI / Mac mini Runner
我们在 SEO 与客服工单里看到:工程师很少输入泛词 GitHub Runner,更常搜的是同一类意图的不同表述,例如:
- GitHub Actions self-hosted runner macOS / macOS ARM64 runner / Apple Silicon runner
- GitHub Runner Mac mini / GitHub Actions Mac mini / self-hosted runner Mac mini
- iOS CI/CD self-hosted runner / GitHub Actions iOS build
- Cloud Mac CI、Xcode on CI、TestFlight 自动化
这些查询背后通常是同一个架构问题:如何让 GitHub Actions 把 job 派发到自己的 Apple Silicon 节点,而不是默认的 Linux 池或排队中的托管 macos-latest。Cloud Mac(或办公室里的 Mac mini)提供机器;在机器上注册 Runner + 打 label,才完成「派单」。本篇讲的是为什么要先立这一层;L1-Q02 会写具体注册步骤。
若你正在对比「买 Mac mini 放机房」与「租 Cloud Mac」,Runner 逻辑相同,差异在 L0 运维与成本——可参考Mac mini vs Cloud Mac 团队选型;L1-Q05 将单独做 Runner 成本对照。
Linux 托管 Runner 不够时;以及「根本不需要 macOS Runner」时
GitHub 的 ubuntu-latest 对 Web 后端极其友好,但对 Apple 交付链是硬边界。对比表:
| 维度 | ubuntu-latest 托管 |
Cloud Mac self-hosted |
|---|---|---|
| Xcode / iOS 构建 | ❌ 不可用 | ✅ 原生 |
| Apple Silicon 与真机架构一致性 | 不一致 | 与 M 系列开发机一致 |
| 与 Claude Code 同机 | ❌ 异构环境 | ✅ 可选同节点 |
| 队列与成本模型 | 按分钟、高峰排队 | 固定节点,适合日更 CI |
反过来,说明「不适用」和说明「适用」同样重要——否则读者会以为你在卖「万物上 Cloud Mac」。下面这类项目,继续用 Linux runner 通常就够,不必为了 AI Stack 硬上 macOS Runner:
- Next.js / 纯前端静态站——构建在 Node 环境完成,无 Xcode。
- Node.js API、Python FastAPI、Go 微服务——Docker job 在 Linux 上更常见、镜像生态更全。
- Docker 化后端——
docker build+ 部署到 K8s,与 macOS 无关。 - 小型个人仓库——每月偶尔跑一次 CI,托管 runner 的队列成本往往低于自建节点。
需要 macOS Runner 的信号很集中:workflow 里出现 Xcode、签名、notarize、iOS Simulator、TestFlight,或团队已在评估 Mac mini vs Cloud Mac的 iOS 基础设施。详见该文的分工模型;本篇只强调 Runner 在其中的 L1 位置。
GitHub 托管 macos-latest vs Cloud Mac 自建
更适合托管 macos-latest |
更适合 Cloud Mac self-hosted |
|---|---|
| 偶尔 archive、每月 <5 次 macOS job | 日更 CI、固定证书与 Provisioning |
| 能接受排队与分钟计费波动 | 要 AI(Claude Code)与 CI 同栈、可观测同一台机器 |
| 无内网依赖、无固定出口 IP 要求 | 要静态 IP、内网 artifact、或 Runner 与CodeGraph 同机错峰 |
经验阈值(非合同承诺):当团队每周 macOS job 超过约 10 次,且其中一半与 iOS 签名/归档相关,我们会建议把 L1 从「临时排队」改为「固定 Cloud Mac + self-hosted」。低于该频率,先用托管 macOS 把流水线跑通往往更省事。
落地顺序:先 Fact,再 Diff——与结构图不是一回事
五层结构图讲的是职责层级;团队落地时要另记一条部署顺序(先让组织有 Fact,再叠 AI)。很多文章按热搜写:先 Claude Code,再 MCP,最后才想起来 CI。我们更推荐:
- L0——Cloud Mac 底座(常驻 macOS、出口、SSH)。
- L1——GitHub Runner,先让
push → 绿/红可重复(本篇)。 - L2–L3——Ollama、Claude Code,在「已有客观构建结果」之上叠 AI。
- L4–L5——MCP、OpenHands,接在稳定 CI 与编码环境之后。
原因很朴素:CodeGraph + Agent 可以改 18 个文件,但若仓库没有稳定的 macOS CI,你永远不知道漏改是否会在 archive 阶段爆炸。Runner 先把「机器验收」固定下来,AI 才不是在沙盒里自嗨。
与Mac mini + Claude Code 一周手记的关系:那篇偏 L3 编码体验;本篇补 L1——同一台机器可以夜间跑 Runner、白天跑 Agent,但两层职责不要混在一篇文章里讲。
决策:谁需要把 Runner 当作执行引擎
结尾不做「Cloud Mac 更好」的口号,只做可执行的判断。
| 适合优先立 L1(self-hosted on Cloud Mac) | 不一定需要 |
|---|---|
| 原生 iOS / macOS App 团队 | 纯静态站、无原生构建 |
| Flutter 需要打 iOS 包、走 Xcode 链路的团队 | 仅 Android / Web 的 Flutter 项目 |
| 已在 Cloud Mac 跑 Claude Code,但 CI 仍在 Linux | Node / Python API,Docker 在 Linux 已绿 |
| TestFlight / 签名 / notarize 要自动化 | 小型个人项目、月更几次 |
| 需要固定出口、内网或同机 CodeGraph 索引 | 只偶尔 macos-latest 能接受的 archive |
如果你命中左列两条以上,下一步不是继续加 MCP Server,而是把 L1 跑通。具体步骤见下文 L1 专题连载。
L1 专题连载:从「执行引擎」到可复制的 macOS CI
本篇是 Cloud Mac AI Stack · L1 基石(L1-Q01)。后续会把 Runner 从概念写成可运维的 Topic Cluster,并与 L2–L5 衔接:
| 篇目 | qid / 方向 | 状态 |
|---|---|---|
| L1-Q01 · 本篇 | 为什么 Runner 是执行引擎(Diff → Fact) | ✅ 你正在读 |
| L1-Q02 | Cloud Mac 上注册 self-hosted Runner(逐步教程) | 下一篇 |
| L1-Q03 | 一台 Runner 如何同时服务 Claude Code 与 CI(错峰与同机) | 计划中 |
| L1-Q04 | Runner workspace 清理与安全隔离策略 | 计划中 |
| L1-Q05 | Mac mini vs Cloud Mac Runner 成本与队列 | 计划中 |
Google 更容易把成组的 L1 文章理解为「GitHub Runner + macOS CI 专题」,而不是单篇教程。L2 起将接 Ollama(Inference)、Claude Code(Diff)、MCP(Context)、OpenHands(Workflow)——均建立在「push 已有 Fact」之上;全系列回链 Cloud Mac AI Stack 五层结构图。
常见问题
Cloud Mac 和 self-hosted Runner 是什么关系?
Cloud Mac 是 L0 底座;Runner 是跑在底座上的 L1 进程与策略。租机器 ≠ 自动有 Runner。
能不能只在 MacBook 上跑 Runner?
可以,但合盖、内存与 Agent 争抢、IP 变化会影响稳定性。日更 macOS CI 更常见做法是 24/7 Cloud Mac 节点。
Runner 和 Claude Code 必须同机吗?
不必须;同机可减少「SSH 里绿了、Actions 里红了」的摩擦。
和 OpenClaw 的区别?
Runner 执行 step;OpenClaw 编排触发与回执。可叠在同一 Cloud Mac,见OpenClaw 云端自动化。
GitHub Actions self-hosted runner macOS 怎么和 Cloud Mac 一起用?
在 Cloud Mac(L0)上安装并注册 Runner 进程,workflow 使用 runs-on: [self-hosted, macOS, ARM64] 等标签,即可把 iOS CI/CD、xcodebuild、TestFlight 步骤派发到该节点。
Claude Code 生产 Diff,Runner 生产 Fact——什么意思?
Diff 是 Agent 会话里的改动与主观结论;Fact 是 GitHub Checks 上的绿勾、日志与可部署 artifact。组织 merge 的是后者。延伸:MCP 生产 Context,OpenHands 生产 Workflow;见 § Stack 语言。
结构图里 Ollama 在 Claude Code 下面,是否必须先跑 Ollama?
否。图表示职责层级,不是调用链。Claude Code 常用 API;Ollama 是 L2 可选 Inference,见 § 五层结构图 说明框。
L1 专题 · 下一篇 L1-Q02
在 Cloud Mac 上注册 GitHub Actions self-hosted runner(macOS)
本篇已回答「为什么必须有执行层」。下一篇写 label、actions/runner、launchd 与 token 轮换——把 Diff 与 Fact 落到同一台 Apple Silicon 节点。再往后:L1-Q03 同机服务 Claude Code 与 CI,L1-Q04 workspace 策略,L1-Q05 Mac mini vs Cloud Mac 成本。
