404 Not Found

404 Not Found


nginx

CI/CD

Bob keeps making mistakes every time he deploys manually—forgetting to run migrations, configuring environment variables incorrectly, and pushing code to production before it passes tests. Charlie needs to automate CI/CD: automatic testing upon code push, automatic pull request previews, and automatic deployment to production from the main branch, all with zero manual intervention.

1. What You'll Learn


2. A True Story of an Administrator

(1) Pain Point: Frequent errors during manual deployment

Bob manually deploys MegaShop. The steps are: 1) Clone the repository 2) Run npm install 3) Run tests (forgot) 4) Build 5) Upload 6) Run migrations (forgot) 7) Restart the service. Missing even one step causes a problem, resulting in an average of two deployment incidents per month.

(2) A Solution Using GitHub Actions CI/CD

The full process runs automatically with every push:

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

(3) Benefits: Zero labor + Zero accidents

Fully automated testing, building, and deployment after code is pushed; pull requests automatically generate preview environments; code merges into the main branch are automatically deployed; and deployment incidents are reduced to zero.


3. GitHub Actions Workflows

(1) CI/CD Pipeline Stages

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 ✅]

(1) ▶ Example: A Complete CI Workflow

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/

Output:

TEXT
CI/CD pipeline loaded
Pipeline status: passed
Tests: 12 passed, 0 failed

4. Code Quality Control

(1) Quality Access Control Configuration

(1) ▶ Example: ESLint + Prettier Configuration

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

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

Output:

TEXT
// Execution Successful

(2) ▶ Example: package.json scripts

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"
  }
}

Output:

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) Quality Access Control Metrics

Metric Tool Access Threshold Description
Code Style ESLint 0 errors Consistent Code Style
Formatting Prettier 0 warnings Auto-formatting
Type Checking vue-tsc 0 errors TypeScript Type Safety
Unit Tests Vitest ≥ 80% coverage Core Logic Coverage
E2E Testing Playwright All Passed Key Process Validation
Bundle Size rollup-plugin < 200 KB gzip Prevent size bloat

5. Multi-Environment Management

(1) ▶ Example: Environment Configuration File

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 }}

(2) ▶ Example: Staging Deployment Workflow

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!"}

Output:

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

(1) Environmental Comparison

Environment Purpose Database Deployment Method Access Permissions
development local development local PostgreSQL npm run dev developers
staging Integration Testing Standalone PostgreSQL Automated (main push) Team
production Production Environment Production PostgreSQL Automatic (tag/release) All Users

6. Deployment Strategy

(1) Comparison of Deployment Strategies

Strategy Principle Downtime Rollback Speed Complexity
Direct Replacement Phase Out and Roll Out 🔴 Yes (5-30 s) 🟡 Redeployment 🟢 Simple
Blue-Green Deployment Switching Between Two Environments 🟢 None 🟢 Switching in Seconds 🟡 Medium
Rolling Update Replace Instances One by One 🟢 None 🟡 Roll Back One by One 🟡 In Progress
Canary Release Validate with a Small User Base First 🟢 None 🟢 Roll Back Immediately 🔴 Complex

(1) ▶ Example: Blue-Green Deployment Script

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"

Output:

TEXT
CONTAINER ID   IMAGE     STATUS    
abc123         latest    Up 2 hours

7. Comprehensive Example: The Complete MegaShop CI/CD Process

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

❓ FAQ

Q Is the free quota for GitHub Actions sufficient?
A Public repositories have unlimited minutes. Private repositories have a monthly limit of 2,000 minutes. A single CI run on MegaShop takes about 10 minutes, so 10 runs per day would total about 100 minutes—which is sufficient.
Q How is the PR preview environment set up?
A It's automatically generated using Vercel Preview. Each PR has its own URL (pr-123-megashop.vercel.app), which is automatically deleted after the pull request is merged.
Q How do I run database migrations in CI?
A In CI, use prisma migrate deploy (which only applies existing migrations and does not create new ones). During development, use prisma migrate dev to create migration files and commit them to Git.
Q How can I achieve zero-downtime deployment?
A Blue-green deployment (switching between two environments) or PM2 cluster reload (replacing workers one by one). For Docker, use rolling updates (docker compose rolling update).
Q How do I perform a rollback?
A Use git revert and redeploy. For Docker, restart using the previous image tag. For PM2, use pm2 stop and then start the old version. To automate rollbacks, add logic for handling failed health checks to the deployment script.
Q How should secrets be securely managed?
A Store secrets in GitHub Secrets as environment variables; never commit them to Git. The private fields in runtimeConfig are server-side only. In CI, reference them using ${{ secrets.XXX }}.

📖 Summary


📝 Exercises

  1. Basic Exercise (Difficulty: ⭐): Create a GitHub Actions CI workflow that automatically runs linting, type checking, and tests when a commit is pushed.
  2. Advanced Exercise (Difficulty ⭐⭐): Add automatic deployment to staging—when the main branch is pushed, automatically deploy to the staging server and run a smoke test after deployment.
  3. Challenge (Difficulty: ⭐⭐⭐): Implement a blue-green deployment script + automatic rollback—automatically switch back to the previous version when a health check fails

---|

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

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

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