feat: 添加 chi 练习与 Gitea CI 实践

- 新增 chi 路由示例并更新依赖
- 用简单 CI 实践笔记替换 Polaris 部署笔记
- 添加本地 Gitea 与 runner 配置示例,注册令牌从环境变量读取
This commit is contained in:
2026-10-16 15:02:25 +08:00
parent 3280c8a7bc
commit d63b006135
7 changed files with 553 additions and 331 deletions
@@ -0,0 +1,143 @@
# Gitea 简单 CI 实践
> 用 Gitea Actions 给仓库搭一条最简单的持续集成:推送代码后自动做语法检查、跑单元测试。
## 1. 启用 Gitea Actions
- Gitea 1.21 及以上版本的实例默认启用 Actions(`app.ini` 中 `[actions] ENABLED` 默认为 true);
- 但仓库级默认禁用,需在仓库 **设置 → Actions** 中打开。
## 2. 准备并注册 act_runner
job 由 act_runner 执行,可以二进制安装或者容器安装,官方文档:<https://docs.gitea.com/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 快速上手:<https://docs.gitea.com/usage/actions/quickstart/>
- act_runner 注册:<https://docs.gitea.com/runner/registration/>
- act_runner 标签:<https://docs.gitea.com/runner/labels/>