mcpField
D
deployment-automation

deployment-automation

github.com/jmsktm云与 DevOps1066 次安装40 周前更新
代码仓库

关于 deployment-automation

面向在生产环境运行 Next.js 应用的团队,汇集 Vercel 部署、GitHub Actions CI/CD、环境管理、检查、监控及回滚指南。

主要功能

  1. 01Vercel 生产与预览部署
  2. 02GitHub Actions CI/CD 示例
  3. 03环境变量管理
  4. 04部署前检查与数据库迁移
  5. 05监控与回滚策略
  6. 06功能标记与性能预算

适用场景

  1. 01设置 Next.js 自动部署
  2. 02为拉取请求配置预览构建
  3. 03发布前运行检查与迁移
  4. 04规划生产环境回滚流程

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jmsktm/claude-settings --skill deployment-automation

安装后在 Claude Code 中输入/deployment-automation确认已加载。

详细介绍

部署自动化技能

概述

此技能可帮助你在生产环境中部署和管理 Next.js 应用。从 Vercel 部署到 CI/CD 流水线,这里涵盖了实现顺畅自动化部署所需的一切。

Vercel 部署

安装 Vercel CLI
npm i -g vercel
vercel login
部署到生产环境
# Deploy to production
vercel --prod

# Deploy with specific environment
vercel --prod --env production
从 Git 部署
# Link project
vercel link

# Auto-deploy on git push (configured in Vercel dashboard)
git push origin main  # Auto-deploys to production
git push origin develop  # Auto-deploys to preview
项目配置
// vercel.json
{
  "buildCommand": "npm run build",
  "devCommand": "npm run dev",
  "installCommand": "npm install",
  "framework": "nextjs",
  "regions": ["iad1"],  // AWS us-east-1
  "env": {
    "NEXT_PUBLIC_APP_URL": "https://myapp.com"
  },
  "build": {
    "env": {
      "NEXT_PUBLIC_VERCEL_ENV": "@vercel-env"
    }
  },
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "X-Content-Type-Options",
          "value": "nosniff"
        },
        {
          "key": "X-Frame-Options",
          "value": "DENY"
        },
        {
          "key": "X-XSS-Protection",
          "value": "1; mode=block"
        }
      ]
    }
  ],
  "redirects": [
    {
      "source": "/old-page",
      "destination": "/new-page",
      "permanent": true
    }
  ],
  "rewrites": [
    {
      "source": "/api/:path*",
      "destination": "https://api.backend.com/:path*"
    }
  ]
}
环境变量

使用 Vercel CLI:

# Add production env var
vercel env add SUPABASE_URL production

# Add to all environments
vercel env add DATABASE_URL

# Pull env vars locally
vercel env pull .env.local

使用 Vercel 控制面板:

  1. 前往 Project Settings → Environment Variables
  2. 为每个环境添加变量:
    • Production
    • Preview
    • Development

加密密钥:

# Add sensitive data
vercel secrets add database-url "postgresql://..."

# Reference in vercel.json
{
  "env": {
    "DATABASE_URL": "@database-url"
  }
}
特定环境配置
// lib/config.ts
const config = {
  development: {
    apiUrl: 'http://localhost:3000/api',
    supabaseUrl: process.env.NEXT_PUBLIC_SUPABASE_URL!,
  },
  preview: {
    apiUrl: process.env.NEXT_PUBLIC_API_URL!,
    supabaseUrl: process.env.NEXT_PUBLIC_SUPABASE_URL!,
  },
  production: {
    apiUrl: 'https://api.myapp.com',
    supabaseUrl: process.env.NEXT_PUBLIC_SUPABASE_URL!,
  },
}

const env = (process.env.NEXT_PUBLIC_VERCEL_ENV || 'development') as keyof typeof config

export const appConfig = config[env]

GitHub Actions CI/CD

基本工作流
# .github/workflows/ci.yml
name: CI

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

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Run linter
        run: npm run lint

      - name: Run type check
        run: npm run type-check

      - name: Run tests
        run: npm test

      - name: Build
        run: npm run build
        env:
          NEXT_PUBLIC_SUPABASE_URL: ${{ secrets.NEXT_PUBLIC_SUPABASE_URL }}
          NEXT_PUBLIC_SUPABASE_ANON_KEY: ${{ secrets.NEXT_PUBLIC_SUPABASE_ANON_KEY }}
从 GitHub Actions 部署到 Vercel
# .github/workflows/deploy.yml
name: Deploy to Vercel

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Deploy to Vercel
        uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          vercel-args: '--prod'
在 PR 上运行测试
# .github/workflows/pr-checks.yml
name: PR Checks

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

jobs:
  quality:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'
          cache: 'npm'

      - name: Install dependencies
        run: npm ci

      - name: Lint
        run: npm run lint

      - name: Type check
        run: npm run type-check

      - name: Unit tests
        run: npm test

      - name: E2E tests
        run: npx playwright test

      - name: Upload test results
        if: always()
        uses: actions/upload-artifact@v3
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30

部署前检查

自定义部署前脚本
// package.json
{
  "scripts": {
    "predeploy": "npm run check:all",
    "check:all": "npm run lint && npm run type-check && npm test",
    "lint": "next lint",
    "type-check": "tsc --noEmit",
    "test": "jest"
  }
}
健康检查端点
// app/api/health/route.ts
import { NextResponse } from 'next/server'

export async function GET() {
  try {
    // Check database connection
    await db.$queryRaw`SELECT 1`

    // Check external services
    const services = await Promise.allSettled([
      fetch(process.env.API_URL!),
      fetch(process.env.SUPABASE_URL!),
    ])

    const allHealthy = services.every(s => s.status === 'fulfilled')

    return NextResponse.json({
      status: allHealthy ? 'healthy' : 'degraded',
      timestamp: new Date().toISOString(),
      version: process.env.NEXT_PUBLIC_APP_VERSION,
      services: {
        database: 'healthy',
        api: services[0].status === 'fulfilled' ? 'healthy' : 'unhealthy',
        supabase: services[1].status === 'fulfilled' ? 'healthy' : 'unhealthy',
      }
    })
  } catch (error) {
    return NextResponse.json(
      { status: 'unhealthy', error: error.message },
      { status: 503 }
    )
  }
}

数据库迁移

部署时运行迁移
// package.json
{
  "scripts": {
    "build": "npm run db:migrate && next build",
    "db:migrate": "npx supabase db push"
  }
}
安全迁移策略
# 1. Test migration locally
npx supabase db reset
npx supabase db push

# 2. Create backup
npx supabase db dump > backup.sql

# 3. Apply to production
npx supabase db push --linked

# 4. Verify
curl https://myapp.com/api/health

监控与警报

Vercel Speed Insights
// app/layout.tsx
import { SpeedInsights } from '@vercel/speed-insights/next'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <SpeedInsights />
      </body>
    </html>
  )
}
Vercel Analytics
import { Analytics } from '@vercel/analytics/react'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        {children}
        <Analytics />
      </body>
    </html>
  )
}
自定义监控
// lib/monitoring.ts
export async function reportDeployment() {
  if (process.env.NODE_ENV === 'production') {
    await fetch('https://monitoring.example.com/deployments', {
      method: 'POST',
      body: JSON.stringify({
        version: process.env.NEXT_PUBLIC_APP_VERSION,
        timestamp: new Date().toISOString(),
        environment: process.env.NEXT_PUBLIC_VERCEL_ENV,
      }),
    })
  }
}

// Call in app initialization
reportDeployment()

功能标志

基于环境的标志
// lib/features.ts
export const features = {
  newDesign: process.env.NEXT_PUBLIC_FEATURE_NEW_DESIGN === 'true',
  betaFeatures: process.env.NEXT_PUBLIC_FEATURE_BETA === 'true',
  analytics: process.env.NEXT_PUBLIC_FEATURE_ANALYTICS === 'true',
}

// Usage
import { features } from '@/lib/features'

export function Component() {
  if (!features.newDesign) {
    return <OldDesign />
  }
  return <NewDesign />
}
高级功能标志(Vercel Edge Config)
import { get } from '@vercel/edge-config'

export async function getFeatureFlag(key: string): Promise<boolean> {
  try {
    return await get<boolean>(key) ?? false
  } catch {
    return false
  }
}

// Usage in Server Component
export default async function Page() {
  const showNewFeature = await getFeatureFlag('new-feature')

  if (showNewFeature) {
    return <NewFeature />
  }
  return <OldFeature />
}

回滚策略

即时回滚
# List deployments
vercel ls

# Promote previous deployment to production
vercel promote <deployment-url>
出错时自动回滚
# .github/workflows/deploy-with-rollback.yml
name: Deploy with Rollback

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Get previous deployment
        id: prev-deploy
        run: |
          PREV_URL=$(vercel ls --token=${{ secrets.VERCEL_TOKEN }} | grep production | head -n 2 | tail -n 1 | awk '{print $2}')
          echo "prev_url=$PREV_URL" >> $GITHUB_OUTPUT

      - name: Deploy to Vercel
        id: deploy
        run: |
          URL=$(vercel --prod --token=${{ secrets.VERCEL_TOKEN }})
          echo "deploy_url=$URL" >> $GITHUB_OUTPUT

      - name: Health check
        id: health
        run: |
          sleep 10
          STATUS=$(curl -s -o /dev/null -w "%{http_code}" ${{ steps.deploy.outputs.deploy_url }}/api/health)
          if [ $STATUS -ne 200 ]; then
            echo "Health check failed"
            exit 1
          fi

      - name: Rollback on failure
        if: failure()
        run: |
          vercel promote ${{ steps.prev-deploy.outputs.prev_url }} --token=${{ secrets.VERCEL_TOKEN }}

预览部署

自动生成预览 URL

每次推送分支都会获得一个唯一 URL:

https://myapp-git-feature-branch-username.vercel.app
在 PR 中添加预览评论
# .github/workflows/preview-comment.yml
name: Preview Deployment Comment

on:
  deployment_status:

jobs:
  comment:
    if: github.event.deployment_status.state == 'success'
    runs-on: ubuntu-latest

    steps:
      - name: Comment PR
        uses: actions/github-script@v6
        with:
          script: |
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: `✅ Preview deployed to ${process.env.DEPLOYMENT_URL}`
            })

性能预算

Lighthouse CI
# .github/workflows/lighthouse.yml
name: Lighthouse CI

on:
  pull_request:
    branches: [main]

jobs:
  lighthouse:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Run Lighthouse CI
        uses: treosh/lighthouse-ci-action@v10
        with:
          urls: |
            https://preview-url.vercel.app
            https://preview-url.vercel.app/dashboard
          uploadArtifacts: true
          temporaryPublicStorage: true
Bundle 大小检查
# .github/workflows/bundle-size.yml
name: Bundle Size Check

on: [pull_request]

jobs:
  size:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - name: Install dependencies
        run: npm ci

      - name: Build
        run: npm run build

      - name: Check bundle size
        uses: andresz1/size-limit-action@v1
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}

最佳实践检查清单

  • 使用环境变量保存密钥
  • 设置从 Git 自动部署
  • 为 PR 配置预览部署
  • 实现健康检查端点
  • 设置监控和警报
  • 部署前运行测试
  • 使用功能标志逐步发布
  • 制定回滚策略
  • 监控 Core Web Vitals
  • 设置性能预算
  • 安全地运行数据库迁移
  • 使用独立环境(dev/staging/prod)
  • 实现完善的错误跟踪
  • 记录部署流程

何时使用此技能

在以下情况下调用此技能:

  • 首次部署到 Vercel
  • 设置 CI/CD 流水线
  • 配置环境变量
  • 实现功能标志
  • 设置监控
  • 制定回滚策略
  • 优化部署速度
  • 调试生产环境问题
  • 管理预览部署
  • 设置自动化测试

评论

支持 Markdown