Continuous Integration

La Continuous Integration è una pratica dello sviluppo software introdotta per i contesti in cui i diversi membri di un progetto integrano il loro lavoro frequentemente, almeno una volta o anche più volte al giorno. Essa consiste nell'esecuzione automatica dei processi di build e testing durante ciascuna fase di integrazione, al fine di individuare i possibili errori il prima possibile. Tale approccio ha portato alla significativa riduzione dei problemi di integrazione e ha consentito lo sviluppo di software più rapidamente.

Il termine Continuous Integration nasce nell'ambito dell'eXtreme Programming ed è una delle pratiche alla base di tale metodologia di sviluppo. Tuttavia non bisogna essere degli sviluppatori XP per capire quanto l'esecuzione automatica dei processi di build e testing sia importante e apportatrice di effetti benefici per ogni progetto.

I vari benefici possono essere riassunti nella nota espressione "riduzione del rischio". Introducendo un meccanismo di Continuous Integration si è in grado, infatti, di sapere esattamente a che punto ci si trova nel ciclo di sviluppo, cosa funziona nel sistema e quali sono i bug. Ciò avviene monitorando continuamente lo stato del repository del progetto e mettendo a disposizione un ambiente allineato e certificato in ogni momento. Per quanto riguarda poi i bug tale pratica non li elimina, ma ne riduce il numero consentendo di intervenire prontamente ed in modo mirato nei casi in cui se ne introducano di nuovi. Questo è tanto più vero quanto più efficaci sono i test: l'automatizzazione del processo di testing consente, infatti, di eseguire le diverse suite di test quotidianamente o anche con frequenza maggiore, migliorando la qualità del codice prodotto senza rallentare il normale ciclo di sviluppo del software.

Per tutti questi motivi la Continuous Integration non è rimasta confinata nel contesto dell'eXtreme Programming, ma è entrata a far parte dello sviluppo software in generale come procedura di quality assurance.

Automatizzazione dei processi in Ant

Ant, come sottolineato in questa serie di articoli, è uno strumento estremamente versatile e consente di eseguire in modo meccanico e ripetibile i vari processi di un progetto. Tuttavia prima di lanciare un target ant si devono spesso compiere dei passi manuali che non sempre rendono immediata l'esecuzione di un processo.

Il primo di questi passi è costituito dalla configurazione del workspace del progetto e riguarda il setting dei path dipendenti dalla propria macchina nei file di properties di ant e nei file di configurazione. Quest'attività va ripetuta ogni volta che si esegue il checkout e, vuoi per la sua meccanicità, vuoi per l'inesperienza dei neofiti del progetto, è spesso fonte di errori e dimenticanze.

Ancora una volta però ant ci fornisce gli strumenti per risolvere tali problemi offrendoci un modo per rendere tutti i files di configurazione parametrici e indipendenti dalla piattaforma utilizzata. Questo meccanismo è noto come tokenizzazione e consiste nella definizione, appunto, di un token, ovvero un marcatore, che poi viene valorizzato a runtime con un valore relativo all'environment locale.

<< figura1.ext - Figura 1 - Effetti della tokenizzazione su un file >>

<< figura1.ext - Figura 1 - Effetti della tokenizzazione su un file >>

L'implementazione di questa tecnica con ant sfrutta il task <filterset>, che è usato nelle copie/spostamenti di file, e richiede i seguenti interventi:

si indicano nel build.xml i valori da sostituire token (si possono ad esempio sfruttare la property basedir di ant e le variabili di environment per specificare proprietà e path relativi all'ambiente locale).

Di seguito è riportato un esempio di build file che esegue una tokenizzazione (si noti l'uso del task <pathconvert> che trasforma i back slash(\) in slash(/) per evitare problemi nel caso dei path calcolati su piattaforma windows):

<?xml version="1.0" encoding="UTF-8"?>
<project name="MokaTokenization" basedir=".." default="init">
  <property name="base.src.dir" location="${basedir}"/>
  <property file="./Local/workspace.properties"/>
  <!-- inizializzazione delle proprieta' con definizione del path OS indipendent -->
  <target name="init">
    <path id="base.src.dir.to.convert">
      <pathelement location="${base.src.dir}"/>
    </path>
    <pathconvert targetos="unix" property="base.src.dir.like.unix" refid="base.src.dir.to.convert"/>
    <echo message="dir di workspace ri-calcolata ${base.src.dir.like.unix}"/>
  </target>
  <target name="workspace-setting" depends="init" description="Prepara il workspace nell'ambiente locale">
    <copy todir="CodeAudit" overwrite="true">
      <fileset dir="Local/Tokenized/CodeAudit"/>
      <filterset>
        <filter token="WORKSPACE_PATH" value="${base.src.dir.like.unix}"/>
        <filter token="WAS_HOME" value="${env.was.home}"/>
      </filterset>
    </copy>
    <copy todir="Configuration/properties/utenze/db" overwrite="true">
      <fileset dir="Local/Tokenized/Configuration/db"/>
      <filterset>
        <filter token="USERNAME" value="${env.USERNAME}"/>
      </filterset>
    </copy>
    <copy todir="Local/CodeCoverage" overwrite="true">
      <fileset dir="Local/Tokenized/CodeCoverage"/>
      <filterset>
        <filter token="WORKSPACE_PATH" value="${base.src.dir.like.unix}"/>
        <filter token="TEMP_DIR" value="${env.TEMP}"/>
        <filter token="WAS_HOME" value="${env.was.home}"/>
      </filterset>
    </copy>
  </target>
</project>

Anche dopo aver configurato correttamente il proprio workspace non si è tuttavia ancora pronti per eseguire in modo automatico tutti i target ant. Per alcuni, si pensi ad esempio al target che lancia i test di integrazione, sono necessari degli ulteriori step manuali come lo start dell'application server e l'avvio di tutte le altre componenti dell'architettura applicativa utilizzate (p.e. motori di tariffazione, documentale, ...).

In questi casi per automatizzare si possono sfruttare i task <exec> e <java>, che permettono di invocare, rispettivamente, applicazioni esterne native e Java. I task indicati consentono di eseguire dei comandi sul sistema operativo ospitante e di effettuare o meno lo "spawn" (ovvero l'esecuzione al di fuori della virtual machine di ant) delle applicazioni lanciate quando l'attributo fork è impostato a true. Nel frammento di build file sottostante è mostrato un esempio di target che lancia i test di integrazione effettuando start, deploy e stop sull'application server:

<target name="integrationTest">
  <echo message="Executing startServer.bat..."/>
  <exec executable="C:/Programmi/WebSphere/AppServer/bin/startServer.bat">
    <arg line=" server1"/>
  </exec>
  <echo message="Executing wsadmin.bat..."/>
  <exec executable="C:/Programmi/WebSphere/AppServer/bin/wsadmin.bat">
    <arg line=" -f D:/workspace/Local/ContinuousIntegration/deployWAS.jacl"/>
  </exec>
  <copy todir="Application_EAR" overwrite="true">
    <fileset dir="D:/workspace/build"/>
  </copy>
  <echo message="Executing test..."/>
  <exec executable="D:/workspace/run-tests.bat"/>
  <echo message="Executing stopServer.bat..."/>
  <exec executable="C:/Programmi/WebSphere/AppServer/bin/stopServer.bat">
    <arg line=" server1"/>
  </exec>
</target>

Continuous Integration con Apache Continuum

Le funzionalità principali di un sistema di Continuous Integration includono:

  • Integrazione di sistemi di versionamento (CVS, Subversion, ...)
  • Integrazione di build-tool (Ant, Maven, ...)
  • Feedback e reporting con diversi sistemi di notifica (E-mail, Instant messenger, RSS, ...)
  • Labeling (cioè la possibilità di taggare sul repository dopo ogni build eseguita con successo)
  • Gestione delle dipendenze tra diversi progetti

Conclusioni

Nell'articolo si è parlato di come automatizzare l'esecuzione dei processi ant, utilizzando la tokenizzazione per la sostituzione dei percorsi relativi nei file di properties/configurazione e i task <exec> e <java> per l'invocazione di applicazioni esterne. Quindi si è mostrato come usare un build file automatizzato in Apache Continuum per realizzare in pochi passi una soluzione di Continuous Integration.