Dicas para automatizar releases com GitHub Actions e semantic versioning
1. Fundamentos do Semantic Versioning no Contexto de Automação
O versionamento semântico (SemVer) segue o formato MAJOR.MINOR.PATCH, onde cada incremento segue regras específicas:
- MAJOR: incrementado quando há mudanças incompatíveis com versões anteriores (breaking changes)
- MINOR: incrementado quando novas funcionalidades são adicionadas de forma compatível
- PATCH: incrementado para correções de bugs compatíveis com versões anteriores
No contexto de automação, o SemVer guia a lógica de releases ao ser combinado com convenções de commits. A especificação Conventional Commits estabelece um padrão que permite extrair automaticamente o tipo de incremento necessário:
feat: adiciona novo endpoint de usuários
fix: corrige validação de email duplicado
feat!: altera schema do banco de dados (breaking change)
Cada tipo de commit mapeia diretamente para um incremento SemVer, permitindo que ferramentas automatizadas determinem a próxima versão sem intervenção manual.
2. Estrutura Básica de um Workflow de Release no GitHub Actions
Um workflow de release no GitHub Actions requer três componentes essenciais: on (gatilhos), jobs (tarefas) e steps (passos). A configuração inicial deve considerar tanto gatilhos manuais quanto automáticos.
name: Automated Release
on:
workflow_dispatch: # Permite execução manual
push:
branches:
- main # Executa automaticamente ao fazer push na main
jobs:
release:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0 # Obtém todo o histórico para análise de tags
Este workflow mínimo já prepara o ambiente para versionamento automático, garantindo que todo o histórico de tags esteja disponível para análise.
3. Estratégias para Extrair e Incrementar a Versão Automaticamente
A automação de versão exige a leitura da última tag do repositório e o cálculo da próxima versão. A ação mathieudutour/github-tag-action simplifica esse processo:
- name: Bump version and push tag
id: tag_version
uses: mathieudutour/github-tag-action@v6.1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
tag_prefix: v
release_branches: main
- name: Display new version
run: echo "New version is ${{ steps.tag_version.outputs.new_tag }}"
Para cenários específicos, como versão inicial ou prefixos personalizados, é possível configurar parâmetros adicionais. O tratamento de versões pré-release permite testar antes de liberar para produção:
- name: Bump version with pre-release
uses: mathieudutour/github-tag-action@v6.1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
pre_release: true
pre_release_branches: develop
4. Geração Automática de Changelog e Notas de Release
A geração de changelog baseada em tipos de commit fornece transparência sobre as mudanças. Utilizando actions/github-script, é possível extrair commits entre tags e formatar notas de release:
- name: Generate release notes
id: release_notes
uses: actions/github-script@v7
with:
script: |
const commits = await github.rest.repos.compareCommits({
owner: context.repo.owner,
repo: context.repo.repo,
base: '${{ steps.tag_version.outputs.previous_tag }}',
head: '${{ steps.tag_version.outputs.new_tag }}'
});
let notes = '## O que mudou\n\n';
commits.data.commits.forEach(commit => {
const msg = commit.commit.message.split('\n')[0];
if (msg.startsWith('feat:')) {
notes += `- 🚀 **Nova funcionalidade**: ${msg.replace('feat:', '').trim()}\n`;
} else if (msg.startsWith('fix:')) {
notes += `- 🐛 **Correção**: ${msg.replace('fix:', '').trim()}\n`;
} else if (msg.includes('BREAKING CHANGE')) {
notes += `- ⚠️ **Breaking change**: ${msg}\n`;
}
});
return notes;
Este script categoriza automaticamente os commits e gera um changelog em markdown dinâmico, que pode ser utilizado na criação da release.
5. Criação e Publicação de Tags e Releases no GitHub
Após determinar a versão e gerar as notas, é necessário criar a tag e a release oficial. A ação softprops/action-gh-release simplifica esse processo:
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.tag_version.outputs.new_tag }}
name: Release ${{ steps.tag_version.outputs.new_tag }}
body: ${{ steps.release_notes.outputs.result }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Para evitar tags duplicadas, o workflow deve validar se a tag já existe antes de tentar criá-la. A validação pode ser feita verificando o output da ação de bump:
- name: Check if tag already exists
if: steps.tag_version.outputs.new_tag == ''
run: |
echo "Tag already exists or no changes detected"
exit 1
6. Integração com Publicação em Registries (npm, NuGet, Docker Hub, etc.)
Após a criação da tag, é possível disparar automaticamente a publicação em registries. O workflow condicional abaixo publica no npm apenas quando uma nova tag é criada:
- name: Publish to npm
if: steps.tag_version.outputs.new_tag != ''
run: |
npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_TOKEN }}
npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
Para Docker Hub, a integração segue padrão similar:
- name: Build and push Docker image
if: steps.tag_version.outputs.new_tag != ''
run: |
docker build -t myapp:${{ steps.tag_version.outputs.new_tag }} .
docker tag myapp:${{ steps.tag_version.outputs.new_tag }} myapp:latest
echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
docker push myapp:${{ steps.tag_version.outputs.new_tag }}
docker push myapp:latest
7. Boas Práticas e Tratamento de Erros em Workflows de Release
Proteção de branches e regras de merge são essenciais para evitar releases acidentais. Configure branch protection rules na interface do GitHub para exigir revisões e verificações de status antes do merge na main.
Para notificações de falha, integre com serviços externos:
- name: Notify Slack on failure
if: failure()
uses: slackapi/slack-github-action@v1.24.0
with:
payload: |
{
"text": "Falha no release automático: ${{ github.repository }} - ${{ github.run_id }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
Em caso de versão incorreta, utilize git revert para desfazer a tag:
- name: Rollback tag
if: failure() && steps.tag_version.outputs.new_tag != ''
run: |
git tag -d ${{ steps.tag_version.outputs.new_tag }}
git push origin :refs/tags/${{ steps.tag_version.outputs.new_tag }}
A automação de releases com GitHub Actions e semantic versioning transforma um processo manual e propenso a erros em um fluxo confiável e repetível. Ao combinar commits convencionais, workflows bem estruturados e ações especializadas, sua equipe pode focar no desenvolvimento enquanto o versionamento e a publicação ocorrem de forma transparente e consistente.
Referências
- Documentação oficial do GitHub Actions — Guia completo sobre criação e gerenciamento de workflows no GitHub
- Especificação Semantic Versioning 2.0.0 — Regras oficiais do versionamento semântico
- Conventional Commits 1.0.0 — Especificação para mensagens de commit padronizadas
- mathieudutour/github-tag-action — Ação para incremento automático de versão baseado em commits
- softprops/action-gh-release — Ação para criação de releases no GitHub com suporte a assets e notas
- actions/github-script — Ação para executar scripts JavaScript com acesso à API do GitHub
- Guia prático de GitHub Actions para automação de releases — Tutorial prático no Dev.to com exemplos reais de implementação