Todo workflow do GitHub Actions que você já escreveu é um arquivo YAML. Tudo — os gatilhos, os jobs, as etapas, as variáveis de ambiente, a estratégia de matrix — é expresso em YAML. O que significa que entender YAML profundamente não é apenas acadêmico: é a diferença entre uma CI que funciona de forma confiável e uma CI que quebra misteriosamente às 16h de uma sexta-feira quando você está tentando lançar uma release.

O GitHub Actions tem excelentes documentações de sintaxe de workflow, mas focam nos recursos do Actions em vez da mecânica YAML subjacente. Este artigo cobre os dois — a estrutura do workflow e os padrões específicos de YAML (e armadilhas) que fazem ou destroem configurações de CI.

Estrutura do Workflow: As Chaves de Nível Superior

Um arquivo de workflow do GitHub Actions fica em .github/workflows/ e tem três chaves obrigatórias de nível superior. A visão geral oficial de workflows explica onde o arquivo fica no seu repositório e como o GitHub o descobre:

yaml
name: CI Pipeline          # optional but shown in the Actions UI

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

jobs:                      # the actual work
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm ci && npm test
Cuidado com on como booleano YAML: No YAML 1.1, a palavra simples on é interpretada como booleano true. O parser do GitHub lida com isso corretamente, mas se você alguma vez ver seu arquivo de workflow sinalizado por um linter YAML genérico, é por isso. A forma segura é usar aspas: "on": — embora o próprio GitHub Actions não exija as aspas.

Gatilhos: A Chave on:

A chave on: controla quando seu workflow é executado. A lista completa de eventos de gatilho cobre tudo, desde revisões de pull request até despachos de repositório. Aqui estão os padrões mais comuns:

yaml
on:
  # Push to specific branches
  push:
    branches:
      - main
      - "release/**"       # glob pattern — quotes needed for /**
    paths-ignore:
      - "docs/**"
      - "*.md"

  # PRs targeting main
  pull_request:
    branches: [main]
    types: [opened, synchronize, reopened]

  # Scheduled (cron syntax)
  schedule:
    - cron: "0 2 * * 1"   # every Monday at 2am UTC

  # Manual trigger with input
  workflow_dispatch:
    inputs:
      environment:
        description: "Target environment"
        required: true
        default: staging
        type: choice
        options: [staging, production]

Comandos run: Multilinha e Escalares de Bloco YAML

A chave run: é onde o escalar de bloco literal do YAML (|) mostra seu valor — veja a referência yaml-multiline.info para cada variação de escalares de bloco e dobrados. Sem ele, você estaria encadeando comandos com && em uma única linha, o que é ilegível:

yaml
steps:
  # Single line — works but hard to read
  - name: Build
    run: npm ci && npm run lint && npm run test && npm run build

  # Multi-line with literal block scalar — much better
  - name: Build
    run: |
      npm ci
      npm run lint
      npm run test -- --coverage
      npm run build

  # Folded scalar (>) joins lines with spaces — NOT what you want for shell
  # This would run as a single command with spaces where the newlines are:
  - name: Wrong for shell
    run: >
      npm ci
      npm test
Sempre use | para scripts shell multilinha, nunca >. O escalar dobrado dobra quebras de linha em espaços, o que significa que seus comandos shell se juntam em uma string longa. Isso causa erros crípticos. O escalar de bloco literal | preserva quebras de linha exatamente, então cada linha é executada como um comando shell separado.

Variáveis de Ambiente e Secrets

Variáveis de ambiente no GitHub Actions usam YAML em combinação com a sintaxe de contexto e expressão do Actions. Veja como elas se encaixam:

yaml
env:
  NODE_ENV: production         # workflow-level env var
  API_VERSION: "2.1"          # quoted to force string type

jobs:
  deploy:
    runs-on: ubuntu-latest
    env:
      DEPLOY_ENV: staging      # job-level env var (overrides workflow-level)

    steps:
      - name: Deploy to staging
        env:
          API_KEY: ${{ secrets.STAGING_API_KEY }}     # step-level, from secret
          DATABASE_URL: ${{ vars.STAGING_DB_URL }}    # step-level, from variable
        run: |
          echo "Deploying to $DEPLOY_ENV"
          ./scripts/deploy.sh

A sintaxe de expressão ${{ }} é a linguagem de template própria do GitHub Actions, colocada em camada sobre o YAML. Ela é avaliada antes do parsing YAML em alguns contextos, o que pode interagir inesperadamente com as próprias regras de aspas do YAML. Na dúvida, envolva valores que começam com ${{ em aspas duplas.

Estratégia de Matrix: Build em Múltiplas Configurações

A estratégia de matrix é um dos recursos mais poderosos do GitHub Actions — e é expresso inteiramente em YAML. Ela executa seu job uma vez para cada combinação de valores de matrix:

yaml
jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false         # don't cancel other matrix jobs on failure
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node: ["18", "20", "22"]
        exclude:
          - os: windows-latest
            node: "18"         # skip this combination

    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}
      - run: npm ci && npm test

Esta definição de um único job produz até 9 execuções paralelas (3 OS × 3 versões Node, menos 1 excluída). Os valores de matrix YAML são arrays simples — a única coisa a observar é que números de versão como 18 devem ser citados como "18" para mantê-los como strings. Sem aspas, o YAML os interpreta como inteiros e algumas Actions podem reclamar.

Etapas Condicionais com if:

A chave if: permite pular etapas com base em condições. Usa a linguagem de expressão do GitHub, não valores YAML simples:

yaml
steps:
  - name: Run tests
    run: npm test

  - name: Upload coverage
    if: success() && github.ref == 'refs/heads/main'
    uses: codecov/codecov-action@v4

  - name: Notify on failure
    if: failure()
    uses: slackapi/slack-github-action@v1
    with:
      slack-message: "Build failed on ${{ github.ref }}"
      channel-id: "C12345ABC"
    env:
      SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }}

  - name: Deploy (only on tag push)
    if: startsWith(github.ref, 'refs/tags/v')
    run: ./scripts/deploy.sh production

Workflows Reutilizáveis

Para eliminar duplicação entre repositórios (não apenas dentro de um arquivo), o GitHub Actions suporta workflows reutilizáveis. O workflow chamado usa on: workflow_call: e o chamador usa uses: no nível do job — não no nível da etapa:

yaml
# .github/workflows/reusable-test.yml (the reusable workflow)
on:
  workflow_call:
    inputs:
      node-version:
        required: false
        type: string
        default: "20"
    secrets:
      NPM_TOKEN:
        required: true

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}
      - run: npm ci && npm test
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
yaml
# .github/workflows/ci.yml (the caller)
on: [push, pull_request]

jobs:
  run-tests:
    uses: my-org/shared-workflows/.github/workflows/reusable-test.yml@main
    with:
      node-version: "22"
    secrets:
      NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

Os Erros YAML Mais Comuns no GitHub Actions

  • Indentação errada nas etapas. As etapas devem ser indentadas sob steps: com espaços consistentes. Um único espaço a mais ou a menos move uma etapa para fora do seu job.
  • Números de versão sem aspas. node-version: 20 passa um inteiro; algumas actions esperam uma string. Use node-version: "20" para garantir.
  • Usando > em vez de | para scripts shell. Escalares dobrados colapsam quebras de linha. Seu script multilinha se torna uma string longa e falha.
  • Aspas ausentes em padrões glob. Padrões como release/** contêm * que o YAML pode interpretar como alias. Sempre use aspas em padrões glob.
  • Secrets usados em condições if:. O GitHub mascara secrets em logs, mas não os permite em expressões if:. Use uma variável de ambiente em vez disso.
  • Esquecer fail-fast: false em jobs de matrix. Por padrão, se um job de matrix falha, todos os outros são cancelados. Geralmente não é o que você quer durante a depuração.

Conclusão

Workflows do GitHub Actions são fundamentalmente arquivos YAML — entender os escalares de bloco literal do YAML, as regras de aspas e a coerção de tipos se traduz diretamente em escrever uma CI mais confiável. As falhas mais comuns vêm de erros de indentação, valores sem aspas que coagem para o tipo errado e usar o operador de string multilinha errado para scripts shell. Acerte essas três coisas e a maior parte da depuração de workflow se torna direta. Use o Validador YAML para capturar erros de sintaxe antes de fazer push, e o Formatador YAML para impor indentação consistente em seus arquivos de workflow.