# .gitea/workflows

Gitea Actions workflow 定义。

## 红线

### 1. CI 检查必须 fail-fast，禁止 silent fail-open

不允许出现这类组合：

```bash
# ❌ 错误：错误被吞成空字符串后下游"看起来 0 个问题"
CHANGED=$(git diff --name-only "origin/${BASE_REF}...HEAD" 2>/dev/null || true)

# ❌ 错误：浅历史下三点语法失败 → 全静默
git fetch origin develop --depth=1
CHANGED=$(git diff --name-only "origin/develop...HEAD" 2>/dev/null || true)
```

正确写法：

```bash
# ✅ 显式可达性校验，找不到就 exit 1
if ! git rev-parse --verify "origin/${BASE_REF}" >/dev/null 2>&1; then
  echo "❌ origin/${BASE_REF} not found; checkout step needs fetch-depth: 0"
  exit 1
fi

# ✅ 不吞错误，让 diff 失败直接 step 红
CHANGED=$(git diff --name-only "origin/${BASE_REF}...HEAD")
```

良性的 `|| true` 保留：`grep` / `sed` 无匹配返回 1 在 pipefail 下需要兜底，但前提是输入本身不是 fail-open 来源（如上面的 `CHANGED`）。

### 2. CI 检查必须有负向测试

新增/修改任意 PR 门禁 step（quality-gates、contract-check、env-coverage、migration-file-count 等）后，必须**构造反例 PR 验证应该红的真红**：

- 加迁移检查 → 故意提交 2 个迁移目录的 PR，确认 CI 红
- 加 env coverage → 故意引用未声明的 `process.env.X`，确认 CI 红
- 加 silent fail-open 修复 → 模拟 base ref 不可达，确认 step exit 1

只看正向通过不算验证。Silent fail-open 的本质是"假装通过"，正向测试覆盖不到。

### 3. 不在 CI 里 `git fetch ... --depth=1`

`actions/checkout@v4` 默认浅克隆，需要历史时显式设 `fetch-depth: 0`。再叠加 `--depth=1` 会把已有历史砍掉，三点 diff（依赖 merge-base）失败。

## 历史踩坑

- `.learnings/2026-05-01-ci-shallow-fetch-fail-open.md`：migration-file-count silent fail-open 完整三层成因
- `.learnings/2026-05-01-test-db-silent-state-corruption.md`：CI runner 共享 docker 容器跨 PR 污染（issue #211）
