Quando Subversion incontra lo sviluppo web

Dopo un lungo periodo di gestazione, finalmente e’ venuto alla luce questo post, che personalmente reputo molto interessante (ma va??).
Come introdotto dal titolo, espongo uno scenario di uso che mostra come poter integrare Subversion con lo sviluppo e il mantenimento della nostra web application. Nel dettaglio, con web application intendo un applicativo PHP installato in diversi server di produzione: quindi, un punto di sviluppo, tanti punti di utilizzo.
Bene, ipotizziamo ora il workflow per l’installazione per un nuovo cliente: connessione remota verso il server, quindi decompressione (se abbiamo una tarball o un archivio compresso) oppure copia (nel caso di singoli files), niente di trascendentale, quindi; il discorso si complica nel caso in cui ci accorgiamo che il nostro prodotto contiene un bug, oppure se abbiamo apportato modifiche interessanti allo stesso, e in questo caso dobbiamo operare a mano nelle n installazioni, con il rischio di sbagliare/dimenticarsi qualcosa.
E’ qui che Subversion viene in nostro aiuto per realizzare una specie di sistema di installazione/aggiornamento automatizzata di tipo “push” (spero che il termine sia giusto).

Prerequisiti:
0) un server con il client Subversion installato ed utilizzabile dall’utente di Apache, nel mio caso www-data;
1) un repository Subversion raggiungibile dal server;
2) un linguaggio di scripting presente nel repository, utilizzabile per scrivere il post-commit hook, nel mio esempio Python;

Semplificazioni:
0) il repository risiede nel mio PC, e ci accedo tramite il protocollo file://;
1) i server remoti, nel dettaglio tre, sono rappresentati da tre diverse directory nella DocumentRoot di Apache;

Ecco una piccola descrizione di cio’ che ho realizzato e di quello che vedrete negli snippets successivi.
Io ho sviluppato una web application general purpose, molto flessibile e adattabile a molti clienti, ed essendo un bravo programmatore utilizzo gia’ subversion per mantenere i miei sorgenti. Aggiungo quindi una specie di installer per l’applicazione, in pratica una pagina PHP che invoca il checkout del progetto dal repository principale.

Ecco il codice dell’installer.

[The requested file http://mcalamelli.netsons.org/files/svnwebapp/installer.txt could not be found]

Che cosa succede quando invochiamo l’esecuzione dell’installer (preventivamente copiato nel server)?
Il client svn sl server preleva il progetto dal repository e crea, con i giusti permessi (quelli del webserver), la copia locale dei files.

Cominciamo con gli snippets!

Ecco il contenuto della directory che ospita il progetto iniziale della web application

massi@aspire:~$ cd Projects/svnwebapp/
massi@aspire:~/Projects/svnwebapp$ ls -l
totale 8
-rw-r--r-- 1 massi massi 104 2007-11-11 22:35 index.php
-rw-r--r-- 1 massi massi  30 2007-11-02 06:48 updater.php
massi@aspire:~/Projects/svnwebapp$

Creiamo ora il repository svn

massi@aspire:~$ svnadmin create /home/massi/svn/svnwebapp
massi@aspire:~$

Effettuiamo ora l’import della web application nel repository appena creato

massi@aspire:~$ cd Projects/
massi@aspire:~/Projects$ svn import -m "Import iniziale" svnwebapp file:///home/massi/svn/svnwebapp
Aggiungo       svnwebapp/updater.php
Aggiungo       svnwebapp/index.php

Commit della Revisione 1 eseguito.
massi@aspire:~/Projects$

Ora ci spostiamo nella root del nostro webserver locale, ed eseguiamo il checkout del progetto per continuare a svilupparlo, sotto svn.

massi@aspire:~$ cd /var/www/
massi@aspire:/var/www$ sudo svn co file:///home/massi/svn/svnwebapp
A    svnwebapp/updater.php
A    svnwebapp/index.php
Estratta revisione 1.
massi@aspire:/var/www$

Guardiamo cosa e’ successo in /var/www

massi@aspire:~$ ls /var/www/svnwebapp/
index.php  updater.php
massi@aspire:~$

Bene, la directory e’ stata creata, cosi’ come i files della web application.
Ora eseguiamola!

massi@aspire:~$ lynx --source http://localhost/svnwebapp
<html>
<head>
</head>
<body>
Versione PHP installata: 5.2.3-1ubuntu6
</body>
</html>
massi@aspire:~$

Per curiosita’ diamo una occhiata a questo complicatissimo script (index.php).

[The requested file http://mcalamelli.netsons.org/files/svnwebapp/index.txt could not be found]

Ora che abbiamo testato le funzionalita’ di svn per la gestione “locale” della web application, creiamo i nostri tre server simulati (con gli opportuni permessi), copiamo in ognuno di essi l’installer e lanciamo quest’ultimo per creare le applicazioni.

massi@aspire:~$ cd /var/www
massi@aspire:/var/www$ sudo mkdir fakesrv{1,2,3}
massi@aspire:/var/www$ sudo chown www-data:www-data fakesrv{1,2,3}
massi@aspire:/var/www$ ls
fakesrv1  fakesrv2  fakesrv3  index.html  svnwebapp
massi@aspire:/var/www$ cd
massi@aspire:~$ sudo cp svnwebapp_installer.php /var/www/fakesrv1/
massi@aspire:~$ sudo cp svnwebapp_installer.php /var/www/fakesrv2/
massi@aspire:~$ sudo cp svnwebapp_installer.php /var/www/fakesrv3/
massi@aspire:~$ lynx --source http://localhost/fakesrv1/svnwebapp_installer.php
massi@aspire:~$ lynx --source http://localhost/fakesrv2/svnwebapp_installer.php
massi@aspire:~$ lynx --source http://localhost/fakesrv3/svnwebapp_installer.php

Giusto per scrupolo vediamo cosa e’ successo dentro a fakesrv1.

massi@aspire:/var/www$ ls -lR fakesrv1
fakesrv1:
totale 4
drwxr-xr-x 3 www-data www-data 136 2007-11-26 13:34 svnwebapp
-rw-r--r-- 1 root     root      59 2007-11-26 13:34 svnwebapp_installer.php

fakesrv1/svnwebapp:
totale 8
-rw-r--r-- 1 www-data www-data 169 2007-11-26 13:34 index.php
-rw-r--r-- 1 www-data www-data  30 2007-11-26 13:34 updater.php
massi@aspire:/var/www$

Tramite lynx verifichiamo che tutto funzioni.

massi@aspire:~$ lynx --source http://localhost/fakesrv1/svnwebapp
<html>
<head>
</head>
<body>
Versione PHP installata: 5.2.3-1ubuntu6
</body>
</html>
massi@aspire:~$

Tralascio gli snipper delle verifiche per gli altri due “server”, tutto funziona ovviamente allo stesso modo.
Riassumiamo quello che e’ successo finora: tramite l’installer ho potuto replicare la mia web application nei server dei miei ipotetici tre clienti, attingendo il codice direttamente dal repository SVN, che contiene l’ultima release del progetto.
Ora pero’ mi sono accorto che la mia web application manca di un requisito fondamentale, e cioe’ una stringa di testo con il mio indirizzo email, percio’ sono costretto ad aggiornare le varie installazioni!
Vediamo quindi in che modo il mio sistema di installer diventa comodissimo in caso di aggiornamenti: aggiungiamo un post-commit hook al repository.
Ecco il contenuto della directory del repository.

massi@aspire:~$ ls -l svn/svnwebapp
totale 8
drwxr-xr-x 2 massi massi 128 2007-11-11 23:20 conf
drwxr-xr-x 2 massi massi  48 2007-11-11 23:20 dav
drwxr-sr-x 5 massi massi 256 2007-11-12 00:12 db
-r--r--r-- 1 massi massi   2 2007-11-11 23:20 format
drwxr-xr-x 2 massi massi 392 2007-11-11 23:58 hooks
drwxr-xr-x 2 massi massi 104 2007-11-11 23:20 locks
-rw-r--r-- 1 massi massi 229 2007-11-11 23:20 README.txt

La directory che ci interessa e’, ovviamente, hooks.

massi@aspire:~$ ls -l svn/svnwebapp/hooks/
totale 40
-rwxr-xr-x 1 massi massi  290 2007-11-11 23:58 post-commit
-rw-r--r-- 1 massi massi 1996 2007-11-11 23:20 post-commit.tmpl
-rw-r--r-- 1 massi massi 1673 2007-11-11 23:20 post-lock.tmpl
-rw-r--r-- 1 massi massi 2290 2007-11-11 23:20 post-revprop-change.tmpl
-rw-r--r-- 1 massi massi 1602 2007-11-11 23:20 post-unlock.tmpl
-rw-r--r-- 1 massi massi 2969 2007-11-11 23:20 pre-commit.tmpl
-rw-r--r-- 1 massi massi 2038 2007-11-11 23:20 pre-lock.tmpl
-rw-r--r-- 1 massi massi 2764 2007-11-11 23:20 pre-revprop-change.tmpl
-rw-r--r-- 1 massi massi 1979 2007-11-11 23:20 pre-unlock.tmpl
-rw-r--r-- 1 massi massi 2137 2007-11-11 23:20 start-commit.tmpl
massi@aspire:~$

Come si vede, e’ presente il mio file post-commit , al quale ho dato di permessi di esecuzione.
Vediamone il contenuto.

[The requested file http://mcalamelli.netsons.org/files/svnwebapp/post-commit.txt could not be found]

Come si puo’ vedere, e’ un semplicissimo script Python che viene eseguito ad ogni commit (da qui’ il nome post-commit): il codice mi sembra piuttosto chiaro, data una lista di URL, quelli relativi alle varie installazioni della web application, un ciclo sulla lista effettua il collegamento alla pagina updater.php di ogni installazione.
Il codice dell’updater e’ molto simile a quello dell’installer, addirittura piu’ semplice.

[The requested file http://mcalamelli.netsons.org/files/svnwebapp/updater.txt could not be found]

Ok, direi che siamo pronti a testare il tutto. Apriamo il file index.php con il nostro editor preferito, apportiamo la modifica di cui sopra e effettuiamo il commit.

massi@aspire:/var/www/svnwebapp$ sudo vi index.php
massi@aspire:/var/www/svnwebapp$ sudo svn commit -m 'Aggiunto autore'
Trasmetto      index.php
Trasmissione dati .
Commit della Revisione 2 eseguito.
massi@aspire:/var/www/svnwebapp$

Ora la prova del 9, verifichiamo i tre server.

massi@aspire:~$ lynx --source http://localhost/fakesrv1/svnwebapp
<html>
<head>
</head>
<body>
Versione PHP installata: 5.2.3-1ubuntu6<br>
---<br>
Autore: Massimiliano Calamelli<br>
Email: mcalamelli@gmail.com</body>
</html>
massi@aspire:~$ lynx --source http://localhost/fakesrv2/svnwebapp
<html>
<head>
</head>
<body>
Versione PHP installata: 5.2.3-1ubuntu6<br>
---<br>
Autore: Massimiliano Calamelli<br>
Email: mcalamelli@gmail.com</body>
</html>
massi@aspire:~$ lynx --source http://localhost/fakesrv3/svnwebapp
<html>
<head>
</head>
<body>
Versione PHP installata: 5.2.3-1ubuntu6<br>
---<br>
Autore: Massimiliano Calamelli<br>
Email: mcalamelli@gmail.com</body>
</html>

Perfetto, tutto mi sembra ok, direi che il sistema ha funzionato!
Ovviamente sono conscio del fatto che il tutto e’ realizzato in un ambiente molto differente dalla realta’, ma mi sento di poter affermare che con poche modifiche il tutto possa funzionare anche nel mondo reale; alcuni perfezionamenti che mi vengono in mente sono questi:

post-commit hook : l’elenco degli host da aggiornare potrebbe essere letto da un file, e non hard-coded all’interno dello script, in modo da poter escludere piu’ facilmente host che per svariati motivi non devono essere aggiornati. Inoltre un controllo sul risultato della connessione tramite urllib2 sarebbe auspicabile, magari con una notifica via mail dell’insuccesso dell’operazione per il/i determinati hosts;

updater.php : la pagina in questione andrebbe in qualche modo protetta, cosi’ da impedire eventuali chiamate non autorizzate.

Che dire, e’ stato veramente interessante realizzare un sistema del genere, anche se molto semplificato rispetto ad un uso reale, un motivo in piu’ per studiarsi i sistemi di controllo versione: io ho usato SVN come RCS e Python per il post-commit hook, ma nulla vieta di realizzare un sistema simile con CVS e un qualsiasi altro file eseguiibile che si chiami post-commit.
Ogni bravo programmatore dovrebbe SEMPRE usare un sistema di controllo versione.

Grazie a Fullo e Jaures per le preziose verifiche.

Alla prox

[tags]subversion, php, python, post-commit hooks, web development[/tags]

Categorie

Archivi

2 Comments

  1. 30 novembre 2007

    Ciao,
    complimenti, ottimo trick.

    Forse ti converrebbe però mettere nel file di update un svn export in modo da non importare nell’ambiente di produzione i file .svn che Subversion crea..
    o ancora meglio un file di installazione che crea la working copy( con svn co come hai descritto) e un file di “upgrade” che fa un svn update, no?

    Ciao!

  2. 30 novembre 2007

    Davvero un’ottima idea.

    Oltretutto il concetto è utilizzabile anche in ambiente windows :D

Comments are closed, but trackbacks and pingbacks are open.