Artigo original: How to Add Commit Hooks to Git with Husky to Automate Code Tasks

Existem muitas ferramentas para automatizar nossos processos de codificação. Nós podemos procurar por erros de sintaxe com ESLint e formatar nosso código com Prettier.

Nem todo mundo na sua equipe se lembrará de rodar todos os comandos toda vez que for feito um commit. Como nós podemos usar o Husky para adicionar hooks do Git e fazer com que eles executem esses comandos para nós?

  • O que são os hooks do Git?
  • O que é Husky?
  • O que nós vamos construir?
  • Passo 0: como configurar um novo projeto
  • Passo 1: como instalar o Husky em um projeto
  • Passo 2: como configurar o Husky para rodar os hooks do Git
  • Passo 3: como usar o Husky para formar o código com Prettier

O que são os hooks do Git?

Os Git hooks (ou hooks do Git) são scripts que você pode configurar para rodar em certos eventos no ciclo de vida do Git. Esses eventos incluem diferentes estágios do commit, como antes de um commit (pré-commit) e depois de um commit (pós-commit).

Eles são muito úteis, pois permitem que desenvolvedores rodem processos de código automatizados ou até forcem padrões ao automatizar outros scripts para rodar esses processos.

O que é o Husky?

O Husky é uma ferramenta que nos permite facilmente configurar hooks do Git e executar scripts que queremos em certos estágios.

Ele inclui um objeto dentro do nosso arquivo package.json  que rodará o Husky nos scripts que nós especificarmos. Depois disso, o Husky gerencia os pontos do ciclo de vida do Git em que nossos scripts rodarão.

O que nós vamos construir?

Vamos configurar um projeto simples onde poderemos testar os hooks do Git.

Você pode acompanhar o artigo com qualquer projeto em que você estiver trabalhando. Usarei o Next.js como ponto inicial, simplesmente pelo fato de que precisamos rodar apenas um comando para ter o projeto iniciado.

Uma consideração é que, ao acompanhar com esse projeto, saiba que estaremos usando o Prettier como um exemplo do que você pode fazer com hooks do Git.

O Prettier é uma ferramenta que automaticamente formatará nosso código sem que tenhamos que fazê-lo, o que, se você não estiver se preparando para fazer, pode causar muita tensão. Acompanhar comigo usando o projeto com Next.js vai permitir que você faça testes sem mudanças não intencionais.

Quanto ao teste dos hooks do Git, começaremos adicionando uma instrução simples na linha de comando para ver o Husky funcionando e nós também testaremos a adição do Prettier, que formatará o nosso código automaticamente.

Por fim, enquanto escrevo esse artigo, soube que o Husky lançou uma versão Alpha v7. Considerando que é apenas uma versão Alpha, seguiremos com a v4, que nos permite instalar facilmente o Husky com o npm.

Passo 0: como configurar um novo projeto

Como eu mencionei, você pode seguir os mesmos passos com qualquer projeto gerenciado com um arquivo package.json.

O Next.js é absolutamente algo além do necessário para um passo a passo básico. Porém, o objetivo é minimizar os passos de configuração para focar no trabalho com o Husky.

Para começar com o Next.js, vá até o diretório em que você deseja começar seu projeto e rode o seguinte comando:

yarn create-next-app meu-projeto-com-husky
# ou
npx create-next-app meu-projeto-com-husky

Nota: sinta-se livre para substituir meu-projeto-com-husky pelo nome que você quiser que o seu diretório tenha.

Isso criará uma nova pasta, um novo projeto Next.js, e instalará todas as dependências.

create-next-app

Ao terminar, navegue até aquela nova pasta e podemos seguir em frente!

Acompanhe aqui com o commit.

Passo1: como instalar o Husky em um projeto

Para instalar o Husky, podemos utilizar o yarn ou o npm.

yarn add husky
# ou
npm install husky

Nota: se, ao instalar o Husky nesse momento, a versão instalada for a v7, isso significa que a v7 foi oficialmente lançada. Confira a documentação atualizada do Husky (em inglês) ou instale a última versão da v4 especificando husky@4.3.0 (ou seja qual for a versão mais recente) ao instalar.

Quando o pacote terminar de instalar, podemos seguir com o Husky.

Acompanhe aqui com o commit.

Passo 2: como configurar o Husky para rodar os hooks do Git

Agora, vamos configurar o Husky para conseguir usar nossos hooks do Git.

Dentro do nosso arquivo package.json, crie uma nova propriedade chamada husky com um objeto vazio.

"husky": {},

Você pode, de fato, adicionar essa propriedade onde você quiser no arquivo package.json. Eu, no entanto, a adicionarei logo após a propriedade scripts  para conseguir gerenciá-las juntas mais facilmente.

Dentro da propriedade Husky, adicionaremos outra propriedade chamada hooks, que também contém um objeto vazio:

"husky": {
  "hooks": {}
},

Aqui é onde adicionaremos nossos hooks do Git. O Husky suporta basicamente todos os hooks do Git definidos pelo próprio Git. Então, podemos ser tão flexíveis quanto quisermos dentro do nosso fluxo de eventos do Git.

Para fins de teste, eu criei uma nova branch onde eu literalmente adicionei todos os hooks do Git daquela página, incluindo um script que simplesmente escreve no terminal [Husky] nome do evento.

Nota: você não precisa fazer isso a não ser que esteja curioso. O objetivo é ser capaz de demonstrar a você, com meu exemplo, como funciona.

“husky”: {
  “hooks”: {
    “applypatch-msg”: “echo \”[Husky] applypatch-msg\””,
    “pre-applypatch”: “echo \”[Husky] pre-applypatch\””,
    “post-applypatch”: “echo \”[Husky] post-applypatch\””,
    “pre-commit”: “echo \”[Husky] pre-commit\””,
	}
}

O que isso fará é dizer ao Husky que, em cada etapa na qual temos permissão para nos conectar ao Git, diremos a ele a ação desejada!

Quando eu fizer os commits dessas mudanças, nós poderemos imediatamente ver o Husky disparar alguns de nossos scripts.

husky-commit-hooks

Esses são todos eventos aos quais o Git permite que nos conectemos e que acontecem durante o processo de commit.

Do mesmo modo, se eu der um push nessas mudanças para o Github, poderei ver que o processo de push roda o hook pre-push!

husky-push-hooks

Pode ser que nunca usemos a maioria dos hooks que o Husky e o Git proporcionam (vimos apenas alguns deles).

Porém, é realmente incrível ver como isso pode ser poderoso, seja rodando códigos que formatarão o nosso código, evitando que se faça o commit de chaves de acesso secretas sejam commitadas, ou qualquer coisa que possa ajudar a automatizar tarefas importantes do nosso fluxo de trabalho.

Conseguimos ver, agora, que configuramos o Husky ao especificar a configuração e os hooks dentro do nosso package.json.

Acompanhe aqui com o commit.

Nota: se quiser conferir minha branch que inclui todos os testes com hooks do Git, você pode encontrá-la no Github.

Passo 3: como usar o Husky para formatar seu código com Prettier

Finalmente, em um caso mais próximo à realidade, testaremos o Prettier e formataremos nosso código automaticamente.

O Prettier é uma ferramenta de formatação de código que permite limpar o código facilmente, fazendo parecer como se uma única pessoa o tivesse escrito.

Por que ferramentas como o Prettier são importantes? Quando estamos programando, especialmente em equipe, é importante manter a consistência para que todo mundo saiba o que esperar. Isso evitará discussões sobre um ponto e vírgula em uma code review, mas também ajudará a identificar erros de sintaxe e a prevenir bugs.

Atenção: rodar o Prettier formatará automaticamente todo o código. Por enquanto, estamos testando isso antes de fazer o commit das mudanças. Assim que você aplicar isso a um Git Hook, ele automatizará esse processo.

Para começar, vamos instalar o Prettier com nosso gerenciador de pacotes:

yarn add prettier -D
# ou
npm install prettier --save-dev

Nota: estamos instalando o Prettier como uma devDependency, já que sua aplicação não precisa disso para rodar.

Depois, podemos adicionar um novo script ao nosso package.json que facilitará rodar o Prettier para fazer os testes.

Dentro da propriedade scripts, adicione:

"lint": "prettier --check ."

Para começar, rodaremos o primeiro teste na modalidade "check", o que nos permite ver quais arquivos seriam alterados.

Execute o seguinte comando:

yarn lint
# ou 
npm run lint

Assim que o fizermos, poderemos perceber que o Prettier nos dirá quais dos arquivos listados serão alterados.

prettier-check

Nesse ponto, nosso código se manterá inalterado. Se quisermos que mudanças reais sejam feitas, primeiros temos que incluir um script adicional:

"format": "prettier --write ."

Se rodarmos esse script, ele atualizará todos aqueles arquivos mencionados de acordo com a especificação do Prettier.

Atenção: executar o Prettier para realizar as mudanças fará alterações em seus arquivos. Todas essas alterações são apenas para fins de adequação de estilo e não deveriam impactar o modo como seu código é executado, apenas a aparência dele. Antes de formatar, é prudente salvar todas as suas mudanças através de um commit no Git, de maneira a poder reverter facilmente essas mudanças caso não esteja feliz com elas.

Você agora pode rodar o script com:

yarn format

E podemos ver que o Prettier atualizou seus arquivos!

prettier-write

Agora, a parte que é relevante nessa jornada: podemos adicionar isso como um hook do Git. Dessa maneira, quando alguém tentar realizar um commit em um código, o Prettier executará antes de o código ser salvo. Isso significa que sempre manteremos um código consistente, com o Prettier formatando o estilo.

Dentro de nossa configuração dos hooks no Husky, vamos adicionar:

"husky": {
  "hooks": {
    "pre-commit": "prettier --write . && git add -A ."
  }
},

Como você pode perceber, no nosso hook pré-commit, também estamos adicionando git add -A ..

Quando o Husky for executado, ele simplesmente executará o script fornecido. Ao rodar o comando do Prettier, estamos apenas formatando o código, mas ainda faltaria salvar essas mudanças como parte do processo. Por isso, usaremos git add para guardar todas essas mudanças e incluí-las no commit.

Para fins de teste, eu reverti as mudanças de todos os arquivos formatados antes. Se você estiver acompanhando com o mesmo projeto, você pode executar o seguinte comando:

git checkout pages

Essa ação apagará todas as mudanças em pages para o último commit.

Agora, vamos testar adicionar todos os nossos arquivos com Git e fazer o commit dessas mudanças.

git-commit-husky-precommit-prettier

Tendo executado o comando de commit, podemos ver que o hook pré-commit do Husky já fez seu trabalho e formatou nosso código.

Acompanhe aqui com o commit.

O que eu posso fazer depois?

Use lint-staged para formatar somente os arquivos modificados

Estamos usando o Prettier no nosso hook pré-commit e especificando ., o que significa rodar em todos os arquivos todas as vezes.

Podemos usar uma ferramenta chamada lint-staged, que ainda nos permite rodar nossos hooks do Git com o Husky, executando-o apenas naqueles arquivos que estão em stage no Git.

Por exemplo, se quiséssemos fazer isso com o Husky e o Prettier, nossa configuração se pareceria com algo do tipo:

"husky": {
  "hooks": {
    "pre-commit": "lint-staged"
  }
},
"lint-staged": {
  "*": "prettier --write"
},

Como parte de como lint-staged é executado, ele anexará as mudanças dos arquivos alterados ao final de nossa instrução para o Prettier automaticamente.

Você também perceberá que não incluímos git add. O lint-staged também adicionará quaisquer mudanças ao Git instantaneamente para nós.

Personalize a configuração do Prettier para formatar as regras

O Prettier é bem opinativo. Há algumas coisas das quais eu, pessoalmente, não gosto e talvez você concorde comigo.

Felizmente, o Prettier permite a você configurar um arquivo que pode sobrescrever algumas regras para fazer com que seu código seja do jeito que você e seu time queiram.

Diga ao Prettier para ignorar arquivos com .prettierignore

Você provavelmente não quer o Prettier rodando em "tudo" (bem, talvez sim).

O Prettier permite que você configure um arquivo .prettierignore  dentro da raiz do seu projeto, próximo ao package.json, de modo similar ao .gitignore, que possibilita a você dizer ao Prettier quais arquivos ele não deve monitorar.

Follow me for more Javascript, UX, and other interesting things!