持续集成与持续部署
代码推送到 main,自动测试、构建、上线
🎯 学习目标
学完本章,你将能够:
- 理解 CI/CD 的核心概念和完整工作流程
- 配置 GitHub Actions 工作流实现自动化测试和构建
- 使用 Matrix 构建和缓存优化 CI 性能
- 实现自动部署和预览部署
- 配置分支保护、环境保护和 Secrets 管理
你有没有想过
为什么大公司的代码从不出现"忘记跑测试就上线"的事故?为什么他们的部署只需要点一个按钮?
因为他们有 CI/CD。每次代码推送,机器自动执行:检查代码 → 跑测试 → 构建 → 部署。
这个比喻只是为了帮你建立直观感受:CI/CD 像一条自动质检流水线,代码像产品一样经过每道工序。不合格的产品(代码)会被自动拦截,不会到达客户手中。
CI/CD 流程
从 git push 到自动部署的完整链路

图:CI/CD 完整流程——从 git push 到自动部署
【AI 生图提示词】请生成一张中文技术流程图,风格简洁、配色柔和、适合教学: - 画面主体:从左到右的流水线,展示代码推送到自动部署的完整链路 - 流程结构: 1. 左侧起点:git push 指向 GitHub Actions 2. GitHub Actions 分出三条并行线:Lint 检查、类型检查、单元测试 3. 三条线汇聚到一个菱形判断框"全部通过?" 4. "是"分支:构建 Docker 镜像 → 推送镜像到 Registry → SSH 部署到服务器 → 健康检查 → 部署完成 5. "否"分支:通知开发者 6. 健康检查失败分支:自动回滚 - 配色:通过步骤用绿色,失败/回滚用红色,判断用黄色
核心概念
GitHub Actions 的六个关键词
| 概念 | 说明 | 类比 |
|---|---|---|
| Workflow | 自动化流程定义 | 一条流水线 |
| Event | 触发条件 | 启动按钮 |
| Job | 一组步骤 | 一个工位 |
| Step | 单个操作 | 工位上的动作 |
| Action | 可复用操作单元 | 可复用的工具 |
| Runner | 执行任务的虚拟机 | 执行环境 |
Part 1: 基础 CI
配置一个完整的持续集成流水线。
CI 工作流配置
推代码时自动跑 Lint + 测试 + 构建
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20', cache: 'npm' }
- run: npm ci
- run: npx eslint src/ --max-warnings=0
- run: npx tsc --noEmit
test:
runs-on: ubuntu-latest
needs: lint
services:
postgres:
image: postgres:16-alpine
env: { POSTGRES_DB: test_db, POSTGRES_USER: test_user, POSTGRES_PASSWORD: test_password }
ports: ['5432:5432']
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20', cache: 'npm' }
- run: npm ci
- run: npx prisma migrate deploy
- run: npx vitest run --coverage
触发事件
什么时候跑流水线?你来定
| 事件 | 触发条件 | 示例 |
|---|---|---|
push | 代码推送到分支 | branches: [main] |
pull_request | 创建/更新 PR | types: [opened] |
release | 发布 Release | types: [published] |
schedule | 定时执行 | cron: '0 2 * * *' |
workflow_dispatch | 手动触发 | UI 点击运行 |
使用 npm ci 而不是 npm install。它严格按照 lock 文件安装,速度更快,结果更可预测。
缓存依赖
让 CI 从 3 分钟缩短到 30 秒
每次 CI 运行默认在全新的虚拟机里执行,不缓存任何文件。依赖安装(npm ci)通常占 CI 总时间的 60% 以上。actions/cache 可以把 node_modules 保存到 GitHub 的存储服务,下次直接复用。
actions/setup-node 内置的 cache: 'npm' 就是对 actions/cache 的一行封装。原理完全相同:根据 lock 文件的 hash 生成缓存 key,如果 key 命中则跳过安装。
- uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
path 是要缓存的目录,key 是缓存的唯一标识,restore-keys 是回退匹配(前缀匹配)。hashFiles 基于文件内容生成 hash——lock 文件变了,缓存就失效。
缓存效果对比
一次配置,每次受益
| 场景 | 无缓存 | 有缓存 | 节省 |
|---|---|---|---|
| 首次 push(冷启动) | ~90s | ~90s | --- |
| 第二次 push(lock 文件未变) | ~90s | ~15s | 83% |
| node 小版本升级 | ~90s | ~20s | 78% |
| 新增一个依赖 | ~90s | ~85s | --- |
最佳实践:永远缓存 lock 文件对应的依赖。不要缓存 node_modules 本身(体积大、跨平台不稳定),而是缓存 npm 的下载缓存目录 ~/.npm。
# 完整示例:带缓存的 setup-node
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # 等价于上面的 actions/cache 配置
node_modules 作为 cache path。不同操作系统的编译产物不同,且目录体积可能超过 GitHub 的 10GB 缓存限制。始终缓存 npm/yarn 的下载缓存目录。
分支保护与环境保护
给 main 分支加一把锁
CI 跑完了,但谁能直接把代码推到 main?如果有人直接 git push origin main 跳过了 PR 审查,CI 就没有意义了。分支保护规则强制所有代码必须经过 PR + CI 才能合并。
| 规则 | 作用 | 配置位置 |
|---|---|---|
| Require a pull request | 禁止直接 push,必须走 PR | Branches → Protection Rules |
| Require status checks | CI 必须通过才能合并 | Branch protection → Status checks |
| Require approvals | 至少 N 人批准 | Branch protection → Review |
| Environments | 部署到 production 需要审批 | Environments → Protection rules |
配置保护规则
Settings → Branches → Add rule
- 进入仓库 Settings → Branches
- 点击 Add branch protection rule
- Branch name pattern 填
main - 勾选 Require a pull request before merging
- 勾选 Require status checks to pass,搜索并添加 CI job 名称(如
lint、test) - 点击 Save changes
# 此时 GitHub 会显示:
# "✓ lint — Job succeeded"
# "✓ test — Job succeeded"
# 只有这两个检查都通过,PR 的 Merge 按钮才会变绿
Environments 审批:在 Settings → Environments → production 中添加 Required reviewers,部署到生产环境前必须有人手动批准。这样即便 CI 通过,部署也不会自动发生——除非有人点击 Approve。
Matrix 构建
一次配置,多版本测试
你的代码需要在 Node 18、20、22 上都能跑。手动写三个 job 太蠢——用 strategy.matrix 自动生成多个组合。
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
node-version: [18, 20, 22]
os: [ubuntu-latest, windows-latest]
exclude:
- os: windows-latest
node-version: 18
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npx vitest run
上面的配置会生成 3 x 2 = 6 个并行任务(减去 exclude 的 1 个 = 5 个)。fail-fast: false 确保一个失败不影响其他任务继续运行。
环境变量与上下文
GitHub Actions 的内置变量
GitHub Actions 提供了丰富的上下文变量,可以在 workflow 中直接引用,无需手动配置。
| 变量 | 说明 | 示例值 |
|---|---|---|
${{ github.event_name }} | 触发事件类型 | push |
${{ github.ref_name }} | 分支或 tag 名 | main |
${{ github.sha }} | 完整的 commit SHA | abc123def456... |
github.actor | 触发者用户名 | xiangzz |
github.repository | 仓库名 | user/repo |
github.run_number | 当前运行编号 | 42 |
# 实用技巧:只在 main 分支推送时部署
deploy:
needs: test
if: github.ref_name == 'main'
runs-on: ubuntu-latest
steps:
- run: echo "Deploying commit ${{ github.sha }}"
# 自定义环境变量
env:
NODE_ENV: production
API_URL: https://api.example.com
env 可以定义在 workflow、job、step 三个级别。step 级别的变量会覆盖 job 级别,job 级别会覆盖 workflow 级别。
Secrets 最佳实践
安全地管理敏感信息
数据库密码、API 密钥、SSH 私钥——这些都不能写在代码里。GitHub Secrets 提供了加密存储和安全的引用方式。
| 层级 | 作用域 | 设置位置 |
|---|---|---|
| Repository | 当前仓库 | Settings → Secrets → Actions |
| Organization | 组织内所有仓库 | Organization Settings → Secrets |
| Environment | 特定环境 | Environment → Secrets |
# 引用 Secret(Secret 值会自动在日志中脱敏)
steps:
- name: Deploy
env:
DB_PASSWORD: ${{ secrets.DB_PASSWORD }}
API_KEY: ${{ secrets.API_KEY }}
run: |
echo "Connecting to database..."
psql -h localhost -U admin -p $DB_PASSWORD
***),但不要 echo $SECRET。虽然 GitHub 会脱敏,但你自己的脚本可能会意外泄露。永远通过 env 传递 Secret,不要直接嵌入命令字符串。
可复用 Workflow
避免重复配置
多个项目需要相同的 CI 配置?用 workflow_call 创建可复用的 workflow,像函数一样调用。
# .github/workflows/reusable-test.yml
name: Reusable Test
on:
workflow_call:
inputs:
node-version:
required: true
type: string
secrets:
api-key:
required: true
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/setup-node@v4
with: { node-version: '${{ inputs.node-version }}' }
- run: npm ci
- run: API_KEY=${{ secrets.api-key }} npx vitest run
# 在另一个 workflow 中调用
jobs:
call-test:
uses: ./.github/workflows/reusable-test.yml
with:
node-version: '20'
secrets:
api-key: ${{ secrets.MY_API_KEY }}
可复用 workflow 类似于编程中的"函数"——定义一次,多处调用。当需要更新 CI 逻辑时,只改一处即可。还支持从其他仓库调用:uses: org/repo/.github/workflows/test.yml@main。
手动触发 Workflow
workflow_dispatch——按需运行
有时候你想手动触发一个 workflow(比如手动部署某个 tag,或者清理缓存)。workflow_dispatch 让你可以在 GitHub UI 上点击按钮运行 workflow。
on:
workflow_dispatch:
inputs:
environment:
description: '部署环境'
required: true
default: 'staging'
type: choice
options:
- staging
- production
tag:
description: '镜像标签 (留空使用 latest)'
required: false
type: string
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- run: |
echo "Deploying to ${{ github.event.inputs.environment }}"
echo "Tag: ${{ github.event.inputs.tag }}"
配置后,在 GitHub 的 Actions 页面会出现一个 "Run workflow" 按钮。点击后可以选择输入参数。这对于生产环境的手动部署非常有用。
Part 2: 自动部署
测试通过后,自动构建镜像并部署到服务器。
Part 3: 部署策略
不止是自动部署——还要会回滚、会预览、会安全上线。
预览部署
每个 PR 自动获得独立 URL
每次提交 PR,CI 系统自动为这个分支构建一个临时环境,生成一个独立 URL(如 https://pr-42.example.app)。产品经理、设计师可以直接打开链接查看效果,无需拉代码到本地运行。
| 平台 | 原生支持? | URL 格式 |
|---|---|---|
| Vercel | 是 | project-abc123-pr42.vercel.app |
| Railway | 是 | pr-42.up.railway.app |
| Netlify | 是 | pr-42--project.netlify.app |
| 自托管 Docker | 需手动实现 | Nginx 动态路由 |
# 自托管方案:为每个 PR 启动独立容器
# .github/workflows/preview.yml
on:
pull_request:
types: [opened, synchronize]
jobs:
preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build & Deploy Preview
run: |
docker build -t preview-pr${{ github.event.number }} .
docker stop preview-pr${{ github.event.number }} 2>/dev/null || true
docker run -d --name preview-pr${{ github.event.number }} \
-p 8000:3000 preview-pr${{ github.event.number }}
自托管方案的核心思路:用 Nginx 做反向代理,根据子域名路由到不同端口的容器。pr-42.example.app 映射到 localhost:8042。PR 合并后自动清理容器。
回滚策略
出事了怎么办——三种经典方案
部署失败不可避免,关键是多快能恢复。不同的回滚策略在速度、资源成本、风险之间取舍。
| 策略 | 原理 | 回滚速度 | 资源开销 |
|---|---|---|---|
| 蓝绿部署 | 两套环境并行,切换流量 | 最快(秒级) | 2x |
| 滚动更新 | 逐个替换实例 | 中(分钟级) | 1x + 少量 |
| 金丝雀发布 | 先让 5% 流量走新版本 | 快(分钟级) | 1x + 少量 |
# Docker 回滚:切回上一个镜像标签
# 查看历史镜像标签
$ docker images myapp
REPOSITORY TAG IMAGE ID CREATED
myapp v1.2.3 abc123 2 hours ago
myapp v1.2.2 def456 1 day ago
# 回滚到 v1.2.2
$ docker stop myapp-latest
$ docker run -d --name myapp-latest \
-p 3000:3000 myapp:v1.2.2
# 或者用 docker-compose
$ docker compose down
$ IMAGE_TAG=v1.2.2 docker compose up -d
latest 标签部署生产。latest 是不可变的——你永远不知道它指向哪里。用 myapp:abc1234 才能精确回滚。
部署通知
让团队知道部署结果
部署成功或失败,团队应该第一时间知道。GitHub Actions 可以发送 Slack、Discord、邮件通知。
- name: Notify Slack on failure
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{{
"text": "Deploy FAILED for ${{ github.ref_name }}",
"blocks": [{{
"type": "section",
"text": {{
"type": "mrkdwn",
"text": "Commit ${{ github.sha }} failed to deploy. Check the Actions log."
}}
}}]
}}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
- name: Notify Slack on success
if: success()
uses: slackapi/slack-github-action@v1
with:
payload: |
{{
"text": "Deployed ${{ github.ref_name }} (${{ github.sha }})"
}}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
最佳实践:只在失败时通知(成功时保持安静)。频繁的成功通知会让人忽略真正重要的失败告警。使用 if: failure() 条件控制通知时机。
健康检查
部署后自动验证服务状态
部署完成不等于服务正常。健康检查在部署后自动请求服务的 /health 端点,确认服务真正启动成功。
- name: Health check
run: |
for i in $(seq 1 10); do
if curl -sf ${{ secrets.SERVER_URL }}/health; then
echo "Health check passed"
exit 0
fi
echo "Attempt $i failed, retrying..."
sleep 5
done
echo "Health check failed after 10 attempts"
exit 1
- name: Rollback on failure
if: failure()
run: |
echo "Rolling back to previous version..."
docker compose down
docker compose up -d
上面的脚本会尝试 10 次,每次间隔 5 秒。如果全部失败,则触发回滚。
你的应用应该暴露一个 /health 端点,返回 200 状态码表示健康。可以检查数据库连接、Redis 连接等关键依赖。
完整 CD 工作流
从 push 到部署的完整流水线
把前面学的所有概念组合起来,就是一个生产级的 CD 工作流。
# .github/workflows/deploy.yml
name: Deploy
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '${{ matrix.node-version }}', cache: 'npm' }
- run: npm ci
- run: npx vitest run
deploy:
needs: test
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Build & Push Docker image
run: |
docker build -t myapp:${{ github.sha }} .
docker push myapp:${{ github.sha }}
- name: Deploy to server
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
script: |
docker pull myapp:${{ github.sha }}
docker stop myapp || true
docker run -d --name myapp -p 3000:3000 myapp:${{ github.sha }}
- name: Health check
run: |
sleep 10
curl -sf ${{ secrets.SERVER_URL }}/health || exit 1
多环境部署
staging → production 流水线
生产环境通常有多个部署阶段:开发环境 → 预发布 → 生产。每个环境独立配置,逐步验证。
jobs:
deploy-staging:
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/checkout@v4
- run: |
echo "Deploying to staging..."
# 部署到 staging 服务器
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- run: |
echo "Deploying to production..."
# 部署到生产服务器
只有 staging 部署成功后,才会触发 production 部署。production 环境还需要人工审批。
环境隔离的关键:每个环境使用独立的数据库、独立的配置。staging 环境的代码和生产环境完全一致,只是数据不同。
排错:CI 性能优化
让 CI 跑得更快
CI 慢是开发者最大的痛点之一。以下是常见的优化手段:
| 问题 | 解决方案 | 预期效果 |
|---|---|---|
| 依赖安装慢 | 使用 cache: 'npm' | -75s |
| 测试串行 | 拆分为并行 job | -50% 总时间 |
| 镜像构建慢 | 使用 docker/build-push-action 的 cache | -60s |
| 全量测试 | 只跑变更相关的测试(paths-filter) | -30% 时间 |
# 只在前端代码变更时跑前端测试
frontend-test:
needs: changes
if: ${{ needs.changes.outputs.frontend == 'true' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npx vitest run --project=frontend
# 检测哪些目录有变更
changes:
runs-on: ubuntu-latest
outputs:
frontend: ${{ steps.filter.outputs.frontend }}
steps:
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
frontend:
- 'src/frontend/**'
backend:
- 'src/backend/**'
排错:CI 超时与失败
常见问题的诊断方法
| 症状 | 诊断 | 解决方案 |
|---|---|---|
| Job 卡住不动 | 是否有 pause 或等待输入? | 添加 timeout-minutes: 10 |
| 偶发性失败 | 网络问题或竞态条件? | 添加重试逻辑或 continue-on-error |
| Secret 为 empty | Secret 名称拼写错误? | 区分大小写,检查拼写 |
| Permission denied | Token 权限不足? | 检查 permissions 配置 |
# 使用 act 在本地调试 GitHub Actions
$ npm install -g act
$ act push # 模拟 push 事件
$ act pull_request # 模拟 PR 事件
$ act -j test # 只运行 test job
$ act --secret-file .env # 使用本地 secrets
act 工具用 Docker 在本地运行 GitHub Actions workflow,可以快速调试而不需要反复 push。
部署到 VPS
通过 SSH + Docker 自动部署
# .github/workflows/deploy-vps.yml
name: Deploy to VPS
on:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20', cache: 'npm' }
- run: npm ci
- run: npx vitest run
deploy:
runs-on: ubuntu-latest
needs: test
environment: production
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.VPS_HOST }}
username: ${{ secrets.VPS_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
script: |
cd /opt/myapp
git pull origin main
docker compose up -d --build
docker compose exec -T app npx prisma migrate deploy
Docker 镜像发布
构建并推送到 GitHub Container Registry
# .github/workflows/docker-publish.yml
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions: { packages: write }
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:latest
cache-from: type=gha
cache-to: type=gha,mode=max
自动 Release
打 Tag 自动生成发布说明
# .github/workflows/release.yml
name: Create Release
on:
push:
tags: ['v*']
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- name: Generate changelog
run: |
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^)
git log --pretty=format:"- %s (%h)" $PREV_TAG..HEAD > changelog.txt
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
env: { GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" }
with:
body_path: changelog.txt
generate_release_notes: true
排错:CI 失败
原因:CI 环境和本地不同——Node 版本不一致、数据库没启动、环境变量缺失。
修复:检查
actions/setup-node 的版本号;确认 services 中的数据库健康检查配置;检查 env 环境变量。
# 本地复现 CI 环境
$ act pull_request # 用 act 工具在本地跑 GitHub Actions
最佳实践
让 CI/CD 更快、更安全
| 实践 | 说明 | 收益 |
|---|---|---|
| 缓存依赖 | cache: 'npm' | 加速 CI |
| 最小权限 | 只授予必要的 permissions | 安全 |
| 并行任务 | lint 和 test 并行执行 | 减少总时间 |
| 环境保护 | GitHub Environments 配审批 | 防误部署 |
| Secrets 管理 | 敏感信息存 GitHub Secrets | 安全 |
| 分支保护 | PR 必须通过 CI 才能合并 | 最后防线 |
试一试
练习一
任务:为你的项目创建 .github/workflows/ci.yml,在 push 到 main 时自动运行 ESLint 和测试。
参考答案
name: CI
on:
push:
branches: [main]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20', cache: 'npm' }
- run: npm ci
- run: npx eslint src/
- run: npx vitest run
练习二
任务:添加 npm audit --audit-level=high 到 CI 流水线,实现自动安全审计。
参考答案
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- run: npm audit --audit-level=high
练习三
任务:修改本章的 ci.yml,添加基于 package-lock.json hash 的缓存步骤,让第二次 CI 运行跳过依赖下载。
参考答案
# 在 steps 中添加 cache 步骤(位于 setup-node 之前)
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Cache npm dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-20-npm-${{ hashFiles('package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-20-npm-
- uses: actions/setup-node@v4
with: { node-version: '20', cache: 'npm' }
- run: npm ci
- run: npx vitest run
练习四
任务:为你的仓库配置分支保护规则,要求 PR 必须通过 CI 的 lint 和 test 检查才能合并到 main。
配置步骤
- 进入 GitHub 仓库 → Settings → Branches
- 点击 Add branch protection rule,pattern 填
main - 勾选 Require a pull request before merging
- 勾选 Require status checks to pass before merging
- 搜索
lint和test,选中作为 required checks - 点击 Create 保存
# 验证:直接 push 到 main 会被拒绝
$ git push origin main
remote: error: GH006: Protected branch update failed for refs/heads/main.
remote: error: 2 of 2 required status checks have not succeeded.
📝 要点总结
本章核心知识点
CI 工作流
push 触发 → 安装依赖 → 运行测试 → 构建产物,自动化质量门禁
缓存优化
缓存 node_modules 和构建产物,CI 时间从分钟级降到秒级
自动部署
测试通过后自动部署到预览/生产环境,减少人为失误
安全实践
Secrets 管理敏感信息,环境保护规则控制部署权限
下一章
CI/CD 让部署自动化。下一章我们学习 部署与运维——从 PaaS 到传统 VPS,从 PM2 到 Nginx。