L'industrialisation au service des développements Mobiles

Introduction

L'intégration continue n'est plus une option lors de la réalisation d'une nouvelle application, l'industrialisation est maintenant entrée dans les mœurs. Cette Bonne Pratique est rassurante pour l’équipe de réalisation du fait de la stabilité de ses nouveaux développements, et pour le client qui évite ainsi les nombreux tests de non-régression. Le déploiement continu est venu enrichir ce principe, en automatisant les livraisons, pour répondre aux préconisations des méthodes Agiles, avec la mise à disposition de livrables de façon itérative.

Côté mobile, cette pratique n’est pas encore très répandue, du moins si l’on souhaite se passer des solutions cloud qui obligent à externaliser des sources, propriétés du client, pour l’automatisation des builds et la gestion des déploiements.
Cette industrialisation mobile est cependant nécessaire pour automatiser un maximum de tâches répétitives, fastidieuses et sources d’erreurs du travail des développeurs.

Dans cet article, nous allons vous présenter une architecture complète et commune d'industrialisation d'applications Android & iOS (& Hybride), ainsi que quelques particularités propres aux applications iOS.
Nous vous présenterons également certaines solutions devenues indispensables aux développements mobiles et à leur industrialisation. Il s’agit principalement de :

  • Fabric / Crashlytics
  • Fastlane
  • GitLab-CI/CD

Dans tout l’article, le terme CD sera employé pour désigner la brique de déploiement continu, incluant l'intégration continue (CI).

Focus sur l’intégration continue Mobile

Nos besoins

Dans un premier temps il est nécessaire de bien distinguer les deux contextes de build que nous allons supporter. Un contexte de build en debug[1], utilisé pour les actions internes et un contexte de build en release[2] pour les actions clients.
Ensuite il faut pouvoir lancer les tests unitaires et d’intégration de l’application, et pour finir nous souhaitons pouvoir déployer l’application sur un environnement testable en interne, ou par le client.

Ces étapes doivent être automatisées et s’intégrer facilement avec notre méthode de travail, GitFlow, ainsi que notifier les acteurs du projet des succès/échecs d’une des étapes décrites au-dessus.
De cette manière, les tâches de build, test et livraison sont sorties de la charge des développeurs. On peut utiliser ce temps supplémentaire en faisant de la revue de code, du refactoring ou en enrichissant les jeux de tests dans le but de mettre l’accent sur la qualité des livrables.

Nos contraintes

Lorsque l’on travaille sur un projet pour un client, les sources lui appartiennent. Il n’est pas envisageable d’exporter ce code sur une plateforme dédiée à la CD dans le cloud sans son accord au préalable. Pour éviter d'avoir cet échange et multiplier les configurations de builds en fonction du retour client, nous avons donc décidé de porter la responsabilité des builds en interne. Enfin, cette solution de fonctionnement on-premise nous assure un livrable lié aux sources, non modifié par l’ajout d’un artefact non désiré.

De plus, les applications iOS nécessitent un Mac sous macOS pour les phases de build et de signature des applications. Cette restriction est principalement due au fait que la suite d’outils Apple pour builder et signer les applications iOS,
respectivement xcodebuild[3] et codesign[4], ne sont pas compatibles avec d’autres OS tels que GNU/Linux ou Windows.

Récapitulatif des écosystèmes iOS et Android

Pour mieux comprendre les éléments qui composent cet article, voici un petit référentiel des éléments qui composent l’environnement d’un développeur mobile.

Android iOS
OS privilégié GNU/Linux, macOS, Windows macOS
IDE principal Android Studio Xcode
Plateforme de gestion et administration d’applications Google Play Console Apple Developer (Gestion des développeurs, des identités de certifications, des provisionnings profiles, des appareils de développements autorisés à lancer des builds de développements)
Plateforme de soumission d’applications - Upload d’app, fiche marketing, données financières, … Google Play Console iTunesConnect
Plateforme de déploiement de versions de tests Google Play Beta testing (intégré à Google Play Console), Fabric Beta TestFlight (intégré à iTunesConnect), Fabric Beta


Historique de l'industrialisation

L'intégration continue est un chantier long, optimisable à souhait.
Nous avons profité des besoins des différents projets pour industrialiser au fur et à mesure notre plateforme de déploiement continu.

v1 - Mise à disposition des livrables

L'idée d’industrialisation mobile est née suite à une problématique projet de mise à disposition des livrables au client à chaque fin de Sprint. Le but étant de nous abstraire des livraisons par mail ou par liens de téléchargement, inconfortables pour le client.

Dans une démarche de type Best Effort, nous avons créé un serveur nodeJS (serveStatic et basicAuth) exposant les applications historisées et installables directement via l’IHM depuis son mobile. Contrairement à Android, iOS nécessite un fichier manifest.plist associé au livrable contenant le chemin d’accès absolu de l’application pour l’installer directement sur son téléphone de test.

v2 - Intégration et déploiement presque continu

Afin de valider la qualité des livrables et de chaque commit, nous avons décidé d’intégrer des phases de tests automatisés via Jenkins. Nous avons alors découvert une solution d’automatisation de tâches adaptée au projets mobiles iOS et Android : Fastlane.
Cette solution sera détaillée dans les prochains paragraphes de cet article. La mutualisation Jenkins & Fastlane nous a permis de programmer et déclencher des jobs basés sur nos interactions avec les sources du projet sous Gitlab.

Afin de garder les sources on-premise, propriété du client, nous avons pris la décision d’utiliser un Mac dédié à cette intégration continue. Les builds iOS nécessitant un système d’exploitation macOS et Ippon ne disposant pas de VM macOS au sein de son infrastructure,
Une fois cette machine de build configurée (Xcode installé, SDK Android installé, VPN Ippon configuré, mode NoSleep sur la machine, clef SSH partagé sur Gitlab, ...) une instance de Jenkins esclave, liée au master Ippon, a été installée dessus. Ce master écoute les changements sur le repository et déclenche des actions sur le slave via des webhooks configurés depuis GitLab. De son côté, le slave se limite à l’appel des scripts Fastlane en fonction des actions demandées.

Cette solution de slave côté machine de build s’est révélée trop complexe. En effet, l’installation d’un slave, sa configuration et maintenance, pour une utilisation limitée à l’appel de scripts Fastlane s’est vite avérée “overkill”.

v3 - Simplification de l’architecture avec Gitlab CI/CD

Chez Ippon, nous disposons d’un environnement GitLab interne, c’était l’occasion de tester la solution GitLab CI/CD.
Ce qui nous a tout de suite convaincus dans cette approche c’est la simplicité de mise en place d’un Runner sur une machine dédiée, dans notre cas un Mac. L’interconnexion entre GitLab et ses Runners se fait de manière transparente.
Et en prime, la configuration des Jobs et des Pipelines se fait très simplement. S’interfacer avec Gitlab se fait sans webhook, il était plus facile d’utiliser cette solution.

Mise en place du déploiement continu

Dans cette partie nous allons poser les bases de notre vision de l’intégration continue appliquée à nos habitudes de travail, en mettant l’accent sur certains points spécifiques aux développements mobiles :

  • Identité de certification
  • Code signing
  • Intégration de nouveaux développeurs aux équipes

Intégration de la CD avec GitFlow

L’intégration continue ne doit pas chambouler les habitudes de travail des développeurs mobiles. Pour cette raison, nous sommes partis sur une intégration en accord avec GitFlow et sa philosophie orientée branche. Voici une version simple des événements déclencheurs d’actions de la part de la CD qui vont nous intéresser ici.

D’une part, la modification de git refs sur la branche develop (push, merge, ...) va déclencher notre pipeline d’actions (build, test, deploy) en debug. Ce pipeline va être souvent déclenché au cours de la vie du projet.
Il doit s’exécuter rapidement et notifier le développeur du résultat de son exécution. Ainsi il pourra réagir en conséquence et éventuellement proposer une correction en cas d’erreur.
Pour diminuer le temps d’exécution de ce pipeline, nous avons décidé de ne jouer que les tests unitaires durant ce pipeline. Ces tests s’exécutent rapidement contrairement aux tests de non-régression qui nécessitent un parcours de l’application selon les scénarios établis par les développeurs pour valider des interactions à l’écran, tester les changements d’états de l’application et constater que les vues sont modifiées en accord avec ces changements.

Ce pipeline permet de s’assurer que chaque feature branch mergée sur develop passe les tests unitaires qui lui sont affectés. L’application buildée en debug est ensuite déployée sur Fabric Beta en interne dans le but de procéder à de la recette interne, par le chef de projet, sur la dernière fonctionnalité développée. Il lui suffit d’utiliser un appareil de test, se rendre sur Fabric et télécharger la dernière version à disposition.

Pipeline develop
Pipeline develop

D’autre part, la modification de git refs sur la branche master (merge, tags, ...) va déclencher notre pipeline d’actions de build, de tests (unitaires et de non-régression) et de déploiement en release dans le but encore une fois de s’assurer de la qualité des développements ainsi que la mise à disposition pour le client de son application en fin de sprint. Il pourra alors la faire tester à son groupe de testeurs, via Google Play Beta Testing pour Android et TestFlight pour iOS et initialiser la recette de l’application.

Pipeline master
Pipeline master

Enfin, un pipeline nightly build va être déclenché périodiquement chaque nuit. Ce pipeline est le même que le pipeline debug à l’exception près qu’il joue les tests de non-régression en plus des tests unitaires, puisque ce build dispose de tout le temps nécessaire à son exécution. Il permet à l’équipe de développement de valider ou non l’ensemble des développements de la journée précédente.

Pipeline nightly build
Pipeline nightly build

Les certificats applicatifs

Lorsque l’on développe sur des plateformes mobiles, livrer une application passe impérativement par une phase de code signing dans le but de :

  • Identité - Certifier la provenance de l’application, qui en est l’auteur via l’identité de certification utilisée pour produire l’application signée.
  • Intégrité - Certifier que l’application n’a pas été modifiée depuis sa dernière signature.
  • (iOS only - signature en Développement) Autoriser une liste d’appareils via leur UDID (Unique Device IDentifier) sur lesquels l’application peut être exécutée.
  • (iOS only - signature en Distribution) Se conformer au protocole de soumission d’applications d’Apple sur iTunesConnect (porte d’entrée de l’AppStore et de TestFlight) qui n’accepte que des applications signées avec un provisionning profile et une identité de certification de Distribution.

La gestion des identités de code signing

Les développements iOS sont très encadrés par Apple, et doivent respecter des règles strictes que sont :

  • L’ obligation pour un développeur d’être rattaché à un compte Apple Developer Program pour pouvoir lancer et tester une application en cours de développement sur un appareil de test préalablement enregistré.
  • Une application en débug est forcément signée avec l’identité de certification de Développement du développeur qui travaille dessus pour être exécuté sur un appareil de test. Ce n’est pas vrai pour l’exécution d’une application sur simulateur.
  • Seulement 3 certificats de Distribution indispensable à la soumission d’applications sur l’AppStore peuvent coexister en même temps dans un environnement Apple Developer Program.

Eclaircissement sur les identités de certification :

Les certificats de Distribution/Développement peuvent être assimilés à des identités de certification. Faire la demande de création d’un certificat de Distribution lie la personne qui en fait la demande avec ledit certificat, elle est la seule personne à disposer du couple clé privée/clé publique associé au certificat. Le certificat (qui intègre la clé publique) et sa clé privée viennent s'enregistrer de manière sécurisée dans l’application macOS Keychain Access. Cette clé privée est indispensable à la signature d’applications aussi bien en Développement qu’en Distribution.
Il n’y a pas de limite dans la création d’identités de certification en Développement, cependant il existe une limitation à 3 identités de certification en Distribution. Ce qui pose problème dès lors que l’équipe de développement évolue et intègre de nouvelles personnes.

Voici un petit rappel des éléments nécessaires à la signature d’une application iOS :

  • Un App ID (ou bundleID), c’est le nom unique de l’application, il respecte le plus souvent la notation en reverse DNS tel que “fr.ippon.myapp”.
  • Une liste d’appareils autorisés, en Développement uniquement. C’est une liste d’UDID d’appareils monitorables depuis l’interface web de votre Apple Developer Program.
  • L’identité de certification du développeur, sa clé publique est contenue dans le certificat lui même et stockée sur l’environnement Apple Developer. Sa clé privée est uniquement stockée dans son application KeyChain Access lors de la demande de ce certificat.
  • Un Provisionning Profile qui va faire le lien entre ces 3 éléments.

image2

Pour pallier cette limitation de 3 identités de certification de Distribution et éviter de faire transiter les clefs privées entre développeurs, et également pour faciliter l’intégration de nouveaux développeurs dans l’équipe mobile iOS, nous avons mis en place un espace centralisé où sont stockées et chiffrées les identités de certification associées cette fois-ci à une seule méta-identité de certification partagée entre tous les développeurs. C’est l’identité “Pole-Mobilite”.

Cet espace centralisé se présente sous la forme d’un repository Git dans lequel chaque branche représente un client et où la branche master représente le compte Apple Developer d’Ippon. La mise en place de cette vision centralisée est simplifiée par l’outil Match qui fait partie de la suite d’outils Fastlane utilisée aussi dans le processus d’automatisation des tâches de la CD.
Plus d’informations sur cette approche sont disponibles à cette adresse :

https://codesigning.guide/

Cette solution nous permet également de s’interfacer avec les comptes de développement Apple des clients avec un seul point d’entrée. Ce point d’entrée est l’identité “Pole-Mobilite” et nous permet de développer et de publier des applications depuis leurs environnements Apple.

L’intégration de nouveaux développeurs iOS

Grâce à Match, les étapes d’intégration d’un nouveau collaborateur iOS sont simples.

  1. Il demande l’accès au repository Git contenant les informations de code signing.
  2. Il installe Fastlane et Match, le README du projet est clair et indique comment y parvenir.
  3. Il se positionne sur la branche de son client, ou la branche master s’il travaille sur des projets internes IPPON.
  4. Il télécharge et installe sur son poste de travail les identités de certification et les provisioning profiles associés en une seule commande suivante: $ fastlane match development.
    L’argument development peut être remplacé par appstore dans le cas ou une publication manuelle en Distribution est nécessaire.
  5. Il est prêt à builder et lancer une application sur un appareil de test autorisé.

Présentation de Fabric et Fabric Beta

Fabric, à l’origine Crashlytics puis acheté par Twitter et renommé Fabric, est un ensemble d’outils orientés mobile, incluant:

  • Crashlytics - Suivi des crashs et corrections
  • Analytics - Suivi des installations et activités des utilisateurs
  • Beta - Mise à disposition de livrables de tests

Fabric se présente sous la forme d’un SDK à intégrer dans l’application et dispose d’une interface web de monitoring permettant la consultation d’informations relatives à l’activité de l’application des derniers jours/mois.

Dashboard Fabric
image7

Dans le contexte de cet article, nous nous attarderons plus sur l’outils de mise à disposition de versions de test des applications mobiles, appelé Fabric Beta.

GitLab-Runner, l’externalisation des jobs mobiles sur un poste dédié

GitLab met à disposition le projet open source GitLab Runner qui va nous permettre d’exécuter des jobs sur une machine sous macOS dans le but de pouvoir builder, tester et déployer des applications iOS, mais également des applications Android ou des applications Hybrides.

Le nombre de projets mobiles étant grandissant, nous avons décidé de dédier un ancien MacBook Pro jusqu’alors inutilisé à l’exécution de nos tâches d’intégration continue et de déploiement continu. Pour ne pas redéfinir un Runner à chaque nouveau projet, nous avons configuré le MacBook comme étant un Shared Runner, il a la particularité de pouvoir exécuter des jobs sur plusieurs projets mobiles à la fois.

Mise en place d’un shared-runner

Nous avons ajouté à ce Shared Runner un ensemble de tags à destination des jobs mobiles. Ces tags sont “mobile”, “iOS” et “Android”. En plus de ces tags, ce Shared Runner n’est pas autorisé à exécuter de jobs non taggués avec l’une des trois valeurs précédentes. Ce qui évite que la machine dédié aux builds mobiles ne soit utilisée par d’autres projets malencontreusement.

Une fois configuré, le shared Runner est visible depuis l’interface web de GitLab.

image8

L’automatisation avec GitLab-CI/CD

GitLab-CI/CD permet de définir des pipelines d’exécution déclenchés à partir d'événements sur votre repository GitLab tel que des pushs, des merges, des créations de tags, … ou des événements externes à votre repository.
La configuration se fait depuis le fichier gitlab-ci.yml, il est à créer s’il n’existe pas encore, à la racine de votre repository GitLab.

Comme vu précédemment au paragraphe “Intégration de la CD avec GitFlow” ce qui nous intéresse c’est de définir les étapes du pipeline build, test et deploy. On va définir les étapes que vont respecter nos pipelines de cette manière:

stages:
  - build
  - test
  - deploy

La définition de ces 3 stages est ordonnée et les jobs affectés à ces étapes vont s’exécuter de manière séquentielle, sauf dans le cas où plusieurs jobs sont affectés au même stage, ils seront exécutés en parallèle.

Dans notre contexte il nous faut un pipeline debug basé sur la branche develop et un pipeline release basé sur la branche master.

Voici un tableau récapitulatif des jobs avec leur stages correspondants :

Stages Jobs - Debug Jobs - Release Jobs - Nightly build
build build_dev build_release build_dev
test unit_test unit_test, ui_test unit_test, ui_test
deploy deploy_beta deploy_release deploy_beta


Infos complémentaires :
Google a annoncé fin 2017 que les applications soumises sur le Play Store devront être buildées en 64bits en prévention de l’arrivée d’appareils compatibles uniquement 64bits. Cette contrainte s’applique à partir du mois d'août 2019. Alors que la majorité des appareils 64bits actuels supportent également des applications 32bits. Ca ne sera plus le cas sur les appareils Android à venir. De son côté Apple ne supporte plus que les applications 64bits sur l’App Store. Depuis l’arrivée d’iOS 11 compatible avec une flotte d’appareils 64bits uniquement.
Les tests unitaires et d’UI nécessitent un build en architectures i386 (simulateur 32bits) ou x86_64 (simulateurs 64bits) pour être exécutés sur un simulateur directement sur la machine dédiée à l’intégration continue mobile. Contrairement aux applications à destination d’appareils physiques qui eux sont buildés en architectures armv7 (équivalent 32 bits qui tend à disparaître) ou arm64. L’ordre des étapes de build et de test n’est donc pas déterminant puisque le test ne va pas utiliser l’application buildée à l’étape de build, mais il va lancer son propre build en architecture i386 ou x86_64.


Commençons par le pipeline debug dont les jobs sont définis de cette manière :

build_dev:
  tags:
    - iOS #Ce job sera exécuté par un runner compatible iOS
  stage: build #Ce job est affecté à l'étape "build" du pipeline
  script:
    - bundle exec fastlane ios build_dev
  only:
    - develop #Les updates sur develop déclenchent le job
  artifacts: #Le contenu de "paths" sera uploadé et téléchargeable sur GitLab
    paths:
      - fastlane/outputs

unit_test:
  tags:
    - iOS
  stage: test
  script:
    - bundle exec fastlane ios unit_test
  only:
    - branches #Seules les actions sur les branches déclenchent ce job

deploy_beta:
  tags:
    - iOS
  stage: deploy
  script:
    - bundle exec fastlane ios deploy_beta
  only:
    - develop

On se rend compte que la définition des jobs est très simple, répétitive et concise.

Voici la définition des jobs du pipeline release :

build_release:
  tags:
    - iOS
  stage: build
  script:
    - bundle exec fastlane ios build_release
  only:
    - master
  artifacts:
    paths:
      - fastlane/outputs

ui_test:
  tags:
    - iOS
  stage: test
  script:
    - bundle exec fastlane ios ui_test
  only:
    - schedules
    - master

Ce job est pris en compte uniquement sur la modification de git refs sur la branche master ou par déclenchement programmé (pipeline nightly build).

deploy_release:
  tags:
    - iOS
  stage: deploy
  script:
    - bundle exec fastlane ios deploy_release
  only:
    - master
  when: manual

La seule différence marquante avec le pipeline debug concerne le fait que le job deploy_release est un job manuel, il n’est pas toujours nécessaire de mettre à disposition une nouvelle version de l’application. Pour cette raison, cette étape ne s’exécute pas automatiquement et permet à l’équipe de synchroniser une release avec un email de fin de sprint/livraison.

Il ne reste plus qu’à s’intéresser à la balise script dont le contenu pour chacun des jobs est le même, c’est un appel une fonction Fastlane - appelée Lane - définie spécialement pour chacun des jobs.

Fastlane simplifie l’automatisation mobile

Fastlane est un ensemble d’outils créés par Felix Krause, écrits en Ruby et Open Source. Ces outils facilitent l’automatisation des tâches utiles aux développements mobiles. Ces outils se présentent sous la forme de petites fonctions simples qui servent un but unique, et sont appelées des Actions.

🔧Fun Fact : Comment est né Fastlane ?

Fastlane propose un grand nombre d’Actions, poussées et maintenues par les contributeurs majeurs de Fastlane ou par la communauté. Elles permettent d’automatiser un grand nombre de tâches en paramétrant les Actions en fonction de votre projet mobile.
Fastlane propose aussi bien des Actions pour Android que pour iOS, bien qu’initialement créé pour pallier la verbosité et la complexité d’automatiser des tâches de build, code signing et soumission d’apps sur iTunnesConnect pour iOS.

Builder sans Fastlane :

xcodebuild clean archive -archivePath build/MyApp \
                         -scheme MyApp
xcodebuild -exportArchive \
           -exportFormat ipa \
           -archivePath "build/MyApp.xcarchive" \
           -exportPath "build/MyApp.ipa" \
           -exportProvisioningProfile "ProvisioningProfileName"

Builder avec Fastlane :

gym(clean: true,
    scheme: "myScheme",
    export_method: "development")

Aperçu du pipeline debug avec Fastlane

Prenons l’exemple du pipeline debug détaillé dans les étapes précédentes et plus particulièrement les Lanes build_dev, test, et deploy_beta déclarées dans la balise script de chacun des jobs build, test et deploy du fichier gitlab-ci.yml.

Pour se lancer avec Fastlane, rien de plus simple, tout commence avec la commande suivante à la racine de votre projet :

$ fastlane init

Cela va vous créer un dossier fastlane dans lequel vous trouverez entre autres un fichier “Fastfile”. C’est dans ce fichier que l’on va définir nos Lanes build_dev, test, et deploy_beta. Chaque Lane va contenir des Actions qui vont être exécutées séquentiellement.
Voici le début du fichier Fastfile de notre projet.

# This is the minimum version number required.
# Update this, if you use features of a newer version
fastlane_version "2.69.2"

default_platform :ios

# Variables
$scheme_name = "MyProjectScheme"
$unit_test_scheme_name = "MyUnitTestScheme"
$ui_test_scheme_name = "MyUITestScheme"
$output_directory = "fastlane/outputs"
$app_name_debug = "#{$scheme_name}-debug"
$app_name_release = "#{$scheme_name}-release"
$app_identifier = "MyAppID"

Dans un premier temps, on externalise tout ce qui peut être réutilisé. La notion de plateforme permet de définir des Lanes pour différents contextes au seins d’un même fichier. On peut par exemple avoir une plateforme applicative - ici “ios” - et une plateforme “system” dans le but de séparer clairement les intentions entre l’automatisation de tâches applicatives et le lancement de tâches systèmes par exemple.
Ce concept de plateforme est retrouvé dans les balises scripts du fichier gitlab-ci.yml

script:
    - bundle exec fastlane ios build_dev

Cette commande exécute la Lane build_dev de la plateforme ios.
L’utilisation de “bundle exec” pour exécuter les commandes Fastlane vient du fait que l’on utilise Bundler comme gestionnaire de dépendances grâce à un fichier Gemfile dont le contenu va être :

source "https://rubygems.org"

gem "cocoapods"
gem "fastlane"

Cette petite configuration va permettre au Shared Runner GitLab d’installer les dépendances du projet mobile incluant Fastlane pour l’automatisation des tâches et Cocoapods qui est un gestionnaire de dépendances des projets iOS.
On ajoute cette instruction dans la balise “before_script” du fichier de configuration gitlab-ci.yml de manière à ce que le Runner Gitlab puisse installer les dépendances si ce n’est pas déjà fait.

before_script:
  - sudo gem install bundler && bundle install

Basculons à nouveau sur notre fichier de configuration Fastlane dans le but de définir notre plateforme “ios” et d’intégrer les notifications Slack à notre pipeline.

platform :ios do

  before_all do
    ENV["SLACK_URL"] = "https://hooks.slack.com/services/XXXXX"
  end

  error do |lane, exception|
    slack(
      message: exception.to_s,
      success: false,
      # Output containing extended log output
      payload: { "Output" => exception.error_info.to_s }
    )
  end
end

Fastlane dispose de l’Action slack qui permet de poster des messages sur Slack en lui fournissant l’URL du Web Hook Slack vers un channel dédié. Nous utilisons un channel par application, préfixé par le nom de la plateforme mobile (iOS ou Android) de l’application en question.

Pour centraliser la gestion des erreurs, celles-ci sont détectées pour chacune des Lanes déclarées et l’Action slack poste un message sur le channel de l’application dont la Lane est en échec. Cette gestion d’erreurs par Lane nous permet d’avoir un message d’erreur explicite avec la raison de l’erreur, la Lane en échec ainsi que les détails du dernier Git commit sur lequel se base le pipeline. Voici un aperçu d’un message d’erreur Slack posté par la CD depuis Fastlane.

image9

Voici la définition de la Lane build_dev :

  lane :build_dev do
    install_pods() #1
    increment_build_number #2
    match( #3
      git_url: "https://gitlab.ippon.fr/myGroupe/myMatchProject.git",
      git_branch: "master",
      username: "GitUserName",
      type: "development",
      app_identifier: $app_identifier,
      readonly: "true"
    )
    gym( #4
      clean: true,
      scheme: $scheme_name,
      configuration: "Debug",
      export_method: "development",
      output_directory: $output_directory,
      output_name: $app_name_debug
    )
    #Clean then commit build number bump
    clean_build_artifacts(exclude_pattern: ".*\.ipa") #5
    commit_push_build_number( #6
      message: "Version bump",
      branch: "develop"
    )
    slack( #7
      message: "App successfully built with debug profile",
      success: true
    )
  end

  1. Installation des dépendances logicielles du projet via CocoaPods.
  2. On incrémente le build number dans le but d’exporter un livrable unique et reconnaissable sur Fabric Beta, qui ne va pas écraser l’ancienne version lors de l’upload.
  3. L’action match détaillée dans le paragraphe "La gestion des identités de code signing" telle qu’utilisée ici va synchroniser les informations de signature en Développement sur la machine qui héberge le GitLab Runner dans le but d’être en mesure de produire une application signée avec une identité de certification et un provisionning profile de développement (embarquant une liste d’appareils autorisés à installer et lancer l’application).
  4. On utilise l’Action gym qui lance un build de l’application avec une configuration de type “Debug” dans le but d’exporter une archive applicative signée avec les informations de signature synchronisées à l’étape 3.
  5. On procède à un clean des éventuelles modifications induites par le build en excluant l’application générée à l’étape précédente pour que le Shared Runner puisse uploader cette application en tant qu’”artifact” GitLab, ce qui rend l’application téléchargeable directement depuis l’interface web de GitLab pour le pipeline en question.
  6. L’instruction commit_push_build_number n’est pas une Action Fastlane. C’est une méthode locale qui regroupe plusieurs Actions Fastlane dans le but d’être réutilisée notamment par la Lane build_release. Les Actions sont commit_version_bump et push_to_git_remote. Dans lesquelles on force le commit message à contenir la chaîne de caractères [skip-ci]. Celle-ci informe Gitlab de ne pas déclencher de pipeline suite à ce commit et donc éviter la boucle infinie de déclenchements de pipelines.
  7. On notifie Slack que la Lane s’est déroulée avec succès.

Passons maintenant à la définition de la Lane unit_test beaucoup moins dense que la précédente :

lane :unit_test do
  scan(
    scheme: $unit_test_scheme_name,
    output_directory: "fastlane/tests",
    clean: true
  )
end

L’Action Scan permet de déclencher des tests unitaires et des tests d’intégration (UI tests) d’applications iOS ou macOS. Le web hook Slack étant configuré dans le fichier Fastfile, Scan va également poster un micro rapport dans Slack une fois les tests effectués dont voici un exemple:

image1

Pour terminer, voici la définition de la Lane deploy_beta qui a pour but de mettre à disposition l’application précédemment buildée par la lane build_dev pour les tests et recettes internes sur Fabric Beta :

lane :deploy_beta  do
    #Upload to Fabric beta
    crashlytics( #1
      api_token: "Your Fabric API TOKEN",
      build_secret: "Your Fabric BUILD SECRET",
      ipa_path: "./#{$output_directory}/#{$app_name_debug}.ipa"
    )
    slack( #2
      message: "🎉 App successfully uploaded to Fabric beta",
	success: true
    )
end

  1. L’Action crashlytics permet d’uploader un build sur le service Bêta de Fabric. Les valeurs de api_token et build_secret sont disponibles dans votre espace d’administration Fabric. Tandis que l’ipa_path pointe vers celui généré dans la Lane précédente build_dev.
  2. On notifie ensuite le channel Slack que la Lane s’est exécutée avec succès.

Depuis l’interface de Fabric, on peut voir la liste de chaque version de l’application uploadée et prête à être testée.

image10

En regardant bien on constate que ces builds sont tous uploadés quasiment à la même heure, 3h25 du matin. C’est le résultat du nightly build sur ce projet. C’est une action programmée chaque nuit pour lancer le pipeline debug sur la branch develop. GitLab propose via son interface web de configurer très simplement ce genre de tâches ou “schedules” depuis le menu “CI / CD” > “Schedules” de votre repository. L’application qui a passé les tests unitaires et de non-régression est disponible pour des tests internes tous les matins. L’équipe de développement peut partir sereinement sur l’ajout de nouvelles fonctionnalités lorsque le rapport du nightly build est en succès chaque matin.

Pour aller plus loin…

L’intégration continue appliquée aux développements mobiles permet de maintenir un niveau de qualité élevé, d’externaliser et d’automatiser des tâches répétitives qui ne sont plus à la charge des développeurs.
La solution d’intégration continue de GitLab s’adapte très bien aux développements mobiles. Sa prise en main est facile, extensible et très bien documentée. Cet article présente un exemple assez simple pour appréhender les différents outils présentés plus facilement. Mais on imagine très bien l’ajout de nouveaux indices de qualité de code comme une intégration Sonar au projet, la génération d’un changelog par version de l’application basée sur les messages de commit. Ou encore plus orienté mobile avec la génération de screenshots marketings automatisés et publiés sur la fiche du Store de l’application...

Sources :

GitLab shared-runner setup :
https://docs.gitlab.com/ee/ci/runners/
GitLab-CI/CD Yaml file configuration :
https://docs.gitlab.com/ee/ci/yaml/README.html
Fabric web site :
https://get.fabric.io/
Fastlane web site :
https://docs.fastlane.tools/
Introduction aux tests unitaires Android avec Espresso
GHOST_URL/2017/09/11/tests-fonctionnels-applications-android-introduction-a-espresso/


  1. Une application en debug est une application qui permet au développeur d'associer un débuggeur au process de l'application. De plus le code de cette application n'est pas obfusqué et les options de compilation ne sont pas optimales. ↩︎

  2. Une application en release est une application dont le code est obfusqué et optimisé à la compilation. Son exécution est plus rapide et l'exécutable produit est plus léger qu'en debug. ↩︎

  3. https://github.com/facebook/xcbuild nous n’avons pas testé cette solution suite à l’utilisation d’un Mac dédié. ↩︎

  4. https://github.com/saucelabs/isign permet de signer les applications sous Linux mais nécessitent une étape manuelle supplémentaire pour exporter les certificats de développement. ↩︎