# Gitea 简单 CI 实践 > 用 Gitea Actions 给仓库搭一条最简单的持续集成:推送代码后自动做语法检查、跑单元测试。 ## 1. 启用 Gitea Actions - Gitea 1.21 及以上版本的实例默认启用 Actions(`app.ini` 中 `[actions] ENABLED` 默认为 true); - 但仓库级默认禁用,需在仓库 **设置 → Actions** 中打开。 ## 2. 准备并注册 act_runner job 由 act_runner 执行,可以二进制安装或者容器安装,官方文档: 准备前提: - runner 所在机器能通过网络访问 Gitea 实例; - 本例用 Docker 执行器(label 里的 `docker://` 前缀),runner 所在机器需有可用的 Docker。 本地 [Compose 示例](local-ci/compose.yaml) 用 `gitea/runner` 容器跑 Runner: 首次启动时 entrypoint 读取 `GITEA_INSTANCE_URL`、`GITEA_RUNNER_REGISTRATION_TOKEN`、 `GITEA_RUNNER_LABELS` 自动完成注册,注册结果写入挂载的 `/data` 下的 `.runner` 文件并持久化,重启容器不必手动注册。只有不用 Compose、直接安装二进制运行时, 才需要手动执行注册与启动命令(见官方文档)。 注册令牌等同凭据:妥善保管,只在环境变量、secrets 或 runner 自己的配置文件里 出现,不要写进命令行历史、文档或仓库文件。 label 的格式是 `名称:执行器://参数`:以 Compose 中的 `demo-ci:docker://node:22-bookworm` 为例,名称为 `demo-ci`, 执行器为 Docker,参数为 job 容器镜像 `node:22-bookworm`。 工作流的 `runs-on` 按**名称部分**匹配。 ### Runner 的 Docker 运行方式 `docker://` 表示 job 在容器中运行,但不决定 Runner 使用哪一个 Docker daemon: | 术语 | 含义 | 取舍 | |---|---|---| | DinD(Docker-in-Docker) | Runner 容器里另起 Docker daemon,由它创建 job 容器 | 与宿主 Docker 分开;job 默认也与外层 Compose 网络分开,需要配置访问 Gitea 的路径 | | DooD(Docker-outside-of-Docker) | Runner 通过宿主机 Docker socket 使用宿主 daemon,不在容器里另起 daemon | 配置和缓存复用较简单;能接触 socket 的 job 可能控制宿主 Docker,不宜在共享宿主上运行不可信代码 | | Rootless | Docker daemon 以非 root 身份运行,不是 DinD 的反义词 | 可以与 DinD 组合;降低部分权限风险,但仍需留意网络、存储限制及宿主资源访问 | 本地 [Compose 示例](local-ci/compose.yaml) 使用需要特权容器的 `3-dind`, 并非 Rootless。三种术语并不处于同一分类:DinD / DooD 说明**使用哪个 daemon**, Rootless 说明**daemon 以什么权限运行**;它也不等于完全隔离。 ## 3. 最小 Node demo 两个零依赖文件,放在仓库根目录。 `demo.js`(CommonJS,一个加法函数): ```js function add(a, b) { return a + b; } module.exports = { add }; ``` `demo.test.js`(用 Node 内置的 `node:test` 与 `node:assert/strict`): ```js const test = require('node:test'); const assert = require('node:assert/strict'); const { add } = require('./demo'); test('整数相加', () => { assert.equal(add(1, 2), 3); }); test('负数相加', () => { assert.equal(add(-1, -2), -3); }); ``` 先在本地验证能跑:`node --test demo.test.js`。 ## 4. 编写工作流 在仓库根目录新建 `.gitea/workflows/ci.yml`: ```yaml name: Demo CI on: push: branches: [main] # 默认分支不是 main 的仓库按需修改 jobs: test: runs-on: demo-ci steps: - name: 检出仓库 uses: https://gitea.com/actions/checkout@v4 - name: 语法检查 run: node --check demo.js && node --check demo.test.js - name: 运行测试 run: node --test demo.test.js ``` 要点: - `runs-on: demo-ci` 必须与 runner 注册 label 的名称部分一致; - Gitea 中 `uses` 不带完整 URL 时默认从 GitHub 拉取,这里显式写 gitea.com 的绝对地址;runner 仍需能访问该 action 源; - job 会跑在 label 指定的 `node:22-bookworm` 容器里:Node 内置测试运行器 开箱即用,不需要 `npm install`,也没有任何第三方依赖。 ## 5. 推送并查看结果 ```bash git add demo.js demo.test.js .gitea/workflows/ci.yml git commit -m "add demo CI" git push ``` 打开仓库 **Actions** 页,能看到这次 push 触发的 run; 点进 job 可展开每个 step 的日志。 常见排查: | 现象 | 排查方向 | |---|---| | run 一直等待、没有 job 被领取 | 仓库 Actions 是否启用;runner 是否在线(runner 页看状态);`runs-on` 与 runner label 名称是否一致。不同版本对不匹配的处理有差异,结合 runner 日志与实际执行环境核对 | | checkout 步骤失败或超时 | runner 到 Gitea 实例、到 action 源(gitea.com)的网络连通性 | | 语法检查或测试失败 | 直接看该 step 日志里的报错/断言输出,在本地用同样命令复现 | 本地用 Compose + Docker-in-Docker 时,还要区分 runner 容器与 job 容器的网络: runner 能解析 `gitea`,不代表 job 也能。若 checkout 的 `git fetch` 报 `Could not resolve host: gitea`,先检查 job 容器能否访问 Gitea,而不只是检查 runner。 本地测试可在 Runner 配置的 `container:` 下设 `network: host`,通过 Compose 挂载该配置、设置 `CONFIG_FILE` 并重建 runner,见 [本地 Compose 示例](local-ci/compose.yaml)。 这里的 `host` 指 runner 容器所在的网络命名空间,不是宿主机网络;job 与 runner 共用网络会降低隔离性,只适合可信的本地测试,不应直接当作生产环境通用方案。 ## 参考 - Gitea Actions 快速上手: - act_runner 注册: - act_runner 标签: