Buddy : Une solution de CI/CD pour le mobile

Lorsqu'on développe une application mobile, on aimerait automatiser certaines actions. On va alors mettre en place une CI/CD dans notre projet qui va se charger de réaliser certaines actions répétitives automatiquement. Il existe de nombreux outils mais, dans cet article, je vais vous parler en particulier de Buddy, une solution qui dispose de nombreuses intégrations sur de nombreuses plateformes (Git, AppStore, Firebase, PlayStore, AWS, etc.).

Le projet Buddy

Pour ajouter une CI/CD sur un projet, il faut lier son projet Buddy à un projet Git existant. Ce projet peut être hébergé sur Github, Gitlab, Bitbucket ou un serveur Git privé. Ainsi, lors de la création du projet Buddy, il nous sera demandé de nous connecter à notre projet Git via les différentes options proposées.

Dans cet article, je vais utiliser un projet Git hébergé sur un serveur privé. Néanmoins, la procédure reste similaire pour les autres choix.

Une fois les champs remplis, notre projet est créé !

Les différents onglets

Une fois le projet créé, on arrive sur le tableau de bord composé de différents onglets.

Pipelines

Le premier onglet “Pipelines” permet de gérer les pipelines de la CI/CD. On peut donc créer une pipeline, la supprimer ou la modifier.

Code

Le deuxième onglet “Code” permet de visualiser le code copié depuis notre dépôt Git. C’est notamment à partir de cet onglet que nous pouvons synchroniser manuellement les modifications apportés dans le dépôt ou encore récupérer le webhook à ajouter dans Git pour automatiser la synchronisation disponible dans le sous-onglet “Deliveries”.

Variables

Dans cet onglet, il sera possible d’ajouter des objets d’environnement utilisables dans nos différentes pipelines. Ces objets peuvent être des variables (des tokens, etc.) ou encore des fichiers (des clés de sécurité, des fichiers de configuration, etc.).

Il est possible de configurer ces objets comme étant des données sensibles. Ainsi, la valeur des variables sera masquée dans les logs des pipelines et la lecture du contenu des fichiers sera impossible à travers les actions des pipelines.

Intégrations

À travers cet onglet, on peut ajouter des intégrations dans notre projet pour simplifier le déploiement ou d’autres intégrations (AWS, Slack, etc.).

Importer/Exporter un projet

Il est possible d’exporter son projet dans un format YAML afin de pouvoir utiliser les mêmes configurations dans d’autres projets. Ainsi, dans un autre projet Buddy, on peut importer une configuration YAML qui aura pour conséquence de créer les pipelines nécessaires.

Les objets d’environnement ne seront pas copiés dans la configuration. Il faudra donc recréer toutes les variables d’environnement et les fichiers qu’on a pu ajouter dans notre projet.

Les pipelines

Ajouter une pipeline

Lorsqu’on crée une nouvelle pipeline, plusieurs options peuvent permettre de la configurer selon nos besoins.

Il existe 3 types de déclencheur : Le déclenchement selon des événements, le déclenchement manuel et le déclenchement périodique.

Le déclenchement manuel nécessite que l’utilisateur déclenche lui-même la pipeline à chaque fois. Le déclenchement périodique permet de lancer la pipeline selon un intervalle de temps défini.

Enfin, pour lancer une pipeline selon des événements Git, il faut choisir l’option “On events”. On peut ainsi définir les événements qui déclencheront la pipeline (un push sur une branche, la création d’une merge request, etc.). Si on veut mélanger plusieurs types d’événements, on peut modifier les paramètres en cliquant sur l’encadré violet associé et mettre les événements voulus dans l’onglet “Wildcard”.

Dupliquer une pipeline

Les pipelines créés dans un projet Buddy peuvent être réutilisés dans d'autres projets assez facilement. Pour cela, dans un nouveau projet du même Workspace, sur la page de création d’une pipeline, il faut sélectionner l’onglet “Duplicate”.

Ainsi, dans la fenêtre qui s’ouvre, on peut choisir le projet dans lequel la pipeline est défini et l’importer.

Ajouter une action à une pipeline

Une pipeline est définie par une suite d’actions exécutées les unes à la suite des autres. On peut définir des actions selon l’état de la branche (lors de l’exécution, lors de l’échec, etc.).

Il existe de nombreuses actions prédéfinies qui nous permettent de configurer plus facilement nos projets. Toutefois, pour des besoins spécifiques, on peut choisir de lancer des VM (Linux ou Mac) et y définir le script à exécuter.

Il existe des actions plus haut niveau qui permettent de manipuler les fichiers de notre Git. La configuration de ces actions est assez simple (par exemple, pour la copie d’un fichier, il faut indiquer le chemin du fichier à copier et la destination du fichier) et Buddy se chargera de créer et configurer la VM associée.

Spécificité iOS : Ajouter les certificats et les provisioning profiles

Dans le cadre d’une CI/CD pour iOS, il sera peut-être nécessaire d’ajouter les certificats de distribution ainsi que les provisioning profiles de notre application dans le trousseaux d’accès de la VM MacOS. Ceux-ci peuvent être ajoutés dans la VM dans l’interface de configuration. On peut ainsi importer les certificats au format .p12 ainsi que les provisioning profiles au format .mobileprovision pour signer notre application.

Trucs et astuces

iOS - Certificats et Provisioning profiles

Pour faciliter l’utilisation des certificats de distribution et provisioning profiles durant le développement (en local sur la machine du développeur) et pour la distribution (au niveau de la CI/CD), il est préférable de ne pas utiliser la signature automatique du projet dans XCode et choisir manuellement les certificats à utiliser pour le projet. Cette option peut être décochée dans les paramètres du projet. Ainsi, vous pourrez facilement utiliser les mêmes certificats sur n’importe quel poste/machine.

Fastlane

Il est tout à fait possible d’utiliser fastlane dans les machines Buddy. Veillez tout de même à garder la version de fastlane à jour sur votre machine et sur la VM de la CICD.

Pour ce faire, préférez la commande shell brew upgrade fastlane que l’action update_fastlane de fastlane car cette dernière peut parfois ne pas fonctionner sur la VM.

Firebase

Il peut arriver qu’on doive utiliser Firebase dans nos lanes Fastlane. Ainsi, pour authentifier le système de CI, il est possible de générer un token grâce à la CLI de Firebase. Toute la documentation est disponible ici. Ce token sera alors à renseigner en tant que variable d’environnement donc le nom sera FIREBASE_TOKEN.

Exemple de Pipeline

Pour finir, on va voir 2 exemples de configuration pour des projets iOS et Android.

Déploiement sur AppStore Connect d’une app iOS

Dans cette section, on va voir un exemple de configuration pour déployer une app iOS pour les bêta testeurs (sur TestFlight).

Le YAML suivant correspond à la configuration de la pipeline sur Buddy. J’ai utilisé l’action Fastlane pour iOS.

YAML Pipeline

- pipeline: "Deploy iOS for beta testing"
  on: "EVENT"
  events:
  - type: "PUSH"
    refs:
    - "refs/heads/main"
  priority: "NORMAL"
  fail_on_prepare_env_warning: true
  actions:
  - action: "Build application"
    # Type de l'action: Fastlane pour iOS
    type: "NATIVE_BUILD_MAC_FASTLANE"
    # Répertoire contenant le code iOS (ici iosApp car c'est un projet KMM)
    working_directory: "/Users/buddy/build/iosApp"
    commands:
    - "brew upgrade fastlane; fastlane release" # Lane release définie dans le Fastfile
    shell: "BASH"
    variables:
      # Variable à définir pour augmenter le timeout de Fastlane (pour pallier le manque de perf des VM)
    - key: "FASTLANE_XCODEBUILD_SETTINGS_TIMEOUT"
      value: "120"
      type: "VAR"
    certificates:
    # Certificat de distribution au format p12 (voir II.C)
    - "certificat_p12"
    provision_profiles:
    # Provisioning profiles de l'application associé au bon certificat (voir II.C)
    - "my_provisioning_profile_nameprovision"
    xcode_version: "13.3.1"

Fastfile

platform :ios do
  desc "Release"
  lane :release do
    app_store_connect_api_key(
        key_id: "THE_KEY_ID",
        issuer_id: "THE_ISSUER_ID",
        key_filepath: "./PRIVATE_KEY_FILE.p8"
    ) # Connexion à AppStore Connect
    increment_build_number(
      build_number: latest_testflight_build_number(app_identifier: "com.app.bundle.id") + 1
    ) # Augmentation du numéro de build selon le dernier build déployé
    gym # Lance gym avec la config présente dans le Gymfile
    upload_to_testflight(
        skip_waiting_for_build_processing: true
    ) # Déploie l'application sur Testflight
  end
end

Pour la connexion à AppStore Connect, il vous faudra créer une clé d’API. Pour cela, vous pouvez vous rendre sur cette page. Une fois créé, vous devez ajouter le fichier .p8 à votre projet dans les variables de la VM (suivre I.B.3)

Gymfile

Les paramètres disponibles sont expliqués ici.

scheme("My_Scheme")

clean(true)

output_directory("./build/ipa/")
output_name("App.ipa")
export_options("./ExportOptions.plist")
build_path("./build/archive/")

ExportOptions

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>provisioningProfiles</key>
	<dict>
		<key>com.app.bundle.id</key>
		<string>My Provisioning Profile Name</string>
	</dict>
	<key>destination</key>
	<string>export</string>
	<key>method</key>
	<string>app-store</string>
</dict>
</plist>

La configuration présentée est un exemple que vous pouvez adapter pour votre projet iOS.

Déploiement Firebase App Distribution d’une app Android signé

Dans cet exemple, nous allons voir comment déployer une application Android sur Firebase App Distribution pour des testeurs internes.

On va tout d'abord voir comment signer facilement une application Android. Pour signer une app Android, il faut générer un fichier .jks appelé le keystore. Ce fichier peut être généré grâce à Android Studio en suivant la documentation officielle ici. Une fois ce fichier créé, on peut l’utiliser dans l’action nommée “Sign APK” de Buddy.

Dans la configuration de cette action, on peut y renseigner notre fichier jks avec sa variable d’environnement afin de signer l’application.

Une fois notre app signé généré, on peut l’uploader sur Firebase App Distribution. Pour ce faire, il faudra tout d’abord intégrer Firebase à notre projet Buddy. Cela se fait à travers l’onglet Intégrations (voir I.B.4).

La section Token est à remplir avec un token pour CI. On peut obtenir ce token via la CLI de Firebase en local grâce à la commande firebase login:ci. Pour plus d’informations, voir la documentation de Firebase ici. Une fois ajouté, on peut utiliser cette intégration dans une action de type Firebase CLI dans nos pipelines.

Une fois créé, on peut définir dans notre action des commandes firebase grâce à la CLI. Toutes les commandes disponibles sont listées ici. Nous allons nous intéresser à la distribution sur AppDistribution. Cela se fait grâce à la commande appdistribution:distribute. Les options possibles à ajouter sont renseignées dans la documentation.

Un exemple de commande est :

firebase appdistribution:distribute --app appId --groups Testers my/apk/path.apk

L’appId se trouve dans les paramètres du projet Firebase.

Le fichier YAML de la pipeline créée est

YAML Pipeline

- pipeline: "Firebase App Distribution"
  on: "EVENT"
  events:
  - type: "PUSH"
    refs:
    - "refs/heads/main"
  priority: "NORMAL"
  fail_on_prepare_env_warning: true
  actions:
  - action: "Execute: ./gradlew assembleRelease" # Exemple de build d'apk non signé
    type: "BUILD"
    working_directory: "/buddy/my-app"
    docker_image_name: "library/openjdk"
    docker_image_tag: "11"
    execute_commands:
    - "export ANDROID_HOME=\"/opt/android/sdk\""
    - "export PATH=$PATH:/opt/android/sdk/cmdline-tools/tools/bin"
    - "if [ ! -d \"$ANDROID_HOME/cmdline-tools\" ]; then"
    - " curl -o sdk.zip https://dl.google.com/android/repository/commandlinetools-linux-7583922_latest.zip"
    - " unzip sdk.zip"
    - " rm sdk.zip"
    - " mkdir \"$ANDROID_HOME/cmdline-tools\""
    - " mv cmdline-tools \"$ANDROID_HOME/cmdline-tools/tools\""
    - " yes | \"$ANDROID_HOME/cmdline-tools/tools/bin/sdkmanager\" --licenses"
    - "fi"
    - ""
    - "chmod +x gradlew"
    - "#./gradlew assembleDebug"
    - "./gradlew assembleRelease"
    cached_dirs:
    - "/root/.gradle"
    - "/opt/android/sdk"
    volume_mappings:
    - "/:/buddy/my-app"
    cache_base_image: true
    shell: "BASH"
  - action: "Sign APK"
    type: "ANDROID_SIGN"
    local_path: "path/to/my-unsigned-apk.apk"
    output_dir: "path/to/directory/signed-apk"
    application_name: "my-signed-app.apk"
    key_path: "$my_jks_file" # Ici, renseignez la variable d'environnement permettant d'accéder à votre fichier jks
    key_password: "my_password"
    keystore_password: "my_password"
    build_tool_version: "31.0.0"
  - action: "Run Firebase CLI"
    type: "FIREBASE"
    working_directory: "/buddy/my-app"
    version: "16"
    execute_commands:
    - "firebase appdistribution:distribute ./path/to/directory/signed-apk/my-signed-app.apk --app 1:010203040506:android:1234randomkey5678 --groups Developers"
    volume_mappings:
    - "/my-app"
    build_tool_version: "11.9.0"
    shell: "SH"
    integration_hash: "my-firebase-integration)hash"

Conclusion

Buddy est un excellent outil permettant de manager vos CI/CD sur vos différents projets, et ce quelle que soit la nature du projet (Mobile, Web, etc.).

En pleine évolution, des fonctionnalités sont souvent ajoutées à l'outil permettant de faciliter la vie des développeurs. Il faut tout aussi féliciter le service client qui est très réactif lorsque vous avez un problème sur vos pipelines.

Si cet article a attisé votre curiosité, n'hésitez pas à suivre l'actualité de Buddy notamment sur leur blog.