0%
Tutorial 10 de 10
100% concluído

GitHub Actions e CI/CD

Configure workflows de integração e entrega contínuas diretamente nos seus repositórios do GitHub.

GitHub Actions e CI/CD

O GitHub Actions revoluciona a automação do desenvolvimento de software ao integrar CI/CD diretamente nos seus repositórios. Este tutorial vai ensinar você a criar workflows poderosos para automatizar testes, builds e deploys.

Introdução ao GitHub Actions

O que é o GitHub Actions?

O GitHub Actions é uma plataforma de automação que permite:

  • Executar workflows em eventos do Git
  • Automatizar os testes e a validação
  • Fazer deploy automaticamente das suas aplicações
  • Gerenciar releases e notificações
Push/PRGitHub ActionsTestsBuildDeployNotifications

Conceitos-chave

Workflow: processo automatizado definido em YAML Job: conjunto de etapas executadas em um runner Step: ação individual dentro de um job Action: componente reutilizável Runner: máquina virtual que executa os jobs

Estrutura de um workflow

Arquivo básico

# .github/workflows/ci.yml
name: CI Pipeline

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          
      - name: Install dependencies
        run: npm ci
        
      - name: Run tests
        run: npm test

Gatilhos (Triggers)

# Eventos do Git
on:
  push:
    branches: [main]
    tags: ['v*']
  pull_request:
    branches: [main]
  release:
    types: [published]

# Eventos agendados
on:
  schedule:
    - cron: '0 2 * * 1'  # Toda segunda-feira às 2h

# Disparo manual
on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environment to deploy'
        required: true
        default: 'staging'

Workflows essenciais

CI básico para Node.js

# .github/workflows/node-ci.yml
name: Node.js CI

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        node-version: [16, 18, 20]
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'
          
      - name: Install dependencies
        run: npm ci
        
      - name: Run linter
        run: npm run lint
        
      - name: Run tests
        run: npm test
        
      - name: Run build
        run: npm run build
        
      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage/lcov.info

CI para Python

# .github/workflows/python-ci.yml
name: Python CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    strategy:
      matrix:
        python-version: [3.8, 3.9, '3.10', 3.11]
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Python ${{ matrix.python-version }}
        uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python-version }}
          
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r requirements.txt
          pip install -r requirements-dev.txt
          
      - name: Lint with flake8
        run: |
          flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
          flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127
          
      - name: Test with pytest
        run: |
          pytest --cov=./ --cov-report=xml
          
      - name: Upload coverage
        uses: codecov/codecov-action@v3

Deploy automático

# .github/workflows/deploy.yml
name: Deploy to Production

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          
      - name: Install and build
        run: |
          npm ci
          npm run build
          
      - name: Deploy to Vercel
        uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.ORG_ID }}
          vercel-project-id: ${{ secrets.PROJECT_ID }}
          vercel-args: '--prod'

Actions avançadas

Matriz de testes

jobs:
  test:
    runs-on: ${{ matrix.os }}
    
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node-version: [16, 18, 20]
        include:
          - os: ubuntu-latest
            node-version: 20
            coverage: true
        exclude:
          - os: windows-latest
            node-version: 16
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          
      - name: Run tests
        run: npm test
        
      - name: Upload coverage
        if: matrix.coverage
        uses: codecov/codecov-action@v3

Condições e expressões

jobs:
  deploy:
    if: github.ref == 'refs/heads/main' && github.event_name == 'push'
    runs-on: ubuntu-latest
    
    steps:
      - name: Deploy to staging
        if: contains(github.event.head_commit.message, '[staging]')
        run: echo "Deploying to staging"
        
      - name: Deploy to production
        if: startsWith(github.ref, 'refs/tags/v')
        run: echo "Deploying to production"
        
      - name: Notify on failure
        if: failure()
        uses: 8398a7/action-slack@v3
        with:
          status: failure
          webhook_url: ${{ secrets.SLACK_WEBHOOK }}

Artifacts e cache

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Cache dependencies
        uses: actions/cache@v3
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-
            
      - name: Install and build
        run: |
          npm ci
          npm run build
          
      - name: Upload build artifacts
        uses: actions/upload-artifact@v3
        with:
          name: build-files
          path: dist/
          
  deploy:
    needs: build
    runs-on: ubuntu-latest
    
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@v3
        with:
          name: build-files
          path: dist/
          
      - name: Deploy
        run: echo "Deploying files from dist/"

Segurança e secrets

Gerenciamento de secrets

# Configuração dos secrets no GitHub
# Settings > Secrets and variables > Actions

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - name: Deploy to AWS
        env:
          AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
          AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
        run: |
          aws s3 sync ./dist s3://my-bucket

Ambientes protegidos

jobs:
  deploy-production:
    runs-on: ubuntu-latest
    environment: production
    
    steps:
      - name: Deploy to production
        run: echo "Deploying to production environment"

Permissões do GITHUB_TOKEN

name: Secure Workflow

on: [push]

permissions:
  contents: read
  issues: write
  pull-requests: write

jobs:
  security-scan:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Run security scan
        uses: github/super-linter@v4
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Actions personalizadas

Action JavaScript

// .github/actions/hello-world/index.js
const core = require('@actions/core');
const github = require('@actions/github');

try {
  const nameToGreet = core.getInput('who-to-greet');
  console.log(`Hello ${nameToGreet}!`);
  
  const time = (new Date()).toTimeString();
  core.setOutput("time", time);
  
  const payload = JSON.stringify(github.context.payload, undefined, 2);
  console.log(`The event payload: ${payload}`);
} catch (error) {
  core.setFailed(error.message);
}
# .github/actions/hello-world/action.yml
name: 'Hello World'
description: 'Greet someone and record the time'

inputs:
  who-to-greet:
    description: 'Who to greet'
    required: true
    default: 'World'

outputs:
  time:
    description: 'The time we greeted you'

runs:
  using: 'node20'
  main: 'index.js'

Uso da action

jobs:
  hello_world_job:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Hello world action step
        uses: ./.github/actions/hello-world
        id: hello
        with:
          who-to-greet: 'GitHub Actions'
          
      - name: Get the output time
        run: echo "The time was ${{ steps.hello.outputs.time }}"

Workflows complexos

Pipeline multiambiente

# .github/workflows/multi-env-deploy.yml
name: Multi-Environment Deploy

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: npm test
  
  deploy-staging:
    needs: test
    if: github.ref == 'refs/heads/develop'
    runs-on: ubuntu-latest
    environment: staging
    
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to staging
        run: echo "Deploying to staging"
        
  deploy-production:
    needs: test
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    environment: production
    
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to production
        run: echo "Deploying to production"
        
  notify:
    needs: [deploy-staging, deploy-production]
    if: always()
    runs-on: ubuntu-latest
    
    steps:
      - name: Notify team
        uses: 8398a7/action-slack@v3
        with:
          status: ${{ job.status }}
          webhook_url: ${{ secrets.SLACK_WEBHOOK }}

Release automática

# .github/workflows/release.yml
name: Release

on:
  push:
    tags:
      - 'v*'

jobs:
  release:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          
      - name: Install dependencies
        run: npm ci
        
      - name: Build
        run: npm run build
        
      - name: Generate changelog
        id: changelog
        uses: metcalfc/changelog-generator@v4.0.1
        with:
          myToken: ${{ secrets.GITHUB_TOKEN }}
          
      - name: Create Release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ github.ref }}
          body: ${{ steps.changelog.outputs.changelog }}
          draft: false
          prerelease: false
          
      - name: Upload Release Asset
        uses: actions/upload-release-asset@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          upload_url: ${{ steps.create_release.outputs.upload_url }}
          asset_path: ./dist/app.zip
          asset_name: app.zip
          asset_content_type: application/zip

Exercícios práticos

Exercício 1: CI básico

# .github/workflows/basic-ci.yml
name: Basic CI

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

jobs:
  test:
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        
      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          
      - name: Install dependencies
        run: |
          echo '{"name": "test-app", "scripts": {"test": "echo \"Tests passed!\""}}' > package.json
          npm install
          
      - name: Run tests
        run: npm test
        
      - name: Success message
        run: echo "✅ All tests passed!"

Exercício 2: Deploy condicional

# .github/workflows/conditional-deploy.yml
name: Conditional Deploy

on:
  push:
    branches: [main, staging]

jobs:
  deploy:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Deploy to staging
        if: github.ref == 'refs/heads/staging'
        run: |
          echo "🚀 Deploying to staging environment"
          echo "Environment: staging"
          
      - name: Deploy to production
        if: github.ref == 'refs/heads/main'
        run: |
          echo "🚀 Deploying to production environment"
          echo "Environment: production"
          
      - name: Notify deployment
        run: |
          echo "📧 Sending notification"
          echo "Deployment completed for branch: ${{ github.ref_name }}"

Exercício 3: Workflow com artifacts

# .github/workflows/artifacts-demo.yml
name: Artifacts Demo

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Create build artifacts
        run: |
          mkdir -p dist
          echo "Built application v1.0.0" > dist/app.txt
          echo "Build timestamp: $(date)" > dist/build-info.txt
          
      - name: Upload artifacts
        uses: actions/upload-artifact@v3
        with:
          name: build-artifacts
          path: dist/
          
  test:
    needs: build
    runs-on: ubuntu-latest
    
    steps:
      - name: Download artifacts
        uses: actions/download-artifact@v3
        with:
          name: build-artifacts
          path: ./downloaded/
          
      - name: Test artifacts
        run: |
          echo "📦 Testing downloaded artifacts:"
          ls -la ./downloaded/
          cat ./downloaded/app.txt
          cat ./downloaded/build-info.txt

Monitoramento e debugging

Logs e debugging

jobs:
  debug:
    runs-on: ubuntu-latest
    
    steps:
      - name: Debug information
        run: |
          echo "🔍 Debug Information:"
          echo "Runner OS: ${{ runner.os }}"
          echo "GitHub Actor: ${{ github.actor }}"
          echo "Repository: ${{ github.repository }}"
          echo "Branch: ${{ github.ref_name }}"
          echo "Commit SHA: ${{ github.sha }}"
          
      - name: Enable debug logging
        run: echo "::debug::This is a debug message"
        
      - name: Set environment variable
        run: echo "CUSTOM_VAR=hello-world" >> $GITHUB_ENV
        
      - name: Use environment variable
        run: echo "Custom variable: $CUSTOM_VAR"

Notificações

jobs:
  notify:
    runs-on: ubuntu-latest
    if: always()
    
    steps:
      - name: Slack notification
        uses: 8398a7/action-slack@v3
        with:
          status: ${{ job.status }}
          channel: '#deployments'
          webhook_url: ${{ secrets.SLACK_WEBHOOK }}
          
      - name: Email notification
        uses: dawidd6/action-send-mail@v3
        with:
          server_address: smtp.gmail.com
          server_port: 465
          username: ${{ secrets.MAIL_USERNAME }}
          password: ${{ secrets.MAIL_PASSWORD }}
          subject: "Build ${{ job.status }}"
          body: "Build completed with status: ${{ job.status }}"
          to: team@company.com

Boas práticas

Organização dos workflows

.github/
├── workflows/
│   ├── ci.yml              # Testes e validação
│   ├── deploy-staging.yml  # Deploy em staging
│   ├── deploy-prod.yml     # Deploy em produção
│   ├── release.yml         # Gerenciamento de releases
│   └── security.yml        # Scans de segurança
├── actions/
│   └── custom-action/      # Actions personalizadas
└── ISSUE_TEMPLATE/         # Templates de issues

Otimização de desempenho

jobs:
  optimized:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      # Cache das dependências
      - name: Cache Node modules
        uses: actions/cache@v3
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          
      # Paralelização
      - name: Install dependencies
        run: npm ci --prefer-offline --no-audit
        
      # Evitar etapas desnecessárias
      - name: Run tests
        if: github.event_name == 'pull_request'
        run: npm test

Segurança

# Permissões mínimas
permissions:
  contents: read
  
jobs:
  secure:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v4
      
      # Scan de segurança
      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: 'fs'
          scan-ref: '.'
          
      # Validação dos secrets
      - name: Check for secrets
        uses: trufflesecurity/trufflehog@main
        with:
          path: ./

Recursos adicionais

Actions populares

  • actions/checkout : Obter o código
  • actions/setup-node : Configurar o Node.js
  • actions/cache : Cache das dependências
  • docker/build-push-action : Build e push de imagens Docker
  • peaceiris/actions-gh-pages : Deploy no GitHub Pages

Marketplace

Documentação

Conclusão

O GitHub Actions transforma seu repositório em uma plataforma DevOps completa. Ao dominar esses conceitos, você pode automatizar todo o seu pipeline de desenvolvimento, da integração contínua ao deploy em produção.

Parabéns! Você concluiu este curso completo sobre Git e GitHub. Agora você tem todas as habilidades necessárias para gerenciar seus projetos com eficiência usando Git, colaborar no GitHub e automatizar seus workflows de desenvolvimento.


Tempo de leitura estimado: 40 minutos
Nível: Avançado
Etapa anterior: Git submódulos e subárvores
🎉 Curso concluído! Agora você domina Git e GitHub!