Apple芯片Mac上部署Gitea Actions:从零到完整CI/CD

Apple芯片Mac上部署Gitea Actions:从零到完整CI/CD 这次我们来看怎么在 Apple 芯片 Mac 上把 Gitea Actions 完整跑起来。Gitea 是开源社区里很常见的轻量级自托管 Git 服务Actions 是它内置的 CI/CD 流水线引擎不需要额外装 Jenkins也不需要搭 Kubernetes。两者拼在一起等于用一台常开的 Mac 就能同时解决代码托管、代码审查、自动构建、自动测试和产物发布的问题链路非常短。最值得关注的点是 Gitea 官方直接提供darwin-arm64的二进制M 系列芯片的 Mac 可以原生运行不用走 Rosetta 转译。Actions 的 Runner 也有对应的 macOS 版本注册方式一条命令就能完成。这套东西对硬件的要求不高核心不是“跑不跑得动”而是怎么把 Gitea 服务端和 Runner 这层关系捋清楚以及选择容器执行还是宿主机执行。这篇文章会从环境准备、二进制下载、Runner 注册、工作流编写、接口调用、资源占用到问题排查全部过一遍适合刚接触自托管 CI/CD、手里正好有一台 Apple 芯片 Mac 的开发者。1. Gitea Actions 核心能力速览先把能力边界说清楚。Gitea Actions 不是又一个独立 CI 平台它是 Gitea 仓库内置的流水线服务工作流文件放在仓库的.gitea/workflows/目录下语法兼容 GitHub Actions因此很多现成的 Action 可以直接复用。Runner 官方配套是act_runner它负责接收 Gitea 下发的任务并执行。能力项说明项目定位自托管 Git 服务 内置 CI/CD 引擎开源协议Gitea 与 act_runner 均为开源项目部署与二次使用按相应开源协议执行主要功能代码托管、PR/Issue、内置 Actions 流水线、Webhook、Release、制品上传、REST APIApple 硬件支持提供 darwin-arm64 原生二进制Apple 芯片 Mac 可直接运行推荐配置Apple 芯片 Mac内存建议 8GB 起步并发任务越多对内存要求越高Runner 执行方式容器模式Docker / OrbStack / Colima或宿主机直跑模式启动方式命令行启动两步先gitea web再act_runner daemonAPI 支持支持 REST API仓库内容、Webhook、创建文件等均可自动化批量任务支持流水线自带任务队列可配置并发容量适合场景个人/小团队内部 Git 服务、macOS 原生构建、iOS 自动化、边缘设备测试从材料看这套方案最大的优势是“轻”Gitea 服务端本质是一个二进制进程配置文件和数据库都可以放在本地目录不需要独立的数据库中间件默认 SQLite 即可。act_runner同样是一个二进制进程注册后常驻后台。整个链路没有多余的中间件在 Apple 硬件上跑 Gitea Actions本质就是两个进程加一个执行环境的问题。2. 适用场景与使用边界先判断这套东西适不适合你。如果你手头有一台 Mac mini 或者 MacBook长期插电、网络稳定想搭一个完全属于自己的 Git 仓库和 CI 系统Gitea Actions 很合适。它在 Apple 芯片上的亮点是macOS 宿主可以直接执行 Runner适合编译 iOS 工程、打.dmg包、跑 Xcode 脚本、做 macOS 原生软件测试。这类任务依赖 macOS 环境很多外部 CI 服务反而不方便用自己的 Mac 当 Runner 是最直接的解法。它也适合小团队内部使用。Gitea 的权限模型比较完整仓库级、组织级权限都能配配合 Actions 可以做到“push 后自动跑测试、打镜像、通知群机器人”。因为部署简单备份也简单整个运维负担很低。但要注意边界。如果你本来就在大规模团队里大量使用 GitHub Actions 或 GitLab CI并且需要 Windows Runner、GPU 训练集群、上千并发任务池Gitea Actions 不是替代方案它的强项是轻量和可控而不是超大规模调度。另外如果服务器网络拉不到容器镜像工作流里依赖公共镜像会经常卡住需要提前规划镜像缓存或本地镜像仓库。使用边界方面要特别强调安全。Runner 一旦注册就能执行仓库里的工作流代码。不要把不可信的代码源放在同一个 Runner 里跑尤其不要使用宿主机直跑模式去执行来自陌生仓库的工作流。仓库密钥、数据库密码、云厂商凭证不要写在工作流文件里要使用 Gitea 的 Secret 机制。涉及人脸、声音、版权素材等数据时必须确认授权在工作流中处理内部数据也要遵守公司隐私和合规要求。3. Apple 硬件本地部署环境准备在 Apple 芯片 Mac 上部署首先要确认的是 Mac 芯片类型。M1、M2、M3、M4 系列的 CPU 架构是arm64和 Intel Mac 的x86_64不一样下载二进制的时候要选对后缀。Gitea 和 act_runner 的官方 Release 都提供了 darwin-arm64 版本直接下载即可不需要编译源码。接下来按下面清单检查环境macOS 系统版本不需要太新能正常使用终端即可但如果你要跑 Docker 容器建议使用 Docker Desktop、OrbStack 或 Colima 任一容器环境。安装命令行工具。执行xcode-select --install确保git、make等基础命令可用。准备一个运行目录。建议把所有文件放在/opt/gitea下面服务端程序、Runner 程序、数据目录、工作目录分开管理避免卸载或升级时误删数据。确认端口。Gitea 默认监听3000端口act_runner 默认连3000如果端口被占用需要调整。如果使用 SQLite磁盘空间就是最小依赖。Gitea 数据库可以存储在本地文件中但建议单独建data目录。# 创建目录结构 sudo mkdir -p /opt/gitea/{data,runner} sudo chown -R $USER /opt/gitea容器环境这块单独提一下。Apple 芯片的 Mac 上 Docker Desktop 跑的是 Linux 虚拟机arm64 的 Linux 容器可以直接运行amd64 容器需要软件模拟速度明显偏慢。如果你的工作流里大量使用公共镜像优先选 arm64 版本镜像。如果不装 Docker也可以让 Runner 用宿主机模式直接跑在 macOS 上后面会详细对比这两种模式。4. Gitea 服务端与 act_runner 安装部署4.1 下载并启动 Gitea 服务端打开 Gitea 官方 Releases 页面找到最新版本的darwin-arm64压缩包下载后解压并赋予执行权限。下面命令中的GITEA_VERSION只是一个占位符实际替换成官方发布的具体版本号即可。# 以实际版本号替换 GITEA_VERSION GITEA_VERSION1.23.0 curl -L -O https://github.com/go-gitea/gitea/releases/download/v${GITEA_VERSION}/gitea-${GITEA_VERSION}-darwin-arm64 chmod x gitea-${GITEA_VERSION}-darwin-arm64 mv gitea-${GITEA_VERSION}-darwin-arm64 /opt/gitea/gitea启动时用web子命令指定端口和工作目录。第一次访问网页会跳到安装向导数据库选 SQLite然后填写管理员账号。安装完成后Gitea 就作为一个前台进程跑起来了。cd /opt/gitea ./gitea web --port 3000 --config /opt/gitea/custom/conf/app.ini启动后浏览器访问http://127.0.0.1:3000能看到安装界面就是成功的。如果端口被占用可以换成--port 8080但后续 Runner 注册时也必须对应改端口。4.2 下载并注册 act_runnerRunner 端官方项目是gitea/act_runner同样从官方 Releases 下载 darwin-arm64 版本。下载后先注册再启动注册命令会要求指定 Gitea 实例地址和注册方式。# 先下载版本号以官方 Release 为准 ACT_RUNNER_VERSION0.2.11 curl -L -O https://github.com/gitea/act_runner/releases/download/v${ACT_RUNNER_VERSION}/act_runner-${ACT_RUNNER_VERSION}-darwin-arm64 chmod x act_runner-${ACT_RUNNER_VERSION}-darwin-arm64 mv act_runner-${ACT_RUNNER_VERSION}-darwin-arm64 /opt/gitea/runner/act_runner注册 Runner 有两种常见方式。一种是使用管理员账号密码直接注册另一种是在 Gitea 管理后台进入 “Runner” 页面先创建注册令牌再用令牌注册。使用令牌的方式更规范。cd /opt/gitea/runner ./act_runner register \ --instance http://127.0.0.1:3000 \ --token 在管理后台创建的令牌 \ --name mac-mini-runner \ --labels macos-host:host,linux-arm64:docker://node:20这条命令里最核心的是--labels。它定义了这个 Runner 能承接哪些runs-on标签。这里注册了两个 labelmacos-host:host表示工作流中runs-on: macos-host的任务直接在 macOS 宿主机执行不做容器隔离。linux-arm64:docker://node:20表示runs-on: linux-arm64的任务会默认用node:20镜像启动一个容器在容器里执行命令。注册完成后当前目录会生成.runner文件里面记录实例地址、注册令牌、标签信息。启动 Runner 只需要执行 daemon 命令./act_runner daemon如果 Gitea 管理后台的 Runner 列表中能看到该 Runner状态为在线说明注册链路已经通了。这一步是整个部署里最容易出错的地方后面排查章节会展开讲。4.3 容器模式与宿主机直跑模式对比在 Apple 硬件上跑 Gitea ActionsRunner 的执行模式直接决定体验这里单独拉出来对比。对比项容器模式docker:// 标签宿主机模式:host 标签依赖Docker Desktop / OrbStack / Colima不需要容器环境执行环境隔离到 Linux 容器直接在 macOS 上执行命令镜像架构推荐 arm64amd64 需模拟原生 arm64无镜像概念安全性相对隔离适合不可信工作流能访问宿主机文件系统只适合可信工作流常见用途跑 Linux 测试、Node/Python 任务iOS 构建、Xcode 脚本、macOS 原生打包实际部署时可以一个 Runner 同时注册多个 label比如用macos-host处理需要 Xcode 的任务用linux-arm64处理常规测试。Gitea 在调度任务时会根据runs-on自动匹配带对应 label 的 Runner非常灵活。4.4 用 LaunchAgent 让服务常驻Gitea 和 Runner 默认是前台进程断开终端就停了。要常驻最简单的方式是用 macOS 的launchd。在~/Library/LaunchAgents/下建立 plist 文件把两个二进制都接入守护进程即可。plist 文件只是示例路径和标签需要按实际情况调整。?xml version1.0 encodingUTF-8? !DOCTYPE plist PUBLIC -//Apple//DTD PLIST 1.0//EN http://www.apple.com/DTDs/PropertyList-1.0.dtd plist version1.0 dict keyLabel/key stringcom.local.gitea/string keyProgramArguments/key array string/opt/gitea/gitea/string stringweb/string string--config/string string/opt/gitea/custom/conf/app.ini/string /array keyRunAtLoad/key true/ keyKeepAlive/key true/ /dict /plistRunner 也按同样的方式创建第二个 plistProgramArguments 换成/opt/gitea/runner/act_runner daemon。之后用launchctl load加载重启 Mac 也能自启动。5. Gitea Actions 工作流功能测试与效果验证服务端和 Runner 都跑起来之后接下来进入验证阶段。我会按“最基础任务 - 矩阵任务 - 产物上传 - 失败定位”的顺序把你需要关心的全部测一遍。5.1 第一个 Push 触发任务在 Gitea 里新建一个测试仓库比如命名为actions-demo。克隆到本地创建.gitea/workflows/hello.yml写入最基础的工作流。这里先验证push 代码后Runner 能不能收到事件、能不能跑通步骤。name: hello on: push: workflow_dispatch: jobs: hello: runs-on: macos-host steps: - name: 查看工作目录 run: pwd - name: 输出系统信息 run: uname -m - name: 输出提示 run: echo gitea actions is okruns-on: macos-host对应前面注册的宿主机标签。提交并 push 之后打开仓库的 “Actions” 标签页应该能看到一个hello工作流正在排队随后变成 running最后变成 success。点击任务展开日志能看到uname -m的输出是arm64这就验证了 Apple 芯片原生执行。这里有个小知识点Gitea Actions 默认监听push事件但只有默认分支的 push 才触发。如果你的仓库默认分支是main但本地还在用master就要注意分支名否则推了也不会触发。5.2 容器模式与矩阵任务测试宿主机模式验证通过后测试容器模式。把工作流改成runs-on: linux-arm64并在矩阵里同时跑 Node 和 Python 两个镜像确认容器能被调度并能拉取镜像。name: container-matrix on: push: jobs: matrix-test: strategy: matrix: image: [node:20, python:3.12] runs-on: linux-arm64 container: image: ${{ matrix.image }} steps: - name: 检查运行时 run: | echo 当前容器镜像: ${{ matrix.image }} uname -m - name: 确认语言版本 run: | node -v || python3 --version这个任务会先启动一个容器然后在容器里执行命令。如果你用 Docker Desktop第一次拉取node:20和python:3.12会花一点时间之后有缓存会快很多。日志中uname -m同样输出arm64说明容器在 Apple 芯片 Mac 上是原生 arm64 架构运行。5.3 产物上传与批次验证CI/CD 里常见的需求是把构建结果保存下来。这里用actions/upload-artifact验证产物链路。act_runner 兼容该 Action所以可以直接复用 GitHub 生态里的写法。name: artifact-demo on: push: jobs: build: runs-on: macos-host steps: - name: 生成产物文件 run: | mkdir -p dist echo demo build finish dist/result.txt - name: 上传产物 uses: actions/upload-artifactv4 with: name: demo-result path: dist/任务成功后在 Actions 任务详情页可以看到 artifact 下载入口。下载解压后result.txt应该包含写入的内容。这一步验证的是 Runner 对 Artifact 存储和下载链路的支持。5.4 故意制造失败验证错误定位在实际使用前建议再验证一次失败场景。把某个步骤改成执行一个不存在的命令故意让任务失败。steps: - name: 故意失败 run: command_not_found_demopush 之后任务会失败Gitea 界面里会明显标红日志末尾会显示exit code 1并且可以看到底层是哪个命令出了问题。这个测试是为了让你提前熟悉以后工作流跑挂了怎么去 Actions 页面看日志怎么区分是代码问题、镜像问题还是 Runner 环境问题。6. Gitea Actions 接口 API 与批量任务Gitea Actions 不是只能在网页上手动触发。它也有 REST API 可以对接适合把流程接到自己的脚本或平台里。这里需要说明一个细节Gitea 的 Actions 相关 API 在不同版本里覆盖范围不一样所以最稳妥的自动化方式是“利用仓库 Contents API 提交文件来触发流水线”这个方式不依赖具体 Actions 管理接口兼容性最好。下面给出一套 Python 调用示例但接口路径需要按你当前 Gitea 版本的官方文档确认。先到用户设置页面创建访问令牌示例中记为GITEA_TOKEN。然后调用/api/v1/repos/{owner}/{repo}/contents/{filepath}向仓库写入或更新一个文件。文件内容需要 Base64 编码提交后 push 事件会触发对应的工作流。import requests import base64 base_url http://127.0.0.1:3000/api/v1 owner 你的用户名 repo actions-demo token 你的访问令牌 filepath data/trigger.txt headers { Authorization: ftoken {token} } content manual trigger payload { content: base64.b64encode(content.encode()).decode(), message: ci: trigger workflow by api } url f{base_url}/repos/{owner}/{repo}/contents/{filepath} response requests.post(url, jsonpayload, headersheaders, timeout60) print(response.status_code) print(response.json())这个示例的核心思路是把 API 当作“提交按钮”通过提交文件让 Git 事件驱动 Actions。如果要批量触发多个仓库就在循环里复用这个函数加上间隔时间避免请求过快被限流。repos [repo-a, repo-b, repo-c] for repo in repos: # 复用上面的请求逻辑逐个提交文件 print(ftriggered: {repo})批量任务的另一个维度是 Runner 的并发容量。act_runner 默认能同时处理的任务数量有限具体值在.runner配置或 Runner 配置文件中定义。需要在 act_runner 的配置文件里找到容量相关字段并调大然后重启 daemon 生效。调大并发会同时拉高内存和 CPU 占用前几个并发建议控制在 2 到 4 之间观察稳定后再向上加。7. 资源占用与性能观察方法很多人关注 Gitea Actions 在 Apple 硬件上到底吃多少资源。这里没办法给一个固定的数字因为不同版本、不同工作流、不同并发任务差的非常多。更稳妥的做法是掌握一套观察流程结合自己的场景测出资源画像。观察手段上macOS 自带的top、htop可以看 Gitea 和 act_runner 进程的 CPU 与内存占用。如果用了 Docker 容器模式docker stats可以列出每个容器的实时占用。启动一个简单任务后保持观察 1 到 2 分钟就能看到规律。docker stats --format table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}几个性能相关因素值得注意容器模式比宿主机模式多一层虚拟化任务启动和文件读写会慢一些但宿主机模式任务直接在 macOS 上执行一旦脚本误操作影响范围是整个系统。任务里如果使用公共镜像第一次拉取镜像的时间占大头。经常使用的镜像建议固定版本不要每次都拉latest。amd64 镜像在 arm64 Mac 上会走模拟执行编译类任务会明显变慢。尽量选 arm64 镜像。高并发任务会增加内存压力。如果你发现某个任务在执行npm install或go build时被系统杀掉很可能是内存不够需要降低并发或者给 Docker Desktop 分配更多内存。性能优化的优先级建议先降低并发再换小体积镜像最后考虑把 Runner 迁移到宿主机模式。不要一上来就在配置里调整一堆参数那样反而不容易定位瓶颈。8. 常见问题与排查方法Gitea Actions 在 Apple 硬件上跑不起来的坑绝大多数集中在注册、触发、镜像架构和容器环境这几块。下面整理了一份排查表建议收藏。问题现象可能原因排查方式解决方案Runner 注册失败提示 token 无效令牌已过期或不是有效注册令牌到 Gitea 管理后台重新创建令牌重新生成令牌重新执行注册命令Runner 在线但任务一直排队工作流的runs-on没有匹配的 label查看 Runner 标签与工作流标签是否一致调整工作流里的runs-on或重新注册补 label任务启动后报exec format error容器镜像是 amd64 架构在 arm64 环境模拟失败查看任务日志中的镜像架构信息改用 arm64 版本镜像或安装 qemu 模拟容器任务找不到 Docker socketDocker Desktop / OrbStack / Colima 未启动执行docker ps检查先启动容器环境再重启 act_runnerpush 后 Actions 页面没有新任务分支名不对或工作流文件路径错误确认默认分支确认文件在.gitea/workflows/*.yml修正分支名修正文件路径宿主机模式任务找不到node宿主机没有安装 Node.js 运行时执行node -v检查通过 Homebrew 安装 Node.js端口占用导致 Gitea 起不来3000 端口被其他进程占用执行lsof -i :3000查看占用进程改 Gitea 启动端口或停掉占用进程act_runner 启动后内存上涨明显并发任务过多或任务里有重编译用htop看进程资源降低 Runner 并发容量限制单任务资源GitHub 生态的 Action 拉不下来网络无法访问 Action 源仓库查看任务日志克隆步骤配置镜像缓存或提前把常用 Action 放到本地可访问位置Artifact 上传失败磁盘空间不足或路径不存在检查dist/是否生成检查磁盘剩余空间清理日志与旧 artifact确认路径后重跑注册问题是出现频率最高的。注册时如果 Gitea 启用了 HTTPS--instance也要写成 HTTPS如果用的是127.0.0.1token 注册时要注意站点可用域名是否包含本机地址。另一种情况是 Runner 状态显示“闲置”但任务一直卡着这通常是标签不匹配Gitea 找不到能承接该 label 的 Runner所以任务停留在队列里。9. 最佳实践与使用建议当 Gitea 和 act_runner 都能稳定运行之后建议按工程化的方式管理这套环境否则用久了会越来越乱。第一目录和文件要分清楚。Gitea 程序、act_runner 程序、SQLite 数据、工作流缓存、artifact 存储尽量分目录。升级时只替换二进制文件不要动数据目录。Gitea 也提供了备份命令建议定期执行把数据库和仓库文件一起备份到外部磁盘。第二标签规划要从一开始就做。不同用途的 Runner 用不同 label比如macos-build、linux-arm64-test、release-builder。不要所有任务都挂在同一个 host 标签上否则一旦某个工作流脚本失误可能直接影响宿主 Mac 上的其他服务。第三把密钥和明文配置分开。Gitea 仓库设置里提供了 Secrets 功能数据库密码、云厂商 AccessKey、推送用的 Token 都应该放在 Secrets 里。工作流里通过${{ secrets.XXX }}引用不要在 YAML 文件里明文写密码更不要把 Secrets 直接echo到日志里。第四涉及 clone、推送、发布等操作时要确认为这些操作配置了正确的凭证。Gitea 的 Token 权限要遵循最小化原则仓库只读 Token 就不要给写权限发布 Token 的作用域越窄越好。第五合规边界要写进流程。工作流里处理的数据如果包含用户隐私、版权素材或公司内部信息需要先确认使用授权。自动构建脚本不要悄悄安装未授权的商业软件也不要利用 CI 资源去做与项目无关的运算。Runner 执行环境里产生的大量日志和产物同样属于需要管理的数据资产过期后要及时清理。第六依赖版本要锁住。工作流里使用的actions/checkout、actions/upload-artifact等 Action 尽量固定到明确的版本标签不要用浮动标签避免上游行为变化导致任务突然失败。10. 总结与下一步Gitea Actions 在 Apple 硬件上最值得尝试的点就一句话用一台 M 系列 Mac 原生跑 Git 服务和 CI/CD部署成本极低而且 macOS 宿主机模式能做很多云端 CI 做不了的事情比如 iOS 构建、Xcode 自动打包、苹果生态内的脚本执行。拿到这套环境后建议先做三件事。第一件跑通第一个push - Actions - success的最简流程确认 Gitea 和 act_runner 的通信链路没问题。第二件用矩阵工作流把容器模式验证一遍因为你后续真正写项目时会大量用到容器隔离。第三件刻意制造一次失败任务提前学会看日志和定位问题。这三件事跑完Gitea Actions 的基本能力就算掌握了。最容易踩的坑也提前说清楚第一个是标签不匹配导致任务永远排队第二个是 amd64 镜像在 arm64 Mac 上的执行格式错误第三个是容器环境和 act_runner 之间的依赖没启动。这三个坑占了大多数排障时间建议把排查表打印一份放旁边。后续可以继续扩展的方向包括接入 Webhook 做自动部署把 Runner 放到多台 Apple 设备上组成一个小型任务池或者用 API 把 Gitea Actions 接到自己的运维平台里。整个链路是开放的先跑通最简核心再一步步增加复杂度这样比一开始就堆很多插件和配置要稳得多。