Tutoriel pour apprendre la mise en place d'un Workflow Continuous Delivery avec Fastlane pour IOS ou Android

Ilan Benichou

Fastlane est un outil open source qui permet de faire du Continuous Delivery sous IOS et Android. Ce tutoriel a pour objectif de vous apprendre la mise en place d'un Workflow Continuous Delivery avec Fastlane.

Commentez

- Titre : Tutoriel pour apprendre la mise en place d'un Workflow Continuous Delivery avec Fastlane pour IOS ou Android

- Auteur : Ilan Benichou

- Parution : 13 décembre 2017

- Licence : Copyright ® 2017 Ilan Benichou. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Qu'est-ce que « Fastlane »

- II. Premiers pas

- II-A. Installation

- II-B. Initialisation

- III. Match

- III-A. Configuration

- III-B. Paramétrer Xcode

- IV. PEM

- V. Scan

- VI. Snapshot - Frameit

- VI-A. Snapshot

- VI-B. Frameit

- VII. Gym

- VIII. Pilot

- IX. Deliver

- X. Conclusion

- XI. Remerciements

I - Qu'est-ce que « Fastlane »

Fastlane est un outil open source qui permet de faire du Continuous Delivery sous IOS et Android. Il permet d'automatiser un certain nombre de tâches fastidieuses comme gérer les screenshots, les certificats, déployer votre application.

Vous l'avez compris, cet outil va changer votre vie.

Voici la liste des bibliothèques à disposition avec Fastlane :

- Deliver : télécharge des captures d'écran, des métadonnées et votre application sur l'App Store en utilisant une seule commande ;

- Snapshot : automatise la prise des captures d'écran localisées de votre application iOS sur tous les périphériques ;

- Frameit : place rapidement vos captures d'écran dans les cadres appropriés (iphone, ipad…) ;

- Pem : génère et renouvelle automatiquement vos profils de notification push ;

- Sigh : crée, renouvelle, télécharge et répare des profils de provisionnement ;

- Produce : crée de nouvelles applications iOS sur le portail développeur Apple et iTunes Connect avec les informations minimales requises ;

- Cert : crée automatiquement et conserve des certificats de signature ;

- Scan : facilite l'exécution des tests de votre application iOS et Mac sur un simulateur ou un périphérique connecté ;

- Gym : construit et crée un package de votre application ;

- Match : synchronise facilement vos certificats et profils de provisionnement à travers votre équipe en utilisant Git.

Se rajoute également à cette liste :

- Pilot: gère vos testeurs TestFlight à partir de votre terminal ;

- Boarding: permet la création d'une page d'inscription simple pour les testeurs bêta TestFlight :

Schéma de lanes fastlane

Personnellement, quand j'ai vu ce que Fastlane était capable de faire, j'ai limite versé une petite larmichette ! Fini le temps de tout faire à la main !

Note : J'ai créé un projet « bidon » avec différents écrans et tests pour cet article.

II - Premiers pas

II-A - Installation

Prérequis : ruby >= 2.0.

Je vous recommande de créer un Gemfile pour définir les éventuelles dépendances de Fastlane :

source "https://rubygems.org"

gem "fastlane"

$ bundle install

II-B - Initialisation

Lancer la commande suivante :

$ bundle exec fastlane init

On vous demandera votre Apple ID, mot de passe et dans mon cas un digit code via mon Iphone. Fastlane vous récapitulera vos informations dans Summary for produce et créera votre application dans Itunes Connect et Dev Center. Il générera également une configuration pour vous, en fonction des informations fournies :

Appfile: l'Appfile stocke des informations utiles qui sont utilisées dans toutes les bibliothèques Fastlane comme votre Apple ID ou le Bundle Identifier, pour déployer vos lanes (voies) plus rapidement, adaptées aux besoins de votre projet.

Par défaut, ce fichier ressemble à :

app_identifier "com.eleven.fastlane" # Bundle identifier de votre app
apple_id "obiwan@kenobi.com"  # Votre Apple adresse email

Si vous avez des identifiants différents pour Itunes Connect et Apple Developer, vous pouvez utiliser le code suivant :

1. app_identifier "tools.fastlane.app"       # Bundle identifier de votre app

3. apple_dev_portal_id "portal@company.com"  # Votre Apple adresse email

4. itunes_connect_id "tunes@company.com"     # Votre Itunes Connect adresse email

6. team_id "Q2CBPJ58CA" # Le team ID du Developer Portal

7. itc_team_id "18742801" # Le team ID du iTunes Connect

Si vous souhaitez accéder à ces valeurs :

identifier = CredentialsManager::AppfileConfig.try_fetch_value(:app_identifier)
team_id = CredentialsManager::AppfileConfig.try_fetch_value(:team_id)

Fastfile: fichier ruby qui définit toutes vos lanes. Une lane est un ensemble d'instructions que vous souhaitez faire exécuter par Fastlane :

1. # Fastfile - J'ai nettoyé le fichier et j'ai créé une lane test

2. fastlane_version "2.39.2"

4. default_platform :ios

6. platform :ios do

7. desc "Runs all the tests"

8. lane :test do

9. scan # lib Fastlane qui lance vos tests.

10. end

11. end

Si vous lancez un :

$ bundle exec fastlane test

il lance alors tous les unit/ui tests de votre projet. Bien évidemment, Scan peut générer des rapports au format HTML, JSON et JUnit :

Magique non ?

Nous allons partir du principe que je travaille en collaboration avec plein de devs mobiles sur ma super application. Le problème se pose au niveau des certificats et profils de provisionnement. Heureusement Fastlane nous met à disposition Match.

III - Match

Match implémente le concept de codesigning guide. Il permet de créer tous vos certificats et profils de provisionnement dans un compte Git distinct. Chaque membre de l'équipe ayant accès au repo peut utiliser ces credentials pour la signature de code.

Match répare également automatiquement les credentials brisés et expirés. Ceci est bien évidemment sécurisé.

Note : l'implémentation de match vous oblige à révoquer vos certificats existants.

Si vous ne voulez pas révoquer vos certificats existants, mais souhaitez toujours une configuration automatisée, cert et sigh sont là pour vous.

- Cert : veillera à ce que vous ayez un certificat valide et sa clé privée installée sur votre machine.

- Sigh : veillera à ce que vous ayez un provisioning profile valide installé sur votre machine, qui correspond au certificat installé.

III-A - Configuration

$ bundle exec fastlane match init

Cela vous demandera d'entrer l'URL de votre repo Git. Vous pouvez aussi utiliser un lien git@ si votre machine peut se connecter en SSH sur ce repo.

Note : cette commande ne lit ou ne modifie pas les certificats ou profils.

Cela va créer un fichier Matchfile qui ressemble à :

1. git_url "https://gitlab.com/prims47/fastlane_article.git"

3. type "development" # Le type par défaut, il peut être appstore, adhoc, enterprise ou development

5. # app_identifier ["tools.fastlane.app", "tools.fastlane.app2"]

6. # username "user@fastlane.tools" # Votre Apple Developer Portal username

Match prend également en charge le stockage des certificats de plusieurs équipes dans un repo, en utilisant des branches. Si vous travaillez dans plusieurs équipes, assurez-vous de définir le paramètre git_branch à une valeur unique par équipe. De là, la correspondance créera et utilisera automatiquement la branche spécifiée pour vous :

1. #Fastfile

2. match(git_branch: "team1", username: "user@team1.com")

3. match(git_branch: "team2", username: "user@team2.com")

Avant d'exécuter match pour la première fois, vous devriez envisager de supprimer vos profils et certificats existants à l'aide de la commande match nuke.

Exécuter match :

$ bundle exec fastlane match appstore

Cela créera un nouveau certificat et un profil de provisionnement (si nécessaire) et les stockera dans votre repo Git. Si vous avez précédemment exécuté match, il installera automatiquement les profils existants à partir du repo Git.

Les provisioning profiles sont installés dans ~ / Library / MobileDevice / Provisioning Profiles alors que les certificats et les clés privées sont installés dans votre Keychain.

Comment faire si vous devez ajouter un nouveau device ?

Non, on ne va pas se connecter et le faire à la main ! On va utiliser l'action register_devices en combinaison avec match :

1. lane :recette do

2. register_devices(devices_file: "./devices.txt")

3. match(type: "adhoc", force_for_new_devices: true)

4. end

1. #devices.txt

2. Device ID    Device Name

3. A123456789012345678901234567890123456789    NAME1

4. B123456789012345678901234567890123456789    NAME2

En utilisant le paramètre force_for_new_devices, match va vérifier si le nombre de devices a changé depuis la dernière exécution et régénère automatiquement le profil de provisionnement si nécessaire.

Vous pouvez utiliser aussi force: true pour générer le profil de provisionnement à chaque exécution.

III-B - Paramétrer Xcode

Avec Xcode 8 vous pouvez définir un profil de provisionnement pour chaque target au lieu d'un provisioning profile UUID. En faisant ça, XCode sélectionne automatiquement le dernier provisioning profile correspondant à son nom. De cette manière, vous n'aurez pas à mettre à jour Xcode à chaque fois que vous générez un profil de provisionnement (ex. : quand vous ajoutez un nouveau device).

Vous pouvez spécifier quel profil de provisionnement utiliser dans General tab après avoir désactivé Automatically manage signing :

On vient de voir avec quelle facilité on gère les certificats et profils de provisionnement. Maintenant on va s'attaquer au push notification profile.

IV - PEM

Si vous avez lu mon précédent article Envoyer des push notifications via Amazon SNS en Swift 3, vous avez vite compris que c'était super « galère » de faire ceci à la main.

Mais ça c'était avant !

Pem est venu me sauver de cette tâche fastidieuse !

pem permet de :

- créer un nouveau signing request ;

- créer un certificat push ;

- télécharger le certificat ;

- générer le nouveau fichier .pem dans le dossier courant.

pem ne permet pas de :

- couvrir la partie upload sur votre serveur/service ;

- révoquer vos certificats existants ;

- télécharger n'importe quel certificat existant, car la clé privée n'est disponible que sur la machine sur laquelle elle a été créée.

Si vous avez déjà activé un certificat push pendant au moins 30 jours, pem ne créera pas de nouveau certificat. Si vous voulez quand même en créer un, vous pouvez utiliser l'option force :

1. #Fastfile

2. desc "Generate Push Certificate"

3. lane :pushCertificat do

4. #pem(new_profile: Proc.new do |value|

5. # Code pour télécharger votre fichier pem sur votre serveur/service.

6. #end)

8. pem(generate_p12: true, save_private_key: true, p12_password: "1234",

9. pem_name: "fastlane_pem_file", output_path: "/Users/prims47/Desktop/blog/Fastlane/push_certificates", development: true

10. )

11. end

$ bundle exec fastlane pushCertificat

Et hop, un jeu d'enfant !

V - Scan

Comme vous l'avez vu au début de l'article, scan permet de lancer tous les tests (sur simulateur ou device) de votre projet. Dans le projet que je me suis créé, j'ai bien évidemment ajouté quelques tests afin de vous montrer ce qu'on peut faire.

Première chose à faire c'est d'initialiser le fichier Scanfile afin de configurer les paramètres par défaut :

$ bundle exec fastlane scan init

1. #Scanfile

3. #workspace "FastlaneArticle.xcodeproj"

4. scheme "FastlaneArticle"

5. clean true #Clean le projet à chaque exécution

6. code_coverage true

7. #slack_only_on_failure true

9. #Vous pouvez spécifier sur quel iphone ou ipad vous souhaitez lancer les tests.

10. #devices [

11. #  "iPhone 6s",

12. #  "iPhone 7"

13. #]"]

Une bonne pratique est de notifier Slack lorsque les tests sont failed. J'ai donc ajouté dans mon fichier Fastfile la configuration Slack :

1. #...

3. platform :ios do

4. before_all do

5. ENV["SLACK_URL"] = "https://hooks.slack.com/services/XXXXXX" # URL Webhook créé via Slack.

6. end

8. desc "Runs all the tests"

9. lane :test do

10. scan()

11. end

13. #...

14. end

Résultat sur Slack :

VI - Snapshot - Frameit

Si vous devez manuellement créer des screenshots pour 20 langues x 6 devices x 5 screenshots (car 5 écrans différents) = 600 screenshots. On voit tout de suite la quantité de travail énorme que demandent les screenshots.

Les screenshots sont une partie importante, car cela influence beaucoup l'utilisateur sur votre application.

Fastlane nous met à disposition deux bibliothèques géniales, snapshot et frameit :

- Snapshot : comme son nom l'indique, cela génère localement les screenshots pour différents devices et langues. On verra par la suite comment les uploader en utilisant deliver ;

- Frameit : permet de mettre votre screenshot créé par snapshot dans un cadre de périphérique tel qu'un iphone, ipad.

VI-A - Snapshot

Installation de snapshot :

$ bundle exec fastlane snapshot init

Cette commande va créer deux fichiers :

- SnapshotHelper.swift : helper pour effectuer les screenshots ;

- Snapfile : pour configurer snapshot.

Il faut tout d'abord ajouter le fichier SnapshotHelper.swift à notre target d'UI Tests. Puis dans notre classe d'ui test, dans la méthode setUp, il faut initialiser snapshot :

1. override func setUp() {

2. super.setUp()

3. // In UI tests it is usually best to stop immediately when a failure occurs.

4. continueAfterFailure = false

6. let app = XCUIApplication()

7. setupSnapshot(app)

8. app.launch()

9. }

Dans chaque test il faut faire appel à la méthode snapsot("Nom du screen") :

1. func testCarList() {

2. let app = XCUIApplication()

4. //Take screenshot

5. snapshot("01CarList")

6. }

8. func testCarDetail() {

9. let app = XCUIApplication()

10. app.collectionViews.cells.otherElements.containing(.staticText, identifier:"Dodge").children(matching: .other).element.forceTap()

12. //Take screenshot

13. snapshot("02CarDetail")

14. }

Attention !

Pour une raison étrange sur certains devices (dans mon cas iphone 5) avec certaines langues le tap() ne marche pas. Il faut créer une méthode qui va forcer celui-ci :

1. extension XCUIElement {

2. func forceTap() {

3. if self.isHittable {

4. self.tap()

5. }

7. let coordinate: XCUICoordinate = self.coordinate(withNormalizedOffset:  CGVector(dx:0.0, dy:0.0))

8. coordinate.tap()

9. }

10. }

Configuration de votre Snapfile :

1. # Snapfile

3. # Liste des devices sur lesquels je souhaite avoir des screenshots

4. devices([

5. "iPhone 5s",

6. "iPhone 6",

7. "iPhone 6 Plus",

8. "iPhone 7",

9. "iPhone 7 Plus"

10. ])

12. # En anglais et en français

13. languages([

14. "en-US",

15. "fr-FR"

16. ])

18. scheme "FastlaneArticle"

20. # Super important sinon vous allez avoir une erreur

21. project "../FastlaneArticle.xcodeproj"

23. # Plaçons-les dans ./screenshots

24. output_directory "./screenshots"

26. # Et avant chaque exécution, on nettoie

27. clear_previous_screenshots true

Lancer la commande :

$ bundle exec fastlane snapshot

Bon c'est le moment d'aller prendre un café ou de manger une pomme ! (Oui les pommes c'est la vie !)

Quinze minutes plus tard…

Fastlane vous crée une page HTML récapitulant tous les devices par langue.

Cool non ?

Vous pouvez fournir des arguments supplémentaires à votre application lors du lancement. En effet, vous pouvez ajouter des valeurs à votre UserDefaults.standard :

1. launch_arguments([

2. "-firstName Hatem -lastName Ben Arfa"

3. ])

name.text = UserDefaults.standard.string(forKey: "firstName")
// name.text = "Hatem"

Fastlane inclut FASTLANE_SNAPSHOT, qui permet de définir temporairement un UserDefaults.standard. Vous pouvez l'utiliser pour détecter quand l'application est exécutée par Snapshot.

1. if UserDefaults.standard.bool(forKey: "FASTLANE_SNAPSHOT") {

2. // Vérifier si vous utilisez le mode snapshot

3. }

Bon, maintenant qu'on a nos beaux screenshots, on va ajouter de beaux cadres non ? Allez c'est parti.

VI-B - Frameit

Prérequis

Pour utiliser Frameit, il faut avoir sur votre machine ImageMagick.

Sur mac, il suffit de faire :

$ brew install imagemagick

Vous pouvez dès à présent essayer ce petit bijou via :

# Exécutez cette commande dans votre dossier où se situent les screenshots.
$ bundle exec fastlane frameit

Vous avez normalement de nouvelles images qui sont arrivées telles que :

Et si vous avez envie d'avoir l'iphone rose on fait comment ?

1. # Tout d'abord, mettez à jour votre liste de frames

2. $ bundle exec fastlane frameit download_frames

4. $ bundle exec fastlane frameit rose_gold

Vous trouverez la liste des frames ici.

Bon, c'est pas mal, mais on peut encore faire mieux. Comment ? En ajoutant un titre à ce beau screenshot.

Dans la version 2.0 de Frameit, vous pouvez maintenant ajouter un fond custom, un titre et des couleurs à vos screenshots.

Créer un fichier Framefile.json dans le dossier screenshots :

1. {

2. "device_frame_version": "latest",

3. "default": {

4. "title": {

5. "color": "#545454"

6. },

7. "background": "./background.jpg", // Ajoute un background

8. "padding": 50,

9. "show_complete_frame": true, // Rétrécit le device et le cadre afin de tout afficher

10. "stack_title" : false // Spécifie si frameit doit afficher le mot-clé au-dessus du titre lorsque le mot-clé et le titre sont définis.

11. },

12. "data": [

13. {

14. "filter": "01CarList", // Si votre screenshot se nomme Iphone iPhone6-01CarList-*.png alors il utilisera ses paramètres

15. "title": {

16. "text": "Mes voitures",

17. "padding": 100,

18. "color": "#d21559"

19. }

20. },

21. {

22. "filter": "02CarDetail",

23. "title": {

24. "text": "Ma mustang",

25. "padding": 100

26. }

27. }

28. ]

29. }

VII - Gym

Gym construit et crée des packages de vos applications iOS pour vous. Il génère un fichier ipa ou app.

Installation de gym afin de configurer les paramètres par défaut :

$ bundle exec fastlane gym init

Configuration de votre Gymfile

1. scheme "FastlaneArticle"

3. clean true

5. sdk "iphoneos10.3" # Le SDK qui devra être utilisé pour le build de l'application

7. output_directory "../build" # Chemin où nous allons stocker le fichier .ipa

8. output_name "Fastlane"

10. silent true # Cache toutes les informations qui ne sont pas nécessaires pendant le build

12. #configuration => Configuration utilisée pour le build. Par défaut la valeur est Release

Nous allons créer une lane pour construire notre application pour Apple TestFlight :

1. desc "Submits to Apple TestFlight"

2. lane :buildTestFlight do

3. match(app_identifier: "com.eleven.fastlane.debug", type: "appstore")

4. increment_build_number # Incrémente le chiffre du build de votre projet

5. gym(configuration: "Debug")

6. end

$ bundle exec fastlane buildTestFlight

Note : il se peut que vous ayez une erreur due au fait que vous n'avez pas de bundle identifié sur Itunes Connect. Pour remédier à ça, il faut juste en créer un via :

$ bundle exec fastlane produce -u MAIL -a com.eleven.fastlane.debug --skip_itc

Avouez que c'est plus facile que de le faire soi-même. Maintenant, on va voir comment envoyer notre fichier ipa pour le tester via TestFlight.

VIII - Pilot

Pilot permet de :

- uploader et distribuer vos builds ;

- ajouter et supprimer vos testeurs ;

- récupérer des informations sur les testeurs et les devices ;

- importer/exporter tous les testeurs disponibles.

Pour uploader un nouveau build vous pouvez exécuter la commande :

$ bundle exec fastlane pilot upload

Cela recherchera automatiquement un ipa dans votre répertoire courant et tentera de récupérer les informations d'identification de connexion à partir de votre configuration Fastlane.

Note : pensez bien à créer votre application sur Itunes Connect avant. (Ou utilisez la bibliothèque produce.)

Nous pouvons reprendre notre lane précédemment créée et y ajouter l'instruction pilot :

1. desc "Submits to Apple TestFlight"

2. lane :buildTestFlight do

3. match(app_identifier: "com.eleven-labs.testflight", type: "appstore")

4. increment_build_number # Incrémente le chiffre du build de votre projet

5. gym(configuration: "Debug", include_bitcode: true)

6. pilot(app_identifier: "com.eleven-labs.testflight", ipa: "../build/Fastlane.ipa")

7. end

$ bundle exec fastlane buildTestFlight

Note

1. Si comme moi, vous avez une double identification via votre téléphone, il suffit de se connecter ici et de générer un password dans la section Security > APP-SPECIFIC PASSWORDS ;

2. Vérifiez sur votre Itunes Connect le bundle identifier renseigné.

IX - Deliver

Il est temps de mettre en production votre application sur Itunes Connect :

# Comme d'habitude on initialise
$ bundle exec fastlane deliver init

# Deliverfile
username "yourItunesEmail@eleven-labs.com"

Il vous suffit dans votre lane d'ajouter l'instruction :

deliver(force: true) # Force: true pour skip le report HTML de vérification

Vous devriez avoir un nouveau dossier metadata qui vient d'apparaître.

À vous maintenant de mettre à jour ces metadata.

X - Conclusion

Si vous n'êtes pas tombé amoureux de Fastlane, je ne comprends pas, vous aimez souffrir. On voit très rapidement que ce petit bijou nous fait gagner un temps monstre et nous évite de faire une erreur humaine (l'avantage de l'automatisation). Après quelques tests et configurations, vous pouvez facilement reprendre vos scripts pour les réutiliser sur d'autres projets.

XI - Remerciements

Nous remercions Eleven Labs qui nous autorise à publier ce tutoriel.

Nous tenons également à remercier Winjerome pour la mise au gabarit et Claude LeLoup pour la relecture orthographique.