DEV Community

Cover image for Configurando Git Hooks com Husky
Helen Dias
Helen Dias

Posted on • Updated on

Configurando Git Hooks com Husky

Sumário

  1. Introdução
  2. O que são Git Hooks?
  3. Quando esses Hooks são normalmente disparados?
  4. O que é Husky?
  5. Instalando o Husky
  6. Criando o primeiro Hook com pre-commit
  7. Hooks com pre-push
  8. Criando o segundo Hook com pre-push
  9. Conclusão

Olá, pessoas!

Gostaria de compartilhar um pouco do que andei estudando sobre Git Hooks com Husky na minha primeira publicação aqui.

Antes de tudo, vamos contextualizar um pouco.

O que são Git Hooks?

Segundo a documentação oficial do Git, Git Hooks são:

Scripts personalizados que podem ser disparados quando alguma ação importante acontece.

Esses Hooks/Scripts podem ser disparados tanto no lado do servidor quanto no lado do cliente.

Quando esses Hooks são normalmente disparados?

No lado do cliente: quando ocorre alguma operação de commit ou merge.
No lado do servidor: quando ocorre alguma operação de rede (como push, por exemplo).

Mas isso não é regra!

Você pode utilizar esses Hooks para qualquer ação que julgar importante e/ou necessária :)

A própria API do Git, por si só, permite a utilização desses Hooks independentemente de quais tecnologias estão sendo utilizadas no projeto.

Para projetos que utilizam o ecossistema Node/Npm (seja cliente ou servidor), pode-se utilizar o Husky para ajudar na configuração desses Hooks.

Se quiser saber um pouco mais sobre o funcionamento desses Hooks por baixo dos panos, recomendo este artigo maravilhoso do Willian Justen 😉

Ta, mas o que é Husky?

É uma raça de cachorros peludos e fofos 🐶

De acordo com o repositório oficial, Husky é uma biblioteca JavaScript que serve para previnir péssimos git commit, git push e mais!

Através do Husky, é possível configurar os Hooks no package.json do seu projeto e compartilhá-los com seu time.

Fazendo da forma padrão do Git, as configurações dos Hooks ficam dentro do diretório .git do projeto (que não é versionado), portanto só vai funcionar na sua máquina.

Aaaaah, então é por isso que utilizamos o Husky! Eu não sabia disso 😱 Descobri enquanto lia este artigo do Cubos.io.

A seguir, vamos ver como adicionar Hooks de pre-commit e pre-push com o Husky ⚡⚡

Obs: para adicionar o Husky e criar os Hooks, é importante que você possua um projeto Node/Npm com um arquivo package.json e o Git já inicializado :)


Então, bora fazer!

A primeira a coisa a ser feita é instalar o Husky, que deve ficar nas dependências de desenvolvimento.
Para isso, execute o seguinte comando no diretório do seu projeto:

npm install husky --save-dev
Enter fullscreen mode Exit fullscreen mode

ou

yarn add -D husky
Enter fullscreen mode Exit fullscreen mode

Feito isso, você vai perceber que uma linha foi adicionada nas devDependencies do seu package.json, conforme exibido abaixo:

  "devDependencies": {
    ...
    "husky": "^4.2.5"
  }
Enter fullscreen mode Exit fullscreen mode

Você pode ver a lista e descrição de todos os Hooks disponíveis na documentação oficial (em inglês) ou neste artigo do Hostinger (em português) 😗✌️

Rapidamente falando, os Hooks que usaremos são:
pre-commit => é invocado quando um git commit é executado, antes da escrita do commit.
pre-push => é invocado quando um git push é executado, antes de enviar os commits.

Normalmente, esses Hooks são utilizados para rodar os testes e lint do projeto, então vamos pegar esses exemplos.


Criando o primeiro Hook com pre-commit!

Obs: As configurações dos Hooks do Husky ficam em husky.hooks no package.json.

Vamos supor que você já tenha um script de lint que seja executado com npm run lint (ou yarn lint) e deseja rodá-lo sempre que o usuário faça uma ação de commit, permitindo ou bloqueando a escrita deste commit.

Neste caso, basta chamar o npm run lint (ou yarn lint) no Hook de pre-commit do Husky, conforme exemplificado abaixo:

{
  "name": "my-project",
  "scripts": {
    ...
    "lint": "eslint . --ext .js,.jsx"
  },
  "devDependencies": {
    ...
    "husky": "^4.2.5"
  }
  "husky": {
    "hooks": {
      "pre-commit": "npm run lint"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Caso queira adicionar outro script para ser executado no pre-commit, basta adicioná-lo na frente do npm run lint com um && entre eles.
Exemplo:

  "husky": {
    "hooks": {
      "pre-commit": "npm run lint && npm run test"
    }
  }
Enter fullscreen mode Exit fullscreen mode

Prontinho! Com isso, os dois scripts serão executados quando alguém tentar fazer um commit no projeto 😊

O problema do pre-push

Como comentado anteriormente, o pre-push serve para executar um script antes de alguém fazer um push no projeto.

Mas, pesquisando um pouco sobre o pre-push, me deparei com este artigo do Kacper Wdowik.

Basicamente, ele diz que nem sempre o pre-push é uma boa ideia, exemplificando isso com a execução dos testes do projeto.

Acontece que, normalmente, ao rodar npm run test é levado em consideração todos os arquivos modificados localmente no projeto; ou seja, considera arquivos/linhas que ainda não foram "commitados".

A dor que ele sentiu foi em relação à coerência e concordância dos testes com o que está, de fato, subindo. Um caso não tão incomum seria os testes passarem porque um arquivo foi corrigido mas não está nos arquivos para subir com o push, o que poderia resultar em um erro em produção numa sexta-feira à noite, por exemplo 👀

Para resolver isso, ele fez uso da seguinte abordagem: comparar o que está no HEAD do projeto com o que está em local, permitindo que a ação de push seja realizada apenas quando a pessoa realizou commits de todos os arquivos modificados, garantindo que o Hook rode com os arquivos que vão, de fato, subir.

Então você tem duas opções:

  1. Permitir que as pessoas desenvolvedoras façam push quando alguma linha ou arquivo não esteja "commitado", sabendo que o script executado no pre-push pode não estar em concordância com o que está, de fato, subido;
  2. Impedir que as pessoas desenvolvedoras façam push quando alguma linha ou arquivo não esteja "commitado" e ter a confiança de que o script executado no pre-push esteja em concordância com o que está, de fato, subindo.

Eu, pessoalmente, prefiro a segunda opção, mas isso vai de cada projeto 😊

Criando o segundo Hook com pre-push

Como comentado acima, vou seguir com a segunda abordagem, indo de acordo com o estudo do Kacper Wdowik no artigo Why using pre-push Git Hooks with Husky is not always a good idea.

Ficaria algo assim:

{
  "name": "my-project",
  "scripts": {
    ...
    "lint": "eslint . --ext .js,.jsx",
    "test": "npm run jest"
  },
  "devDependencies": {
    ...
    "husky": "^4.2.5"
  }
  "husky": {
    "hooks": {
      "pre-commit": "npm run lint",
      "pre-push": "git diff HEAD --quiet && npm run test && npm run lint"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Note que, no exemplo acima, o HEAD está sendo comparado com a sua árvore de trabalho atual. Caso sejam iguais, continua a execução. Caso contrário, termina a execução retornando um erro.

Utilizando esta abordagem, caso tente rodar um git push com arquivos modificados apenas localmente (sem "commitar"), você pode se deparar com uma mensagem de erro semelhante a esta:

Alt Text

Caso isso aconteça, não siga a orientação dele de adicionar um --no-verify ao executar o git push, pois assim o Hook vai ser ignorado e seu esforço será em vão 😅
Opte por "commitar" as modificações e refazer a operação de push ou seguir pela primeira abordagem comentada lá em cima (removendo o git diff HEAD --quiet &&).

Feito isso, seus Hooks de commit e push já vão estar funcionando para todo mundo que mexer no projeto :D

Concluindo

No geral, os Git Hooks com Husky tendem a ficar mais simples e gerenciáveis, permitindo que mais de uma pessoa tenha acesso às mesmas regras.

Dei exemplos simples de implementação, chamando scripts já existentes no seu projeto. Mas você pode criar seus próprios scripts manualmente e chamá-los nos Hooks.

Pretendo falar um pouco sobre a criação de scripts manualmente em um artigo futuro, focado unicamente neste ponto, que tende a ser um pouco mais complexo e deixaria este artigo muito longo xD

Além do que foi falado aqui, recomendo o uso do lint-staged para facilitar a organização e visualização dos scripts de lint executados nos Hooks do Husky.

Se quiser saber um pouco mais sobre as vantagens de utilização do lint-staged, recomendo este artigo incrível da Evellyn Lima, onde ela fala sobre otimização do pre-commit e mostra exemplos práticos com lint-staged.

Ah, lembre-se que o Git possui vários Hooks que podem ser utilizados e cabe a você decidir quais Hooks fazem sentido (ou não) para aplicar no Workflow do seu projeto 😉

Qualquer dúvida, sugestão, correção, comentário etc, me procure no Twitter ou no Linkedin, que estou sempre disponível ✌️

Top comments (7)

Collapse
 
felipefialho profile image
Felipe Fialho

Foi um dos melhores e mais completos artigos sobre configuração de Git hooks, Husky e etc, que já li. Parabéns demais Helen!

Já estou ansioso por novos artigos dessa qualidade, isso faz grande diferença pra aumentar ainda mais o nível de conteúdo que produzimos no Brasil 🇧🇷

Collapse
 
helendias profile image
Helen Dias

Muito obrigada, Fe!
Você é foda!
Sempre li suas publicações (que me ajudaram/ajudam muito) e pensava em um dia eu mesma passar a escrever sobre coisas que ando estudando.
Finalmente criei coragem hahaahha
Obrigada pelo apoio, foi essencial 🚀❤️

Collapse
 
vmnog profile image
Victor M. Nogueira

Neste caso o que acontece quando alteramos arquivos que não necessariamente impactam nos testes? Eu teria que rodar os testes toda vez que fosse fazer um push mesmo não alterando nada relacionado aos testes?

Collapse
 
brunoromeiro profile image
Bruno Romeiro

Que artigo completo! Parabéns!
Os Git Hooks são muito úteis no dia a dia!

Collapse
 
helendias profile image
Helen Dias

Muito obrigada 😊
Ajudam muito, né?

Collapse
 
houstondapaz profile image
Houston da Paz

Muito boa a técnica de verificar o HEAD antes do push, já sofri algumas vezes com teste passando na máquina e quebrando em prd

Collapse
 
helendias profile image
Helen Dias

Sim, esse é um grande problema hahaha
Já aconteceu comigo também! O artigo do Kacper Wdowik me abriu a mente hahahaha