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

Image non disponible

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 Donner une note à l'article (5)

Article lu   fois.

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

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 :

Image non disponible
  • 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 :
Image non disponible
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 :

 
Sélectionnez
source "https://rubygems.org"

gem "fastlane"
 
Sélectionnez
$ bundle install

II-B. Initialisation

Lancer la commande suivante :

 
Sélectionnez
$ 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 :

Image non disponible

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 à :

 
Sélectionnez
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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
app_identifier "tools.fastlane.app"       # Bundle identifier de votre app

apple_dev_portal_id "portal@company.com"  # Votre Apple adresse email
itunes_connect_id "tunes@company.com"     # Votre Itunes Connect adresse email

team_id "Q2CBPJ58CA" # Le team ID du Developer Portal
itc_team_id "18742801" # Le team ID du iTunes Connect

Si vous souhaitez accéder à ces valeurs :

 
Sélectionnez
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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
# Fastfile - J'ai nettoyé le fichier et j'ai créé une lane test
fastlane_version "2.39.2"

default_platform :ios

platform :ios do
  desc "Runs all the tests"
  lane :test do
    scan # lib Fastlane qui lance vos tests.
  end
end

Si vous lancez un :

 
Sélectionnez
$ 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 :

Image non disponible

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

 
Sélectionnez
$ bundle exec fastlane match init
Image non disponible

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 à :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
git_url "https://gitlab.com/prims47/fastlane_article.git"

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

# app_identifier ["tools.fastlane.app", "tools.fastlane.app2"]
# 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 :

 
Sélectionnez
1.
2.
3.
#Fastfile
match(git_branch: "team1", username: "user@team1.com")
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 :

 
Sélectionnez
$ bundle exec fastlane match appstore
Image non disponible

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 :

 
Sélectionnez
1.
2.
3.
4.
lane :recette do
  register_devices(devices_file: "./devices.txt")
  match(type: "adhoc", force_for_new_devices: true)
end
 
Sélectionnez
1.
2.
3.
4.
#devices.txt
Device ID    Device Name
A123456789012345678901234567890123456789    NAME1
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 :

Image non disponible

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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
#Fastfile
desc "Generate Push Certificate"
  lane :pushCertificat do
    #pem(new_profile: Proc.new do |value|
        # Code pour télécharger votre fichier pem sur votre serveur/service.
      #end)

    pem(generate_p12: true, save_private_key: true, p12_password: "1234",
        pem_name: "fastlane_pem_file", output_path: "/Users/prims47/Desktop/blog/Fastlane/push_certificates", development: true
    )
end
 
Sélectionnez
$ bundle exec fastlane pushCertificat
Image non disponible

Et hop, un jeu d'enfant !

Image non disponible

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 :

 
Sélectionnez
$ bundle exec fastlane scan init
 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
#Scanfile

#workspace "FastlaneArticle.xcodeproj"
scheme "FastlaneArticle"
clean true #Clean le projet à chaque exécution
code_coverage true
#slack_only_on_failure true

#Vous pouvez spécifier sur quel iphone ou ipad vous souhaitez lancer les tests.
#devices [
#  "iPhone 6s",
#  "iPhone 7"
#]"]

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

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
#...

platform :ios do
  before_all do
    ENV["SLACK_URL"] = "https://hooks.slack.com/services/XXXXXX" # URL Webhook créé via Slack.
  end

  desc "Runs all the tests"
  lane :test do
    scan()
  end

  #...
end

Résultat sur Slack :

Image non disponible Image non disponible

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 :

 
Sélectionnez
$ 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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
override func setUp() {
    super.setUp()
    // In UI tests it is usually best to stop immediately when a failure occurs.
    continueAfterFailure = false

    let app = XCUIApplication()
    setupSnapshot(app)
    app.launch()
}

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

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
func testCarList() {
    let app = XCUIApplication()

    //Take screenshot
    snapshot("01CarList")
}

func testCarDetail() {
        let app = XCUIApplication()
        app.collectionViews.cells.otherElements.containing(.staticText, identifier:"Dodge").children(matching: .other).element.forceTap()

        //Take screenshot
        snapshot("02CarDetail")
    }

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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
extension XCUIElement {
    func forceTap() {
        if self.isHittable {
            self.tap()
        }

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

Configuration de votre Snapfile :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
# Snapfile

# Liste des devices sur lesquels je souhaite avoir des screenshots
devices([
  "iPhone 5s",
  "iPhone 6",
  "iPhone 6 Plus",
  "iPhone 7",
  "iPhone 7 Plus"
])

# En anglais et en français
languages([
  "en-US",
  "fr-FR"
])

scheme "FastlaneArticle"

# Super important sinon vous allez avoir une erreur
project "../FastlaneArticle.xcodeproj"

# Plaçons-les dans ./screenshots
output_directory "./screenshots"

# Et avant chaque exécution, on nettoie
clear_previous_screenshots true

Lancer la commande :

 
Sélectionnez
$ 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.

Image non disponible
Image non disponible

Cool non ?

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

 
Sélectionnez
1.
2.
3.
launch_arguments([
  "-firstName Hatem -lastName Ben Arfa"
])
 
Sélectionnez
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.

 
Sélectionnez
1.
2.
3.
if UserDefaults.standard.bool(forKey: "FASTLANE_SNAPSHOT") {
    // Vérifier si vous utilisez le mode snapshot
}

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 :

 
Sélectionnez
$ brew install imagemagick

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

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

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

Image non disponible

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

 
Sélectionnez
1.
2.
3.
4.
# Tout d'abord, mettez à jour votre liste de frames
$ bundle exec fastlane frameit download_frames

$ bundle exec fastlane frameit rose_gold
Image non disponible

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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
{
  "device_frame_version": "latest",
  "default": {
    "title": {
      "color": "#545454"
    },
    "background": "./background.jpg", // Ajoute un background
    "padding": 50,
    "show_complete_frame": true, // Rétrécit le device et le cadre afin de tout afficher
    "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.
  },
  "data": [
    {
      "filter": "01CarList", // Si votre screenshot se nomme Iphone iPhone6-01CarList-*.png alors il utilisera ses paramètres
      "title": {
        "text": "Mes voitures",
        "padding": 100,
        "color": "#d21559"
      }
    },
    {
      "filter": "02CarDetail",
      "title": {
        "text": "Ma mustang",
        "padding": 100
      }
    }
  ]
}

Image non disponible Image non disponible

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 :

 
Sélectionnez
$ bundle exec fastlane gym init

Configuration de votre Gymfile

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
scheme "FastlaneArticle"

clean true

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

output_directory "../build" # Chemin  nous allons stocker le fichier .ipa
output_name "Fastlane"

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

#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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
desc "Submits to Apple TestFlight"
  lane :buildTestFlight do
    match(app_identifier: "com.eleven.fastlane.debug", type: "appstore")
    increment_build_number # Incrémente le chiffre du build de votre projet
    gym(configuration: "Debug")
end
 
Sélectionnez
$ 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 :

 
Sélectionnez
$ 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 :

 
Sélectionnez
$ 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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
desc "Submits to Apple TestFlight"
  lane :buildTestFlight do
    match(app_identifier: "com.eleven-labs.testflight", type: "appstore")
    increment_build_number # Incrémente le chiffre du build de votre projet
    gym(configuration: "Debug", include_bitcode: true)
    pilot(app_identifier: "com.eleven-labs.testflight", ipa: "../build/Fastlane.ipa")
end
 
Sélectionnez
$ 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 :

 
Sélectionnez
# Comme d'habitude on initialise
$ bundle exec fastlane deliver init
 
Sélectionnez
# Deliverfile
username "yourItunesEmail@eleven-labs.com"

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

 
Sélectionnez
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.

Image non disponible

À 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.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

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.