Sincronizzazione e Deploy di un progetto web su un server proprio via ssh e git hooks

un momento di ispirazione ci viene in mente un progetto web rivoluzionario, nell'ambiente di sviluppo quasi tutto funziona, quindi vogliano iniziare a condividere su un server pubblico il suo output e il suo sviluppo con persone fidate, che fare?

myproject-local (_running-branch_) ⇆ ☁ myproject.git → myproject (master)

Copiare i file via ftp/ssh/sftp ? nah.. ogni volta vengono sovrascritti molti file uguali, si rischia il timeout e poi non è detto che le configurazioni usate in locale siano le stesse che si usano in remoto.
Rsync ? Già meglio, va bene per le sincronizzazioni o backup, ma non ha le versioni dei file e a volte è più il tempo che perde a generare l'indice dei file cambiati che non tanto il loro trasferimento.
Git ? Siiii! Con qualche accortezza lo si può usare per sincronizzare un progetto tra più persone e mantenere il versioning dei file, vediamo come.

Workflow git + ssh + git hooks

Git è un VCS decentralizzato, che permette di sviluppare e fare i commit senza la connessione ad internet. Il vantaggio principale è che si può avere una cartella sotto versioning senza disporre di un server remoto, ma se vogliamo condividere lo sviluppo del nostro lavoro con altri necessariamente ci vorrà un account github o un altro server con git e possibilmente ssh.

Locale: ambiente di sviluppo (development)

Partiamo con una cartella di esempio chiamata myproject-local, che è stata inizializzata per git e contiene diversi file.
Immaginate contenga il vostro progetto web che a cadenze più o meno regolari deve essere pubblicato sul web (deploy), in questo caso nella cartella remota myproject.

Remoto: Repository git e web server

Le la cartella del progetto di sviluppo non comunica direttamente con l'istanza della cartella pubblicata, ma in mezzo c'é un repository git che è contenuto nella cartella myproject.git
Quindi arriviamo ai comandi necessari per configurare l'ambiente

[remoto] git init --bare myproject.git

Crea il repository "ufficiale" (vuoto) che conterrà, tutti i branch e le modifiche pubblicate da uno o più sviluppatori.

[locale] git remote add _running-branch_ ssh://user@server.com/~/repo/myproject.git
[locale] git remote show _running-branch_

Nella cartella myproject-locale che è già stata inizializzata aggiungiamo il branch remoto "_running-branch_" che sarà sincronizzato con quello attivo pubblicato. Il secondo comando serve solo a controllare che il branch sia stato effettivamente aggiunto.

[locale] git push _running-branch_ master

Dopo qualche commit possiamo condividere le modifiche locali col server remoto, "master" è il nome del branch remoto che corrisponderà alla versione "online".

[remoto] git clone /path/locale/myproject.git
[remoto] git pull

Inizializziamo "myproject" che è la copia "online" del progetto oppure aggiorniamo il suo contenuto. Questa cartella è quella che sarà puntata dal web server.
Un nuovo sviluppatore darà gli stessi comandi per avere la propria copia, l'unica differenza è che il path non sarà locale ma del tipo ssh://

Normale workflow

Una volta fatto il setup, l'ideale è non effettuare il login con ssh, ma fare tutto da locale. L'utilizzo di una chiave ssh è vivamente consigliato.

[locale] git pull

Sincronizza la copia di sviluppo con eventuali modifiche fatte da altri sviluppatori remoti

<Modifiche e commit vari>

[locale] git push _running-branch_ master
[locale] ssh user@server.com 'cd repo/myproject && git pull'

Infine condividiamo le modifiche e facoltativamente le rendiamo attive nella copia online o di produzione eseguendo "git pull" in remoto.

Quest'ultimo passaggio può essere ulteriormente automatizzato utilizzando i git hooks, cioé degli script fatti da noi che verranno attivati all'occorrenza di certi eventi.
In questo caso vogliamo che ad ogni push sul branch remoto venga aggiornata la copia attiva online, cioé "myproject".
Nel mondo ruby progetti grossi usano un tool chiamato Capistrano (esempio) per fare il deploy, perché magari devono gestire diversi server ridondati e hanno necessità maggiori, ma non è questo il caso.

Nella cartella "myproject.git/hooks" potete automatizzare l'evento di quando il repository remoto riceve un push, cioé post-receive che corrisponde a un file eseguibile con all'interno i comandi per fare il deploy, quindi aggiornare la copia live con la versione successiva.

#!/bin/sh # via: stackoverflow
unset $(git rev-parse --local-env-vars)
cd ~/repo/myproject
git pull 

In questo esempio ci limitiamo ad aggiornare i dati contenuti nella cartella live, ma in realtà molte altre azioni potrebbero essere fatte:

  • riavviare un web server
  • aggiornare lo schema del db
  • scrivere un log di quando sono stati fatti i deploy
  • eseguire dei test automatici sul codice
  • inviare un'email agli interessati al progetto

Ma la cosa più importante è ridurre il "Time to Market" e per farlo ci vanno delle soluzioni che accorpino il più possibile i comandi ripetitivi o che potrebbero causare errori e rendano il deploy semplice e scalabile.