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