如果你手上正好有一台 Mac,又想把这台机器变成自建代码托管平台里的 CI/CD 执行节点,那这篇文章就是写给你看的。很多团队已经有自己的 Gitea 内网仓库,但 CI/CD 还在用 GitLab Runner、Jenkins,或者干脆依赖 GitHub Actions;而当你尝试把 Apple 硬件(Mac mini、MacBook Pro、Mac Studio)纳入 Gitea 的自动化流水线时,会遇到架构差异、runner 模式选择、系统环境隔离等一系列问题。本文要给出一个清晰判断:Gitea Actions 完全可以在 Apple 硬件上真正跑起来,而且对想构建 iOS/macOS 应用、又想保持私有的团队来说,这是目前最轻量的开源方案之一。
读完这篇文章,你能搞懂三件事:Gitea Actions 和 act_runner 的架构关系是什么;如何在 Mac 上安装、注册、运行 act_runner;如何用一份.gitea/workflows/*.yml文件,把构建、测试、打包流程跑在 Apple 硬件上。文中还会给出苹果芯片(Apple Silicon)和 Intel Mac 的不同处理思路、常见坑和排查方法,建议收藏备用。
1. 为什么要关注 Gitea Actions on Apple Hardware
很多人一开始接触 Gitea,是从“替换 GitHub/GitLab”这个角度出发的。Gitea 用 Go 写的,单个二进制文件就能跑起来,内存占用远低于 GitLab,特别适合内网部署、个人 NAS、小型团队。但仅仅是代码托管还不够,开发流程里最刚需的是持续集成和持续部署。以前在 Gitea 仓库里想跑 CI,最常见的选择是外接 Jenkins 或 GitLab Runner,配置复杂不说,Jenkins Pipeline 和 Gitea 的结合始终不够原生。
Gitea 从 1.19 版本开始正式支持 Gitea Actions,它的目标很明确:**让你能直接写类似 GitHub Actions 的 workflow 文件,由官方提供的 act_runner 来执行。**工作流文件放在仓库的.gitea/workflows/下,触发机制、语法结构、上下文对象都和 GitHub Actions 高度兼容。对已经从 GitHub Actions 迁移过来的团队,几乎不需要重新学一套技能。
但为什么单拎出 Apple Hardware 来说?因为常规 CI 服务器都是 Linux x86/ARM 架构,而 Apple 生态有特殊性:
- 如果你要构建 iOS 或 macOS 应用,必须用 Xcode、
codesign、altool这些工具,它们只存在于 macOS 系统上; - 如果你想避免购买 Mac 云服务或受限于第三方平台,用自己手里的 Mac mini 或 MacBook 当 runner,是把成本降到最低的方式;
- Apple Silicon(M 系列)是 arm64 架构,和常见的 Linux amd64 环境不同,工作流里的容器、依赖、缓存都要考虑架构匹配问题。
这不是纯技术炫技,而是很多开发团队的真实痛点:自己的代码放在内网 Gitea,想构建 Apple 应用却买不起 Mac 云主机,或者公司政策不允许把代码上传到第三方 CI 平台。于是让 Apple 硬件亲自干活,就是一个合理又省钱的选择。
2. Gitea Actions 的核心机制与架构
在直接上手之前,先把 Gitea Actions 的架构讲清楚。否则你在配置 runner 时,很可能不理解为什么 job 一直卡在队列里,或者为什么 runner 注册成功但工作流不执行。
2.1 一个工作流从提交到执行发生了什么
Gitea Actions 沿用了 GitHub Actions 的模型:
- 仓库里有一个
.gitea/workflows/ci.yml文件; - 当 push、PR、tag 等事件发生时,Gitea 实例会分析事件对应的 workflow;
- Gitea 会把任务放进任务队列;
- 一个或多个 act_runner 进程从 Gitea 实例那里拉取任务;
- act_runner 根据工作流定义的
runs-on标签匹配自己声明的 label; - 匹配成功后,runner 负责创建 job 运行环境,执行各个 step。
这里的核心角色是act_runner,它是 Gitea Actions 的执行器,本质上是 GitHub Actions Runner 的一个兼容实现,内部基于act项目来执行容器内或宿主机上的步骤。
2.2 为什么 runner 要用 label 匹配
GitHub Actions 有runs-on: ubuntu-latest这类标签,GitLab Runner 有 tag,Gitea Actions 同样使用 label。
act_runner 在注册时可以声明:
--labels macos-arm64:arm64意思是“这个 runner 能处理声明为macos-arm64的 job,并且默认 Job 容器架构是 arm64”。当 workflow 里写runs-on: macos-arm64时,这个 runner 才会被选中。
很多新手忽略 label,导致注册后的 runner 显示在线,但任务一直不执行,根本原因就是 workflow 里用的 label 和 runner 注册时声明的不一致。
2.3 运行模式:宿主机执行与容器执行
act_runner 支持多种执行方式,常见两种:
宿主机模式:runner 直接在当前系统环境执行命令,不额外启动容器。这种方式对 Apple 硬件尤其重要,因为 Xcode、xcodebuild等工具只存在于 macOS 系统里,没法放进 Linux 容器。
Docker 模式:runner 通过 Docker 启动一个隔离环境来执行 step。优点是干净、依赖隔离;缺点是 macOS 上 Docker 本质是虚拟机,跑 Linux 容器与宿主机 macOS 之间不能直接访问 Xcode,除非你特意将宿主路径挂载进去,但这不是干净的方案。
因此,在 Apple 硬件上跑原生 Apple 应用构建,首选宿主机模式,或者使用自定义 labels 指向宿主机执行。这就是为什么这篇文章要专门讲 Apple Hardware 的特殊性。
2.4 Gitea Actions 与 GitLab Runner、Jenkins 的差异
很多搜索词里出现“gitlab和gitea”“jenkins + gitea 实现 springboot 打包部署”,这里顺便做一个直接对比:
| 方案 | 配置文件位置 | 学习成本 | 资源占用 | Apple 硬件支持 |
|---|---|---|---|---|
| Gitea Actions | 仓库内.gitea/workflows/*.yml | 低,和 GitHub Actions 接近 | 较低(act_runner 是单进程) | 需要自行配置 Mac runner |
| GitLab Runner | 仓库内.gitlab-ci.yml或项目设置 | 中等 | 中等偏高 | 支持 macOS runner,但要 GitLab EE 部分高级功能 |
| Jenkins | Jenkinsfile 或 Web 配置 | 较高 | 高,Master/Agent 模式复杂 | 支持 macOS agent,但维护成本高 |
Gitea Actions 最舒服的地方是:整个 CI 配置跟着仓库走,代码审查时能看到 workflow 变更,原生支持 Actions 语法,不需要额外搭建调度系统。
3. 环境准备:Apple 硬件 + Gitea 实例 + 基础依赖
下面开始实战。先确认你的硬件和软件环境,再安装组件。
3.1 硬件与系统要求
- Apple 硬件:Mac mini、MacBook、Mac Pro 都行。
- 处理器:Apple Silicon(M1/M2/M3 等)或 Intel。
- 系统:建议 macOS 12 及以上,具体以你用的 Gitea 和 act_runner 版本支持为准。
- 内存:至少 8GB,建议 16GB;因为 Xcode 本身很吃内存。
- 磁盘:至少留 50GB 左右空间,Xcode 命令行工具和缓存比较占空间。
注意:这里不写死版本号。技术演进很快,以官方最新 release 和文档为准。
如果你只是为了学习,可以先在一台配置普通的 MacBook 上跑通;如果是团队生产环境,建议用固定的 Mac mini 当 runner,避免笔记本休眠导致任务中断。
3.2 安装 Git 和基础工具
macOS 自带 git,但版本可能偏旧。推荐用 Homebrew 安装最新的 git:
brew install git git-lfs安装完成后验证:
git --version如果还没有 Homebrew,先去安装 Homebrew。这一步不是必须的,但后面安装一些依赖工具会方便很多。
3.3 安装并初始化 Gitea 实例
如果你想先看 Flow,可以用一份现成的 Gitea 实例;但为了完整,这里给出二进制方式在 macOS 上跑 Gitea 的简要步骤(也可以直接用 Docker,但如果同一台 Mac 上还要跑 act_runner,二进制方式更简单)。
从 Gitea 官方下载对应架构的二进制。Apple Silicon 用arm64,Intel Mac 用amd64。下载后放到 /usr/local/bin 这类目录:
chmod +x gitea sudo mv gitea /usr/local/bin/gitea创建数据目录:
mkdir -p ~/gitea/{data,log}启动(临时启动,测试用):
gitea web --config ~/gitea/app.ini浏览器访问http://localhost:3000,完成首次安装配置。数据库可以先选 SQLite,简单够用;团队规模大了再切 PostgreSQL 或 MySQL。
关键是开启 Actions 功能。修改配置文件~/gitea/app.ini,在合适位置加上:
[actions] ENABLED = true修改配置后重启 Gitea:
pkill -f "gitea web" gitea web --config ~/gitea/app.ini如果一切正常,在 Gitea 的「站点管理 / 管理面板」里能看到 Actions 已启用。
3.4 创建仓库与访问令牌
在 Gitea 中新建一个测试仓库,比如actions-demo。之后需要在 Gitea 里生成一个 runner token。
在 Gitea 管理后台的「站点管理 -> Actions -> Runner」里,可以创建 token。也可以调用 API,但更简单的方法是打开管理界面,点击创建 Runner,把生成的 token 记录下来。
安全提醒:runner token 等同于给了 runner 拉取并执行任务的权利,不要提交到公开仓库,也不要泄露到任何日志里。
4. 安装并注册 act_runner
act_runner 是 Gitea Actions 的执行器,你需要在 Apple 硬件上安装它。重点说明:建议直接在宿主机上跑二进制,而不是放进 Docker 容器。原因前面已经说过——构建 Apple 应用需要访问宿主机 macOS 的 Xcode 环境。
4.1 下载 act_runner 二进制
去 Gitea 官方仓库的 Releases 页面下载 act_runner。Apple Silicon 选择darwin-arm64,Intel Mac 选择darwin-amd64。
例如(用变量表示,以实际下载文件名为准):
# Apple Silicon 示例 wget https://gitea.com/gitea/act_runner/releases/download/<版本>/act_runner-<版本>-darwin-arm64 chmod +x act_runner-<版本>-darwin-arm64 sudo mv act_runner-<版本>-darwin-arm64 /usr/local/bin/act_runner如果你在下载过程中发现 Gitea 官方仓库访问不便,可以先通过镜像或公司内网代理解决,本文不再展开。
4.2 注册 runner
在终端里进入任意目录,执行注册命令。需要提供 Gitea 实例地址和之前生成的 token:
act_runner register --instance http://localhost:3000 --token <你的TOKEN> --labels macos-arm64:arm64 --no-interactive说明:
--instance:Gitea 实例地址,如果 runner 和实例不同机器,要写内网可达地址。--token:第 3 节生成的 runner token。--labels:指定这个 runner 能处理的 label。这里macos-arm64:arm64表示 job 使用macos-arm64标签时,容器架构默认是 arm64。--no-interactive:非交互注册。
对于 Intel Mac,可以注册为:
act_runner register --instance http://localhost:3000 --token <你的TOKEN> --labels macos-amd64:amd64 --no-interactive注册成功后,当前目录会生成.runner文件。这个文件保存 runner 身份,不要删除。
之后启动 runner:
act_runner daemon在终端里会看到类似于“listening on ...”“runner 已就绪”的日志。打开 Gitea 管理后台的 Actions Runner 页面,应该能看到这台 runner 在线。
4.3 使用配置文件定制 runner
.runner文件记录身份,真正的运行参数在config.yaml中。可以用默认配置,也可以手动生成一份:
act_runner generate-config > config.yaml常用配置片段:
runner: file: .runner capacity: 1 insecure: false job: timeout: 30m如果要支持并发任务,可以调整capacity,但 Apple 硬件上通常不建议太大,因为构建任务可能很吃资源。
4.4 把 runner 注册为 macOS 服务
如果希望 runner 开机自动启动,可以使用 launchd。下面是一个简单的 plist 模板,路径可以用/Library/LaunchDaemons/com.gitea.act-runner.plist:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.gitea.act-runner</string> <key>ProgramArguments</key> <array> <string>/usr/local/bin/act_runner</string> <string>daemon</string> <string>--config</string> <string>/Users/你的用户名/gitea-runner/config.yaml</string> </array> <key>RunAtLoad</key> <true/> <key>KeepAlive</key> <true/> <key>WorkingDirectory</key> <string>/Users/你的用户名/gitea-runner</string> </dict> </plist>然后加载服务:
sudo launchctl load /Library/LaunchDaemons/com.gitea.act-runner.plist注意检查 plist 里的路径、用户名必须正确。
5. 编写第一个 Gitea Actions 工作流
现在执行器和仓库都准备好了,开始在仓库里创建 workflow 文件。
5.1 最简工作流:验证 runner 是否真的在 Apple 硬件上执行
在actions-demo仓库中添加文件.gitea/workflows/demo.yml:
name: Demo on Apple on: push: branches: - main jobs: build: runs-on: macos-arm64 steps: - name: Checkout code uses: actions/checkout@v4 - name: Show system info run: | uname -a sw_vers uname -m xcodebuild -version 2>/dev/null || true解释:
on.push.branches:只在 push 到 main 分支时触发。runs-on: macos-arm64:必须和注册 runner 时的--labels macos-arm64匹配。uses: actions/checkout@v4:Gitea Actions 兼容 GitHub Actions 生态,可以直接使用 checkout 动作。- 最后几步会在宿主机执行 macOS 命令,输出系统版本和处理器架构。
如果你用的 Intel Mac,把runs-on改成macos-amd64。
推送到仓库后,在 Gitea 仓库页面的「Actions」标签里能看到运行记录。点进去查看日志,如果输出里出现Darwin和arm64(或x86_64),说明 runner 确实在 Apple 硬件上执行任务。
5.2 在容器里跑 Linux 任务的情况
Gitea Actions 也可以让你在一个 job 中运行容器,用 Docker 模式启动一个 Linux 容器执行步骤。这种场景适合做 Linux 兼容性测试。
但注意:前面注册 label 时用macos-arm64:arm64,这个 label 代表 runner 的默认架构是 arm64,如果你在 workflow 里显式指定container:,runner 会尝试用 Docker/Podman 去启动对应的容器。
jobs: test-on-linux: runs-on: macos-arm64 container: node:20-alpine steps: - run: | node --version uname -m如果 Docker 没有运行,或者 Apple Silicon 上无法直接运行某些 amd64 容器(未开启 Rosetta),这个 job 会失败。建议先保证 Docker Desktop 或 Colima 已启动,并开启 arm64 镜像支持。
6. Apple 硬件上更实用的场景:构建 macOS/iOS 应用
当你确认基础 runner 没问题后,就能发挥 Apple 硬件的真正价值了。很多公共 CI 服务不提供 macos runner,或者排队时间极长。把 Gitea Actions 跑在自己的 Mac 上,等于有了一个私有的 macOS 构建机。
6.1 构建 macOS 命令行的示例
以一个 Swift 包为例,workflow 可以这样写:
name: Swift Build on: pull_request: push: branches: [main] jobs: build: runs-on: macos-arm64 steps: - uses: actions/checkout@v4 - name: Swift version run: swift --version - name: Build run: swift build - name: Test run: swift test如果环境里没有安装 Swift 工具链,runner 会报找不到swift命令。macOS 系统通常自带swift,但如果你用的 macOS 环境没有命令行工具,需要先安装。
6.2 构建 iOS 应用需要注意什么
iOS 应用比普通 CI 复杂得多,核心原因是签名。这里给出一个简化模型,不代表生产全部细节。
name: iOS Archive on: push: tags: - 'v*' jobs: archive: runs-on: macos-arm64 steps: - uses: actions/checkout@v4 - name: Select Xcode run: | sudo xcode-select -s /Applications/Xcode.app/Contents/Developer xcodebuild -version - name: Archive env: DEVELOPMENT_TEAM: ${{ secrets.TEAM_ID }} run: | xcodebuild archive \ -project YourApp.xcodeproj \ -scheme YourApp \ -configuration Release \ -archivePath build/YourApp.xcarchive \ DEVELOPMENT_TEAM=$DEVELOPMENT_TEAM \ CODE_SIGNING_ALLOWED=NO这只是一个演示。生产环境里签名、导出、上传到 TestFlight 等步骤会根据项目强依赖更多配置。
这里强烈建议:
- 所有证书和描述文件不要直接放仓库,使用 Gitea Actions secrets 管理;
- 不要把私钥放到工作流日志;
- 先用普通项目验证 Xcode 构建链路,再加签名上传。
如果你用的是 macOS 硬件,却只需要跑 Linux 容器,那没必要用 Mac,直接用一台 Linux 服务器更省成本。Apple 硬件跑 Gitea Actions 的核心收益,永远是能在自己家/内网里构建 Apple 生态产物。
7. 常见问题与排查思路
以下问题都是实际运行中比较容易遇到的,按“现象 -> 可能原因 -> 排查方式 -> 解决方案”整理成表,方便快速定位。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| runner 离线 | act_runner daemon 没启动、网络不通 | 查看 runner 日志、ping Gitea 实例 | 启动 act_runner,确认实例地址可达 |
| 注册失败 | token 错误、[actions] ENABLED=false | 检查 Gitea 设置、重新生成 token | 在配置中开启 Actions,重新获得 token 注册 |
| 任务一直卡在排队 | workflow 中runs-on标签与 runner 声明不匹配 | 查看 workflow 里的runs-on,查看 runner 注册时的--labels | 统一两边 label;或重新注册 runner |
| Job 执行后立即失败 | runner 默认执行容器但 Docker 未启动 | 查看日志中的 “docker” 相关错误 | 启动 Docker;或者调整为宿主机执行,通过 label 定义不需要容器 |
| 无法访问 Xcode | job 在容器内执行,容器是 Linux | 查看 step 日志,xcodebuild不存在 | 使用宿主机模式;不要将 iOS 构建步骤放到容器 job 中 |
| 下载依赖慢 | 网络环境、DNS、代理 | 在 runner 环境测curl | 配置内网镜像/缓存,或修改 runner 的 HTTP 代理 |
| 构建产物乱跑 | 多个 job 并发时共享环境 | 检查 runner 目录、缓存目录 | 设置capacity: 1或区分不同工作目录 |
| Apple Silicon 上拉取 amd64 镜像失败 | 镜像架构不兼容、Docker 未开启 Rosetta | 查看容器启动日志 | 使用 arm64 镜像,或配置 Docker Desktop 的 Rosetta 兼容模式 |
| 用户 SSH 密钥管理 | runner 进程用户和开发用户不同 | 检查~/.ssh权限、部署密钥 | 将 Gitea 部署密钥添加到 runner 用户~/.ssh |
| runner 不执行新的 workflow | 版本过旧、缓存 | 查看 Gitea 和 act_runner 版本 | 升级到最新稳定版,清理 runner 临时目录 |
下面单独讲一个高频问题:macOS 系统睡眠导致 runner 掉线。
MacBook 默认会进入休眠,runner 进程可能在休眠后失联。如果你用 MacBook 当 runner,建议在系统设置里关闭“在此时间后关闭显示器”相关的节能设置,或者用caffeinate命令保持系统唤醒状态。
简单方式:
caffeinate -s act_runner daemon-s参数会阻止系统在连接电源时睡眠。生产环境更推荐用 Mac mini 接电源,配合 launchd 常驻。
8. 最佳实践与工程化建议
跑通最小 demo 只是一个开始,把 Gitea Actions on Apple Hardware 用在真实项目里,需要从并发、安全、维护性三个维度做工程化规划。
8.1 明确 runner 的职责边界
不要在一台 Mac 上既跑 Gitea 实例,又跑多个 runner,还同时处理多个大型 iOS 编译任务。Apple Silicon 性能虽然强,但内存带宽、磁盘 IO、每次 Xcode 构建的缓存都会成为瓶颈。
建议:
- Gitea 实例放服务器或 NAS;
- Mac runner 只负责执行构建任务;
- 同一个 runner 的
capacity默认设置为 1,避免多个 iOS 构建同时抢占资源导致超时或失败。
8.2 使用 secrets 管理敏感信息
Gitea Actions 支持 Secrets。在仓库设置中配置 Secrets,然后在 workflow 中通过${{ secrets.XXX }}引用。务必遵循最小权限原则:
- 不要在 workflow 文件里写死证书、密码、token;
- 不要把 Secrets 打印到日志;
- runner 进程以独立用户运行,避免使用管理员 root 权限;
- 对 runner 工作目录、缓存目录设置严格权限。
8.3 将 workflow 拆成可复用的 composite action
如果你有多个仓库需要构建 iOS 包,可以把“Checkout -> 选择 Xcode -> 构建 Archive -> 导出 ipa”提取成 composite action,放到一个专用仓库或内网可访问的模块里。这样每次修改构建逻辑,不需要改动每个业务仓库。
8.4 缓存依赖与构建缓存
Apple 构建场景里下载依赖和 Xcode 缓存通常非常耗时。act_runner 支持缓存服务,具体启用方式可以参考 Gitea 官方 actions 缓存文档。简单思路:
- 使用
actions/cache或 Gitea 内置缓存; - 配置归档缓存路径(如
~/Library/Developer/Xcode/DerivedData); - 定期清理过大的 DerivedData,避免磁盘占满。
8.5 日志与可观测性
Gitea Actions 的日志默认托管在 Gitea 中。但生产环境建议把 runner stdout 连接到日志采集工具(如 Loki、ELK),便于检索多次构建的输出。如果 runner 异常退出,至少能看到启动日志。
8.6 版本升级策略
Gitea 和 act_runner 都在快速迭代。升级前认真阅读 release note,特别注意 Gitea Actions 和 act_runner 的兼容性。先在一台测试 Mac 上升级并跑通 demo,再逐步替换生产 runner。不要在生产环境直接升级,否则可能出现 runner 注册失效、工作流语法不兼容等问题。
8.7 考虑多台 Mac 的扩展
当团队构建量上去后,可以加入多台 Mac runner。每种 runner 声明不同的 label,例如:
macos-arm64-14:Apple Silicon + macOS 14;macos-amd64-13:Intel Mac + macOS 13;ios-build:专门处理 iOS 构建的 runner。
在 workflow 中精确指定runs-on,让任务路由到最合适的设备。这种模式下,Gitea Actions 就变成了一个私有的 Apple 硬件 CI 集群,比维护 Jenkins 的 Mac agent 简单得多。
9. 总结与后续方向
把 Gitea Actions 跑在 Apple 硬件上,本质上是在做一次“自托管 CI 与 Apple 生态”的桥接。它真正降低的是:为构建 iOS/macOS 应用而托管额外 Mac 服务的成本,以及把代码交给外部 CI 平台的隐私担忧。如果你身边正好有闲置的 Mac,花一个下午按本文步骤跑通基础流程,应该就能感受到仓库内 workflow 驱动 macOS 构建的爽快感。
下一步可以研究几个方向,让这套体系更完善:
- 深入 act_runner 的 config.yaml 配置,理解 labels、container 选项、缓存机制;
- 给 Gitea 实例配置反向代理和 HTTPS,保证 runner 与实例之间的通信安全;
- 尝试把 iOS 签名、TestFlight 上传集成到 workflow 中,形成完整的发布流水线;
- 如果有多个 Mac,研究如何划分 label、做优雅的任务调度和资源监控。
无论如何,记住一个原则:Apple 硬件上的 Gitea Actions 不能照搬 Linux CI 的思路,它更适合把任务直接交给 macOS 宿主机,让 Xcode 和原生工具链发挥真正价值。先把最小的苹果构建流水线跑通,再逐步扩展复杂逻辑,是这个方向最稳的实践路径。