
guide
Tutti i linguaggi per diventare uno sviluppatore di app per Android.
Continuous Integration: automatizziamo i client con Phing. Esempi pratici e codice da scaricare
Dopo aver introdotto a livello teorico la pratica della Continuous Integration, è giunto il momento di mettere in pratica quanto abbiamo visto. In questo articolo parleremo dell’automazione lato client, nello specifico introdurremo phing, uno strumento di build basato su Ant, attraverso il quale definire una serie di operazioni automatizzate, tramite l’uso di un file XML.
I nostri esempi saranno contestualizzati in ambiente LAMP (nel nostro caso Ubuntu), ma attraverso opportuni adattamenti saranno trasportabili anche su altri ambienti.
Per prima cosa definiamo una struttura cartelle coerente per i nostri progetti:
.
├── application/
│ ├── model/
│ └── bootstrap.php
│
├── build/
│
├── _data /
│ │
│ ├── build.properties
│ ├── citest.create.sql
│ ├── citest.data.sql
│ ├── citest.fixtures.sql
│ ├── citest.structure.sql
│ ├── dbdump.sh
│ ├── dbload.sh
│ └── install.sh
│
├── _tests/
│ │
│ └── SomeThingTest.php
│
├── utils/
│ │
│ └── yuicompressor-2.4.7.jar
│
├── web
│ │
│ ├── css/
│ ├── js/
│ └── index.php
│
└── build.xml
All’interno di _data avremo una serie di files “di servizio” utili all’automatizzazione di nostri compiti. All’interno di web e application troveremo, rispettivamente, la parte pubblica della nostra webapp e la relativa business logic. In _test troveremo i test automatici, mentre in utilis tutte le librerie esterne che possono essere a supporto del nostro sistema di build.
Il file build.xml è infine l’oggetto principale di questo articolo, ed andremo a dettagliarlo bene nelle prossime righe.
Procediamo con l’installazione di phing, completo di tutte le dipendenze, attraverso PEAR.
Con permessi di root:
# pear channel-discover pear.phing.info
# pear config-set auto_discover 1
# pear config-set preferred_state alpha
# pear install --alldeps phing/phing
# pear config-set preferred_state stable
Attraverso la prima istruzione diremo a PEAR di eseguire la discover del canale per l’installazione di phing, mentre nelle successive 2, lo istruiremo ad eseguire la discover automatica per tutte le dipendenze e di utilizzare anche pacchetti in versione alpha (questo è necessario per ottenerne alcuni che possono risultare utili durante la scrittura delle nostre procedure automatiche).
Installiamo infine lo strumento e rimettiamo a posto PEAR, ripristinando la preferenza di scaricare elementi marcati come stable.
Se tutto è andato a buon fine, avremo a disposizione il comando phing, insieme ad altri tool come phpunit e phpcs.
Ora che phing è installato non ci resta che lanciarlo la prima volta, ottenendo il messaggio:
$ phing
Buildfile: build.xml does not exist!
Questo errore è normale: phing fa uso di un file xml per la definizione dei task da automatizzare, andiamo a crearlo:
<?xml version="1.0" encoding="UTF-8"?>
<project name="citest" basedir="." default="hello">
<property file="_data/build.properties"/>
<!-- HELLO TARGET-->
<target name="hello">
<echo msg="Hello World !!!" />
</target>
</project>
Il file è composto da un tag root <project> all’interno del quale troveremo uno o più <target>. Sono questi ultimi che definiranno le nostre operazioni, attraverso l’uso di task. Nel nostro primo esempio abbiamo definito un target “hello”, che stampa da console la celebre frase tramite il task <echo>.
Attraverso il tag <property> possiamo definire una serie di proprietà, in questo caso salvate su file, da poter utilizzare come variabili all’interno del nostro script (es: parametri di connessione a db, path dove risiede la cartella pubblica del progetto, ecc…).
E’ consigliabile utilizzare due versioni del file build: build.properties_template da porre sotto controllo di versione ed uno specifico dell’utente, build.properties, che verrà personalizzato alla prima checkout e posto in ignore per evitare il dilagare di configurazioni personali nel repository. Anche in questo caso vale la regola di non stravolgere il template e seguire il più possibile le linee guida di default, definite in fase di avvio del progetto.
Iniziamo a creare dei target utili ad impostare il nostro ambiente di Continuous Integration.
Definiamo un target per l’automatizzazione del setup iniziale del client di sviluppo:
<!-- INIT DEVELOPER ENVIRONMENT -->
<target name="init-dev-env"
description="### necessita permessi di root ### prepara l'ambiente di
sviluppo locale configurando apache e file di host">
<echo msg="Installazione in locale del progetto" />
<exec command="sh install.sh ${public-dir} ${vhosts-dir} ${apache-exe}"
dir="_data" checkreturn="true" passthru="true" />
</target>
Ottenendo quindi il comando (da lanciare con permessi di root):
# phing init-env-dev
Attraverso questo primo target, abbiamo automatizzato il setup del client, impostando il file degli hosts e creando un virtual host dedicato alla nostra web application. Phing si occuperà di passare i parametri necessari allo script di installazione (vi rimando al download dei sorgenti per i dettagli). In questo caso lo script è per Ubuntu, ma attraverso il task <if>, la proprietà os.name ed altri possibili parametri di configurazione in build.properties sarà possibile supportare qualsiasi sistema operativo e configurazione (es: lanciare dei .bat su windows, piuttosto che dei .sh).
Uno dei compiti che risulta vantaggioso automatizzare è la gestione dei dati del database. La pratica della Continuous Integration vuole che il DB sia posto sotto controllo di versione, per cui possiamo definire due target: uno per popolare il DB locale tramite dati provenienti da file .sql (db-load), l’altro per l’operazione opposta (db-dump).
<!-- LOAD DB DATA FROM SQL FILE -->
<target name="db-load"
description="Popola il database partendo da _data/data.sql se non diversamente specificato tramite -Ddb.data-source=datasource">
<property name="db.data-source" value="data" override="false" />
<echo msg="Loading ${db.data-source} on ${db.name} ..." />
<exec command="sh dbload.sh '${db.params}' ${db.name} ${db.data-source}" dir="_data" checkreturn="true"/>
<echo msg="Done!" />
</target>
<!-- DUMP DB DATA TO SQL FILE -->
<target name="db-dump"
description="Esegue il dump del database su _data/data.sql se non diversamente specificato tramite -Ddb.data-dest=datadest">
<property name="db.data-dest" value="data" override="false" />
<echo msg="Dumping ${db.name} on ${db.data-dest} ..." />
<exec command="sh dbdump.sh '${db.params}' ${db.name} ${db.data-dest}" dir="_data" checkreturn="true"/>
<echo msg="Done!" />
</target>
Entrambi i target accettano un parametro per specificare quale sia la fonte (o la destinazione) dei dati. Nel nostro esempio siamo interessati a data.sql contenente generici dati simili a quelli di produzione, e fixtures.sql per dati utili nei test.
Per l’esecuzione dei test automatici, definiamo due target: do-tests che lancia i test, e test che usa il primo e si occupa inoltre di sistemare il database.
<!-- TEST WITH PHPUNIT -->
<target name="do-tests"
description="Lancia i test automatici sulla code base">
<phpcodesniffer standard="Zend" format="summary" file="${path}"
allowedFileExtensions="php php5 inc"
haltonwarning="true" haltonerror="true" />
<phplint>
<fileset dir="web">
<include name="**/*.php"/>
</fileset>
<fileset dir="_tests">
<include name="**/*.php"/>
</fileset>
</phplint>
<phpunit bootstrap="./application/bootstrap.php"
haltonfailure="true" haltonerror="true">
<formatter type="plain" usefile="false"/>
<batchtest>
<fileset dir="_tests">
<include name="**/*Test*.php"/>
</fileset>
</batchtest>
</phpunit>
</target>
Con il primo target abbiamo introdotto i task <phpcodesniffer> per il rispetto del code style, <phplint> per individuare subito eventuali parse error nel codice ed infine <phpunit> per il lancio dei test automatici veri e propri.
Introduciamo adesso il target test: come possiamo vedere esso salva il database corrente all’interno di un file temporaneo, lo popola con dati di fixtures e successivamente lancia do-tests. Infine ripristina il database allo stato precedente al lancio dei test.
<!-- TEST WITH DB -->
<target name="test"
description="Carica le fixtures nel database, lancia i test automatici e riporta il database allo stato precedente">
<phingcall target="db-dump">
<property name="db.data-dest" value="tempdata" />
</phingcall>
<phingcall target="db-load">
<property name="db.data-source" value="fixtures" />
</phingcall>
<trycatch property="test-error">
<try>
<phingcall target="do-tests" />
</try>
<catch>
<!-- just catch the exception to keep clean the output -->
</catch>
<finally>
<phingcall target="db-load">
<property name="db.data-source" value="tempdata" />
</phingcall>
</finally>
</trycatch>
<fail if="test-error" msg="Something gone wrong with your tests!" />
</target>
In questa fase è interessante notare l’utilizzo del task <trycatch>: qualora i test fallissero (lanciati all’interno della clausola <try>) è possibile definire un’operazione di recovery all’interno di <catch> (niente nel nostro caso).
Abbiamo messo infine il ripristino del database nella clausola <finally>: questa viene sempre e comunque eseguita, così da non finire in uno stato incoerente qualora i nostri test fallissero. Attraverso la proprietà test-error, riportata da <trycatch> possiamo far fallire la build in caso di errore nei test, con il task <fail>.
Abbiamo quasi tutto il necessario per lanciare la nostra build, attraverso l’uso di un solo comando. Introduciamone il relativo target ed andiamo a spiegarlo:
<target name="build" depends="clean,test"
description="Testa il software, copia tutti i file necessari al deploy nella cartella build ed esegue il minify dei file .js e .css" >
<mkdir dir="build" />
<copy todir="build" >
<fileset dir=".">
<include name="application/**" />
<include name="web/**" />
</fileset>
</copy>
<echo msg="Minifying JS files ..." />
<jsMin targetDir="build/web/js" failOnError="false" suffix="">
<fileset dir="web/js">
<include name="**/*.js"/>
</fileset>
</jsMin>
<echo msg="Done!" />
<echo msg="Minifying CSS files ..." />
<foreach param="filename" absparam="absfilename" target="minify-css-file">
<fileset dir="build/web/css">
<include name="*.css"/>
</fileset>
</foreach>
<echo msg="Done!" />
</target>
Come possiamo vedere il nostro target, prima di essere eseguito, ne lancia altri due: il primo, clean, per eliminare residui delle build precedenti, ed il secondo, test, per testare il software.
Fatto ciò verranno eseguite una serie di operazioni per ottenere tutti i nostri files necessari al deploy: dalla copia dei sorgenti nella cartella build, al lancio del minify dei file .js tramite il task <jsMin> e quelli .css tramite un target da noi creato, il minify-css-file, che fa uso del tool yui-compressor.
A build completata, troveremo nella cartella build il nostro software pronto al deploy.
Per completezza, introduciamo i due target mancanti:
<!-- CLEAN -->
<target name="clean"
description="Elimina la build corrente">
<delete dir="build" includeemptydirs="true"
verbose="true" failonerror="true" />
</target>
<!-- UTILITY PER IL MINIFY DEI CSS CON YUI COMPRESSOR -->
<target name="minify-css-file">
<exec command="java -jar ${yui-jarfile} -o build/web/css/${filename} build/web/css/${filename}" checkreturn="true" />
</target>
Attraversi l’utilizzo di phing, abbiamo automatizzato la gran parte dei compiti lato client. Come possiamo vedere abbiamo tutto quanto richiesto dalla Continuous Integration: il database viene trattato come codice sorgente ed i test sono automatizzati. Come ultima cosa, ma non meno importante, la possibilità di lanciare una “one-commend build”, tramite la quale ottenere il nostro software testato e funzionante, pronto per il deploy.
A build funzionante, non ci resta altro che eseguire la commit verso il repository e passare la palla al Continuous Integration server.. ma questa è un’altra storia :)
Scarica i sorgenti proposti in questo articolo e crea il tuo ambiente.
Se vuoi aggiornamenti su Continuous Integration: automatizziamo i client con Phing inserisci la tua email nel box qui sotto:
Compilando il presente form acconsento a ricevere le informazioni relative ai servizi di cui alla presente pagina ai sensi dell'informativa sulla privacy.
La tua iscrizione è andata a buon fine. Se vuoi ricevere informazioni personalizzate compila anche i seguenti campi opzionali:
Compilando il presente form acconsento a ricevere le informazioni relative ai servizi di cui alla presente pagina ai sensi dell'informativa sulla privacy.
Come gestire i Big Data con MongoDB e molto di più
Tutti i linguaggi per diventare uno sviluppatore di app per Android.
Come creare applicazioni per il Web con PHP e MySQL per il DBMS.
Tutte le principali tecnologie per diventare uno sviluppatore mobile per iOS.
I fondamentali per lo sviluppo di applicazioni multi piattaforma con Java.
Diventare degli esperti in tema di sicurezza delle applicazioni Java.
Usare Raspberry Pi e Arduino per avvicinarsi al mondo dei Maker e dell’IoT.
Le principali guide di HTML.it per diventare un esperto dei database NoSQL.
Ecco come i professionisti creano applicazioni per il Cloud con PHP.
Lo sviluppo professionale di applicazioni in PHP alla portata di tutti.
Come sviluppare applicazioni Web dinamiche con PHP e JavaScript.
Fare gli e-commerce developer con Magento, Prestashop e WooCommerce.
Realizzare applicazioni per il Web utilizzando i framework PHP.
Creare applicazioni PHP e gestire l’ambiente di sviluppo come un pro.
Percorso base per avvicinarsi al web design con un occhio al mobile.
Realizzare siti Web e Web application con WordPress a livello professionale.
Come creare database e collection, inserire, estrarre, aggiornare e rimuovere dati da una base di dati MongoDB con Python
Impariamo ad utilizzare Takamaka, una blockchain Java Full Stack, per scrivere codice Java installabile ed eseguibile su una blockchain
È possibile rifattorizzzare il codice per concentrare l’attenzione maggiormente sui dati? In questo articolo concentriamo la nostra attenzione su un approccio “Data Centric”.
Come creare un’applicazione per la gestione di un negozio di commercio elettronico con il framework PHP Laravel. Una guida completa che, partendo dalla configurazione di un ambiente di sviluppo basato su Laravel, descrive nel dettaglio tutti i passaggi necessari per pubblicare un e-commerce con cui promuovere e vendere i propri prodotti online