Estudo de caso
Go Release Manager
Uma ferramenta de linha de comando em Go que decide a próxima versão de um software pelas mensagens de commit e cria a tag. Está em produção: é ela que versiona o sdd-kit.
- v1.3.0 em produção, versionando o sdd-kit
- 97% de cobertura de testes
- 3 sistemas no CI: Linux, macOS e Windows
Números da release v1.3.0, medidos em 15/10/2026 (cobertura de 96,9%). Tudo pode ser conferido no repositório público.
Ver o repositório →Ler a documentação →Baixar as releases →
$ git log --oneline
fa4ed96 docs: usage examples
1be5e0c feat(cli)!: drop --legacy
57bc93a fix(parser): handle empty input
43d79e8 (tag: v1.3.0) feat: first command
$ go-release-manager create --dry-run --explain
No .go-releaserc.yml found. Using the default rules (feat/fix).
Latest stable version: v1.3.0
Analyzing 3 commit(s) since tag v1.3.0...
Analysis done. Highest increment: Major
Increment: Major. Next version: v2.0.0
--- DRY RUN ---
Latest tag: v1.3.0
Commits analyzed: 3
Increment: Major
Decided by:
Major feat(cli)!: drop --legacy ("!" in the header)
Tag that would be created: v2.0.0
--- END OF DRY RUN ---
$ go-release-manager check-pr --title "feat: drop --legacy" \
--body "BREAKING CHANGE: --legacy was removed"
Pull request: feat: drop --legacy
Breaking change announced in: PR description
✗ [breaking-not-in-title] the pull request announces a breaking change (PR description), but the title has no "!", and the title is what a squash merge keeps: use "feat!: drop --legacy"
1 error, 0 warningsO problema
Decidir a próxima versão costuma ser feito à mão: alguém lê os commits, escolhe entre patch, minor e major e cria a tag. É lento, depende de memória e erra fácil, principalmente com pré-releases e mudanças incompatíveis.
Ferramentas como o semantic-release resolvem, mas trazem Node.js, plugins e configuração para projetos que não usam nada disso.
A solução
Um binário único, sem dependências, que lê os commits desde a última versão estável, aplica o padrão Conventional Commits e calcula a próxima tag SemVer. A ferramenta faz uma coisa só: o build, as notas e a publicação da release ficam com o GoReleaser.
- O comando next imprime só a versão, para scripts; a saída JSON entrega o resultado completo.
- A simulação (--dry-run) mostra tudo sem escrever nada e sem pedir credenciais.
- Pré-releases por canal (beta, rc) com contador contínuo, versão forçada quando o plano exige e tag num commit específico.
- Uma GitHub Action pronta, que baixa o binário da release e confere o checksum antes de rodar.
- O --explain lista os commits que decidiram o incremento e a regra de cada um, para ninguém precisar adivinhar por que a versão saiu major.
Quebras de compatibilidade em squash merges
Depois da divulgação, um leitor perguntou o que acontece num squash merge, em que só a mensagem final do commit decide a versão. Se o aviso BREAKING CHANGE ficou só na descrição do PR ou num commit do meio, e o título não tem "!", a release sai minor quando deveria ser major, e quem atualiza não sabe que algo quebrou.
A v1.3.0 fecha essa brecha em camadas, todas com a mesma regra de detecção do cálculo de versão:
- O comando check-pr reprova o PR quando a quebra anunciada em qualquer lugar não está no título, e sugere o título certo; também avisa de rodapé fora do lugar e de "BREAKING CHANGES" escrito errado.
- Uma Action check-pr para o CI, que lê título e descrição do arquivo do evento, e não do script, para que um título malicioso não injete código.
- Uma rede de segurança depois do merge, que compara o commit que entrou na main com o PR e abre uma issue se a quebra se perdeu.
- Um guia de boas práticas, em português e em inglês, com o que o GitHub guarda em cada configuração do squash e como escrever o PR.
Como a qualidade é garantida
O projeto segue o mesmo processo que uso no trabalho:
- Desenvolvimento guiado por especificação: cada regra de versão tem critérios de aceite, e cada critério aponta para o teste que o prova.
- Testes de unidade e de ponta a ponta, com 97% de cobertura do código; o CI exige no mínimo 80%.
- CI em todo pull request: lint, testes com detecção de condição de corrida, build, os comandos do README, a Action rodando em Linux, macOS e Windows e o próprio check-pr conferindo o título.
- Release automatizada: a ferramenta calcula a própria versão, e o GoReleaser publica binários para cinco plataformas com checksum SHA-256.
- Uma branch e um pull request por tarefa, com Conventional Commits e CHANGELOG.
Decisões técnicas
- A base é sempre a maior tag estável alcançável, então uma pré-release nunca vira base por engano.
- Em versões 0.x, uma mudança incompatível sobe a minor, como manda o SemVer.
- Diagnóstico vai para o stderr e o resultado para o stdout: scripts leem a saída sem filtrar nada.
- A ferramenta fala inglês, e a documentação é um guia completo em português e em inglês.
Resultado
A ferramenta calcula as versões do sdd-kit, meu kit de desenvolvimento guiado por especificação, a cada fechamento de fase. Na revisão para produção, testes novos levaram a cobertura de todo o código de 7,9% para 97,1%, e um defeito no --version do binário publicado foi achado e corrigido antes da divulgação.
Limites
O uso real ainda é no sdd-kit e nos meus projetos; falta a validação de outros times. O check-pr pega só quebras declaradas (com "!" ou BREAKING CHANGE): uma quebra que ninguém anunciou exigiria comparar a API pública entre versões. Não há imagem Docker publicada, e a ferramenta, de propósito, não gera changelog nem publica a release sozinha.
Precisa de alguém que entregue assim?
Trabalho com QA e desenvolvimento: especificação, testes automatizados, CI e entrega até produção, com documentação para quem vai usar.
Fale comigo →