back to top

Introduzione ai Git hook

In quest’ultima lezione cerchiamo di introdurre i cosiddetti Git Hooks, ovvero degli script che vengono eseguiti ogni volta che si verifica un certo evento nel repository. Possono essere personalizzati e scritti in un qualsiasi linguaggio di scripting. La prima linea del file presenta solitamente la sequenza di caratteri ‘#!’ (shebang o hashbang) che indica al sistema operativo qual è l’interprete da usare per l’esecuzione dello script. I Git hook possono essere usati per vari scopi. Nell’esempio che vedremo in questa lezione, verranno eseguiti dei test d’unità ogni volta che viene eseguito un nuovo commit. A seconda dell’esito dei test, Git porta a termine il commit o meno. In ogni caso le possibilità offerte sono innumerevoli e l’impiego di script personalizzati è direttamente collegato alle esigenze proprie o del team di lavoro.

Tipologie di Git Hook

Esistono sostanzialmente due diverse tipologie di Git Hook:

  • script lato Client
  • script lato Server

La seconda tipologia viene eseguita in repository remoti al verificarsi di alcuni eventi come nel caso di un’operazione di push. Il loro output viene visualizzato nella shell della persona che ha richiesto l’esecuzione dell’operazione. Per esempio lo script pre-receive viene eseguito in repository remoti prima che i file ricevuti dal server vengano aggiornati e può essere usato, fra l’altro, per annullare un’operazione di push qualora non vengano rispettati determinati criteri. (Per esempio se il formato del messaggio del commit non rispetta degli standard prestabiliti) In questa lezione non parleremo di questa tipologia di script, ma è possibile consultare la documentazione per maggiori dettagli.

Git Hook a livello Client

Esistono degli script eseguiti lato Client al verificarsi di eventi prestabiliti. Si tratta di script presenti nel repository locale che non vengono distribuiti in un repository remoto. Ogni volta che si inizializza un nuovo repository, vengono inseriti degli script predefiniti all’interno della cartella .git/hooks. Presentano l’estensione .sample che ne impedisce l’esecuzione. Se si rimuove tale estensione, lo script verrà eseguito al verificarsi dell’omonimo evento. Quando creiamo uno script personalizzato, basterà salvarlo nella cartella .git/hooks con il nome dell’evento specifico per cui vogliamo che venga eseguito.

(master) $ ls -lh .git/hooks/
total 88
-rwxr-xr-x  1 claudio  staff   3.5K 28 Dec 19:42 update.sample
-rwxr-xr-x  1 claudio  staff   1.5K 28 Dec 19:42 prepare-commit-msg.sample
-rwxr-xr-x  1 claudio  staff   544B 28 Dec 19:42 pre-receive.sample
-rwxr-xr-x  1 claudio  staff   4.8K 28 Dec 19:42 pre-rebase.sample
-rwxr-xr-x  1 claudio  staff   1.3K 28 Dec 19:42 pre-push.sample
-rwxr-xr-x  1 claudio  staff   1.6K 28 Dec 19:42 pre-commit.sample
-rwxr-xr-x  1 claudio  staff   424B 28 Dec 19:42 pre-applypatch.sample
-rwxr-xr-x  1 claudio  staff   189B 28 Dec 19:42 post-update.sample
-rwxr-xr-x  1 claudio  staff   896B 28 Dec 19:42 commit-msg.sample
-rwxr-xr-x  1 claudio  staff   478B 28 Dec 19:42 applypatch-msg.sample

I file presenti nella cartella .git/hooks devono essere eseguibili. In sistemi operativi Unix-like, possiamo cambiare i permessi con il comando chmod. Per esempio, se volessimo cambiare i permessi del file pre-commit, dovremmo eseguire:

$ chmod +x pre-commit

Potete trovare maggiori dettagli sui diversi Git hooks, sugli eventi in seguito ai quali vengono eseguiti e sui parametri passati a ciascuno script nella documentazione ufficiale. In questa guida ci limitiamo a mostrare un semplice esempio di script. In particolare vedremo come usare l’hook pre-commit.

Un semplice esempio di Git Hook

All’interno del file .git/hooks/pre-commit possiamo inserire il codice dello script che sarà eseguito ogni volta che lanciamo il comando git commit prima che venga eventualmente mostrato l’editor predefinito per inserire un messaggio e venga creato il nuovo oggetto commit nel repository. Vediamo allora un banale esempio in Node.js in cui creiamo un file somma.js nel quale viene definita l’omonima funzione e un file somma.test.js che contiene il codice per testare la funzione somma(). Per effettuare i test, useremo Jest, che abbiamo precedentemente installato grazie a NPM.

All’interno della cartella base che abbiamo inizializzato con il comando git init, avremo i seguenti file:

(master) $ ls -1A
.git
.gitignore
node_modules/
package.json
somma.js
somma.test.js

Abbiamo aggiunto il file .gitignore per assicurarci di ignorare la cartella node_modules in cui NPM salva i diversi package installati.

$ cat .gitignore
node_modules

Nel file package.json, creato col comando npm init, (è necessario installare Node.js) abbiamo inserito Jest nell’elenco delle dipendenze.(npm install –save-dev jest) Inoltre abbiamo modificato il file in modo tale da eseguire i test con Jest nel momento in cui lanciamo il comando npm test. Jest individuerà da solo il file somma.test.js ed eseguirà i test in esso definiti.

(master) $ cat package.json
{
  "name": "hooks_test",
  "version": "1.0.0",
  "description": "",
  "main": "somma.js",
  "scripts": {
    "test": "jest"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "jest": "^22.1.4"
  }
}

Fra gli altri file abbiamo somma.js che inizialmente contiene una versione errata dell’omonima funzione. In questo modo i test eseguiti da Jest falliranno e non verrà eseguito il commit visto che lo script definito nel file .git/hooks/pre-commit terminerà con un codice d’uscita diverso da 0.

(master) $ cat somma.js
// versione iniziale errata della funzione somma
function somma(a, b) {
  // abbiamo commesso un errore per far fallire inizialmente il test
  return a - b;
}

module.exports = somma;

Il file somma.test.js contiene un solo test in cui verifichiamo che fornendo in ingresso due addendi alla funzione somma() otteniamo un risultato corretto. Nel caso specifico ci si aspetta che il valore restituito da somma(0, 1) sia pari a 1. In caso contrario il test fallisce.

(master) $ cat somma.test.js 
const somma = require('./somma');

test('0+1 è uguale a 1', () => {
  expect(somma(0,1)).toBe(1);
});

Passiamo ora alla cartella .git/hooks dalla quale rimuoviamo tutti i file e aggiungiamo lo script pre-commit che verrà eseguito prima della creazione del nuovo commit. Nel caso specifico, sarà lanciato il comando npm test grazie al quale Jest esegue il test definito nel file somma.test.js.

(master) $ rm .git/hooks/*

Modifichiamo ora il file pre-commit con un editor di codice in modo che risulti uguale a quello riportato sotto.

(master)$ cat .git/hooks/pre-commit
#!/usr/bin/env bash

npm test

Assicuriamoci inoltre che il file sia eseguibile.

[.git/hooks] (master) $ chmod +x pre-commit

Se a questo punto aggiungiamo i file alla Staging Area ed eseguiamo il comando git commit, otteniamo un output nella shell come quello mostrato sotto.

(master) $ git add .
(master) $ git commit

> hooks_test@1.0.0 test /Users/claudiomarotta/Desktop/hooks_test
> jest

 FAIL  ./somma.test.js
  ✕ 0+1 è uguale a 1 (18ms)

  ● 0+1 è uguale a 1

    expect(received).toBe(expected)
    
    Expected value to be (using Object.is):
      1
    Received:
      -1

      2 | 
      3 | test('0+1 è uguale a 1', () => {
    > 4 |     expect(somma(0,1)).toBe(1);
      5 | });
      6 | 
      
      at Object.<anonymous>.test (somma.test.js:4:25)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 1 total
Snapshots:   0 total
Time:        0.833s

Come possiamo notare, è stato lanciato il comando npm test. Dal momento che il test definito in somma.test.js fallisce a causa del bug presente nel file somma.js, non viene eseguito nessun commit e le modifiche presenti nella Staging Area restano intatte.

Correggiamo allora l’errore inserito volutamente nel file somma.js.

(master) $ cat somma.js

function somma(a, b) {
  return a + b;
}

module.exports = somma;

Aggiungiamo il file alla staging area e proviamo a eseguire nuovamente il commit.

(master) $ git add .
(master) $ git commit -m 'corregge il bug presente sulla riga 2 nel file somma.js'

> hooks_test@1.0.0 test /Users/claudiomarotta/Desktop/hooks_test
> jest

 PASS  ./somma.test.js
  ✓ 0+1 è uguale a 1 (6ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.966s
Ran all test suites.
[master (root-commit) 7878fc6] corregge il bug presente sulla riga 2 nel file somma.js
 4 files changed, 27 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 package.json
 create mode 100644 somma.js
 create mode 100644 somma.test.js

$ git status
git status
On branch master
nothing to commit, working tree clean

In questo caso il commit è stato eseguito senza problemi dal momento che tutti i test sono stati completati con successo.

Condividere gli script con altri collaboratori

Gli script presenti nella cartella .git/hooks sono locali e non vengono distribuiti col repository. Nel caso si vogliano condividere degli script con altri collaboratori, è possibile inserirli in una cartella della directory base di un progetto e aggiungere tale cartella nel repository remoto. Ogni persona che ha accesso al repository potrà così usare a livello locale gli script personalizzati. Per far ciò, basterà creare un link simbolico allo script personalizzato.

Supponendo di avere quindi una cartella hooks_personalizzati nella cartella base del nostro progetto contenente degli script personalizzati, basterà rimuovere gli script non necessari dalla cartella .git/hooks e creare un link per ogni script presente nella cartella hooks_personalizzati.

# rimuove tutti i file presenti nella cartella .git/hooks
# utile se i file presenti non sono necessari
$ rm .git/hooks/*

$ cd .git/hooks

# da eseguire per ogni script personalizzato che si vuole usare
$ ln -s ../../hooks_personalizzati/<nome-script> .git/hooks/<nome-script>

# oppure più semplicemente
$ ln -s ../../hooks_personalizzati/* .

Conclusioni

Per questa guida introduttiva a Git è tutto. Spero sia stata utile a coloro i quali usano Git per la prima volta e abbia fornito abbastanza informazioni per capire come funziona Git internamente, permettendo così di usare i diversi strumenti messi a disposizione con consapevolezza.

Pubblicità
Articolo precedente