GitHub Actions
O GitHub Actions1 é a automação embutida no GitHub: um arquivo YAML no seu repositório diz quando algo deve rodar (um push, um pull request, um agendamento) e o que roda (build, testes, publicar uma imagem, deploy). Nesta disciplina, ele é o caminho mais curto entre um commit e um microsserviço no ar: cada push faz o build do projeto Maven, roda os testes, constrói a imagem Docker e, na main, faz o rollout no Kubernetes.
Esta página responde a três perguntas: como um workflow é montado, o que decide quanto tempo uma pipeline leva e o que roda quando um job falha. Ela termina com o básico de segurança (secrets, GITHUB_TOKEN, pinning) e uma comparação com o Jenkins.
Como funciona
Os workflows são orientados a eventos. O arquivo de workflow fica em .github/workflows/, então é versionado com o seu código e revisado nos mesmos pull requests.
flowchart LR
event(["Evento<br/>ex.: push na main"]) --> workflow["Workflow<br/>.github/workflows/*.yml"]:::comp
workflow --> jobs["Jobs<br/>em paralelo por padrão,<br/>ordenados por needs"]:::comp
jobs --> steps["Steps<br/>run: um comando de shell<br/>uses: uma action"]:::tech
steps -.->|"cada job executa em"| runner["Runner<br/>uma VM nova do GitHub<br/>ou uma máquina sua"] | Conceito | Descrição |
|---|---|
| Workflow | Um processo automatizado definido em um arquivo YAML. Um repositório pode ter vários workflows. |
| Event | O que inicia um workflow: push, pull_request, schedule, workflow_dispatch (um botão), workflow_run (outro workflow terminou)… |
| Job | Um conjunto de steps que rodam no mesmo runner. Os jobs rodam em paralelo, a menos que needs os ordene. |
| Step | Uma tarefa: um comando de shell (run) ou uma action reutilizável (uses). Os steps de um job compartilham o sistema de arquivos do runner. |
| Action | Uma unidade reutilizável de automação, em geral do GitHub Marketplace (ex.: actions/checkout). |
| Runner | A máquina que executa um job (ubuntu-latest, windows-latest, macos-latest ou uma sua). Um runner hospedado pelo GitHub é uma VM nova para cada job. |
Um primeiro workflow: CI para um microsserviço Spring Boot
O workflow abaixo roda a cada push na main e em todo pull request. Um único job faz o checkout do código, configura o Java, roda os testes, empacota o jar e constrói e publica a imagem Docker.
permissions: contents: readdá aoGITHUB_TOKENdo job só o que ele precisa (veja Secrets eGITHUB_TOKEN).cache: mavenrestaura o~/.m2/repositoryde execuções anteriores (veja Cache).- A imagem recebe como tag o SHA do commit (
github.sha), então cada imagem aponta de volta para o código exato que a construiu. - O step de push só roda em eventos
push: um pull request faz o build e os testes, mas não publica nada.
Jobs, needs e o caminho crítico
Um único job é fácil de ler, mas lento: cada step espera o anterior. Dividir o trabalho em jobs permite que trabalhos independentes rodem em runners separados ao mesmo tempo, e needs declara a ordem onde ela importa. Os jobs formam um DAG (grafo acíclico dirigido):
Os steps de checkout e de configuração ficaram de fora para mostrar a forma. Duas regras decidem tudo o que vem a seguir2:
- Um job começa quando todos os jobs do seu
needsterminaram com sucesso. Jobs semneedscomeçam na hora, cada um no seu runner. - Quando um job necessário falha ou é pulado, todo job que precisa dele, direta ou indiretamente, é pulado (skipped), a menos que tenha uma condição como
if: always()ouif: failure(). Os jobs que não precisam do que falhou continuam rodando.
Experimente
Aperte o play com os valores iniciais: a execução leva 14 min de tempo de relógio, mas 18 minutos de runner, porque os três jobs do meio rodam lado a lado. As barras contornadas são o caminho crítico, build → testes de integração → imagem → deploy. Agora rode os testes unitários em 3 versões do Java: continua em 14 min, mas com 24 minutos de runner — uma matriz custa minutos, não tempo, enquanto houver runners livres. Ligue o cache de dependências: o build está no caminho crítico, então a execução cai para 11 min (e 21 minutos de runner). Por fim, faça os testes unitários falharem: lint e testes de integração rodam até o fim, imagem e deploy são pulados, e a execução para em 8 min, depois de 18 minutos de runner.
O que o simulador mostra:
- O tempo de relógio é o caminho mais longo do DAG (o caminho crítico), não a soma dos jobs. Para acelerar uma pipeline, encurte um job nesse caminho, ou divida-o: acelerar o lint aqui não muda nada.
- Os minutos de runner são a soma de todos os jobs. É isso que um repositório privado paga: o GitHub cobra cada job arredondado para cima ao minuto inteiro3. Jobs em paralelo trocam minutos por tempo.
- Execuções reais também gastam tempo na fila por um runner e preparando cada job (uma VM nova, checkout, Java). Muitos jobs pequenos pagam esse custo muitas vezes.
- Cada job começa numa máquina limpa: arquivos não passam de um job para outro. Passe os resultados do build com as actions de artefatos (
actions/upload-artifact/actions/download-artifact), ou construa-os de novo.
Matriz
Uma matriz (matrix) roda o mesmo job uma vez para cada combinação de valores, em paralelo. Um uso típico é testar em várias versões do Java ou sistemas operacionais:
| unit tests on three Java versions | |
|---|---|
- Cada combinação é um job separado, com o seu runner e os seus minutos; um job com
needs: unit-testsespera todas elas. fail-fastvaletruepor padrão: quando uma combinação falha, o GitHub cancela as que ainda estão rodando ou na fila. Usefalsepara ver todas as falhas.max-parallellimita quantas combinações rodam ao mesmo tempo; uma matriz gera no máximo 256 jobs por execução2.
Cache
Cada job começa numa VM nova, então, sem cache, o Maven baixa todas as dependências a cada execução. Um cache salva um diretório sob uma chave (key) e o restaura nas execuções seguintes:
actions/setup-javacomcache: mavenguarda o~/.m2/repository, com a chave calculada a partir de um hash dos seus arquivospom.xml: mude uma dependência e a chave muda.- Para o resto,
actions/cacherecebe umpathe umakeyque você monta, por exemplo com${{ hashFiles('**/package-lock.json') }}. - Um cache acelera, mas nunca é a fonte da verdade: o build precisa funcionar também quando o cache falta.
Secrets e GITHUB_TOKEN
Duas credenciais diferentes chegam a um workflow:
| Secrets | GITHUB_TOKEN | |
|---|---|---|
| O quê | Valores que você guarda: DOCKERHUB_TOKEN, KUBECONFIG, chaves de API | Um token que o GitHub cria automaticamente para cada job |
| Onde | Settings → Secrets and variables → Actions (repositório, environment ou organização) | Nada a configurar: ${{ secrets.GITHUB_TOKEN }} ou ${{ github.token }} |
| Alcança | Outros sistemas (Docker Hub, o cluster, uma nuvem) | Só este repositório: checkout, comentários no PR, releases, GitHub Packages |
| Validade | Até você trocá-lo | Expira quando o job termina |
| Escopo | Quem pode mudar um workflow pode usá-lo | A chave permissions:, por workflow ou por job |
- Os secrets são ocultados nos logs e não são passados a workflows disparados a partir de um fork (o
GITHUB_TOKENé a exceção)4. Mesmo assim, qualquer pessoa com acesso de escrita pode escrever um workflow que os use: trate acesso de escrita como acesso aos secrets. - Declare
permissions:explicitamente e conceda o mínimo necessário: ao listar uma permissão, todas as que você não listar passam anone2. Aumente-as só no job que precisa:
| least privilege | |
|---|---|
Fixando actions por SHA
uses: actions/checkout@v7 aponta para uma tag, e uma tag pode ser movida: se o repositório da action for comprometido, a próxima execução roda o que quer que v7 aponte agora, com os seus secrets ao alcance. O guia de segurança do GitHub recomenda fixar (pin) as actions num SHA de commit completo, que não muda5:
| pinned to a commit | |
|---|---|
- Mantenha a versão num comentário, para que uma pessoa (ou o Dependabot, com o ecossistema
github-actions) saiba o que é e possa atualizá-la. - Confira que o SHA vem do repositório da própria action, e não de um fork:
git ls-remote --tags https://github.com/actions/checkout. - O mesmo guia também descreve os lockfiles de dependências de workflow, que registram o commit exato de cada action (inclusive as actions que uma action usa) e substituem o pinning por SHA nos repositórios que os adotam5.
CD: deploy no Kubernetes após uma execução bem-sucedida da CI
Este segundo workflow começa quando o workflow CI termina na main, e só faz o deploy se a CI passou.
Qual commit?
Num workflow iniciado por workflow_run, github.sha é o último commit da branch padrão, não necessariamente o commit que a CI construiu6. Quando dois pushes chegam muito próximos, ele pode nomear uma imagem que ainda não existe. Use github.event.workflow_run.head_sha, o commit da execução de CI que disparou esta.
GitHub Actions vs Jenkins
As duas ferramentas implementam os mesmos conceitos de CI/CD, com trade-offs diferentes:
| Aspecto | GitHub Actions | Jenkins |
|---|---|---|
| Configuração inicial | Nenhuma — roda na nuvem do GitHub | Exige um servidor (Docker, VM ou bare metal) |
| Configuração | YAML em .github/workflows/ | Jenkinsfile em Groovy |
| Escalabilidade | Gerenciada pelo GitHub | Você gerencia a frota de agentes |
| Reúso | Actions do GitHub Marketplace | Plugins do update center do Jenkins |
| Custo | Gratuito para runners padrão em repositórios públicos; privados têm uma franquia mensal e depois pagam por minuto3 | Gratuito e open source; você paga pela infraestrutura |
| Runners self-hosted | Suportados | Nativo — o Jenkins é o servidor |
| Indicado para | Projetos novos, open source, equipes centradas no GitHub | Ambientes on-premise, pipelines complexas, código em vários hosts |
Pontos-chave
- Um workflow é evento → jobs → steps, versionado em
.github/workflows/; cada job roda num runner novo. - Os jobs rodam em paralelo, a menos que
needsos ordene. O tempo de relógio é o caminho crítico desse DAG e os minutos de runner são a soma de todos os jobs: acelere o caminho, não os jobs laterais. - Quando um job falha, os jobs que precisam dele são pulados, enquanto os independentes continuam rodando;
if: always()ouif: failure()muda isso de propósito. - Uma matriz multiplica jobs (e minutos), não tempo; um cache elimina downloads repetidos, e rende mais no caminho crítico.
- Secrets alcançam outros sistemas; o
GITHUB_TOKENalcança este repositório e é limitado porpermissions:— conceda o mínimo. Fixe as actions num SHA de commit.
-
Workflow syntax for GitHub Actions:
needs,strategy.fail-fast,max-parallel,permissions. ↩↩↩