Como configurar pre-commit hooks para garantir qualidade de código
1. Introdução aos pre-commit hooks e seu papel na qualidade
Pre-commit hooks são scripts executados automaticamente pelo Git antes que um commit seja efetivado. Eles funcionam como um gatekeeper: se qualquer hook falhar, o commit é bloqueado até que o problema seja corrigido. Esse mecanismo permite capturar erros antes mesmo de o código entrar no repositório.
A principal vantagem da automação via hooks é a prevenção de bugs e a padronização do código. Em vez de depender de checklists manuais ou da boa vontade de cada desenvolvedor, você define regras que são aplicadas automaticamente a cada commit. Isso reduz drasticamente o retrabalho em code reviews e acelera o onboarding de novos membros no time.
O ecossistema de pre-commit hooks é variado. O pre-commit framework (para Python e multilinguagem) é o mais flexível, enquanto o Husky é popular em projetos Node.js. Há também hooks nativos do Git, mas o framework oferece gerenciamento centralizado e atualizações automáticas.
2. Instalação e configuração básica do pre-commit framework
A instalação é simples com pip:
pip install pre-commit
Em seguida, crie o arquivo de configuração .pre-commit-config.yaml na raiz do repositório:
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
Para ativar os hooks no repositório local:
pre-commit install
Comandos essenciais para o dia a dia:
# Executar hooks em todos os arquivos (útil na primeira configuração)
pre-commit run --all-files
# Atualizar versões dos hooks para as releases mais recentes
pre-commit autoupdate
3. Hooks para formatação e estilo de código
A formatação consistente é o primeiro passo para qualidade. Exemplos de hooks populares:
repos:
- repo: https://github.com/psf/black
rev: 24.4.2
hooks:
- id: black
language_version: python3.12
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.1.0
hooks:
- id: prettier
types_or: [javascript, typescript, css, markdown]
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: v0.5.0
hooks:
- id: ruff
args: [--fix]
Para evitar conflitos entre formatadores e linters, configure a ordem correta — geralmente o formatador deve ser executado antes do linter. Use args para definir opções específicas, como --fix no Ruff para correção automática de problemas.
4. Hooks para análise estática e prevenção de bugs
Além de estilo, é crucial detectar bugs potenciais e vulnerabilidades:
repos:
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.10.0
hooks:
- id: mypy
args: [--strict]
- repo: https://github.com/PyCQA/bandit
rev: 1.7.9
hooks:
- id: bandit
args: [-ll, -c, pyproject.toml]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-added-large-files
args: [--maxkb=500]
- id: detect-private-key
O check-added-large-files impede que arquivos grandes (como datasets ou binários) sejam commitados acidentalmente. O detect-private-key varre o código em busca de chaves SSH ou tokens expostos — uma camada extra de segurança.
5. Hooks para validação de testes e cobertura
Para garantir que testes passem antes de cada commit, configure um hook personalizado:
repos:
- repo: local
hooks:
- id: pytest
name: pytest
entry: pytest --cov --cov-fail-under=80
language: system
types: [python]
stages: [pre-push]
Observe o uso de stages: [pre-push] — isso evita que testes lentos sejam executados a cada commit, rodando apenas antes do push. Para hooks mais rápidos, use always_run: false e defina types específicos. Estratégias como essa mantêm o fluxo de trabalho ágil sem sacrificar a qualidade.
6. Hooks personalizados e scripts locais
Quando as ferramentas existentes não atendem, crie hooks locais:
repos:
- repo: local
hooks:
- id: commit-msg-validator
name: Validar mensagem de commit
entry: scripts/validate_commit_msg.sh
language: script
stages: [commit-msg]
- id: file-naming-convention
name: Verificar nomes de arquivos
entry: python scripts/check_filenames.py
language: python
types: [python]
Boas práticas para hooks locais:
- Idempotência: executar várias vezes deve produzir o mesmo resultado.
- Performance: evite scripts que demorem mais que alguns segundos.
- Logs claros: mensagens de erro devem indicar exatamente o que corrigir.
Exemplo de script validate_commit_msg.sh:
#!/bin/bash
# Valida se a mensagem de commit segue o padrão Conventional Commits
commit_msg=$(cat "$1")
pattern="^(feat|fix|docs|style|refactor|perf|test|chore)(\(.+\))?: .{1,50}"
if ! [[ $commit_msg =~ $pattern ]]; then
echo "ERRO: Mensagem de commit inválida. Use: tipo(escopo): descrição"
exit 1
fi
7. Integração contínua e manutenção dos hooks
Para garantir que todos os desenvolvedores e a CI rodem os mesmos hooks, adicione um passo no pipeline. Exemplo para GitHub Actions:
name: Pre-commit Checks
on: [push, pull_request]
jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- run: pip install pre-commit
- run: pre-commit run --all-files
A manutenção contínua é facilitada pelo pre-commit autoupdate, que atualiza as versões dos hooks. Para automação total, configure o Dependabot para criar PRs de atualização:
version: 2
updates:
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
Para lidar com falsos positivos, use args para excluir arquivos ou padrões específicos. Em casos urgentes, o desenvolvedor pode pular hooks com git commit --no-verify, mas isso deve ser exceção, não regra.
8. Conclusão e próximos passos
Configurar pre-commit hooks é um investimento que paga dividendos rapidamente. Os benefícios incluem:
- Consistência: todo commit segue o mesmo padrão de formatação e estilo.
- Redução de retrabalho: bugs e más práticas são capturados antes do review.
- Onboarding mais rápido: novos desenvolvedores aprendem as convenções automaticamente.
Checklist final para adoção no time:
1. Documente os hooks escolhidos e como executá-los localmente.
2. Faça um teste piloto com um pequeno grupo antes de aplicar a todos.
3. Revise periodicamente os hooks — remova os que geram muitos falsos positivos.
Lembre-se: hooks são ferramentas, não prisões. Ajuste as configurações conforme o amadurecimento do projeto. O objetivo é tornar a qualidade um subproduto natural do fluxo de trabalho, não um obstáculo.
Referências
- Documentação oficial do pre-commit — Guia completo de instalação, configuração e todos os hooks disponíveis.
- pre-commit-hooks — repositório oficial — Coleção de hooks mantida pela equipe do pre-commit, com exemplos de uso.
- Black — The Uncompromising Code Formatter — Documentação do formatador Python, incluindo integração com pre-commit.
- Ruff — An extremely fast Python linter — Configuração do Ruff como hook, com opções de correção automática.
- Bandit — Security linter for Python — Guia de uso do Bandit para detectar vulnerabilidades comuns em código Python.
- Husky — Git hooks for Node.js — Alternativa ao pre-commit framework para projetos JavaScript/TypeScript.
- Conventional Commits — Especificação de mensagens de commit, útil para hooks de validação personalizados.