GitHub Actions básico: workflow, job e step
1. Visão geral
GitHub Actions é uma ferramenta CI/CD integrada nativamente ao GitHub, que ajuda você a automatizar build, test e deploy sem precisar de um servidor CI separado. Este artigo explica três conceitos fundamentais: workflow, job e step, com exemplos práticos para um projeto Spring Boot usando Maven.
Depois de ler, você saberá escrever um arquivo de workflow que execute testes automaticamente cada vez que você faz push do código, entender como os jobs rodam em paralelo ou em sequência, e saber como fazer debug quando o workflow falha.
2. O que é um Workflow?
Um workflow é um arquivo YAML localizado na pasta .github/workflows/ do seu repositório. Cada workflow define quando executar (trigger) e o que fazer (jobs).
# .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Build with Maven
run: mvn -B package
Giải thích:
nameno início do arquivo é o nome exibido na aba Actions do GitHub.ondeclara o trigger: aqui o workflow roda quando hápushoupull_requestpara a branchmain.jobs.buildé um job único chamadobuild, que roda em uma máquina virtualubuntu-latest.
Atenção: O arquivo deve estar no caminho correto
.github/workflows/*.yml; se estiver na pasta errada, o GitHub não reconhecerá o workflow.
3. Job: unidade de execução independente
Um workflow pode ter vários jobs. Cada job roda em um runner (máquina virtual) separado, e por padrão os jobs rodam em paralelo a menos que você declare needs.
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: mvn -B test
deploy:
runs-on: ubuntu-latest
needs: test
steps:
- run: echo "Deploying application..."
Giải thích:
testedeploysão dois jobs distintos; cada job tem seu próprio ambiente de runner limpo e não compartilha arquivos do sistema com o outro.needs: testforçadeploya esperar quetestseja bem-sucedido antes de rodar — criando uma relação sequencial.- Se não houver
needs, o GitHub Actions rodarátestedeploysimultaneamente.
3.1. Compartilhando dados entre jobs
Como cada job tem seu próprio runner, para passar arquivos de um job para outro você precisa usar artifacts.
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: mvn -B package
- uses: actions/upload-artifact@v4
with:
name: app-jar
path: target/*.jar
deploy:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/download-artifact@v4
with:
name: app-jar
Giải thích:
upload-artifactsalva o arquivo.jardo jobbuildno armazenamento temporário do GitHub.download-artifactno jobdeployrecupera esse arquivo para o novo runner.- Esta é a única maneira de passar dados entre jobs (você não pode usar variáveis de ambiente comuns).
4. Step: a menor unidade de operação
Um step é uma ação específica dentro de um job, executado sequencialmente de cima para baixo. Existem dois tipos principais de steps: usar uses (chamar uma action pronta) ou usar run (executar um comando shell).
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Cache Maven dependencies
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
- name: Run tests
run: mvn -B test
- name: Print result
run: echo "Test step finished with status ${{ job.status }}"
Giải thích:
- Step
Checkout codeusa a action prontaactions/checkoutpara trazer o código-fonte para o runner. - Step
Cache Maven dependenciesfaz cache do diretório~/.m2/repository, ajudando builds futuros a serem mais rápidos porque não precisam baixar as dependências novamente. keydo cache é baseado no hash dopom.xml— quandopom.xmlmuda, o cache antigo é descartado e um novo é criado.- O último step usa
runpara executar um comando shell comum, podendo referenciar contextos como${{ job.status }}.
5. Variáveis de ambiente e secrets
Frequentemente você precisa passar configurações sensíveis (chaves de API, tokens) para o workflow sem fazer hardcode no arquivo YAML.
jobs:
deploy:
runs-on: ubuntu-latest
env:
SPRING_PROFILES_ACTIVE: prod
steps:
- uses: actions/checkout@v4
- name: Deploy to server
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
run: |
echo "Deploying with profile $SPRING_PROFILES_ACTIVE"
curl -H "Authorization: Bearer $DEPLOY_TOKEN" https://api.example.com/deploy
Giải thích:
envno nível do job se aplica a todos os steps desse job (SPRING_PROFILES_ACTIVE).envno nível do step se aplica apenas a esse step (DEPLOY_TOKEN).secrets.DEPLOY_TOKENé obtido de Settings → Secrets and variables → Actions do seu repositório e nunca mostra o valor real nos logs.
Atenção: Nunca imprima diretamente o valor do secret com
echo $DEPLOY_TOKENnos logs — o GitHub automaticamente fará o mask, mas o melhor é evitar imprimir secrets.
6. Matrix: rodando múltiplas versões simultaneamente
Quando você precisa testar em múltiplas versões de Java ou de SO, matrix ajuda você a não ter que escrever jobs repetidos.
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
java: [17, 21]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
- run: mvn -B test
Giải thích:
strategy.matrix.javadeclara uma lista de valores[17, 21].- O GitHub Actions automaticamente cria 2 jobs em paralelo, cada job rodando com uma versão de Java correspondente.
- Esta abordagem economiza esforço ao escrever YAML repetitivo, sendo ideal quando você precisa garantir que o código roda bem em múltiplos ambientes.
7. Testando o workflow com testes simples
O workflow não testa diretamente com JUnit, mas você deve garantir que o passo mvn test no workflow execute corretamente a suite de testes do seu projeto. Por exemplo, um teste simples para OrderService que a CI executará:
class OrderServiceTest {
private final OrderService orderService = new OrderService();
@Test
void shouldCalculateTotalPriceCorrectly() {
Order order = new Order("ORD-001", List.of(
new OrderItem("Keyboard", 2, BigDecimal.valueOf(50)),
new OrderItem("Mouse", 1, BigDecimal.valueOf(20))
));
BigDecimal total = orderService.calculateTotal(order);
assertThat(total).isEqualByComparingTo(BigDecimal.valueOf(120));
}
}
Giải thích:
- Este teste está em
src/test/javae será executado pormvn -B testno step do workflow automaticamente. - Se o teste falhar, o step
Run testsreporta um erro (exit code diferente de 0), o job para e jobs dependentes (needs) não continuam. - Este é o motivo pelo qual CI é útil: código com erro é bloqueado antes de fazer merge para
main.
Resultado ao rodar no GitHub Actions UI (aba Actions):
✓ Checkout code
✓ Set up JDK 21
✓ Build with Maven
✓ Run tests (shouldCalculateTotalPriceCorrectly PASSED)
8. Erros comuns
- Indentação YAML incorreta: GitHub Actions usa YAML e uma indentação incorreta de um espaço faz com que o job ou step não seja reconhecido, geralmente reportando erro “Invalid workflow file”.
- Esqueceu de
runs-on: cada job deve ter obrigatoriamenteruns-on; sua falta fará o workflow falhar durante o parsing. - Usar variáveis de ambiente de um job em outro:
enve variáveis de output normalmente não são automaticamente passadas entre jobs; você precisa usaroutputsouartifact. - Cache key não muda quando dependencies mudam: se o
keydo cache for muito genérico (por exemplo, apenasmaven-cache), você pode usar acidentalmente um cache antigo contendo dependências desatualizadas. - Rodar workflow pesado em todo push: se você não limitar
pathsoubranches, até pequenos commits (corrigir README) dispararão todo o pipeline de build + test, desperdiçando minutos de execução do Actions (há limite de uso gratuito).
Atenção: Se seu projeto precisa apenas de um build simples e não tem muitos ambientes, não é necessário usar
matrixouartifact— adicionar complexidade desnecessária deixará o workflow difícil de manter.
9. Conclusão
Um workflow é um arquivo YAML que define todo o pipeline; um job é um bloco de execução independente que roda em seu próprio runner; e um step é cada ação específica dentro de um job. Entender bem esses três níveis ajuda você a organizar seu pipeline CI/CD claramente: qual job roda em paralelo, qual deve esperar o outro via needs, e quando usar artifact para compartilhar dados.
Quando está começando, você deve manter o workflow simples (um job, alguns steps básicos: checkout, setup Java, test). Conforme seu projeto crescer, adicione matrix, cache, e separe build/test/deploy em jobs distintos para facilitar o gerenciamento e debug.