În procesul de dezvoltare de software, una dintre practicile devenite din ce în ce mai comune, chiar necesare în majoritatea echipelor, este integrarea continuă. În funcție de tehnologie, integrarea continuă se poate realiza în mai multe moduri.
În acest articol vom prezenta o soluție la această problemă din punctul de vedere al dezvoltării aplicațiilor mobile pe platforma iOS. Soluția noastră se bazează pe Xcode Server și pe platforma online de distribuție Fabric.IO.
Integrarea continuă (Continous Integration) este o practică în care codul mai multor programatori dintr-un proiect este integrat într-o ramură partajată dintr-un sistem de versionare. Această practică a fost propusă pentru prima dată de către Grady Booch în anul 1991. Scopul integrării continue este să ajute la rezolvarea problemelor ce pot să apară când ramurile locale ale programatorilor trebuie să fie integrate în ramura principală a proiectului. Este recomandat ca programatorii să integreze ramura principală în ramurile locale o dată pe zi, în acest mod probabilitatea de a avea viitoare conflicte este redusă.
Integrarea continuă nu se referă doar la cele spuse anterior, ci acoperă și testarea unitară a funcționalității programului și a componentelor acestuia. Aceste teste pot să fie configurate astfel încât să ruleze de fiecare dată când o ramură este integrată în ramura principală a proiectului. O dată ce testele au fost rulate cu succes un executabil se poate crea pentru echipa de QA (quality assurance) unde aceștia pot rula mai multe teste manuale.
O altă practică recomandată este aceasta: în cazul în care codul din ramura locală are teste unitare și unul dintre programatorii din echipa integrează ramura principală a proiectului în ramura sa locală, este recomandat rularea respectivelor teste pentru a verifica dacă schimbările locale nu au făcut testele să raporteze erori.
Există mai multe sisteme care oferă integrare continuă și cel mai popular dintre acestea este Jenkins. Jenkins oferă multe extensii pentru rularea de scenarii (scripturi), conexiune cu diferite sisteme de versionare (git, svn), extensii pentru trimitere de notificări prin e-mail și multe altele. Pentru a afla mai multe despre Jenkins se poate citi consultând documentația oficială.
Apple oferă o soluție pentru integrarea continuă sub forma serverului Xcode care face parte din mac OS Server. În serverul Xcode se pot crea boți pentru proiecte create în programul(IDE) Xcode. Acești boți pot fi configurați pentru a crea pachete de instalare a proiectului, rularea de teste și scenarii automate. Serverul Xcode poate fi folosit pentru aplicații care rulează pe sistemul de operare mac OS, iOS, TV OS și Watch OS, toate dezvoltate de către Apple. Avantajul serverului Xcode este foarte buna integrare a acestuia în mediul de dezvoltare de la Apple.
Serverul Mac OS poate să fie instalat din App Store sau din portalul de dezvoltator din programul de dezvoltare de la Apple. Mai multe detalii despre cum se instalează serverul Mac OS se pot găsi consultând documentația oficială.
O dată ce serverul Xcode este configurat se poate crea un bot pentru un anumit proiect prin deschiderea proiectului respectiv, apoi din meniul Product se alege opțiunea Create Bot
Apoi se urmează pașii din Wizard.
În primul rând noul bot are nevoie de un nume, apoi trebui specificat o adresă IP care reprezintă adresa mașinii unde serverul mac OS rulează.
După ce s-a efectuat logarea în sistemul de versionare folosit în proiect, trebuie specificată ramura din proiect pe care botul o va folosi. Pentru proiectul nostru am ales ramura develop, care este și ramura principală.
Următorul ecran, ilustrează opțiunile de configurare pentru crearea pachetului de instalare. Conform documentației de la Apple, schema agregă mai multe ținte de creare, o configurație de creare și teste de executat. Vizibilitatea schemei trebuie să fie publică pentru a fi vizibilă în botul Xcode. Pentru a face o schemă publică, în proiectul pentru care se dorește crearea botului, se face click pe itemul din stânga numelui proiectului și se alege opțiunea Manage Schemes
De aici se selectează opțiunea Shared pentru schema care se va folosi în bot.
După ce s-a selectat o schemă publică, acțiunea care ne interesează este "perform archive", dar se poate opta și pentru a analiza sau rula o suită de teste. Cleaning se referă la ștergerea cache-ului și a datelor temporare a proiectului. Pentru opțiunea de configurare se pot folosi setările din schemă sau se poate alege opțiunea "release", fiindcă țelul nostru e să distribuim aplicația unui grup de testatori, nu să o rulăm în modul debug. În cazul nostru, am selectat opțiunea "release" pentru schemă la pasul de creare a arhivei de instalare.
Următorul pas este de a decide când botul Xcode va crea pachetul de instalare. Am selectat opțiunea "On Commit", deoarece vrem ca de fiecare dată când o ramură, ce conține funcționalități noi, este integrată în ramura principală, un nou pachet de instalare să fie creat. Chiar dacă există multe opțiuni la categoria "Schedule" la finalul wizardului, botul va executa o integrare, indiferent de opțiunea aleasă. Acest lucru se va întâmpla o singură dată după ce botul a fost configurat. Ulterior se va respecta opțiunea aleasă în Schedule.
Ecranul de mai sus oferă opțiunea de a alege dispozitivele fizice sau simulatoare pe care testele vor fi rulate.
Pentru diferite scopuri se pot defini variabile de sistem, altele decât cele pe care le poți accesa din Xcode Server (i.e. XCS_INTEGRATION_ID, XCS_BOT_NAME, etc.).
Există patru tipuri de declanșatori:
Scenarii pre-integrare,
Scenarii post-integrare,
Trimitere de e-mail la probleme noi,
Suntem interesați de al doilea tip de declanșatori pentru că vom crea un scenariu pentru setarea automată a numărului de versiune a aplicației (Figura 10). Pentru moment nu este obligatoriu să creăm scenarii post-integrare, vom acoperi acest aspect puțin mai târziu.
Când vine vorba despre aplicații pentru dispozitivele mobile se dorește ca noile funcționalități să ajungă pe mâna echipei de testare cât mai repede. Pentru a obține acest lucru există două opțiuni:
trimiterea pachetului de instalare manual,
Folosirea unei infrastructuri de integrare continuă este mai rapidă decât trimiterea manuală. O astfel de soluție presupune ca de fiecare dată când se integrează cod în ramura principală a proiectului, se va crea un pachet de instalare și se vor rula teste. Apoi, acel pachet va fi trimis echipei de testare.
Incrementarea numărului de versiune poate să fie automatizată cu ușurință folosind programul avgtool. Programul utilitar este folosit într-un scenariu shell (shell script) care este executat înainte de fiecare creare a unui pachet de instalare. Problema cu incrementarea automată a numărului de versiune este detectarea de către sistemul de versionare a unei noi schimbări, iar acea schimbare trebuie integrată manual. Pentru a evita această problemă, propunem ideea ca numărul de versionare să fie în concordanță cu numărul de integrare de la serverul Xcode. Valoarea este obținută dintr-o variabilă de sistem având numele "XCS_INTEGRATION_NUMBER".
Următorul scenariu (script) este adăugat la faza de creare a pachetului de instalare și este adăugat în ținta curentă din Xcode.
#!/bin/sh
if [ "${CONFIGURATION}" = "Release" ]; then
agvtool new-version -all "$XCS_INTEGRATION_NUMBER"
fi
De obicei, notele informative constau într-un text care descriu ce funcționalități noi au fost introduse sau ce erori au fost corectate, iar notele se distribuie împreună cu pachetul de instalare. Cel mai frecvent, notele informative se introduc manual de către o persoană, dar noi am considerat că acest lucru poate să fie automatizat elegant.
Soluția noastră se bazează pe faptul că sistemul de versionare Git este foarte răspândit și folosit și pe faptul că procesul de dezvoltare urmărește modul de lucru din ramurile cu funcționalități (Feature Branch Workflow). Integrarea este declanșată de fiecare dată când o ramură cu funcționalități noi este integrată în ramura principală a proiectului. Notele informative sunt extrase din mesajele care sunt acompaniate cu noul codul din ramura pe care funcționalitățile au fost create și care nu se găsesc pe ramura principală. Prima data, verificăm dacă există fișierul "hash.txt" și dacă acesta conține un hash de commit. Pentru o înțelegere mai bună vom abrevia acest commit hash ca OCH (old commit hash). De asemenea, menționăm că HEAD se referă la ultimul commit de pe ramura principală. Identificare mesajului de commit pentru a fi adăugat în notele informative implică folosirea funcției "git log" furnizând două granițe ca parametru ce reprezintă două hashuri și anume de la OCH la HEAD. Rezultatul rulării acestei funcții este scris în fișierul "release_notes.txt" folosind un anumit format: numele autorului pentru commit și mesajul de commit. Mesajele de integrare explicite, care conțin cuvântul merge, nu sunt incluse. În cazul în care nu avem nici un mesaj textul "No changes since last build" este folosit pentru note informative. Apoi salvăm OCH în fișierul "hash.txt" și executăm distribuirea către testatori folosind platforma Fabric.IO.
Acțiunile descrise anterior sunt împachetate într-un scenariu shell care este rulat cu succes după fiecare împachetare (build). Pentru a permite acest lucru, programatorul trebuie să deschidă secțiunea "Triggers" din secțiunea "Edit Bot", se efectuează click pe butonul "+" și se selectează "Post-Integration Script"(Figura 12).
#!/bin/sh
CM_HASH=$(cat $XCS_SOURCE_DIR/hash.txt)
echo git --git-dir $XCS_SOURCE_DIR/<NAME_OF_GIT_PROJECT>/.git log --no-merges $CM_HASH..HEAD --format="%cn: %s" > RELEASE_NOTES
if [ "${RELEASE_NOTES}" = "" ]; then
RELEASE_NOTES = "No changes since last build."
fi
echo "${RELEASE_NOTES}" > $XCS_SOURCE_DIR/release_notes.txt
git --git-dir $XCS_SOURCE_DIR/<NAME_OF_GIT_PROJECT>/.git rev-parse HEAD > $XCS_SOURCE_DIR/hash.txt
"${XCS_SOURCE_DIR}/<PROJECT_CRASHLYTICS_PATH>/Crashlytics/submit" <API_KEY> <BUILD_SECRET> -groupAliases <TESTING_GROUP_NAME> -ipaPath "${XCS_PRODUCT}" -notesPath $XCS_SOURCE_DIR/release_notes.txt
Observații: XCS_SOURCE_DIR este o variabilă de sistem care conține calea până la directorul ce conține codul sursă din sistemul de versionare.
În urma implementării soluției descrise în articol, am învățat cum să integrăm git cu Xcode Server și să distribuim pachetul de instalare echipei de QA cu ajutorul platformei Fabric.IO.
Ușurința rulării testelor automate împreună cu raportarea de erori și distribuția rapidă a pachetului de instalare către echipa de QA sunt avantaje care ne vor determina să folosim această soluție de integrare continuă.