Como configurar CI/CD com GitHub Actions

1. Fundamentos do GitHub Actions para CI/CD

O GitHub Actions é uma plataforma de automação nativa do ecossistema GitHub que permite construir pipelines de Integração Contínua (CI) e Entrega Contínua (CD) diretamente no repositório. Seu funcionamento baseia-se em quatro componentes principais: workflows, eventos, jobs e steps.

  • Workflow: um processo automatizado definido em um arquivo YAML dentro do diretório .github/workflows/.
  • Evento: atividade que dispara o workflow (push, pull_request, schedule, etc.).
  • Job: conjunto de steps executados no mesmo runner. Jobs podem rodar em paralelo ou sequencialmente.
  • Step: tarefa individual dentro de um job, que pode executar comandos ou usar ações pré-definidas.

A estrutura básica de um workflow YAML segue este padrão:

name: CI/CD Pipeline
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: echo "Hello, CI/CD!"

A sintaxe essencial inclui:
- on: define o evento gatilho.
- jobs: contém todos os jobs do workflow.
- runs-on: especifica o tipo de runner (ex: ubuntu-latest, windows-latest, macos-latest).
- steps: lista de ações ou comandos.
- uses: referencia uma ação pública ou privada.
- run: executa comandos shell diretamente.

2. Configuração do Ambiente e Gatilhos de Execução

Os eventos de disparo são configurados na chave on do workflow. Os mais comuns são:

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]
  schedule:
    - cron: '0 2 * * 1'  # toda segunda às 2h
  workflow_dispatch:       # disparo manual

Filtros avançados permitem execução condicional baseada em branches, paths e tags:

on:
  push:
    branches:
      - main
      - 'release/**'
    paths:
      - 'src/**'
      - '!docs/**'
    tags:
      - 'v*'

Quanto aos runners, você pode escolher entre:
- GitHub-hosted: máquinas gerenciadas pelo GitHub (Ubuntu, Windows, macOS).
- Self-hosted: runners próprios, instalados em sua infraestrutura para maior controle e economia.

3. Estruturação de Jobs e Dependências

Jobs paralelos são executados simultaneamente por padrão. Para criar dependências, use needs:

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - run: echo "Linting..."
  test:
    needs: lint
    runs-on: ubuntu-latest
    steps:
      - run: echo "Testing..."
  deploy:
    needs: [lint, test]
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying..."

Matrizes (matrix strategy) permitem testar múltiplas combinações de versões ou ambientes:

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        node-version: [16, 18, 20]
        os: [ubuntu-latest, windows-latest]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
      - run: npm test

Para compartilhar artefatos entre jobs:

- name: Upload build artifact
  uses: actions/upload-artifact@v4
  with:
    name: build-output
    path: dist/

# Em outro job:
- name: Download build artifact
  uses: actions/download-artifact@v4
  with:
    name: build-output

4. Pipeline de Integração Contínua (CI)

Um pipeline CI típico inclui checkout, instalação de dependências, linting e testes:

name: CI Pipeline

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

jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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

      - name: Cache dependencies
        uses: actions/cache@v4
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-

      - name: Install dependencies
        run: npm ci

      - name: Lint
        run: npm run lint

      - name: Run unit tests
        run: npm test

      - name: Run integration tests
        run: npm run test:integration

      - name: Upload coverage report
        uses: actions/upload-artifact@v4
        with:
          name: coverage
          path: coverage/

O cache com actions/cache acelera significativamente os builds ao reutilizar dependências já baixadas.

5. Pipeline de Entrega Contínua (CD)

Para CD, criamos builds otimizados e automatizamos a publicação:

jobs:
  build-and-publish:
    runs-on: ubuntu-latest
    needs: ci
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v4

      - name: Build optimized
        run: npm run build

      - name: Version artifact
        run: |
          VERSION=$(node -p "require('./package.json').version")
          echo "VERSION=$VERSION" >> $GITHUB_ENV

      - name: Publish to GitHub Packages
        uses: actions/setup-node@v4
        with:
          registry-url: 'https://npm.pkg.github.com'
      - run: npm publish
        env:
          NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Deploy to production
        uses: actions/deploy-pages@v4
        with:
          artifact_name: build-output

Para ambientes protegidos, configure environments no repositório (Settings > Environments) e use:

deploy:
  environment:
    name: production
    url: https://meusite.com
  runs-on: ubuntu-latest
  steps:
    - run: echo "Deploying to production..."

6. Segredos, Variáveis e Ambientes Protegidos

Secrets e variáveis são gerenciados em Settings > Secrets and variables > Actions:

steps:
  - name: Deploy to AWS
    run: |
      aws configure set aws_access_key_id ${{ secrets.AWS_ACCESS_KEY_ID }}
      aws configure set aws_secret_access_key ${{ secrets.AWS_SECRET_ACCESS_KEY }}
      aws s3 sync ./dist s3://meu-bucket

Para ambientes protegidos com regras de aprovação:

jobs:
  deploy-staging:
    environment:
      name: staging
    steps:
      - run: echo "Deploying to staging"

  deploy-production:
    needs: deploy-staging
    environment:
      name: production
      url: https://meusite.com
    steps:
      - run: echo "Deploying to production"

O uso de OIDC (OpenID Connect) permite autenticação segura sem secrets estáticos:

- name: Configure AWS credentials
  uses: aws-actions/configure-aws-credentials@v4
  with:
    role-to-assume: arn:aws:iam::123456789012:role/github-actions-role
    aws-region: us-east-1

7. Boas Práticas, Monitoramento e Depuração

Workflows reutilizáveis promovem DRY (Don't Repeat Yourself):

# .github/workflows/reusable-ci.yml
on:
  workflow_call:
    inputs:
      node-version:
        required: true
        type: string

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

Para depuração, ative logs detalhados com ACTIONS_STEP_DEBUG:

env:
  ACTIONS_STEP_DEBUG: true

Valide workflows localmente com act (ferramenta CLI que simula o GitHub Actions):

# Instalação
brew install act

# Execução local
act push -j ci

Notificações de falha podem ser integradas via Slack:

- name: Notify Slack on failure
  if: failure()
  uses: slackapi/slack-github-action@v1
  with:
    payload: |
      {
        "text": "Pipeline falhou no job ${{ github.job }} do repositório ${{ github.repository }}"
      }
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}

Referências