来源:本文档由重组合并生成 ——
20-cicd-pipeline.md+21-release-and-rollback.md(CI/CD 与发布属同一交付流程)+ examples 精选示例。冲突处以更具体/更新版本为准,双方独有内容均保留。
建立自动化的持续集成/持续部署流水线,确保每次代码变更都经过自动验证,每次发布都经过标准化流程,减少人为失误和重复劳动。
- 项目初始化时搭建 CI/CD
- 新增自动化检查环节
- 部署流程需要标准化
- 流水线失败需要排查
- 优化构建速度
标准流水线阶段:
代码提交 → Lint → 类型检查 → 单元测试 → 构建 → 集成测试 → 部署 → 健康检查
| 阶段 | 内容 | 失败处理 | 耗时目标 |
|---|---|---|---|
| Lint | ESLint/Biome 代码检查 | 阻断 | < 30s |
| Type Check | tsc --noEmit | 阻断 | < 30s |
| Unit Test | 单元测试 + 覆盖率 | 阻断 | < 2min |
| Build | 编译/打包 | 阻断 | < 3min |
| Integration Test | API/数据库测试 | 阻断 | < 5min |
| Security Scan | 依赖漏洞扫描 | 警告/阻断 | < 1min |
| Deploy (staging) | 部署到预发布 | 阻断 | < 3min |
| E2E Test | 关键路径测试 | 阻断 | < 5min |
| Deploy (prod) | 部署到生产 | 手动确认 | < 3min |
| Health Check | 验证服务正常 | 自动回滚 | < 1min |
| 事件 | 触发的阶段 | 说明 |
|---|---|---|
| Push to dev | Lint → Test → Build | 快速反馈 |
| Pull Request | 全部检查(不部署) | 合并门禁 |
| Push to main | 全部 + 部署 staging | 预发布验证 |
| Tag (v*) | 全部 + 部署 production | 正式发布 |
| 手动触发 | 可选阶段 | 特殊场景 |
| 定时(每日) | 依赖扫描 + E2E | 定期巡检 |
## .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, dev]
pull_request:
branches: [main]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm typecheck
- run: pnpm test -- --coverage
- run: pnpm build
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: pnpm audit --audit-level=high加速构建的缓存策略:
- 依赖缓存:
node_modules/ pnpm store - 构建缓存:
.next/cache/dist/ - 测试缓存:Jest/Vitest 缓存
- Docker 层缓存:多阶段构建
缓存失效条件:
- lock 文件变更 → 重新安装依赖
- 源代码变更 → 重新构建
- 配置变更 → 清除相关缓存
失败处理策略:
- Lint/Test 失败:阻断合并,通知提交者
- Build 失败:阻断部署,通知团队
- Deploy 失败:自动回滚到上一版本
- E2E 失败:阻断生产部署,允许人工判断
通知规则:
- PR 检查失败 → 通知 PR 作者
- main 分支构建失败 → 通知团队
- 生产部署失败 → 立即通知 + 自动回滚
- 依赖发现高危漏洞 → 创建 Issue
构建制品:
- Docker 镜像:推送到 Container Registry(GHCR)
- 标签策略:
latest(最新)+v1.2.3(版本)+sha-abc123(提交) - 保留策略:最近 30 个版本 + 所有 release 标签
制品不可变原则:
- 同一版本号的制品不可覆盖
- 生产部署使用确定版本(非 latest)
- 回滚 = 部署上一个确定版本
## 部署环境配置
environments:
staging:
url: https://staging.example.com
auto_deploy: true # main 分支自动部署
production:
url: https://example.com
auto_deploy: false # 需要手动确认或 tag 触发
reviewers: [admin] # 需要审批- CI 流水线已配置(lint + test + build)
- PR 合并有自动化门禁
- 部署流程自动化(至少 staging)
- 生产部署有确认机制
- 构建使用了缓存(速度可接受)
- 失败有通知机制
- 部署失败有回滚方案
- 密钥通过 Secrets 注入(非明文)
- 制品有版本标签
- 流水线总耗时 < 10 分钟
| 输出物 | 格式 | 存放位置 |
|---|---|---|
| CI 配置 | YAML | .github/workflows/ci.yml |
| CD 配置 | YAML | .github/workflows/deploy.yml |
| Dockerfile | Docker | docker/Dockerfile |
| 流水线文档 | Markdown | docs/ci-cd.md |
| 误区 | 正确做法 |
|---|---|
| CI 太慢(>15min) | 使用缓存、并行、只跑受影响的测试 |
| 生产部署用 latest 标签 | 使用确定版本号 |
| 密钥写在配置文件中 | 使用 GitHub Secrets |
| 部署失败无回滚 | 自动回滚或一键回滚 |
| 所有分支都跑完整流水线 | 按分支/事件分级触发 |
| 流水线失败忽略继续 | 失败必须阻断,不允许带病合并 |
规范版本发布的完整流程,确保每次发布可控、可追溯、可回滚,降低发布风险,保障线上服务稳定。
- 准备发布新版本到生产环境
- 发布后发现问题需要回滚
- 紧急热修复(Hotfix)
- 制定发布计划和检查清单
格式:MAJOR.MINOR.PATCH
| 部分 | 何时递增 | 示例 |
|---|---|---|
| MAJOR | 不兼容的 API 变更 | 1.0.0 → 2.0.0 |
| MINOR | 向后兼容的新功能 | 1.0.0 → 1.1.0 |
| PATCH | 向后兼容的 Bug 修复 | 1.0.0 → 1.0.1 |
预发布标签:
v1.2.0-alpha.1— 内部测试v1.2.0-beta.1— 公开测试v1.2.0-rc.1— 发布候选
代码就绪:
- 所有计划功能已合并到 main
- CI 全部通过(lint/test/build)
- 代码审查已完成
- 无已知的阻断性 Bug
质量就绪:
- 测试覆盖率达标
- E2E 关键路径通过
- 安全审查通过
- 性能无退化
运维就绪:
- 数据库迁移已准备(且可回滚)
- 环境变量/配置已更新
- 依赖服务已确认兼容
- 回滚方案已确认
- 监控/告警正常
文档就绪:
- CHANGELOG 已更新
- API 文档已更新(如有变更)
- 部署文档已更新(如流程变化)
1. 确认发布范围(包含哪些变更)
2. 更新 CHANGELOG
3. 更新版本号(package.json / 版本文件)
4. 创建 Release PR → 审查 → 合并到 main
5. 打 Tag:git tag -a v1.2.0 -m "Release v1.2.0"
6. 推送 Tag → 触发 CI/CD 部署
7. 验证生产环境(健康检查 + 冒烟测试)
8. 确认发布成功
9. 通知相关方
当生产环境出现紧急 Bug:
1. 从 main 创建 hotfix 分支:hotfix/fix-xxx
2. 修复 Bug(最小改动)
3. 补充回归测试
4. 快速审查(可简化,但不能跳过)
5. 合并到 main + 打 patch tag(v1.0.1)
6. 部署
7. 验证
8. 将修复同步回 dev 分支
Hotfix 原则:
- 只修复紧急问题,不夹带其他改动
- 改动尽可能小
- 修复后必须补测试
- 事后复盘(为什么会到生产才被发现?)
回滚触发条件:
- 发布后错误率显著上升(> 5%)
- 核心功能不可用
- 数据异常/损坏
- 性能严重退化(P95 > 正常值 3 倍)
回滚步骤:
1. 确认需要回滚(判断影响面)
2. 决策:回滚代码 or 回滚数据 or 两者
3. 执行回滚:
- 代码回滚:部署上一个稳定版本的镜像
- 数据回滚:执行 down 迁移(如适用)
4. 验证回滚成功
5. 通知相关方
6. 排查问题根因
7. 修复后重新走发布流程
回滚注意事项:
- 数据库迁移如果有破坏性变更(删列/删表),回滚前评估数据影响
- 回滚后确认缓存是否需要清理
- 如果新旧版本 API 不兼容,考虑是否需要前端同步回滚
每次发布必须更新 CHANGELOG:
### [1.2.0] - 2024-03-15
#### Added(新增)
- 用户头像上传功能 (#45)
- 订单导出 CSV (#52)
#### Changed(变更)
- 优化列表页加载速度 (#48)
#### Fixed(修复)
- 修复并发下订单号重复 (#51)
#### Security(安全)
- 升级 jsonwebtoken 修复 CVE-2024-xxxx
#### Breaking Changes(破坏性变更)
- 移除废弃的 /api/v1/legacy 端点- 版本号遵循 SemVer
- 发布前检查清单全部通过
- CHANGELOG 已更新
- Tag 已打并推送
- 部署成功且健康检查通过
- 冒烟测试通过(核心功能可用)
- 监控指标正常(无异常波动)
- 回滚方案已确认可执行
- 相关方已通知
- 发布记录已归档
| 输出物 | 格式 | 存放位置 |
|---|---|---|
| CHANGELOG | Markdown | 项目根目录 CHANGELOG.md |
| Git Tag | Tag | Git 仓库 |
| Release Notes | Markdown | GitHub Releases |
| 发布记录 | 日志 | docs/releases/ |
| 误区 | 正确做法 |
|---|---|
| 不打 tag 直接部署 | 每次发布必须有对应 tag |
| 发布后不验证 | 必须健康检查 + 冒烟测试 |
| 回滚没有预案 | 发布前就确认回滚方案 |
| Hotfix 夹带私货 | Hotfix 只修紧急问题 |
| CHANGELOG 不写 | 每次发布必须更新 |
| 周五下午发布 | 避免在非工作时间/无人值守时发布 |
# GitHub Actions CI/CD 流水线示例
# 复制到 .github/workflows/ci.yml,按项目实际命令调整
# 配套文档:phase-5-delivery/20-cicd-pipeline.md
name: CI/CD
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
# 同一分支新提交自动取消旧的运行,省资源
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
# ---------- 代码质量 + 测试 ----------
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # 缓存依赖,加速
- name: Install dependencies
run: npm ci # 用 ci 而非 install,保证 lock 一致
- name: Lint
run: npm run lint
- name: Type check
run: npm run typecheck
- name: Unit tests
run: npm test -- --coverage
- name: Security audit
run: npm audit --audit-level=high
continue-on-error: true # 审计告警不阻断,但会提示
# ---------- 构建 + 部署(仅 main 分支)----------
deploy:
needs: test # 测试通过才部署
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: docker build -t myapp:${{ github.sha }} .
# 部署方式二选一:
# 方式 A:SSH 到自托管服务器拉取并重启
- name: Deploy to server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /app
git pull
docker compose pull
docker compose up -d --build
docker image prune -f # 清理旧镜像
# 方式 B(如推送到镜像仓库):
# - docker login / docker push ...
- name: Notify on failure
if: failure()
run: echo "部署失败,检查日志" # 可接入邮件/webhook 通知# 多阶段构建 Dockerfile 示例(Node 全栈应用)
# 配套文档:phase-5-delivery/22b-server-hardening.md
# 关键点:多阶段减小镜像体积 + 非 root 用户运行
# ---------- 构建阶段 ----------
FROM node:20-alpine AS builder
WORKDIR /app
# 先只复制依赖清单,利用缓存层(依赖没变则不重装)
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# ---------- 运行阶段 ----------
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# 只装生产依赖
COPY package*.json ./
RUN npm ci --omit=dev && npm cache clean --force
# 从构建阶段拷贝产物
COPY --from=builder /app/dist ./dist
# 创建非 root 用户并切换(安全加固:容器不以 root 运行)
RUN addgroup -S app && adduser -S app -G app
USER app
EXPOSE 3000
# 健康检查(可选,供编排层探活)
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -qO- http://127.0.0.1:3000/health || exit 1
CMD ["node", "dist/main.js"]