GitHub Actions é uma plataforma de CI/CD (Integração Contínua/Entrega Contínua) integrada ao GitHub. Você pode automatizar testes, builds e deploys com triggers de push ou pull request. Este artigo explica em detalhes como construir workflows práticos.
O que você aprenderá neste artigo
- Conceitos básicos do GitHub Actions
- Sintaxe de arquivos de workflow
- Automação de testes para projetos Node.js/Python
- Aceleração com cache
- Matrix build
- Deploy para produção
- Melhores práticas de segurança
Conceitos Básicos
Componentes do GitHub Actions
flowchart TB
subgraph Workflow["Workflow<br/>.github/workflows/ci.yml"]
subgraph TestJob["Job: test"]
Step1["Step 1: actions/checkout@v4"]
Step2["Step 2: actions/setup-node@v4"]
Step3["Step 3: npm install"]
Step4["Step 4: npm test"]
Step1 --> Step2 --> Step3 --> Step4
end
subgraph DeployJob["Job: deploy"]
DeploySteps["Deploy steps..."]
end
TestJob -->|depends on| DeployJob
end
| Termo | Descrição |
|---|---|
| Workflow | Processo de automação completo definido em YAML |
| Job | Unidade de execução dentro do workflow. Executa no mesmo runner |
| Step | Tarefas individuais dentro do job. Actions ou comandos shell |
| Action | Unidade de tarefa reutilizável (actions/checkout, etc.) |
| Runner | Servidor que executa o workflow |
Primeiro Workflow
Estrutura de Arquivos
project/
└── .github/
└── workflows/
├── ci.yml # Para testes e build
├── deploy.yml # Para deploy
└── release.yml # Para release
Workflow CI Básico
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
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 tests
run: npm test
- name: Build
run: npm run build
Detalhes do Trigger (on)
Vários Triggers
on:
# No push
push:
branches:
- main
- 'release/**' # Pattern matching
paths:
- 'src/**' # Apenas quando alterações em paths específicos
- '!src/**/*.md' # Excluir markdown
tags:
- 'v*' # Quando criar tag
# Pull request
pull_request:
branches: [main]
types: [opened, synchronize, reopened]
# Execução manual
workflow_dispatch:
inputs:
environment:
description: 'Deployment environment'
required: true
default: 'staging'
type: choice
options:
- staging
- production
# Execução agendada (UTC)
schedule:
- cron: '0 0 * * 1' # Segunda-feira 00:00 UTC
# Chamada de outros workflows
workflow_call:
inputs:
config-path:
required: true
type: string
Execução Condicional
jobs:
deploy:
# Executar apenas em push para branch main
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-latest
steps:
# ...
notify:
# Executar apenas se job anterior falhou
if: failure()
needs: [test, deploy]
runs-on: ubuntu-latest
steps:
# ...
Variáveis de Ambiente e Secrets
Configurando Variáveis de Ambiente
# Nível de workflow
env:
NODE_ENV: production
CI: true
jobs:
build:
runs-on: ubuntu-latest
# Nível de job
env:
DATABASE_URL: postgres://localhost/test
steps:
- name: Build with environment
# Nível de step
env:
API_KEY: ${{ secrets.API_KEY }}
run: |
echo "Node env: $NODE_ENV"
echo "Building..."
Usando Secrets
steps:
- name: Deploy to production
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
aws s3 sync ./dist s3://my-bucket
# GITHUB_TOKEN está automaticamente disponível
- name: Create release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Environment Secrets
jobs:
deploy-prod:
runs-on: ubuntu-latest
environment:
name: production
url: https://myapp.com
steps:
# Pode acessar variáveis de ambiente e secrets de production
- name: Deploy
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: ./deploy.sh
Estratégias de Cache
Cache npm
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # Cache automático
- run: npm ci
Cache Personalizado
steps:
- name: Cache node modules
id: cache-npm
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
- name: Install dependencies
if: steps.cache-npm.outputs.cache-hit != 'true'
run: npm ci
# Cache de artefatos de build
- name: Cache build output
uses: actions/cache@v4
with:
path: |
.next/cache
dist
key: ${{ runner.os }}-build-${{ github.sha }}
restore-keys: |
${{ runner.os }}-build-
Cache de Dependências (Múltiplas Linguagens)
steps:
# Python
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
# Go
- uses: actions/setup-go@v5
with:
go-version: '1.22'
cache: true
# Rust
- uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
Matrix Build
Testes em Múltiplas Versões
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
include:
- node-version: 20
is-primary: true
fail-fast: false # Continua outros se um falhar
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
# Relatório de cobertura apenas na versão primária
- name: Upload coverage
if: matrix.is-primary
uses: codecov/codecov-action@v4
Testes Cross-Platform
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
node-version: [18, 20]
exclude:
# Pular combinação Windows + Node 18
- os: windows-latest
node-version: 18
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
Dependências Entre Jobs
Execução Sequencial
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run lint
test:
needs: lint # Executa após lint ter sucesso
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm test
build:
needs: [lint, test] # Depende de múltiplos jobs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
Passando Artefatos Entre Jobs
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
retention-days: 1
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: dist/
- name: Deploy
run: ./deploy.sh dist/
Exemplos Práticos de Workflow
CI/CD Completo para Projeto Node.js
# .github/workflows/ci-cd.yml
name: CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
NODE_VERSION: '20'
jobs:
# Verificação de qualidade de código
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- name: Type check
run: npm run type-check
- name: Lint
run: npm run lint
- name: Format check
run: npm run format:check
# Testes
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: test
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- name: Run tests
env:
DATABASE_URL: postgres://test:test@localhost:5432/test
run: npm run test:coverage
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }}
# Build
build:
needs: [quality, test]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: build
path: dist/
# Deploy para staging (no merge do PR)
deploy-staging:
needs: build
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment:
name: staging
url: https://staging.myapp.com
steps:
- uses: actions/download-artifact@v4
with:
name: build
path: dist/
- name: Deploy to staging
env:
DEPLOY_KEY: ${{ secrets.STAGING_DEPLOY_KEY }}
run: |
echo "Deploying to staging..."
# Script de deploy
# Deploy para produção (após aprovação manual)
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment:
name: production
url: https://myapp.com
steps:
- uses: actions/download-artifact@v4
with:
name: build
path: dist/
- name: Deploy to production
env:
DEPLOY_KEY: ${{ secrets.PRODUCTION_DEPLOY_KEY }}
run: |
echo "Deploying to production..."
# Script de deploy
Build e Push de Imagem Docker
name: Docker Build
on:
push:
tags: ['v*']
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
Workflows Reutilizáveis
Definindo Workflow Reutilizável
# .github/workflows/reusable-test.yml
name: Reusable Test Workflow
on:
workflow_call:
inputs:
node-version:
required: false
type: string
default: '20'
secrets:
npm-token:
required: false
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: 'npm'
registry-url: 'https://registry.npmjs.org'
- run: npm ci
env:
NODE_AUTH_TOKEN: ${{ secrets.npm-token }}
- run: npm test
Chamando Workflow Reutilizável
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
call-test:
uses: ./.github/workflows/reusable-test.yml
with:
node-version: '20'
secrets:
npm-token: ${{ secrets.NPM_TOKEN }}
Melhores Práticas de Segurança
Scan de Dependências
name: Security
on:
push:
branches: [main]
schedule:
- cron: '0 0 * * 1' # Semanalmente
jobs:
dependency-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run npm audit
run: npm audit --audit-level=high
- name: Run Snyk scan
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
Tratamento Seguro de Secrets
steps:
# Mau exemplo: Pode expor secret no log
- name: Bad example
run: echo ${{ secrets.API_KEY }} # Pode aparecer no log
# Bom exemplo: Passar como variável de ambiente
- name: Good example
env:
API_KEY: ${{ secrets.API_KEY }}
run: ./deploy.sh # Usar $API_KEY no script
Princípio do Menor Privilégio
# Definir permissões mínimas no nível do workflow
permissions:
contents: read
packages: write
jobs:
build:
runs-on: ubuntu-latest
# Também pode sobrescrever no nível do job
permissions:
contents: read
steps:
# ...
Debug e Troubleshooting
Habilitando Logs de Debug
# Configurar nos Secrets do repositório
# ACTIONS_STEP_DEBUG: true
# ACTIONS_RUNNER_DEBUG: true
steps:
- name: Debug info
run: |
echo "GitHub Context:"
echo '${{ toJson(github) }}'
echo "Runner Context:"
echo '${{ toJson(runner) }}'
Re-execução Manual de Jobs
on:
workflow_dispatch:
inputs:
debug_enabled:
description: 'Run with debug logging'
required: false
default: 'false'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Setup tmate session
if: inputs.debug_enabled == 'true'
uses: mxschmitt/action-tmate@v3
Resumo
Resumo dos pontos-chave de pipelines CI/CD com GitHub Actions.
Melhores Práticas de Design de Workflow
- Separação de jobs: Separar lint, test, build, deploy
- Uso de cache: Acelerar com cache de npm/pip, etc.
- Matrix build: Testes paralelos em múltiplos ambientes
- Separação de ambientes: Separar claramente staging/production
Segurança
- Princípio do menor privilégio: Definir permissions mínimas
- Gerenciamento de secrets: Usar Environment Secrets
- Scan de dependências: Verificação regular de vulnerabilidades
- Code scanning: Introduzir CodeQL
Pontos para Acelerar
- Configuração adequada de cache
- Maximizar execução paralela
- Remover steps desnecessários
- Usar imagens Docker slim
Com GitHub Actions, você pode melhorar significativamente a eficiência do processo de desenvolvimento.