Full Stack

Creare un hook personalizzato in Git

Autore

Manuel Ricci

Dopo averli introdotti durante l’esplorazione della git directory è giunto il momento di spendere qualche parola in più sugli hook in Git.

Gli hooks sono una funzionalità di Git che permette di automatizzare delle attività e rafforzare l’utilizzo delle linee guida durante l’esecuzione dei vari workflow.

Si tratta di script che Git esegue automaticamente al verificarsi di certi eventi (es. commit, push, merge).

Gli hooks si dividono in due categorie client side e server side. Quelli client side sono a loro volta suddivisi in un paio di sotto categorie:

  • Committing-Workflow
  • Email Workflow

In realtà non tutti gli hook client-side ricadono in queste due sub-categorie, molti non ne hanno una, ma rimangono comunque agganci estremamente utili.

Giusto per dare un’idea di cosa possiamo implementare con gli hook in Git, ecco qualche esempio:

  • controllo del messaggio di commit
  • applicazione degli standard prima di un push
  • mandare una mail o un SMS dopo una commit
  • formattazione, esecuzione dei test prima di una commit
  • generazione di documentazione, aggiornamento delle dipendenze dopo un merge
  • inviare il codice in produzione dopo un push

Nomenclatura degli hook

Tra poco vedremo una lunghissima lista di hook, anche se poi ci focalizzeremo solo su alcuni, quelli più comuni, ma sappiate che sulle nomenclature ci sono delle cose da tenere a mente:

  • Gli eventi con radice pre- vengono eseguiti prima dell’evento
  • Gli eventi con radice post- vengono eseguito dopo l’evento
  • Gli eventi senza nessuna radice specifica vengono eseguiti durante l’evento

Il momento specifico di esecuzione varia da evento ad evento ed è consigliabile controllare sempre la documentazione.

Gli hook a disposizione

Gli hook a disposizione sono numerosissimi, 28 per la precisione:

  • applypatch-msg
  • pre-applypatch
  • post-applypatch
  • pre-commit
  • pre-merge-commit
  • prepare-commit-msg
  • commit-msg
  • post-commit
  • pre-rebase
  • post-checkout
  • post-merge
  • pre-push
  • pre-receive
  • update
  • proc-receive
  • post-receive
  • post-update
  • reference-transaction
  • push-to-checkout
  • pre-auto-gc
  • post-rewrite
  • sendemail-validate
  • fsmonitor-watchman
  • p4-changelist
  • p4-prepare-changelist
  • p4-post-changelist
  • p4-pre-submit
  • post-index-change

Nella documentazione di Git trovate una spiegazione per ognuno, qui di seguito darò una spiegazione dei più utilizzati:

pre-commit (Committing-Workflow)

Viene eseguito prima di una commit, prima ancora che venga scritto un messaggio di commit. Il suo utilizzo è vario, ma di base viene impiegato per: verificare di non aver dimenticato niente e/o per eseguire i test

prepare-commit-msg (Committing-Workflow)

Viene eseguito prima dell’avvio dell’editor dei messaggi di commit, ma dopo la creazione del messaggio di default e consente di modificarlo prima che l’autore lo veda.

Di per sé non è utilissimo se prendiamo in considerazione lo normali commit, ma è piuttosto utile quando il messaggio predefinito viene generato in automatico come nel caso di merge, squash o modifiche al messaggio originale (--amend).

commit-msg (Committing-Workflow)

Questo hook è utile per fare delle verifiche di conformità sul come è stato scritto il messaggio di commit.

post-commit (Committing-Workflow)

Eseguito dopo tutti quelli precedenti è utile se si vogliono implementare sistemi di notifica o cose simili.

applypatch-msg, pre-applypatch, post-applypatch (Email Workflow)

Tutti e tre i comandi vengono eseguiti dal comando git am, un comando che non abbiamo mai visto durante le lezioni precedenti.

Il comando git am accetta una o più patch di posta elettronica e le incorpora come commit nella branch locale.

Mentre una patch di posta elettronica è una rappresentazione testuale delle modifiche in una commit, formattata in modo che Git possa ricostruire la commit e applicarlo su un ramo in un altro repository.

applypatch-msg è il primo hook ad essere eseguito ed esamina il messaggio di commit proposto. pre-applypatch è il successivo ed è eseguito dopo l’applicazione della patch, ma prima della commit.

Infine, post-applypatch viene eseguito dopo l’applicazione della commit.

Non vado troppo nel dettaglio perché non avendo mai affrontato il comando git am verrebbe fuori solo un gran pastrocchio.

pre-rebase

Viene eseguito prima di un rebase e può interrompere il processo. Questo hook è usato per non dare la possibilità di fare il rebasing di una branch o, se ricordate le best practice di git rebase, per evitare che venga fatto il rebase di una commit che è stata già sincronizzata con la repo remota.

post-merge

Viene eseguito subito dopo un merge concluso con successo. Questo tipo di hook è utile per ripristinare dei file nel working tree che Git non tiene d’occhio.

pre-push

Viene eseguito durante il git push dopo con le refs remote sono state aggiornate, ma prima di qualsiasi trasferimento. Si può utilizzare per validare delle ref in seguito all’aggiornamento.

pre-auto-gc

Viene eseguito prima dell’esecuzione del garbage collector e può essere usato per notificare l’avvio di questo processo o per fermarlo qualora non fosse un buon momento.

post-checkout

Viene eseguito subito dopo un git checkout e può essere usato per impostare la working directory con specifiche impostazioni, le quali potrebbero anche essere semplicemente lo spostamento di grossi file che non vuoi tenere sotto traccia o generare della documentazione.

pre-receive

Il primo script ad essere eseguito server side quando si riceve un push da un client.

update

Simile a pre-receive, ma viene eseguito una volta per ogni branch che il client sta cercando di aggiornare.

post-receive

Viene eseguito alla conclusione del processo di ricezione dei dati e può essere usato per notificare l’utente dell’avvenuto push. Le applicazioni in questo caso sono molteplici:

  • Inviare email
  • Avviare un processo di continuous integration
  • Parsificando il messaggio di commit si può anche aprire, chiudere o modificare un ticket

Quali linguaggi usare per scrivere gli hook in Git

Se ti stai chiedendo se puoi usare Python o Node, la risposta è sì. Gli script built-in sono in bash o PERL, ma in realtà è possibile usare un qualunque linguaggio di scripting, compresi Python e Node.

In ogni script basterà modificare la linea shebang #!/bin/sh nel linguaggio con la quale si vuole interpretare lo script:

  • #!/usr/bin/env python
  • #!/usr/bin/env node

Implementare un hook

Nella cartella .git/hook possiamo rimuovere l’estensione .sample a quello che vogliamo implementare. In caso di rinomina non bisognerà procedere con l’assegnazione dei privilegi di esecuzione, altrimenti nel caso creiate un nuovo file, perché volete mantenere il file sample dovrete, una volta creato fornirgli suddetti privilegi con il comando:

1$ chmod +x <path-hook>

Eseguendo il comando ls -l dovreste vedere nella colonna dei permessi una x alla fine -rwxr-xr-x. Se così fosse allora il comando è andato a buon fine.

Un primo hook di test con Node

Cominciamo con un primo semplicissimo hook scritto in Node.

  1. Rinominiamo pre-commit.sample in pre-commit e apriamolo in Visual Studio Code
  2. Eliminiamo il contenuto e scriviamo quanto segue
1#!/usr/bin/env node
2console.log(“Ciao dal pre-commit in Node”);
  1. Facciamo una modifica nella nostra directory e salviamola con git commit
  2. Subito prima del messaggio che solitamente visualizzavamo in seguito ad una commit vedremo il nostro messaggio
1Ciao dal pre-commit in Node
2[main 9a623cc] Aggiunto testo
3 1 file changed, 1 insertion(+)
  1. Complimenti hai creato il tuo primo hook in Node

Un secondo hook in Shell

Come prima lavoreremo nel pre-commit, se non hai fatto nessuna modifica dovrai seguire il punto 1 del precedente esperimento, altrimenti proseguire pure:

  1. Aprire pre-commit in VS Code
  2. Eliminare il contenuto (o commentatelo)
  3. Scrivere quanto segue:
1#!/bin/sh
2
3echo "Stai per fare la commit di " $(git diff --cached --name-only --diff-filter=ACM)
4echo "in" $(git branch --show-current)
5
6while : ; do
7    read -p "Sicuro di voler procedere? [s/n] " RESPONSE < /dev/tty
8    case "${RESPONSE}" in
9        [Ss]* ) exit 0; break;;
10        [Nn]* ) exit 1;;
11    esac
12done

Lo script originale l’ho trovato su redhat.com h/t a loro per averlo scritto. Io mi sono limitato a tradurlo.

  1. Facciamo una modifica nella nostra directory e salviamola con git commit
  2. Prima ancora di vedere l’esito della commit, vedremo un prompt che ci chiede se vogliamo proseguire, se accettiamo la commit prosegue e vedremo il solito output, altrimenti il processo verrà interrotto.
  3. Complimenti hai creato il tuo secondo hook.

Altri hook molto interessanti sono:

Conclusioni

Gli hook possono essere estremamente utili se usati correttamente. Passiamo quindi alla prossima e ultima lezione del corso di git per principianti.

Caricamento...

Diventiamo amici di penna? ✒️

Iscriviti alla newsletter per ricevere una mail ogni paio di settimane con le ultime novità, gli ultimi approfondimenti e gli ultimi corsi gratuiti puubblicati. Ogni tanto potrei scrivere qualcosa di commerciale, ma solo se mi autorizzi, altrimenti non ti disturberò oltre.

Se non ti piace la newsletter ti ricordo la community su Discord, dove puoi chiedere aiuto, fare domande e condividere le tue esperienze (ma soprattutto scambiare due meme con me). Ti aspetto!

Ho in previsione di mandarti una newsletter ogni due settimane e una commerciale quando capita.