Nuxt: CI/CD

最后更新:2026-08-26

Bob 每次手动部署都出错——忘记跑迁移、环境变量配错、代码没过测试就上线。Charlie 需要自动化 CI/CD:代码推送自动测试、PR 自动预览、main 自动部署生产,零人工干预。

1. 你将学到


2. 一个管理员的真实故事

(1) 痛点:手动部署频繁出错

Bob 手动部署 MegaShop,步骤:1) 拉代码 2) npm install 3) 跑测试(忘了)4) 构建 5) 上传 6) 跑迁移(忘了)7) 重启服务。漏一步就出问题,平均每月 2 次部署事故。

(2) GitHub Actions CI/CD 的解法

每次 push 自动执行完整流程:

YAML
# .github/workflows/deploy.yml
on: push
jobs:
  test:    → lint + typecheck + vitest
  build:   → npm run build
  deploy:  → docker compose up -d

(3) 收益:零人工 + 零事故

代码推送后全自动测试/构建/部署,PR 自动生成预览环境,main 合并自动上线,部署事故降到零。


3. GitHub Actions 工作流

(1) CI/CD 流水线阶段

100%
flowchart LR
    A[Push / PR] --> B[Lint + TypeCheck]
    B --> C[Unit Tests]
    C --> D[Build]
    D --> E{Branch?}
    E -->|PR| F[Preview Deploy]
    E -->|main| G[Staging Deploy]
    G --> H[Smoke Test]
    H --> I[Production Deploy]
    I --> J[Health Check]
    J --> K[Done ✅]

▶ 示例:完整的 CI 工作流

YAML
# .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: npm run lint
      - run: npx nuxi typecheck

  test:
    runs-on: ubuntu-latest
    needs: lint
    services:
      postgres:
        image: postgres:16-alpine
        env:
          POSTGRES_USER: test
          POSTGRES_PASSWORD: test
          POSTGRES_DB: megashop_test
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5
    env:
      DATABASE_URL: postgresql://test:test@localhost:5432/megashop_test
    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: npm run test:coverage
      - name: Upload coverage
        uses: codecov/codecov-action@v4

  build:
    runs-on: ubuntu-latest
    needs: test
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: 'npm'
      - run: npm ci
      - run: npx prisma generate
      - run: npm run build
      - name: Upload build artifact
        uses: actions/upload-artifact@v4
        with:
          name: nuxt-build
          path: .output/

输出:

TEXT 📖 仅展示
CI/CD pipeline loaded
Pipeline status: passed
Tests: 12 passed, 0 failed

4. 代码质量门禁

(1) 质量门禁配置

▶ 示例:ESLint + Prettier 配置

TYPESCRIPT
// nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxt/eslint'],

  eslint: {
    config: {
      stylistic: {
        indent: 2,
        quotes: 'single',
        semi: false
      }
    }
  }
})

输出:

TEXT 📖 仅展示
// 执行成功

▶ 示例:package.json 脚本

JSON
{
  "scripts": {
    "dev": "nuxi dev",
    "build": "nuxi build",
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "typecheck": "nuxi typecheck",
    "test": "vitest run",
    "test:watch": "vitest",
    "test:coverage": "vitest run --coverage",
    "test:e2e": "playwright test",
    "validate": "npm run lint && npm run typecheck && npm run test"
  }
}

输出:

JSON
{
  "scripts": {
    "dev": "nuxi dev",
    "build": "nuxi build",
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "typecheck": "nuxi typecheck",
    "test": "vitest run",
    "test:watch": "vitest",
    "test:coverage": "vitest run --coverage",
    "test:e2e": "playwright test",
    "validate": "npm run lint && npm run typecheck && npm run test"
  }
}

(2) 质量门禁指标

指标 工具 门禁阈值 说明
代码风格 ESLint 0 errors 统一代码风格
格式化 Prettier 0 warnings 自动格式化
类型检查 vue-tsc 0 errors TypeScript 类型安全
单元测试 Vitest ≥ 80% coverage 核心逻辑覆盖
E2E 测试 Playwright 全部通过 关键流程验证
Bundle 大小 rollup-plugin < 200KB gzip 防止体积膨胀

5. 多环境管理

▶ 示例:环境配置文件

TEXT 📖 仅展示
# .env.development
DATABASE_URL=postgresql://dev:dev@localhost:5432/megashop_dev
REDIS_URL=redis://localhost:6379
JWT_ACCESS_SECRET=dev-access-secret

# .env.staging
DATABASE_URL=postgresql://staging:xxx@staging-db.internal:5432/megashop_staging
REDIS_URL=redis://staging-redis.internal:6379
JWT_ACCESS_SECRET=${{ secrets.STAGING_JWT_SECRET }}

# .env.production
DATABASE_URL=postgresql://prod:xxx@prod-db.internal:5432/megashop
REDIS_URL=redis://prod-redis.internal:6379
JWT_ACCESS_SECRET=${{ secrets.PROD_JWT_SECRET }}

▶ 示例:Staging 部署工作流

YAML
# .github/workflows/deploy-staging.yml
name: Deploy Staging

on:
  push:
    branches: [main]

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    needs: [lint, test]
    environment: staging
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to staging
        run: |
          ssh staging-server << 'EOF'
          cd /opt/megashop
          git pull origin main
          docker compose up -d --build
          docker compose exec web npx prisma migrate deploy
          EOF

      - name: Smoke test
        run: |
          sleep 10
          curl -f https://staging.megashop.com/api/products?limit=1 || exit 1

      - name: Notify team
        if: failure()
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {"text": "Staging deployment failed!"}

输出:

TEXT 📖 仅展示
CONTAINER ID   IMAGE          STATUS         PORTS
abc123         nginx:latest   Up 2 hours     0.0.0.0:80->80/tcp

(1) 环境对比

环境 用途 数据库 部署方式 访问权限
development 本地开发 本地 PostgreSQL npm run dev 开发者
staging 集成测试 独立 PostgreSQL 自动(main push) 团队
production 正式环境 生产 PostgreSQL 自动(tag/release) 全部用户

6. 部署策略

(1) 部署策略对比

策略 原理 停机时间 回滚速度 复杂度
直接替换 停旧启新 🔴 有(5-30s) 🟡 重新部署 🟢 简单
蓝绿部署 两套环境切换 🟢 无 🟢 秒级切换 🟡 中
滚动更新 逐个替换实例 🟢 无 🟡 逐个回滚 🟡 中
金丝雀发布 小流量先验证 🟢 无 🟢 立即切回 🔴 复杂

▶ 示例:蓝绿部署脚本

BASH
#!/bin/bash
# deploy-blue-green.sh

CURRENT=$(docker compose ps --format '{{.Name}}' | grep -o 'blue\|green' | head -1)
if [ "$CURRENT" = "blue" ]; then
  NEXT="green"
else
  NEXT="blue"
fi

echo "Deploying to $NEXT environment..."

# Build and start next environment
docker compose -f docker-compose.yml -f docker-compose.$NEXT.yml up -d --build

# Wait for health check
for i in {1..30}; do
  if curl -sf http://localhost:3001/health; then
    echo "Health check passed"
    break
  fi
  sleep 2
done

# Switch Nginx to new environment
sed "s/$CURRENT/$NEXT/g" nginx.conf > /tmp/nginx.conf
docker compose exec nginx nginx -s reload

echo "Switched from $CURRENT to $NEXT"

输出:

TEXT 📖 仅展示
CONTAINER ID   IMAGE     STATUS    
abc123         latest    Up 2 hours

7. 综合示例:MegaShop CI/CD 全流程

YAML
# .github/workflows/deploy-production.yml
name: Deploy Production

on:
  release:
    types: [published]

jobs:
  deploy:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4

      - name: Build Docker image
        run: docker build -t megashop:${{ github.sha }} .

      - name: Push to registry
        run: |
          docker tag megashop:${{ github.sha }} ghcr.io/megashop/megashop:latest
          echo ${{ secrets.GHCR_TOKEN }} | docker login ghcr.io -u $ --password-stdin
          docker push ghcr.io/megashop/megashop:latest

      - name: Deploy to production
        run: |
          ssh prod-server << EOF
          cd /opt/megashop
          docker pull ghcr.io/megashop/megashop:latest
          docker compose up -d --no-build
          docker compose exec web npx prisma migrate deploy
          EOF

      - name: Health check
        run: |
          for i in {1..10}; do
            if curl -sf https://megashop.com/api/health; then exit 0; fi
            sleep 5
          done
          exit 1

      - name: Rollback on failure
        if: failure()
        run: |
          ssh prod-server << EOF
          cd /opt/megashop
          docker compose down
          docker tag megashop:previous megashop:latest
          docker compose up -d
          EOF

❓ 常见问题

Q GitHub Actions 免费额度够用吗?
A 公开仓库无限分钟。私有仓库每月 2000 分钟,MegaShop 一次 CI 约 10 分钟,每天 10 次约 100 分钟,够用。
Q PR 预览环境怎么实现?
A 用 Vercel Preview 自动生成。每个 PR 有独立 URL(pr-123-megashop.vercel.app),合并后自动删除。
Q 数据库迁移在 CI 中怎么跑?
A CI 中用 prisma migrate deploy(只应用已有迁移,不创建新的)。开发时用 prisma migrate dev 创建迁移文件并提交到 Git。
Q 怎么实现零停机部署?
A 蓝绿部署(两套环境切换)或 PM2 cluster reload(逐个替换 worker)。Docker 用滚动更新(docker compose rolling update)。
Q 回滚怎么操作?
A Git revert + 重新部署。Docker 用之前的镜像 tag 重新启动。PM2 用 pm2 stop + 启动旧版本。自动化回滚在部署脚本中加入 health check 失败逻辑。
Q 密钥怎么安全管理?
A GitHub Secrets 存环境变量,永远不提交到 Git。runtimeConfig 私有字段只服务端可用。CI 中通过 ${{ secrets.XXX }} 引用。

📖 小节


📝 作业

  1. 基础题(难度⭐):创建 GitHub Actions CI 工作流,push 时自动跑 lint + typecheck + test
  2. 进阶题(难度⭐⭐):添加 staging 自动部署——main push 自动部署到 staging 服务器,部署后跑 smoke test
  3. 挑战题(难度⭐⭐⭐):实现蓝绿部署脚本 + 自动回滚——health check 失败时自动切回旧版本

---|

Web-Tutorial.com

Web-Tutorial 技术团队

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

100%

🙏 帮我们做得更好

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

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