1. 目标定义:这条流水线要“证明什么”
在画图之前先写威胁模型与成功标准:你要防御的是依赖投毒、配置漂移、还是秘密泄露? 可信 CI 的最低承诺通常是:(1)每次合并前自动验证测试与静态检查; (2)构建产物可指向唯一源码版本;(3)关键步骤有审计日志与不可抵赖的制品 digest。 把目标写清,才能避免“扫描器堆成圣诞树却没人看报告”。
2. 端到端蓝图:从触发到制品入库
典型路径:触发(PR / push)→ 准备(检出、缓存恢复)→ 静态与单元 → 安全与合规扫描(可并行) → 构建(容器镜像或二进制)→ SBOM / 签名元数据 → 推送到制品库。 其中“安全扫描”与“构建”的顺序可依策略调整:有时需要先构建出可扫描的中间产物。
3. 多语言:矩阵、容器与可复用工作流
单仓库多服务(monorepo)常用 affected 分析只构建变更子图;多仓库则靠可复用工作流(reusable workflow)统一门禁。 每种语言固定工具链版本(.tool-versions、mise、Docker 基础镜像),避免“本地能过 CI 不能过”的玄学。
# Caller workflow invokes reusable pipeline (illustrative GitHub Actions)
name: ci
on:
pull_request:
jobs:
gate:
uses: my-org/pipelines/.github/workflows/reusable-ci.yml@main
with:
language: java
java-version: "21"
secrets: inherit
4. 缓存与隔离:快而不脏
缓存提速,但也可能投毒缓存:密钥按分支隔离,fork PR 默认不给写缓存或限定作用域。 容器构建优先多阶段镜像与BuildKit cache mount;语言依赖锁定文件(lockfile)是缓存 key 的核心组成部分。
| Layer | What to pin | Why it matters for trust |
|---|---|---|
| Toolchain | JDK / Node / Go minor | reproducible compiler behavior |
| Dependencies | lockfile + hash | supply-chain integrity |
| Base image | digest, not floating tag | eliminate silent drift |
| Test data | versioned fixtures | flake detection |
5. 门禁与度量:让失败“可行动”
门禁不是越多越好:覆盖率阈值、关键路径单测、SCA 严重级别应与发布节奏匹配。 指标建议同时跟踪:变更前置时间、主分支恢复时间、 flaky 测试率、CI 成本/次合并(衔接 FinOps 章)。 失败信息要指向修复 owner与文档链接,否则开发者只会点 re-run 直到绿。
6. 多环境在 CI 中的边界
CI 验证构建与测试;指向 staging 的集成测试可用环境密钥与部署预览实现。 生产秘密不应长期躺在仓库变量里:用 OIDC 换短期令牌、用 KMS/Vault 做运行时注入。 “多环境”在 CI 里体现为不同输入集合与门禁强度(例如 main 分支才跑全套 SAST)。
7. 落地检查清单(可贴到 PR 模板)
- 分支保护与必需状态检查(required checks)已启用。
- lockfile + 基础镜像 digest 已纳入策略。
- fork PR / 外部贡献的缓存与密钥边界已审查。
- 制品以 digest 引用;SBOM 随 release 存档。
- 度量:CI 时长、失败分类、flaky 列表周更。
8. 本章清单与预告
- 能画出可信 CI 主干:触发 → 验证 → 构建 → 证据 → 入库。
- 能设计多语言复用策略与工具链固定方式。
- 理解缓存的安全边界与 lockfile 在完整性中的角色。
- 能定义门禁与度量,使失败可行动、可跟踪。
- 下一章:案例 2——安全 CD 交付链(审批、回滚、GitOps、审计)。