Files
crearte-monorepo/docs/specs/2026-09-30-ci-test-baseline-design.md
T

77 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# P2 CI + 测试基线 — 设计规格
日期:2026-09-30 | 状态:已批准(用户指令:写计划、派子代理、端到端交付、不再提问)
涉及仓库:crearte-server(GitHub 私有)、crearte-deploy(GitHub 私有)、crearte(GitHub 公开)
前置事实:P0/P1/P4/P5 已交付;`crearte/.github/workflows/validate.yml` 已存在(pull_request 触发,node 24,check + e2e 两 job);后端/部署仓零 CI。
## 1. 问题定义(ROADMAP 原文拆解)
1. **后端/部署仓无 CI** → 两仓各建 `validate.yml`,风格对齐前端现有文件。
2. **Postgres 集成层静默 skip** → 后端 CI 必须有真实 Postgres service,且**当且仅当集成测试真的跑了才算绿**(skip 在 CI 上下文里视为失败)。
3. **e2e:stack 全链路** → 真栈 Playwright full-loop 首次进 CI(宿主仓因凭据限制定为 crearte-server,见 2.2 修订)。
## 2. 范围决策
### 2.1 crearte-server `validate.yml`(新文件)
- 触发:`pull_request`(paths: `src/**`、workflow 自身)+ `push: master`。与前端文件同构。
- Job `check`(无 DB):`gofmt -l` 空断言 → `go vet ./...` → `go build ./...` → `go test ./...`。pg-gated 在此 job 正常 skip(语义与本地开发一致)。
- Job `integration`:GitHub `services: postgres`(`postgres:17-alpine`,与 crearte-deploy 所用镜像一致,user/pass/db = crearte);`TEST_DATABASE_URL` 指向**空库**——集成测试自带 `repository.Migrate` 引导(`migrate_test.go:testDB` sync.Once + Migrate;service 层 gated 测试也各自 Migrate),所以**空库即全量真跑,同时白赚一条"从空库可完整迁移"的每 PR 回归**。
- `go test -v -count=1 -p 1 ./...`:**`-p 1` 必须**——各 gated 测试对共享表做 `TRUNCATE`,包间并行(默认并行度)必互相踩踏;`-p 1` 序列化包执行是既有测试写法(不改测试)下唯一的正确姿势。
- **skip 守卫**:`tee` 落日志后 `grep 'skipping postgres integration test'` 命中即 FAIL(pipefail 显式开)。没有守卫,这个 job 退化成 check 的复读机。
- Go 版本:`actions/setup-go@v5` + `go-version-file: src/go.mod`(1.24.1)+ `cache-dependency-path: src/go.sum`。
### 2.2 真栈 e2e 的宿主仓:放在 crearte-server,不放 crearte(计划阶段勘察后的范围修订)
原稿把 e2e-stack 挂进 crearte CI——**勘察后不可行**:`XingfenD/crearte-server` 是私有仓(GitHub API 404 验证),公开仓的 workflow 用默认 token 无法 checkout 它;本机无 GitHub API 凭据,不能自助签发 PAT。反向则天然可行:server 的 workflow checkout 公开的 crearte 无需任何 token。
修订后的布局:
- **e2e-stack job 落进 crearte-server 的 validate.yml**:双 checkout(server=默认 token,crearte=公开无 token,均带 path)+ setup-go(go-version-file 指向 server 仓内 go.mod)+ node 24 + playwright chromium;env `CREARTE_STACK_BACKEND_REF: ${{ github.sha }}`——直接测 **PR 那个后端 commit**,信号强于拉 master(原布局只能测 master 后端)。路径适配:两仓 path 必须使 `dirname(REPO_ROOT)/crearte-server` 命中兄弟仓(即 `path: crearte` 与 `path: crearte-server` 同级);脚本用 `$BACKEND/.git` 的 origin 去 fetch SHA(GitHub 允许 fetch 任意 commit SHA)。working-directory 全部 `crearte/src`。
- **crearte 仓的改动缩小为**:`e2e-stack.sh` 支持 `CREARTE_STACK_GO`(显式指定 go 二进制,解 runner 上 `/usr/local/go/bin/go` 版本地板假绿隐患;不设即旧逻辑,本地零变化)与 `CREARTE_STACK_REQUIRED`(置 1 时栈起不来直接 exit 1,封死“CI 里静默 skip=假绿”通道;不设即本地 skip 模式);`validate.yml` 补 `push: branches: [master]` 触发(既有两 job 不动);CHANGELOG 0.17.0。server CI 的 e2e-stack job 设这两个 env。
- full-loop 假绿风险由 REQUIRED 模式从根上封死:webServer 命令非零退出 → playwright job 红。无需 JSON 报告器断言,不赌 reporter 输出格式。
- 已接受的限制(如实记录):纯前端 PR(只动 crearte)不再有真栈 e2e 信号,改由 server 侧每次 PR/push 带最新 crearte master 补位(前端变更合入后即被覆盖);若 owner 日后在 crearte 仓配 `STACK_BACKEND_TOKEN` secret,可镜像同构 job 回 crearte——列为可选增强,不阻塞交付。
### 2.3 crearte-deploy `validate.yml`(新文件)
- 无编译产物,CI 价值 = compose 健全性:`docker compose --profile dev --profile prod --profile full config -q`(`:?` 守卫变量注入假值,另测**缺 `POSTGRES_PASSWORD` 必须非零退出**,证明守卫没被无声旁路)。
- `config` 不校验 bind 路径存在性,故 `../crearte` 挂载在单仓 checkout 下也能过——**如实记录**:单仓 config ≠ 可部署;跨仓挂载完整性仍由本 monorepo 演练承担(P1 已证)。deploy 仓 README「单仓 clone」小节加一行指向本 spec,防误读。
- deploy 的 AGENTS.md「no host-run workflows」指本机别跑 npm/go;GH Actions 跑 `compose config` 不落任何宿主机进程,不违反。
- CHANGELOG 新 patch 条目(双语)。
### 2.4 全局非目标
- **不做分支保护**:无 GitHub API 凭据(`gh` 缺失、token 库空),owner 在 Settings→Branches 手动开启(server/deploy 必开,crearte 建议开)。写进 spec 交付清单,不进计划任务。
- **不验证 Actions 已启用**:私有仓 Actions 可能因计划限制未开/额度耗尽——推送后无 API 可查证。**如实交付**:计划里把「owner 在 GitHub UI 看 Actions 出现绿勾」列为部署后人工尾巴(与 P5 浏览器验收同类 manual-deferred);本机能验的全验(见 3)。
- 不动 git.yoresee.cc 的 Gitea Actions(内层三仓不在 Gitea)。
- 不重构测试文件(除任务 4 实测证明假绿通道时的最小修复)。
- 不引入 lint(golangci-lint)、不引 codecov。
## 3. 验证策略(无 GitHub API,全部本地等价模拟)
任务 4 在本机以与 CI job **同构的容器化步骤**跑三套:
1. `check`:docker `golang:1.24-alpine`(GOPROXY 走 goproxy.cn——本机网络事实)四连。
2. `integration`:`postgres:17-alpine` 容器(端口 5434,全新空库,`--add-host=host.docker.internal:host-gateway` 让测试容器可达)+ `go test -v -count=1 -p 1 ./...` + skip 守卫。跑**两遍**:第二遍证明 Migrate 幂等下的共存。
3. `e2e:stack`:在 monorepo 真实布局(兄弟仓天然就位、宿主 go 1.26.8)跑 `npm run e2e:stack`,断言日志出现 `[stack] ready:`(真绿非假绿),另跑一次「无 docker 假绿反向验证」用 PATH 屏蔽 docker 令 `stack_ok=0`,确认 SKIP 模式在报告中显式可见。
4. deploy config 正反两路。
推送本身(git push 成功)是 Actions 接收 workflow 的充分条件;job 结果查证为人工尾巴。
## 4. 交付物清单
| 仓库 | 文件 | 动作 |
|---|---|---|
| crearte | `src/scripts/e2e-stack.sh` | `CREARTE_STACK_GO` + `CREARTE_STACK_REQUIRED` 两个 env 开关 |
| crearte | `.github/workflows/validate.yml`、`docs/CHANGELOG.md` | push:master 触发;0.16.1 |
| crearte-server | `.github/workflows/validate.yml`、`docs/CHANGELOG.md` | check + integration + **e2e-stack** 三 job;0.11.1 |
| crearte-deploy | `.github/workflows/validate.yml`、`docs/CHANGELOG.md`、`README.md` | compose config 正反两路;0.5.1 |
| wrapper | 本 spec + 计划、ROADMAP P2 行、CHANGELOG 0.2.4 | 收尾 |
| GitHub UI | Actions 启用确认 + 分支保护 | **owner 手动**(见 2.4) |
## 5. 验收标准(Definition of Done)
1. 三仓 `.github/workflows/validate.yml` 合入各自 master 并推送;分支删除。
2. 任务 4 四组模拟全绿且证据留档(`.superpowers/sdd/p2-task-4-report.md`):integration 首跑必须含 `RUN Test…Postgres`/真跑字样、零 "skipping postgres integration test"。
3. `e2e:stack` 真栈一跑 `[stack] ready:` 出现、full-loop 用例计数 >0 且全过;`CREARTE_STACK_REQUIRED=1` 反路在栈起不来时 exit 1(不静默 skip)。
4. ROADMAP P2 → 完成(三 hash);wrapper CHANGELOG 0.2.4;人工尾巴(Actions 查证、分支保护)在 ROADMAP 行或 spec 有明确记载。