Next.js: CI/CD 与 GitHub Actions

最后更新:2026-08-26

CI/CD 是开发团队的"自动驾驶"——每一次提交都自动经过质量门禁,只有合格的代码才能到达生产环境。

1. 你将学到


2. 一个技术主管的真实故事

(1) 痛点:手动部署,凌晨三点还在盯着终端

Bob 是 TaskFlow 团队的技术主管,团队 5 人维护着一个服务 10,000+ 用户的 SaaS 平台。每周五的发布流程是这样的:

"Alice 合并 PR → 通知 Bob → Bob 在本地跑测试 → 测试通过后手动 vercel deploy --prod → 盯着终端等 10 分钟 → 确认部署成功 → 给团队发消息"

上周,Bob 因为手滑在 npm run build 前忘了 pull 最新代码,部署了一个过时的版本。5 分钟后用户无法登录,回滚又花了 15 分钟。客服收到了 100+ 投诉。

痛点 影响
手动部署 人为失误率高(30% 的发布存在问题)
无自动测试门禁 有问题的代码也能到达生产
环境不一致 本地能跑,生产报错
无 PR Preview 合并前看不到效果

(2) GitHub Actions 的解法

Bob 设计了三条自动化流水线:

YAML
# 每次 PR → 自动 lint + test + build
# 合并到 main → 自动构建 + 部署到 Vercel
# 每天凌晨 → 自动构建 Docker 镜像

(3) 收益

维度 手动部署 CI/CD 自动化
发布耗时 30 分钟 8 分钟
人为失误 30% < 1%
PR 审查效率 只看代码 代码 + Preview URL
部署频率 每周 1 次 每天 5 次

3. GitHub Actions 基础

Actions 是 GitHub 内置的 CI/CD 平台,通过 YAML 文件定义工作流。

100%
graph LR
    A[git push] --> B[GitHub Actions]
    B --> C[Events 触发]
    C --> D[Jobs 并行/串行]
    D --> E[Steps 步骤]
    E --> F[Actions 市场组件]
    
    style B fill:#cce5ff
    style D fill:#d4edda
概念 说明 示例
Workflow 一个 YAML 文件 = 一个自动化流程 .github/workflows/test.yml
Event 触发工作流的事件 pushpull_requestschedule
Job 一组步骤(可并行或依赖) linttestdeploy
Step 单个命令或 Action npm run lintactions/checkout
Runner 执行环境 ubuntu-latestwindows-latest

(1) 工作流文件结构

YAML
# .github/workflows/ci.yml
name: CI Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

env:
  NODE_VERSION: '20'
  TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
      - run: npm ci
      - run: npm run lint

  test:
    needs: [lint]
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: ['18', '20', '22']
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm ci
      - run: npm test
      - run: npm run test:coverage
      - uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}

  build:
    needs: [test]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
      - run: npm ci
      - run: npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: build-output
          path: .next/

4. test.yml:并行矩阵测试

100%
graph TB
    A[Git Push / PR] --> B[lint]
    B --> C[test 18.x]
    B --> D[test 20.x]
    B --> E[test 22.x]
    C --> F[build]
    D --> F
    E --> F
    F --> G[上传构建产物]
    
    style A fill:#cce5ff
    style C fill:#d4edda
    style D fill:#d4edda
    style E fill:#d4edda

(1) 完整的 test.yml

YAML
# .github/workflows/test.yml
name: Test Suite

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  lint:
    name: Lint Check
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
          
      - name: Install dependencies
        run: npm ci
          
      - name: TypeScript type check
        run: npx tsc --noEmit
        
      - name: ESLint check
        run: npm run lint
        
      - name: Prettier check
        run: npx prettier --check "src/**/*.{ts,tsx}"

  unit-and-integration:
    name: Unit & Integration Tests
    needs: [lint]
    runs-on: ubuntu-latest
    timeout-minutes: 15
    
    strategy:
      matrix:
        node-version: ['18', '20', '22']
      fail-fast: false
    
    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
          POSTGRES_DB: testdb
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'
          
      - name: Install dependencies
        run: npm ci
          
      - name: Generate Prisma client
        run: npx prisma generate
          
      - name: Run database migrations
        run: npx prisma migrate deploy
        env:
          DATABASE_URL: postgresql://test:test@localhost:5432/testdb
          
      - name: Run unit tests
        run: npm run test
        env:
          DATABASE_URL: postgresql://test:test@localhost:5432/testdb
          
      - name: Run integration tests
        run: npm run test:integration
        env:
          DATABASE_URL: postgresql://test:test@localhost:5432/testdb
          
      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          flags: unittests
          name: codecov-node-${{ matrix.node-version }}

  e2e:
    name: E2E Tests
    needs: [lint]
    runs-on: ubuntu-latest
    timeout-minutes: 20
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
          
      - name: Install dependencies
        run: npm ci
          
      - name: Install Playwright browsers
        run: npx playwright install --with-deps chromium
        
      - name: Run E2E tests
        run: npx playwright test --project=chromium
        env:
          TEST_DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}
          
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 7

  build:
    name: Build Check
    needs: [unit-and-integration]
    runs-on: ubuntu-latest
    timeout-minutes: 10
    
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'
      - run: npm ci
      - run: npm run build
      
      - uses: actions/upload-artifact@v4
        with:
          name: next-build
          path: .next/
          retention-days: 1

▶ 示例:Matrix 并行测试运行结果

TEXT 📖 仅展示
# GitHub Actions 控制台输出
Job: unit-and-integration (node-version: 18.x)  ✓ 3m 12s
Job: unit-and-integration (node-version: 20.x)  ✓ 2m 58s
Job: unit-and-integration (node-version: 22.x)  ✓ 3m 05s

Summary:
  ✓ lint                        (1 job)  0m 45s
  ✓ unit-and-integration        (3 jobs) 3m 12s
  ✓ e2e                         (1 job)  4m 30s
  ✓ build                       (1 job)  1m 20s

5. deploy.yml:自动部署到 Vercel

部署环境 触发条件 目标
Preview PR 创建/更新 预览分支部署
Production 合并到 main 生产环境更新
Staging 发布 tag 预发布验证

(1) Vercel Deploy 配置

YAML
# .github/workflows/deploy.yml
name: Deploy to Vercel

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

env:
  VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
  VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

jobs:
  deploy-preview:
    name: Deploy Preview
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    timeout-minutes: 15
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Install Vercel CLI
        run: npm install --global vercel@latest
      
      - name: Pull Vercel Environment
        run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }}
        
      - name: Build Project Artifacts
        run: vercel build --token=${{ secrets.VERCEL_TOKEN }}
        
      - name: Deploy to Vercel Preview
        run: |
          vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} > deployment-url.txt
          echo "PREVIEW_URL=$(cat deployment-url.txt)" >> $GITHUB_ENV
      
      - name: Comment Preview URL on PR
        uses: thollander/actions-comment-pull-request@v2
        with:
          message: |
            🚀 **Preview Deployment Ready**
            
            | Environment | URL |
            |:-----------|:----|
            | Preview | ${{ env.PREVIEW_URL }} |
            | Branch | ${{ github.head_ref }} |
            
            _This comment is automatically updated on each push._

  deploy-production:
    name: Deploy Production
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    needs: [deploy-preview]
    runs-on: ubuntu-latest
    timeout-minutes: 15
    environment: production
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Install Vercel CLI
        run: npm install --global vercel@latest
      
      - name: Pull Vercel Environment
        run: vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
        
      - name: Build Project Artifacts
        run: vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
        
      - name: Deploy to Vercel Production
        run: vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}
      
      - name: Notify deployment success
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "✅ Production deployment completed: ${{ github.repository }}@${{ github.sha }}"
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

6. Docker 镜像构建(多阶段 358MB)

对于自托管部署,需要在 CI 中自动构建 Docker 镜像。

(1) 多阶段 Dockerfile

DOCKERFILE
# Dockerfile — Next.js 16 standalone 生产镜像
# Phase 1: 依赖安装(使用缓存层)
FROM node:20-alpine AS deps
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --only=production && \
    npm cache clean --force

# Phase 2: 构建(生成 .next 产物)
FROM node:20-alpine AS build
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
RUN npm run build

# Phase 3: 运行(最小化镜像 ~358MB)
FROM node:20-alpine AS runner
WORKDIR /app

ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1

RUN addgroup --system --gid 1001 nodejs && \
    adduser --system --uid 1001 nextjs

COPY --from=build /app/public ./public
COPY --from=build --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=build --chown=nextjs:nodejs /app/.next/static ./.next/static

USER nextjs

EXPOSE 3000

ENV PORT=3000
ENV HOSTNAME="0.0.0.0"

CMD ["node", "server.js"]

(2) Docker Build Action

YAML
# .github/workflows/docker.yml
name: Docker Build & Push

on:
  push:
    tags: ['v*']
  schedule:
    - cron: '0 2 * * 0'  # 每周日凌晨 2 点

jobs:
  docker:
    runs-on: ubuntu-latest
    timeout-minutes: 30
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3
      
      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      
      - name: Docker meta
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ghcr.io/${{ github.repository }}
          tags: |
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha,format=short
            type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }}
      
      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

▶ 示例:Docker 镜像构建输出

TEXT 📖 仅展示
# Build 日志
#1 [deps 1/1] RUN npm ci --only=production
#1 DONE 15.2s

#2 [build 1/4] COPY --from=deps /app/node_modules ./node_modules
#2 DONE 0.1s

#3 [build 2/4] COPY . .
#3 DONE 0.3s

#4 [build 3/4] RUN npm run build
#4 DONE 28.5s

#5 [runner 1/6] COPY --from=build /app/public ./public
#5 DONE 0.1s

#6 Exporting layers
#6 DONE 3.2s

# 镜像大小分析
ghcr.io/myorg/taskflow:latest   358 MB

7. 环境变量与 Secrets 管理

100%
graph TB
    A[GitHub Secrets] --> B[Actions 运行时]
    B --> C[Vercel Token]
    B --> D[数据库 URL]
    B --> E[API 密钥]
    B --> F[Slack Webhook]
    C --> G[部署到 Vercel]
    D --> H[运行测试]
    E --> I[构建时注入]
    F --> J[部署通知]
    
    style A fill:#cce5ff
    style B fill:#d4edda
环境变量名 用途 来源
VERCEL_TOKEN Vercel API 认证 GitHub Secrets
VERCEL_ORG_ID Vercel 团队 ID Vercel Dashboard
VERCEL_PROJECT_ID Vercel 项目 ID Vercel Dashboard
DATABASE_URL 数据库连接 GitHub Secrets
CODECOV_TOKEN 覆盖率上传 Codecov 网站
SLACK_WEBHOOK_URL 部署通知 Slack 应用配置

(1) 设置 GitHub Secrets

BASH
# 在 GitHub 仓库 → Settings → Secrets and variables → Actions 中添加

gh secret set VERCEL_TOKEN --body "your-vercel-token"
gh secret set DATABASE_URL --body "postgresql://user:pass@host:5432/db"
gh secret set SLACK_WEBHOOK_URL --body "https://hooks.slack.com/services/..."

(2) 环境隔离策略

YAML
# 不同环境的变量覆盖
jobs:
  test:
    env:
      DATABASE_URL: postgresql://test:test@localhost:5432/testdb
      NODE_ENV: test
      
  deploy-preview:
    environment: preview
    env:
      DATABASE_URL: ${{ secrets.PREVIEW_DATABASE_URL }}
      NEXT_PUBLIC_API_URL: ${{ vars.PREVIEW_API_URL }}
      
  deploy-production:
    environment: production
    env:
      DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}
      NEXT_PUBLIC_API_URL: https://api.taskflow.io

8. PR Preview 部署工作流

PR Preview 允许开发者在合并前预览改动效果。

100%
graph TB
    A[开发者创建 PR] --> B[Actions 触发 deploy-preview]
    B --> C[Vercel 创建 Preview Deployment]
    C --> D[生成 preview URL]
    D --> E[Bot 评论 PR 添加链接]
    E --> F[审查者在 Preview 上验证]
    F -->|批准| G[合并到 main]
    G --> H[Actions 触发 deploy-production]
    
    style A fill:#cce5ff
    style F fill:#fff3cd
    style H fill:#d4edda

▶ 示例:完整的 PR Preview + 清理工作流

YAML
# .github/workflows/preview.yml
name: PR Preview

on:
  pull_request:
    types: [opened, synchronize, closed]

jobs:
  preview:
    if: github.event.action != 'closed'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Deploy to Vercel Preview
        id: deploy
        run: |
          npx vercel --token=${{ secrets.VERCEL_TOKEN }} \
            --scope=${{ secrets.VERCEL_ORG_ID }} \
            --confirm > preview-url.txt
          echo "url=$(cat preview-url.txt)" >> $GITHUB_OUTPUT
      
      - name: Comment URL
        uses: actions/github-script@v7
        with:
          script: |
            const url = '${{ steps.deploy.outputs.url }}'
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `🚀 **Preview deployed**\n\n${url}\n\n_Commit: ${context.sha}_`
            })

  cleanup:
    if: github.event.action == 'closed'
    runs-on: ubuntu-latest
    steps:
      - name: Remove Vercel Preview
        run: |
          npx vercel remove \
            taskflow-git-${GITHUB_HEAD_REF//\//-} \
            --token=${{ secrets.VERCEL_TOKEN }} \
            --yes --scope=${{ secrets.VERCEL_ORG_ID }} || true

9. 完整示例:TaskFlow CI/CD 全套流水线

YAML
# .github/workflows/full-pipeline.yml
# ============================================
# TaskFlow 完整 CI/CD 流水线
# ============================================
name: TaskFlow Full Pipeline

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
  release:
    types: [published]

env:
  NODE_VERSION: '20'
  PNPM_VERSION: '9'

jobs:
  # === Phase 1: 质量门禁 ===
  quality-gate:
    name: Quality Gate
    runs-on: ubuntu-latest
    timeout-minutes: 10
    
    steps:
      - uses: actions/checkout@v4
      
      - uses: pnpm/action-setup@v4
        with:
          version: ${{ env.PNPM_VERSION }}
      
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'pnpm'
      
      - run: pnpm install --frozen-lockfile
      
      - name: TypeScript check
        run: pnpm typecheck
        
      - name: ESLint + Prettier
        run: pnpm lint && pnpm format:check
        
      - name: Unit tests with coverage
        run: pnpm test:coverage
        
      - name: Upload coverage
        uses: codecov/codecov-action@v4
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          fail_ci_if_error: false

  # === Phase 2: 集成测试 ===
  integration:
    name: Integration Tests
    needs: [quality-gate]
    runs-on: ubuntu-latest
    timeout-minutes: 20
    
    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_USER: taskflow
          POSTGRES_PASSWORD: taskflow
          POSTGRES_DB: taskflow_test
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
        with:
          version: ${{ env.PNPM_VERSION }}
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'pnpm'
      
      - run: pnpm install --frozen-lockfile
      - run: npx prisma generate
      - run: npx prisma migrate deploy
        env:
          DATABASE_URL: postgresql://taskflow:taskflow@localhost:5432/taskflow_test
      
      - name: Integration tests
        run: pnpm test:integration
        env:
          DATABASE_URL: postgresql://taskflow:taskflow@localhost:5432/taskflow_test
      
      - name: E2E tests
        run: npx playwright install --with-deps chromium && pnpm test:e2e
        env:
          DATABASE_URL: postgresql://taskflow:taskflow@localhost:5432/taskflow_test

  # === Phase 3: 构建 ===
  build:
    name: Build Application
    needs: [integration]
    runs-on: ubuntu-latest
    timeout-minutes: 15
    outputs:
      image-tag: ${{ steps.meta.outputs.tags }}
    
    steps:
      - uses: actions/checkout@v4
      - uses: pnpm/action-setup@v4
        with:
          version: ${{ env.PNPM_VERSION }}
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'pnpm'
      
      - run: pnpm install --frozen-lockfile
      - run: pnpm build
      
      - name: Build Docker image
        if: github.event_name == 'push'
        uses: docker/build-push-action@v5
        with:
          context: .
          load: true
          tags: taskflow:ci-${{ github.sha }}
      
      - name: Save build outputs
        uses: actions/upload-artifact@v4
        with:
          name: build-artifacts
          path: |
            .next/
            public/
            package.json

  # === Phase 4: 部署 ===
  deploy:
    name: Deploy
    needs: [build]
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production
    timeout-minutes: 15
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Deploy to Vercel
        run: |
          npx vercel deploy --prod \
            --token=${{ secrets.VERCEL_TOKEN }} \
            --scope=${{ secrets.VERCEL_ORG_ID }}
      
      - name: Notify Slack
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "✅ TaskFlow deployed to production\nCommit: ${{ github.sha }}\nBy: ${{ github.actor }}"
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
      
      - name: Run Lighthouse CI
        run: npx lhci autorun
        env:
          LHCI_GITHUB_APP_TOKEN: ${{ secrets.LHCI_GITHUB_TOKEN }}

❓ 常见问题

Q GitHub Actions 免费额度够用吗?
A GitHub Free 计划提供 2,000 分钟/月的 Actions 运行时间(公开仓库无限制)。一个典型的 Next.js CI 流水线大约 8-12 分钟/次,2,000 分钟可支撑 ~200 次运行/月。如果需要更多,GitHub Team 提供 3,000 分钟/月,也可以自建自托管 Runner。
Q 为什么需要矩阵测试多个 Node.js 版本?
A 你的用户可能在各种 Node 版本上运行应用(Vercel 用 18.x,Docker 可能用 20.x 或 22.x)。矩阵测试确保代码在所有支持的 Node 版本上都能正常工作。Next.js 16 官方支持 Node 18.17+,测试最新三个 LTS 版本是最佳实践。
Q vercel-action 和 Vercel CLI 哪个更好?
A vercel-actionamondnet/vercel-action)封装了 CLI 的常见操作,但更新可能滞后。推荐直接使用 Vercel CLI(npm install -g vercel),在 Actions 中用 run: vercel deploy --prod --token=... 更灵活且版本最新。
Q 如何保护 Secrets 不被泄露?
A (1) 使用 GitHub Encrypted Secrets 而非明文写在 YAML 中;(2) 对 fork 的 PR 禁用 Secrets 自动注入(Settings → Actions → Fork pull request workflows);(3) 定期轮换 Secrets;(4) 使用 actions/secrets-scanning 检测意外提交。
Q PR Preview 和 Vercel 默认 Preview 有什么区别?
A Vercel 为每个 PR 自动创建 Preview Deployment(与 Git 集成),但你需要登录 Vercel Dashboard 查看 URL。通过 GitHub Actions 手动部署并在 PR 中评论 URL,可以让团队成员直接在 PR 页面点击访问,体验更好。
Q CI 流水线太慢怎么优化?
A (1) 使用 actions/cache 缓存 node_modules.next/cache;(2) 并行执行独立 Job;(3) 使用 fail-fast: false 避免一个版本失败导致全部取消;(4) 使用 pnpm 替代 npm(更快的安装速度);(5) 自托管 Runner(如果仓库很大)。

📖 小节


📝 作业

  1. 基础题(⭐):创建一个 .github/workflows/ci.yml,包含 Lint、Test、Build 三个串行 Job,并在每次 pushmain 时触发。

  2. 进阶题(⭐⭐):为你的项目配置完整的 Vercel 自动部署流水线:(1) 在 GitHub Secrets 中设置 VERCEL_TOKEN;(2) 编写 deploy.yml 实现 PR Preview + 生产部署;(3) 验证 PR 评论中出现 Preview URL。

  3. 挑战题(⭐⭐⭐):实现一个多环境 CI/CD 流水线(dev → staging → production):(1) 每个环境有不同的数据库和 Secrets;(2) dev 分支自动部署到 dev 环境;(3) main 分支自动部署到 staging;(4) 手动批准后才能部署到 production;(5) 部署成功后通过 Webhook 通知 Slack 频道。

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏