Expérience CICD mobile : une norme fastlane pour de nombreuses applications mobiles

Expérience CICD mobile : une norme fastlane pour de nombreuses applications mobiles
J'aimerais parler de l'intégration et de la livraison continues d'applications mobiles utilisant Fastlane. Comment nous implémentons le CI/CD sur toutes les applications mobiles, comment nous y sommes arrivés et que s'est-il passé au final.

Il y a déjà suffisamment de matériel sur le réseau sur l'outil, ce qui nous manquait tellement au début, c'est pourquoi je ne décrirai délibérément pas l'outil en détail, mais me référerai uniquement à ce que nous avions alors :

L'article se compose de deux parties :

  • Contexte de l’émergence du CI/CD mobile dans l’entreprise
  • Solution technique de déploiement de CI/CD pour N-applications

La première partie est davantage une nostalgie du bon vieux temps, et la seconde est une expérience que vous pouvez appliquer à vous-même.

C'est comme ça que ça s'est passé historiquement

Année 2015

Nous venions tout juste de commencer à développer des applications mobiles, alors nous ne connaissions rien à l’intégration continue, au DevOps et autres choses à la mode. Chaque mise à jour de l'application était déployée par le développeur lui-même depuis sa machine. Et si pour Android c'était assez simple - assemblé, signé .apk et l'a téléchargé sur la console de développement Google, puis pour iOS, l'outil de distribution via Xcode nous a laissé de belles soirées - les tentatives de téléchargement de l'archive se terminaient souvent par des erreurs et nous devions réessayer. Il s'est avéré que le développeur le plus avancé n'écrit pas de code plusieurs fois par mois, mais publie plutôt l'application.

Année 2016

Nous avons grandi, nous avions déjà réfléchi à la manière de libérer les développeurs d'une journée entière pour une version, et une deuxième application est également apparue, ce qui n'a fait que nous pousser davantage vers l'automatisation. La même année, nous avons installé Jenkins pour la première fois et écrit un tas de scripts effrayants, très similaires à ceux que fastlane montre dans sa documentation.

$ xcodebuild clean archive -archivePath build/MyApp 
    -scheme MyApp

$ xcodebuild -exportArchive 
                        -exportFormat ipa 
                        -archivePath "build/MyApp.xcarchive" 
                        -exportPath "build/MyApp.ipa" 
                        -exportProvisioningProfile "ProvisioningProfileName"

$ cd /Applications/Xcode.app/Contents/Applications/Application Loader.app/Contents/Frameworks/ITunesSoftwareService.framework/Versions/A/Support/

$ ./altool —upload-app 
-f {abs path to your project}/build/{release scheme}.ipa  
-u "[email protected]" 
-p "PASS_APPLE_ID"

Malheureusement, jusqu'à présent, seuls nos développeurs savaient comment fonctionnaient ces scripts et pourquoi cette pile infinie de clés était nécessaire, et lorsque quelque chose tombait à nouveau en panne, ils bénéficiaient de «soirées magnifiques» pour analyser les journaux.

Année 2017

Cette année, nous avons appris qu’il existe une voie rapide. Il n’y avait pas autant d’informations qu’aujourd’hui – comment en démarrer un, comment l’utiliser. Et l’outil lui-même était encore rudimentaire à cette époque : des erreurs constantes ne faisaient que nous décevoir et il était difficile de croire à l’automatisation magique qu’elles promettaient.

Cependant, les principaux services publics inclus dans le noyau Fastlane sont gym и pilot, nous avons réussi à le démarrer.

Nos scripts ont été un peu améliorés.

$ fastlane gym  —-workspace "Example.xcworkspace" 
                --scheme "AppName" 
                —-buildlog_path "/tmp" 
                -—clean

Ils ont été améliorés, ne serait-ce que parce que tous les paramètres nécessaires à leur xcodebuild, vous devez indiquer - gym comprendra indépendamment où et ce qui se trouve. Et pour un réglage plus fin, vous pouvez spécifier les mêmes clés que dans xcodebuild, seule la dénomination des touches est plus claire.

Cette fois, grâce à Gym et au formateur xcpretty intégré, les journaux de construction sont devenus beaucoup plus lisibles. Cela a commencé à gagner du temps sur la réparation des assemblys cassés, et parfois l'équipe de publication pouvait le résoudre par elle-même.

Malheureusement, les mesures de vitesse d'assemblage xcodebuild и gym Nous ne l'avons pas fait, mais nous ferons confiance à la documentation - jusqu'à 30 % d'accélération.

Processus unique pour toutes les demandes

Année 2018 et présent

En 2018, le processus de création et de déploiement d'applications a été entièrement transféré vers Jenkins, les développeurs ont cessé de publier depuis leurs machines et seule l'équipe de publication avait le droit de publier.

Nous souhaitions déjà améliorer le lancement des tests et des analyses statiques, et nos scripts ont grandi et grandi. A grandi et changé avec nos applications. À l’époque, il y avait environ 10 applications, ce qui, étant donné que nous avons deux plateformes, représente une vingtaine de scripts « vivants ».

Chaque fois que nous voulions ajouter une nouvelle étape au script, nous devions copier-coller les éléments dans tous les scripts shell. Nous aurions peut-être pu travailler plus soigneusement, mais souvent de tels changements se terminaient par des fautes de frappe, qui se transformaient en soirées pour l'équipe de publication pour corriger les scripts et découvrir quel type intelligent avait ajouté cette commande et ce qu'elle faisait réellement. En général, on ne peut pas dire que les scripts d'assemblage pour une plate-forme étaient au moins quelque peu similaires. Même s’ils ont certainement fait la même chose.

Afin de démarrer un processus pour une nouvelle application, il était nécessaire de passer une journée à sélectionner une version « fraîche » de ces scripts, à la déboguer et à dire que « oui, cela fonctionne ».

À l’été 2018, nous nous sommes à nouveau tournés vers la voie rapide, encore en développement.

Tâche n° 1 : résumer toutes les étapes du script et les réécrire dans Fastfile

Lorsque nous avons commencé, nos scripts ressemblaient à un chausson composé de toutes les étapes et béquilles dans un seul script shell dans Jenkins. Nous ne sommes pas encore passés au pipeline et à la division par étapes.

Nous avons examiné ce dont nous disposons et identifié 4 étapes qui correspondent à la description de notre CI/CD :

  • build - installation des dépendances, assemblage de l'archive,
  • test - exécution de tests unitaires de développeur, calcul de la couverture,
  • sonar - lance tous les linters et envoie des rapports à SonarQube,
  • déployer - envoyer un artefact à alpha (TestFlight).

Et si vous n’entrez pas dans les détails, en omettant les clés utilisées dans les actions, vous obtiendrez ce Fastfile :

default_platform(:ios)

platform :ios do
  before_all do
    unlock
  end

  desc "Build stage"
  lane :build do
    match
    prepare_build
    gym
  end

  desc "Prepare build stage: carthage and cocoapods"
  lane :prepare_build do
    pathCartfile = ""
    Dir.chdir("..") do
      pathCartfile = File.join(Dir.pwd, "/Cartfile")
    end
    if File.exist?(pathCartfile)
      carthage
    end
    pathPodfile = ""
    Dir.chdir("..") do
      pathPodfile = File.join(Dir.pwd, "/Podfile")
    end
    if File.exist?(pathPodfile)
      cocoapods
    end
  end

  desc "Test stage"
  lane :test do
    scan
    xcov
  end

  desc "Sonar stage (after run test!)"
  lane :run_sonar do
    slather
    lizard
    swiftlint
    sonar
  end

  desc "Deploy to testflight stage"
  lane :deploy do
    pilot
  end

  desc "Unlock keychain"
  private_lane :unlock do
    pass = ENV['KEYCHAIN_PASSWORD']
    unlock_keychain(
      password: pass
    )
  end
end

En fait, notre premier Fastfile s'est avéré monstrueux, compte tenu de certaines des béquilles dont nous avions encore besoin et du nombre de paramètres que nous avons remplacés :

lane :build do
carthage(
  command: "update",
  use_binaries: false,
  platform: "ios",
  cache_builds: true)
cocoapods(
  clean: true,
    podfile: "./Podfile",
    use_bundle_exec: false)

gym(
  workspace: "MyApp.xcworkspace",
  configuration: "Release",
  scheme: "MyApp",
  clean: true,
  output_directory: "/build",
  output_name: "my-app.ipa")
end 

lane :deploy do
 pilot(
  username: "[email protected]",
  app_identifier: "com.example.app",
  dev_portal_team_id: "TEAM_ID_NUMBER_DEV",
  team_id: "ITS_TEAM_ID")
end

Dans l'exemple ci-dessus, seule une partie des paramètres que nous devons spécifier : ce sont les paramètres de construction - schéma, configuration, noms de profil de provisionnement, ainsi que les paramètres de distribution - identifiant Apple du compte développeur, mot de passe, identifiant d'application, etc. sur. En première approximation, nous mettons toutes ces clés dans des fichiers spéciaux - Gymfile, Matchfile и Appfile.

Désormais, dans Jenkins, vous pouvez appeler des commandes courtes qui ne brouillent pas la vue et sont facilement lisibles à l'œil nu :

# fastlane ios <lane_name>

$ fastlane ios build
$ fastlane ios test
$ fastlane ios run_sonar
$ fastlane ios deploy

Hourra, nous sommes géniaux

Qu'est-ce que vous obtenez? Des commandes claires pour chaque étape. Scripts nettoyés, soigneusement organisés dans des fichiers fastlane. En nous réjouissant, nous avons couru vers les développeurs pour leur demander d'ajouter tout ce dont ils avaient besoin à leurs référentiels.

Mais nous avons réalisé avec le temps que nous rencontrerions les mêmes difficultés - nous aurions toujours 20 scripts d'assemblage qui, d'une manière ou d'une autre, commenceraient à vivre leur propre vie, il serait plus difficile de les éditer, puisque les scripts seraient déplacés vers des référentiels, et nous n'y avions pas accès. Et, en général, il ne sera pas possible de résoudre notre douleur de cette façon.

Expérience CICD mobile : une norme fastlane pour de nombreuses applications mobiles

Tâche n°2 : obtenir un seul Fastfile pour N applications

Maintenant, il semble que résoudre le problème ne soit pas si difficile - définissez les variables et c'est parti. Oui, en fait, c’est ainsi que le problème a été résolu. Mais au moment où nous avons tout gâché, nous n'avions ni expertise dans Fastlane lui-même, ni dans Ruby, dans lequel Fastlane est écrit, ni d'exemples utiles sur le réseau - tous ceux qui écrivaient sur Fastlane étaient alors limités à un exemple pour une application pour un développeur.

Fastlane peut gérer les variables d'environnement, et nous l'avons déjà essayé en définissant le mot de passe du trousseau :

ENV['KEYCHAIN_PASSWORD']

Après avoir examiné nos scripts, nous avons identifié les parties communes :

#for build, test and deploy
APPLICATION_SCHEME_NAME=appScheme
APPLICATION_PROJECT_NAME=app.xcodeproj
APPLICATION_WORKSPACE_NAME=app.xcworkspace
APPLICATION_NAME=appName

OUTPUT_IPA_NAME=appName.ipa

#app info
APP_BUNDLE_IDENTIFIER=com.example.appName
[email protected]
TEAM_ID=ABCD1234
FASTLANE_ITC_TEAM_ID=123456789

Maintenant, pour commencer à utiliser ces clés dans les fichiers fastlane, nous devions trouver comment les y livrer. Fastlane a une solution pour cela : chargement de variables via dotenv. La documentation indique que s'il est important pour vous de charger des clés à des fins différentes, créez plusieurs fichiers de configuration dans le répertoire fastlane. .env, .env.default, .env.development.

Et puis nous avons décidé d'utiliser cette bibliothèque un peu différemment. Plaçons dans le référentiel des développeurs non pas les scripts fastlane et ses méta informations, mais les clés uniques de cette application dans le fichier .env.appName.

Eux-mêmes Fastfile, Appfile, Matchfile и Gymfile, nous l'avons caché dans un référentiel séparé. Un fichier supplémentaire contenant des clés de mot de passe d'autres services y était caché - .env.
Vous pouvez voir un exemple ici.

Expérience CICD mobile : une norme fastlane pour de nombreuses applications mobiles

Sur CI l'appel n'a pas beaucoup changé, une clé de configuration pour une application spécifique a été ajoutée :

# fastlane ios <lane_name> --env appName

$ fastlane ios build --env appName
$ fastlane ios test --env appName
$ fastlane ios run_sonar --env appName
$ fastlane ios deploy --env appName

Avant d'exécuter les commandes, nous chargeons notre référentiel avec des scripts. Ça n'a pas l'air si joli :

git clone [email protected]/FastlaneCICD.git fastlane_temp

cp ./fastlane_temp/fastlane/* ./fastlane/
cp ./fastlane_temp/fastlane/.env fastlane/.env

J'ai laissé cette solution pour le moment, bien que Fastlane ait une solution pour télécharger Fastfile via action import_from_git, mais cela ne fonctionne que pour Fastfile, mais pas pour les autres fichiers. Si vous voulez « vraiment beau », vous pouvez écrire le vôtre action.

Un ensemble similaire a été réalisé pour les applications Android et ReactNative, les fichiers sont dans le même référentiel, mais dans des branches différentes iOS, android и react_native.

Lorsque l'équipe de publication souhaite ajouter une nouvelle étape, les modifications dans le script sont enregistrées via MR dans git, il n'est plus nécessaire de rechercher les coupables des scripts cassés, et en général, il faut maintenant essayer de le casser.

Maintenant c'est sûr

Auparavant, nous passions du temps à maintenir tous les scripts, à les mettre à jour et à corriger toutes les conséquences des mises à jour. C'était très décevant lorsque les raisons des erreurs et des temps d'arrêt dans les versions étaient de simples fautes de frappe si difficiles à suivre dans le fouillis des scripts shell. Désormais, ces erreurs sont réduites au minimum. Les modifications sont déployées simultanément sur toutes les applications. Et il faut 15 minutes pour insérer une nouvelle application dans le processus - configurez un pipeline de modèles sur CI et ajoutez les clés au référentiel du développeur.

Il semble que l'intérêt de Fastfile pour Android et de la signature de l'application reste inexpliqué ; si l'article est intéressant, j'écrirai une suite. Je serai heureux de voir vos questions ou suggestions « comment résoudriez-vous ce problème » dans les commentaires ou sur Telegram Bachkirova.

Source: habr.com

Ajouter un commentaire