O GitHub Actions é uma plataforma de integração contínua e entrega contínua (CI/CD) que permite automatizar fluxos de trabalho diretamente no repositório do GitHub. Com ele, é possível compilar, testar e implantar código, além de executar tarefas como revisão de código, gerenciamento de issues e muito mais. Nesta aula, vamos explorar os conceitos essenciais para criar seus próprios pipelines, desde a estrutura de um workflow até o uso seguro de variáveis sensíveis.

Antes de mergulharmos nos detalhes, é importante entender que o GitHub Actions se baseia em eventos do repositório (como push, pull request) para disparar workflows. Cada workflow é definido em um arquivo YAML dentro do diretório .github/workflows do seu repositório. Vamos começar com o coração do sistema: os workflows e a sintaxe YAML.

Workflows e YAML

Um workflow é um processo automatizado composto por um ou mais jobs. Ele é definido em um arquivo YAML com extensão .yml ou .yaml no diretório .github/workflows. O YAML é uma linguagem de serialização de dados legível por humanos, que usa indentação para estruturar informações. No GitHub Actions, o YAML define triggers, jobs, steps e ações.

Um workflow básico começa com um nome (name), o evento que o dispara (on) e a lista de jobs (jobs). Por exemplo, um workflow que é executado a cada push na branch main pode ser assim:

name: CI

on:
  push:
    branches: [ main ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: echo "Hello, world!"

No exemplo acima, o workflow chamado "CI" é acionado por push na branch main. Ele contém um job chamado "build" que executa em um runner Ubuntu. O passo uses: actions/checkout@v3 é uma action reutilizável que faz checkout do código, e o passo run executa um comando shell. A indentação é crucial: cada nível é dois espaços (recomendado) ou tabs, mas consistência é obrigatória.

Jobs e steps

Jobs são as unidades de execução de um workflow. Cada job roda em um runner (máquina virtual) independente e pode conter vários steps. Os steps são comandos ou actions executados sequencialmente dentro de um job. Por padrão, jobs executam em paralelo, mas é possível definir dependências entre eles usando needs.

Vamos a um exemplo mais completo com dois jobs, onde o segundo depende do primeiro:

name: Build and Test

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Install dependencies
        run: npm install
      - name: Build
        run: npm run build
  test:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Run tests
        run: npm test

Aqui, o job test só inicia após o build ser concluído com sucesso. Cada step tem um nome opcional e pode usar run para comandos shell ou uses para actions. O runner é uma máquina limpa a cada execução, então é comum usar actions como actions/checkout para obter o código e actions/setup-node para configurar ferramentas.

Actions reutilizáveis

Actions são blocos de código reutilizáveis que podem ser compartilhados entre workflows e repositórios. Elas podem ser escritas em JavaScript, Docker ou shell script, e publicadas no GitHub Marketplace. O uso de actions evita repetição e padroniza tarefas comuns, como checkout, configuração de linguagens, implantação em nuvem, etc.

Para usar uma action, utilize a sintaxe uses: com o nome da action e a versão. Por exemplo:

steps:
  - uses: actions/setup-node@v3
    with:
      node-version: '18'

Você também pode criar suas próprias actions em um repositório e referenciá-las com owner/repo@ref. Actions podem receber inputs definidos no campo with. Além disso, é possível criar actions compostas (que combinam steps) ou actions em contêiner Docker. A reutilização reduz a complexidade e acelera a criação de pipelines.

Secrets

Secrets são variáveis de ambiente criptografadas usadas para armazenar informações sensíveis, como tokens de API, senhas ou chaves SSH. No GitHub Actions, secrets podem ser definidos no nível do repositório, da organização ou do ambiente (environment). Eles são injetados no workflow como variáveis de ambiente e nunca aparecem nos logs (a menos que explicitamente expostos).

Para usar um secret, defina-o nas configurações do repositório (Settings > Secrets and variables > Actions) e referencie-o no workflow com ${{ secrets.NOME_DO_SECRET }}. Exemplo:

steps:
  - name: Deploy to production
    run: deploy.sh
    env:
      API_TOKEN: ${{ secrets.API_TOKEN }}

É importante lembrar que secrets não estão disponíveis em forks de repositórios públicos por segurança. Para proteger ainda mais, você pode usar ambientes (environments) com regras de aprovação e secrets específicos. Nunca hardcode informações sensíveis no arquivo YAML.

Boas práticas

Ao criar workflows, siga estas recomendações: mantenha os workflows pequenos e focados em uma tarefa; use actions oficiais ou verificadas do Marketplace; aproveite a matriz de estratégia (matrix) para testar múltiplas versões de linguagens ou sistemas operacionais; e sempre utilize versões específicas das actions (ex: @v3) para evitar quebras inesperadas. Além disso, considere usar workflow_dispatch para permitir execução manual e schedule para tarefas periódicas.

Referências

Exercícios

  1. Crie um workflow YAML que seja disparado em todo push para qualquer branch e execute um job que imprime "Hello, GitHub Actions!" no log.

    ✓ Resposta:
    name: Hello World
    
    on: [push]
    
    jobs:
      hello:
        runs-on: ubuntu-latest
        steps:
          - run: echo "Hello, GitHub Actions!"
  2. Adicione um segundo job ao workflow anterior, chamado "test", que depende do job "hello" e executa o comando pwd.

    ✓ Resposta:
    name: Hello World
    
    on: [push]
    
    jobs:
      hello:
        runs-on: ubuntu-latest
        steps:
          - run: echo "Hello, GitHub Actions!"
      test:
        needs: hello
        runs-on: ubuntu-latest
        steps:
          - run: pwd
  3. Use a action actions/checkout@v3 no job "hello" para fazer checkout do código antes de imprimir a mensagem.

    ✓ Resposta:
    name: Hello World
    
    on: [push]
    
    jobs:
      hello:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v3
          - run: echo "Hello, GitHub Actions!"
  4. Crie um secret chamado MY_SECRET no seu repositório (simulado) e modifique o workflow para imprimir seu valor usando ${{ secrets.MY_SECRET }} (apenas para teste, nunca imprima secrets reais).

    ✓ Resposta:
    name: Print Secret
    
    on: [push]
    
    jobs:
      print:
        runs-on: ubuntu-latest
        steps:
          - run: echo "${{ secrets.MY_SECRET }}"

    Nota: Na prática, imprimir secrets não é seguro; isso é apenas para fins de aprendizado.

  5. Crie um workflow que use a action actions/setup-node@v3 para configurar o Node.js versão 18 e execute node --version.

    ✓ Resposta:
    name: Node Setup
    
    on: [push]
    
    jobs:
      node:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/setup-node@v3
            with:
              node-version: '18'
          - run: node --version