404 Not Found

404 Not Found


nginx

CI/CD

Bob continua cometendo erros toda vez que implanta manualmente—esquece de executar migrações, configura variáveis de ambiente incorretamente e faz push de código para produção antes que passe nos testes. Charlie precisa automatizar o CI/CD: testes automáticos ao fazer push de código, preview automático em pull requests, e implantação automática em produção a partir do branch main, tudo com zero intervenção manual.

1. O Que Você Vai Aprender


2. Uma História Real de um Administrador

(1) Dor: Erros frequentes durante a implantação manual

Bob implanta o MegaShop manualmente. Os passos são: 1) Clonar o repositório 2) Executar npm install 3) Executar testes (esqueceu) 4) Build 5) Upload 6) Executar migrações (esqueceu) 7) Reiniciar o serviço. Faltar um único passo causa um problema, resultando em uma média de dois incidentes de implantação por mês.

(2) Uma Solução Usando GitHub Actions CI/CD

O processo completo roda automaticamente a cada push:

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

(3) Benefícios: Zero trabalho manual + Zero acidentes

Testes, build e implantação totalmente automatizados após o push de código; pull requests geram automaticamente ambientes de preview; merges de código no branch main são implantados automaticamente; incidentes de implantação são reduzidos a zero.


3. Workflows do GitHub Actions

(1) Estágios do Pipeline CI/CD

100%
flowchart LR
    A[Push / PR] --> B[Lint + TypeCheck]
    B --> C[Testes Unitários]
    C --> D[Build]
    D --> E{Branch?}
    E -->|PR| F[Deploy Preview]
    E -->|main| G[Deploy Staging]
    G --> H[Smoke Test]
    H --> I[Deploy Produção]
    I --> J[Health Check]
    J --> K[Concluído ✅]

(1) ▶ Exemplo: Um Workflow CI Completo

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/

Saída:

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

4. Controle de Qualidade de Código

(1) Configuração de Controle de Qualidade

(1) ▶ Exemplo: Configuração ESLint + Prettier

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

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

Saída:

TEXT
// Execução bem-sucedida

(2) ▶ Exemplo: Scripts do 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"
  }
}

Saída:

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) Métricas de Controle de Qualidade

Métrica Ferramenta Limite de Acesso Descrição
Estilo de Código ESLint 0 erros Estilo de Código Consistente
Formatação Prettier 0 avisos Auto-formatação
Verificação de Tipos vue-tsc 0 erros Segurança de Tipos TypeScript
Testes Unitários Vitest ≥ 80% cobertura Cobertura da Lógica Central
Testes E2E Playwright Todos Passaram Validação de Fluxos Críticos
Tamanho do Bundle rollup-plugin < 200 KB gzip Prevenir inchaço de tamanho

5. Gerenciamento Multi-Ambiente

(1) ▶ Exemplo: Arquivo de Configuração de Ambiente

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) ▶ Exemplo: Workflow de Implantação 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!"}

Saída:

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

(1) Comparação de Ambientes

Ambiente Finalidade Banco de Dados Método de Implantação Permissões de Acesso
development desenvolvimento local PostgreSQL local npm run dev desenvolvedores
staging Teste de Integração PostgreSQL Independente Automatizado (push main) Equipe
production Ambiente de Produção PostgreSQL de Produção Automático (tag/release) Todos os Usuários

6. Estratégia de Implantação

(1) Comparação de Estratégias de Implantação

Estratégia Princípio Downtime Velocidade de Rollback Complexidade
Substituição Direta Desligar e Ligar 🔴 Sim (5-30 s) 🟡 Redeploy 🟢 Simples
Blue-Green Deployment Troca Entre Dois Ambientes 🟢 Nenhum 🟢 Troca em Segundos 🟡 Média
Rolling Update Substituir Instâncias Uma a Uma 🟢 Nenhum 🟡 Reverter Uma a Uma 🟡 Média
Canary Release Validar com Pequena Base de Usuários Primeiro 🟢 Nenhum 🟢 Reverter Imediatamente 🔴 Complexa

(1) ▶ Exemplo: Script de Blue-Green Deployment

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

# Construir e iniciar próximo ambiente
docker compose -f docker-compose.yml -f docker-compose.$NEXT.yml up -d --build

# Aguardar 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

# Trocar Nginx para novo ambiente
sed "s/$CURRENT/$NEXT/g" nginx.conf > /tmp/nginx.conf
docker compose exec nginx nginx -s reload

echo "Switched from $CURRENT to $NEXT"

Saída:

TEXT
CONTAINER ID   IMAGE     STATUS    
abc123         latest    Up 2 hours

7. Exemplo Completo: O Processo CI/CD Completo do MegaShop

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

❓ Perguntas Frequentes

P A cota gratuita do GitHub Actions é suficiente?
R Repositórios públicos têm minutos ilimitados. Repositórios privados têm um limite mensal de 2.000 minutos. Uma única execução de CI no MegaShop leva cerca de 10 minutos, então 10 execuções por dia totalizariam cerca de 100 minutos—o que é suficiente.
P Como o ambiente de preview de PR é configurado?
R É gerado automaticamente usando Vercel Preview. Cada PR tem sua própria URL (pr-123-megashop.vercel.app), que é automaticamente excluída após o merge do pull request.
P Como executar migrações de banco de dados no CI?
R No CI, use prisma migrate deploy (que apenas aplica migrações existentes e não cria novas). Durante o desenvolvimento, use prisma migrate dev para criar arquivos de migração e commitá-los no Git.
P Como alcançar implantação sem downtime?
R Blue-green deployment (troca entre dois ambientes) ou recarga cluster PM2 (substituindo workers um a um). Para Docker, use rolling updates (atualização rolling do docker compose).
P Como realizar um rollback?
R Use git revert e redeploy. Para Docker, reinicie usando a tag de imagem anterior. Para PM2, use pm2 stop e depois inicie a versão antiga. Para automatizar rollbacks, adicione lógica de tratamento de health checks falhos ao script de implantação.
P Como gerenciar segredos de forma segura?
R Armazene segredos nos GitHub Secrets como variáveis de ambiente; nunca os commit no Git. Os campos privados em runtimeConfig são apenas server-side. No CI, referencie-os usando ${{ secrets.XXX }}.

📖 Resumo


📝 Exercícios

  1. Exercício Básico (Dificuldade: ⭐): Crie um workflow CI no GitHub Actions que execute automaticamente linting, verificação de tipos e testes quando um commit for feito push.
  2. Exercício Avançado (Dificuldade ⭐⭐): Adicione implantação automática para staging—quando o branch main receber push, implantar automaticamente no servidor de staging e executar um smoke test após a implantação.
  3. Desafio (Dificuldade: ⭐⭐⭐): Implemente um script de blue-green deployment + rollback automático—trocar automaticamente de volta para a versão anterior quando um health check falhar

---|

Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%