代码质量门禁与 CI 集成:把 Lint/TypeCheck/Test 变成“可执行的合并规则”

HTMLPAGE 团队
18 分钟阅读

给出一套可落地的代码质量门禁方案:如何定义门禁标准、如何把 lint/typecheck/test 组合成可维护的 CI 工作流、如何在 monorepo 中按影响范围执行,以及如何降低误报与噪音,让团队真正愿意遵守。

#CI #代码规范 #质量门禁 #TypeScript #ESLint #单元测试 #monorepo

代码质量门禁与 CI 集成:把 Lint/TypeCheck/Test 变成“可执行的合并规则”

“代码质量”如果只靠口头约定,最终会变成:

  • 高手自律、普通人随缘
  • 临近发版集中修,成本爆炸
  • 代码风格争论消耗协作时间

真正有效的做法是:把质量标准写成可执行的门禁,并且让门禁:

  • 对业务有价值(能拦住真实问题)
  • 对开发友好(失败原因清晰、能快速修复)
  • 对协作友好(噪音低、不折磨人)

本文给你一套可复用的“门禁体系 + CI 工作流”方案,重点放在落地细节。


1. 什么是“门禁”(Quality Gates),它和“规范”有什么区别

  • 规范:建议你怎么写
  • 门禁:不满足就不能合并

门禁的关键不是“更严格”,而是:

  • 把团队的最低质量底线固化下来
  • 把问题尽可能前置(PR 阶段解决,而不是上线后)

一个成熟团队通常会有三道核心门禁:

  1. 静态检查(Lint):拦住明显 bug 与坏味道
  2. 类型检查(TypeCheck):拦住 TS 类型层面的错误
  3. 自动化测试(Test):拦住行为回归

2. 门禁标准怎么定:先从“最小可行”开始

门禁失败会影响开发节奏,所以第一版标准必须“可持续”。

2.1 推荐的最小可行门禁(MVP)

  • Lint:必须通过(允许少量 legacy 目录豁免)
  • TypeCheck:必须通过
  • Unit Test:必须通过(先覆盖核心模块/关键流程)

如果你还没有测试体系,建议先把 Lint + TypeCheck 立起来,再逐步补测试。

2.2 让门禁“可维护”的 3 条原则

  1. 失败信息要可读:一眼知道怎么修
  2. 修复路径要清晰:本地一条命令能复现
  3. 噪音要可控:不要因为格式/低价值规则导致频繁失败

3. 本地脚本设计:让开发者“跑得起、修得动”

门禁要成功,必须先把本地体验做好。

建议把脚本按“检查/修复”拆开:

  • lint:只检查
  • lint:fix:自动修复
  • typecheck:类型检查
  • test:单测

示例(思路为主,按你项目实际替换命令):

{
  "scripts": {
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "typecheck": "tsc -p tsconfig.json --noEmit",
    "test": "vitest run"
  }
}

团队约定:

  • 本地提交前优先跑 lint:fix
  • PR 门禁永远跑 lint(只检查,不改代码)

4. CI 工作流:把门禁做成可重复、可定位的流水线

4.1 GitHub Actions 示例(最小可用)

name: quality
on:
  pull_request:

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
        with:
          version: 9
      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: pnpm
      - run: pnpm install --frozen-lockfile
      - run: pnpm lint
      - run: pnpm typecheck
      - run: pnpm test

关键点:

  • --frozen-lockfile:确保依赖解析可重复
  • 每一步独立输出:失败更容易定位

4.2 并行化:让 CI 更快

当项目变大后,可以拆成多个 job 并行跑:

  • lint job
  • typecheck job
  • test job

这样单步失败不阻塞其他步骤,也更容易看出瓶颈。


5. Monorepo 的门禁策略:从“全量”走向“按影响范围”

在 monorepo 中,如果每次 PR 都全量跑,很快就会遇到:

  • CI 太慢,大家开始绕开门禁
  • 失败太多,大家对门禁失去信任

推荐分两阶段:

5.1 第一阶段:先跑通全量(建立基线)

  • pnpm -r lint
  • pnpm -r typecheck
  • pnpm -r test

先让流程稳定。

5.2 第二阶段:按包过滤(增量门禁)

用 pnpm 的 --filter 精准执行:

  • 只跑当前 app:pnpm --filter landing lint
  • 修改了共享包时,再跑依赖它的 app

如果仓库规模更大,可以引入任务编排与缓存(例如 Turborepo/Nx),但这属于“增量优化”,不是起步必需。


6. 降噪:门禁失败的“真实成本”来自误报

门禁最容易失败的原因不是 bug,而是:

  • 规则过度严格
  • 历史债务太多
  • 失败信息太难读

实战策略:

6.1 对 legacy 目录做“隔离与渐进收敛”

  • 先对新增代码严格
  • 旧代码允许临时豁免
  • 用“每周清债”逐步降低豁免范围

6.2 把格式化从争论变成自动化

  • 本地保存/提交自动格式化
  • CI 只检查

这样既统一风格,又避免 CI 改代码。


7. 让门禁真正有效:从“拦住问题”到“推动改进”

成熟的门禁体系会进一步做到:

  • 对关键指标做趋势(失败率、平均修复时间)
  • 对常见失败原因沉淀 FAQ
  • 对质量薄弱模块增加专项测试

当门禁成为团队的“反馈系统”,它就不再是负担。


延伸阅读