Skip to content

ADR-0009: CI Quality Gates ​

Date: 2026-07-28 Status: Accepted Last revised: 2026-09-23 — 门禁表按现状更新:git 钩子改由 lefthook 托管(ADR 0018),pre-push 是一张门禁表(并行) 聚合命令 pnpm --silent prepush(含 lint:root / types:check:root / lint:docs-refs / lint:adr / lint:doc-ts / lint:doc-budget);CI 步骤补 boundaries / affected 自检 / syncpack / pnpm change check / lint:root / types:check:root / lint:adr / lint:doc-ts / lint:doc-budget; test 任务的依赖是 ["^build"];@walnut/axios 已更名 @walnut/http。

Context ​

The monorepo has 3 apps and 4 packages. Pre-merge validation needs to catch type errors, lint violations, and regressions before they reach production.

Current state:

  • simple-git-hooks: pre-commit runs lint-staged, pre-push runs pnpm types:check
  • turbo.json has test task but only @walnut/server has vitest configured
  • @walnut/utils, @walnut/contract, @walnut/client have vitest config but no tests yet
  • No CI workflow running tests on push/PR

Decision ​

Three-tier quality gate:

GateWhenWhatBlocks
Pre-commitgit commitlint-staged (ESLint auto-fix on staged files)Commit
Pre-pushgit pushpnpm --silent prepush — five sections: boundaries → types:check → syncpack:lint → lint:workflows (actionlint) → pnpm change check (fixed-group lockstep). Hook definition lives in lefthook.yml, see ADR 0018Push
CIPush to main / PRpnpm boundaries → affected lint / types:check / test → affected-set sanity check → pnpm syncpack:lint → pnpm change check → buildsMerge

Test requirements by package:

PackageTest requirement
@walnut/utilsRequired — pure functions, easy to test
@walnut/contractRequired — snapshot tests for constants, no-duplicate-code validation
@walnut/clientEventually — complex browser/Vue code, needs jsdom setup
@walnut/httpEventually — adapter tests need mock server
@walnut/adminEventually — complex SPA, needs component/E2E tests
@walnut/serverExisting — vitest + playwright already configured

turbo.json test task: dependsOn: ["^build"] — a package's tests need its upstream dependencies' build output (@walnut/contract / @walnut/utils publish a CJS dist/ for the backend's require path, see ADR 0002), while the package under test is not built first: Vitest compiles it from source with esbuild. ["^build"] (not ["build"]) is the correct chain per industry standard (see 行业调研:测试体系).

Alternatives considered ​

  • Keep the pre-decision setup (simple-git-hooks with a pre-push that only ran pnpm types:check, and no CI workflow running tests) —— pre-merge validation has to catch type errors, lint violations, and regressions before they reach production.
  • Run the test suite in the pre-push hook instead of in CI —— pre-push is deliberately kept in the ten-second class, so it executes no tests; CI is what catches test failures without blocking the local development flow.

Consequences ​

  • Pre-push stays in the ten-second class (no test execution) even though it now runs five sections
  • CI catches test failures without blocking local development flow
  • Shared packages (utils, contract) get test coverage first — highest blast radius
  • Version drift inside versioning.fixed is caught on the PR that introduces it (pnpm change check), not at release time
  • turbo test runs affected packages only (via --affected with an explicit TURBO_SCM_BASE/TURBO_SCM_HEAD base)

基于 MIT 许可发布