TL;DR — Resumo Rápido

Guia de GitHub Actions: automatize pipelines com proteção de ambientes, segredos, múltiplos destinos, cache de dependências e estratégias de rollback.

PIPELINE DE IMPLANTAÇÃO AUTOMATIZADA — GITHUB ACTIONS git push gatilho Lint + Test qualidade Build artefatos Aprovação gate Deploy produção Notificar Slack push → lint → test → build → aprovar → deploy → notificar

Fazer deploy manualmente — conectar via SSH a um servidor, rodar comandos de build, torcer para dar certo — é propenso a erros, inauditável e não escala. GitHub Actions permite definir todo o pipeline de implantação como código: lint, testes, build, aprovação, deploy e notificação — tudo disparado automaticamente a cada push para main. Este guia cobre a criação de um workflow de implantação automatizado para produção, desde o primeiro arquivo YAML até deploys multi-destino, proteção de ambientes e estratégias de rollback.

Pré-requisitos

Antes de criar seu workflow de deploy, tenha o seguinte em ordem:

  • Um repositório no GitHub com código de aplicação (exemplos em Node.js ao longo do guia)
  • Um ambiente destino — conta Cloudflare Workers, um VPS com acesso SSH, ou um runner auto-hospedado em Windows para IIS
  • Familiaridade básica com YAML — workflows do GitHub Actions são arquivos YAML onde a indentação importa
  • Acesso às configurações do repositório no GitHub para armazenar segredos e criar ambientes

Conceitos Básicos do GitHub Actions

Um workflow do GitHub Actions é um arquivo YAML armazenado em .github/workflows/. É disparado por um ou mais eventos (push, pull request, agendamento, disparo manual). Um workflow contém um ou mais jobs. Cada job roda em um runner — uma máquina virtual hospedada pelo GitHub (Ubuntu, Windows, macOS) ou auto-hospedada. Cada job contém steps sequenciais, cada um executando um comando shell (run) ou uma unidade reutilizável chamada action (uses).

Os blocos fundamentais:

name: Deploy                      # nome do workflow na aba Actions

on:                               # gatilho
  push:
    branches: [main]

jobs:
  deploy:                         # ID do job
    runs-on: ubuntu-latest        # runner
    steps:
      - uses: actions/checkout@v4 # action (baixa o código-fonte)
      - run: npm ci               # comando shell
      - run: npm run build

Jobs rodam em paralelo por padrão. Use needs: [job-id] para impor uma ordem.

Seu Primeiro Workflow de Implantação

Aqui está um workflow completo para um projeto Node.js que faz deploy no Cloudflare Workers usando Wrangler. Primeiro executa lint e testes, depois faz build e por fim faz deploy — apenas em pushes para main.

name: Deploy no Cloudflare Workers

on:
  push:
    branches: [main]

concurrency:
  group: deploy-${{ github.ref }}
  cancel-in-progress: true

jobs:
  lint-and-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "npm"
      - run: npm ci
      - run: npm run lint
      - run: npm test

  build:
    needs: lint-and-test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "npm"
      - run: npm ci
      - run: npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist/

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      - uses: actions/download-artifact@v4
        with:
          name: dist
          path: dist/
      - name: Deploy com Wrangler
        uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CF_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
      - name: Notificar sucesso no Slack
        if: success()
        run: |
          curl -s -X POST "${{ secrets.SLACK_WEBHOOK }}" \
            -H "Content-Type: application/json" \
            -d "{\"text\":\"Deploy de ${{ github.sha }} em produção por ${{ github.actor }}\"}"

O bloco concurrency no topo evita que dois deploys se sobreponham. Se você fizer push duas vezes rapidamente, a primeira execução é cancelada antes que a segunda comece.

Regras de Proteção de Ambiente

Fazer push diretamente para produção sem uma verificação humana é arriscado para sistemas críticos. Os ambientes do GitHub permitem adicionar regras de proteção a qualquer job que os aponte.

Para configurar a proteção de ambiente:

  1. Vá para Settings → Environments no seu repositório
  2. Clique em New environment e nomeie-o production
  3. Ative Required reviewers — adicione um ou mais usuários ou equipes do GitHub
  4. Opcionalmente defina um Wait timer (por exemplo, 10 minutos) antes que o job possa executar
  5. Opcionalmente restrinja quais branches podem fazer deploy neste ambiente (por exemplo, apenas main)

Uma vez configurado, qualquer job com environment: production pausará e aguardará a aprovação de um revisor antes de prosseguir. O revisor vê o deploy pendente na UI do GitHub e pode aprovar ou rejeitar com um comentário.

Gerenciamento de Segredos

Nunca escreva tokens de API, chaves SSH ou senhas nos arquivos de workflow. O GitHub oferece três níveis de armazenamento de segredos:

Segredos de repositório — Disponíveis para todos os workflows do repo. Vá para Settings → Secrets and variables → Actions → New repository secret.

Segredos de ambiente — Limitados a um ambiente específico (por exemplo, production). Só são expostos quando um job aponta para esse ambiente.

Segredos de organização — Compartilhados entre múltiplos repositórios de uma organização do GitHub, ideais para tokens usados por muitos projetos.

Referencie segredos nos workflows com a sintaxe ${{ secrets.NOME_SEGREDO }}:

- name: Deploy com Wrangler
  uses: cloudflare/wrangler-action@v3
  with:
    apiToken: ${{ secrets.CF_API_TOKEN }}
    accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}

OIDC (OpenID Connect) é uma alternativa a segredos de longa duração para provedores de nuvem que o suportam (AWS, GCP, Azure). Em vez de armazenar um token estático, o runner solicita uma credencial de curta duração ao provedor em tempo de execução.

Deploy em Diferentes Destinos

Cloudflare Workers com Wrangler

A action cloudflare/wrangler-action cuida da autenticação e executa qualquer comando Wrangler:

- uses: cloudflare/wrangler-action@v3
  with:
    apiToken: ${{ secrets.CF_API_TOKEN }}
    accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
    command: deploy --env production

VPS via SSH e rsync

Para um VPS Linux, use SSH para conectar e rsync para transferir arquivos:

- name: Deploy no VPS
  uses: appleboy/ssh-action@v1
  with:
    host: ${{ secrets.DEPLOY_HOST }}
    username: ${{ secrets.DEPLOY_USER }}
    key: ${{ secrets.DEPLOY_SSH_KEY }}
    script: |
      rsync -az --delete dist/ /var/www/myapp/dist/
      systemctl restart myapp

Armazene a chave SSH privada nos segredos do GitHub. Adicione a chave pública correspondente a ~/.ssh/authorized_keys no servidor. Use um usuário de deploy dedicado com permissões mínimas — nunca root.

IIS via Runner Auto-Hospedado

Para deploys no Windows IIS, execute um runner auto-hospedado diretamente no servidor:

deploy-iis:
  needs: build
  runs-on: [self-hosted, iis-deploy]
  environment: production
  steps:
    - uses: actions/download-artifact@v4
      with:
        name: dist
        path: C:\inetpub\wwwroot\myapp
    - name: Reiniciar site IIS
      run: |
        Import-Module WebAdministration
        Restart-WebItem 'IIS:\Sites\MyApp'
      shell: powershell

Cache e Performance

Cache de Dependências

Re-baixar pacotes npm em cada execução desperdiça tempo. Faça cache deles:

- uses: actions/setup-node@v4
  with:
    node-version: "22"
    cache: "npm"

Para .NET:

- uses: actions/cache@v4
  with:
    path: ~/.nuget/packages
    key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
    restore-keys: ${{ runner.os }}-nuget-

Grupos de Concorrência

Grupos de concorrência evitam deploys em paralelo — uma fonte comum de race conditions:

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

Comparativo: GitHub Actions vs GitLab CI vs Jenkins

RecursoGitHub ActionsGitLab CIJenkins
HospedagemGitHub (SaaS)GitLab ou auto-hospedadoApenas auto-hospedado
Arquivo de config.github/workflows/*.yml.gitlab-ci.ymlJenkinsfile (Groovy)
Plano gratuito2.000 min/mês (privado)400 min/mês (privado)Ilimitado (sua infra)
Marketplace20.000+ actionsLimitado1.800+ plugins
Gates de ambienteNativos (grátis para público)Nativos (Ultimate para regras)Via plugins
OIDCAWS, GCP, Azure, CloudflareAWS, GCP, AzureVia plugins
Curva de aprendizadoBaixaBaixaAlta (DSL Groovy)
Ideal paraProjetos no GitHubMonorepos no GitLabPipelines legados complexos

Cenário Real: Pipeline Completo

Um pipeline de produção para Node.js com notificações Slack:

name: Deploy Produção

on:
  push:
    branches: [main]

concurrency:
  group: production-deploy
  cancel-in-progress: true

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "npm"
      - run: npm ci
      - run: npm run lint

  test:
    needs: lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "npm"
      - run: npm ci
      - run: npm test -- --coverage

  build:
    needs: test
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: "22"
          cache: "npm"
      - run: npm ci
      - run: npm run build
      - uses: actions/upload-artifact@v4
        with:
          name: dist-${{ github.sha }}
          path: dist/
          retention-days: 14

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4
      - uses: actions/download-artifact@v4
        with:
          name: dist-${{ github.sha }}
          path: dist/
      - uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CF_API_TOKEN }}
          accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
      - name: Notificar sucesso
        if: success()
        run: |
          curl -s -X POST "${{ secrets.SLACK_WEBHOOK }}" \
            -d "{\"text\":\"Deploy de ${{ github.sha }} por ${{ github.actor }}\"}"
      - name: Notificar falha
        if: failure()
        run: |
          curl -s -X POST "${{ secrets.SLACK_WEBHOOK }}" \
            -d "{\"text\":\"FALHA no deploy para ${{ github.sha }}\"}"

Badges de Status

Adicione um badge de status ao vivo ao seu README:

![Deploy](https://github.com/sua-org/seu-repo/actions/workflows/deploy.yml/badge.svg)

Casos Extremos e Problemas Comuns

O mascaramento de segredos só funciona para correspondências exatas. O GitHub mascara o valor literal do segredo nos logs, mas se o segredo for codificado em base64 ou URL antes do uso, o valor original pode aparecer sem máscara.

Mudanças na imagem do runner quebram versões de ferramentas ancoradas. O GitHub atualiza ubuntu-latest periodicamente. Se seu workflow depende de uma versão específica de uma ferramenta pré-instalada, instale-a explicitamente com uma action de setup.

cancel-in-progress: true cancela a execução atual, não a que está chegando. O novo push vence. Isso é quase sempre o que você quer para deploys.

Segredos de ambiente não estão disponíveis para pull requests de forks. Isso é intencional — forks não podem acessar segredos para evitar vazamentos.

Solução de Problemas

O workflow não dispara no push para main. Verifique o filtro on.push.branches — o nome da branch deve corresponder exatamente. Confirme que o arquivo de workflow está em .github/workflows/.

O segredo está disponível mas a autenticação do Wrangler falha. Confirme que o nome do segredo corresponde exatamente (sensível a maiúsculas). Verifique que o token da API do Cloudflare tem as permissões corretas: Workers Scripts: Edit e Account: Read.

O job aguarda indefinidamente a aprovação do ambiente. Verifique que você está listado como revisor obrigatório do ambiente. Confirme que o nome do ambiente no YAML corresponde exatamente ao nome em Settings.

Resumo

  • Defina workflows como arquivos YAML em .github/workflows/ — eles são versionados junto com seu código
  • Estruture pipelines como jobs sequenciais: lint → test → build → deploy, usando needs para impor ordem
  • Use environment: production com revisores obrigatórios para gates de aprovação humana
  • Armazene todos os segredos no armazenamento de segredos do GitHub — nunca no arquivo YAML
  • Deploys no Cloudflare Workers usam cloudflare/wrangler-action; destinos VPS usam SSH/rsync; IIS usa runner auto-hospedado
  • Adicione grupos de concurrency com cancel-in-progress: true para evitar deploys sobrepostos
  • Faça cache de dependências com actions/setup-node cache: npm para execuções mais rápidas
  • Nomeie artefatos com ${{ github.sha }} para facilitar rollback re-executando o workflow em qualquer commit anterior

Artigos Relacionados