Commitlint: validando mensagens de commit automaticamente
1. Por que padronizar mensagens de commit?
O histórico de commits de um repositório Git é o diário de bordo de um projeto. Mensagens claras e consistentes transformam esse diário em uma ferramenta poderosa de comunicação entre desenvolvedores, permitindo entender rapidamente o que foi alterado, por que e como.
Mensagens não padronizadas geram problemas graves:
# Exemplos de mensagens problemáticas
"arrumei o bug"
"commit"
"varias coisas"
"fix"
""
Essas mensagens são inúteis para revisão de código, debugging e geração automatizada de changelogs. Sem padronização, ferramentas como git log --oneline ou git shortlog perdem grande parte de seu valor.
A padronização facilita:
- Geração automática de changelogs: ferramentas como
standard-versionousemantic-releasedependem de mensagens estruturadas para criar notas de release. - Rastreamento de mudanças: identificar rapidamente commits que introduzem novas funcionalidades, corrigem bugs ou quebram a compatibilidade.
- Automação de versionamento semântico: mensagens padronizadas permitem determinar automaticamente se a próxima versão deve ser major, minor ou patch.
2. O que é o Commitlint?
Commitlint é uma ferramenta de linha de comando que valida mensagens de commit contra um conjunto de regras configuráveis. Ela se integra diretamente ao fluxo do Git através do hook commit-msg, que é executado imediatamente após o desenvolvedor digitar a mensagem e antes do commit ser efetivado.
Diferente de validadores manuais ou scripts caseiros, o Commitlint oferece:
- Regras predefinidas: o pacote
@commitlint/config-conventionalimplementa o padrão Conventional Commits, amplamente adotado. - Flexibilidade: permite customizar tipos, escopos, formatos e até criar regras próprias.
- Mensagens de erro claras: quando a validação falha, o Commitlint exibe exatamente qual regra foi violada e como corrigir.
A principal diferença entre Commitlint e outros validadores é que ele opera especificamente no hook commit-msg, recebendo a mensagem completa como entrada e retornando um código de saída que o Git interpreta para aceitar ou rejeitar o commit.
3. Configuração inicial do Commitlint
A instalação é feita via npm ou yarn. Primeiro, instale os pacotes necessários:
npm install --save-dev @commitlint/cli @commitlint/config-conventional
Em seguida, crie o arquivo de configuração. O formato mais comum é commitlint.config.js:
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional']
};
Ou, alternativamente, usando JSON:
// .commitlintrc.json
{
"extends": ["@commitlint/config-conventional"]
}
Com essa configuração mínima, o Commitlint passa a validar mensagens seguindo o padrão Conventional Commits, que exige o formato:
tipo(escopo): descrição
[corpo opcional]
[rodapé opcional]
Exemplos de mensagens válidas:
feat: adiciona autenticação por OAuth2
fix(auth): corrige validação de token expirado
docs: atualiza README com instruções de instalação
refactor!: remove suporte a API legada
4. Regras e configurações avançadas
O Commitlint organiza suas regras em uma estrutura de três elementos: [nível, aplicabilidade, valor].
- Nível:
0para desativar,1para warning,2para error. - Aplicabilidade:
'always'ou'never'. - Valor: o valor a ser verificado (ex.: array de tipos permitidos).
Exemplo de configuração avançada:
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'chore']],
'scope-enum': [2, 'always', ['core', 'auth', 'api', 'ui']],
'subject-case': [2, 'always', ['sentence-case', 'lower-case']],
'header-max-length': [2, 'always', 72],
'body-max-line-length': [2, 'always', 100],
'footer-empty': [1, 'never']
}
};
Regras comuns:
type-enum: define os tipos permitidos (feat, fix, docs, etc.)scope-enum: define escopos válidos para o projetosubject-case: força um formato de capitalização no assuntoheader-max-length: limita o tamanho da primeira linhabody-max-line-length: limita linhas do corpo da mensagemfooter-empty: exige ou proíbe rodapés
5. Integração com Git Hooks (local)
O hook commit-msg é o ponto de integração natural. Localize o diretório .git/hooks no seu repositório e crie ou edite o arquivo commit-msg:
#!/bin/sh
# .git/hooks/commit-msg
npx commitlint --edit $1
Dê permissão de execução:
chmod +x .git/hooks/commit-msg
Agora, ao tentar fazer um commit com mensagem inválida:
git commit -m "corrige bug"
O Commitlint rejeitará o commit com uma mensagem como:
⧗ input: corrige bug
✖ type must be one of [feat, fix, docs, style, refactor, test, chore] [type-enum]
✖ found 1 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint
A vantagem dos hooks locais é que a validação ocorre antes mesmo do commit ser registrado, evitando poluir o histórico. A desvantagem é que desenvolvedores podem pular o hook com git commit --no-verify.
6. Integração com Husky e ferramentas de CI
O Husky simplifica o gerenciamento de hooks Git, permitindo configurá-los diretamente no package.json. Instale:
npm install --save-dev husky
Ative os hooks:
npx husky install
Adicione um hook commit-msg via Husky:
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit $1'
O package.json pode conter a configuração completa:
{
"scripts": {
"prepare": "husky install"
},
"husky": {
"hooks": {
"commit-msg": "npx --no -- commitlint --edit $1"
}
}
}
Para ambientes de CI, o Commitlint pode validar mensagens de pull requests ou commits em pipelines. Exemplo para GitHub Actions:
# .github/workflows/commitlint.yml
name: Commitlint
on: [push, pull_request]
jobs:
commitlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-node@v3
with:
node-version: 18
- run: npm install
- run: npx commitlint --from ${{ github.event.before }} --to ${{ github.sha }}
Uma pipeline completa pode incluir lint-staged para formatar código, Commitlint para validar mensagens e testes automatizados:
{
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"commit-msg": "commitlint --edit $1"
}
},
"lint-staged": {
"*.js": ["eslint --fix", "git add"]
}
}
7. Boas práticas e resolução de problemas
Mensagens inválidas: quando o Commitlint rejeita uma mensagem, a melhor prática é corrigi-la e refazer o commit. Use git commit --amend para editar a mensagem do último commit ou git rebase -i para corrigir commits mais antigos. Evite --no-verify a menos que seja absolutamente necessário (ex.: durante rebase interativo).
Commits de merge: por padrão, o Commitlint pode rejeitar mensagens geradas automaticamente pelo Git durante merges. Para ignorar validação nesses casos, configure:
// commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
ignores: [(message) => message.startsWith('Merge')]
};
Versionamento da configuração: mantenha o commitlint.config.js no repositório para que todos os desenvolvedores usem as mesmas regras. Isso garante consistência mesmo em equipes grandes ou distribuídas.
Dica para times: comece com regras permissivas e vá endurecendo gradualmente. Uma transição brusca pode frustrar a equipe. Use warnings (nível: 1) inicialmente e evolua para errors (nível: 2) quando todos estiverem adaptados.
Referências
- Documentação oficial do Commitlint — Guia completo de instalação, configuração e todas as regras disponíveis.
- Conventional Commits specification — Especificação formal do padrão de mensagens que o Commitlint valida por padrão.
- Husky: Git hooks made easy — Documentação oficial do Husky para gerenciar hooks Git de forma declarativa.
- Commitlint + Husky: tutorial prático — Artigo no Dev.to com exemplos práticos de configuração em projetos reais.
- Git Hooks documentation — Documentação oficial do Git sobre hooks, incluindo o hook commit-msg utilizado pelo Commitlint.