Mobile CICD-Erfahrung: ein Fastlane-Standard für viele mobile Anwendungen

Mobile CICD-Erfahrung: ein Fastlane-Standard für viele mobile Anwendungen
Ich würde gerne über kontinuierliche Integration und Bereitstellung für mobile Apps mit Fastlane sprechen. Wie wir CI/CD in allen mobilen Anwendungen implementieren, wie wir dorthin gekommen sind und was am Ende passiert ist.

Zu dem Tool, das uns am Anfang so gefehlt hat, gibt es im Netz bereits genügend Material, daher beschreibe ich das Tool bewusst nicht im Detail, sondern beziehe mich nur auf das, was wir damals hatten:

Der Artikel besteht aus zwei Teilen:

  • Hintergrund zur Entstehung von mobilem CI/CD im Unternehmen
  • Technische Lösung zur Einführung von CI/CD für N-Anwendungen

Der erste Teil ist eher Nostalgie für die alten Zeiten und der zweite Teil ist eine Erfahrung, die Sie auf sich selbst anwenden können.

So hat es sich historisch zugetragen

Jahr 2015

Wir haben gerade erst angefangen, mobile Anwendungen zu entwickeln, damals wussten wir nichts über kontinuierliche Integration, über DevOps und andere modische Dinge. Jedes Anwendungsupdate wurde vom Entwickler selbst auf seinem Computer ausgerollt. Und wenn es für Android ganz einfach ist – zusammengebaut, signiert .apk und lud es in die Google Developer Console hoch, dann bescherte uns das damalige Verteilungstool über Xcode für iOS tolle Abende – Versuche, das Archiv herunterzuladen, endeten oft mit Fehlern und wir mussten es erneut versuchen. Es stellte sich heraus, dass der fortschrittlichste Entwickler nicht mehrmals im Monat Code schreibt, sondern die Anwendung veröffentlicht.

Jahr 2016

Als wir aufwuchsen, hatten wir bereits darüber nachgedacht, wie wir Entwicklern einen ganzen Tag für eine Veröffentlichung freigeben könnten, und es erschien auch eine zweite Anwendung, die uns nur noch mehr in Richtung Automatisierung drängte. Im selben Jahr haben wir Jenkins zum ersten Mal installiert und eine Reihe gruseliger Skripte geschrieben, die denen sehr ähnlich sind, die Fastlane in seiner Dokumentation zeigt.

$ 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"

Leider wussten bisher nur unsere Entwickler, wie diese Skripte funktionieren und warum dieser endlose Stapel von Schlüsseln benötigt wird, und als wieder etwas kaputt ging, bekamen sie die „wunderschönen Abende“ für die Protokollanalyse.

Jahr 2017

Dieses Jahr haben wir gelernt, dass es so etwas wie Fastlane gibt. Es gab nicht so viele Informationen wie jetzt – wie man ein Programm startet und wie man es nutzt. Und das Tool selbst war damals noch grob: Ständige Fehler enttäuschten uns nur und man konnte kaum an die magische Automatisierung glauben, die es versprach.

Die wichtigsten im Fastlane-Kern enthaltenen Dienstprogramme sind jedoch: gym и pilot, wir haben es geschafft, es zu starten.

Unsere Skripte wurden ein wenig verbessert.

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

Sie wurden verbessert, schon allein deshalb, weil nicht alle dafür notwendigen Parameter vorhanden sind xcodebuild, müssen Sie angeben - gym wird selbstständig verstehen, wo und was liegt. Und für eine weitere Feinabstimmung können Sie die gleichen Schlüssel wie in angeben xcodebuild, nur die Benennung der Tasten ist klarer.

Dieses Mal sind die Build-Protokolle dank Gym und dem integrierten xcpretty-Formatierer viel besser lesbar geworden. Dies sparte Zeit bei der Reparatur defekter Baugruppen und manchmal konnte das Release-Team es selbst herausfinden.

Leider Montagegeschwindigkeitsmessungen xcodebuild и gym Wir haben es nicht getan, aber wir vertrauen der Dokumentation – bis zu 30 % Beschleunigung.

Ein Prozess für alle Anwendungen

Jahr 2018 und heute

Bis 2018 wurde der Prozess der Erstellung und Einführung von Anwendungen vollständig auf Jenkins verlagert, Entwickler stellten die Veröffentlichung von Veröffentlichungen auf ihren Maschinen ein und nur das Veröffentlichungsteam hatte das Recht zur Veröffentlichung.

Wir wollten bereits den Start von Tests und statischen Analysen verbessern und unsere Skripte wuchsen und wuchsen. Mit unseren Anwendungen ist es gewachsen und hat sich verändert. Damals gab es etwa 10 Anwendungen. Wenn man bedenkt, dass wir zwei Plattformen haben, sind das etwa 20 „lebende“ Skripte.

Jedes Mal, wenn wir dem Skript einen neuen Schritt hinzufügen wollten, mussten wir die Teile kopieren und in alle Shell-Skripte einfügen. Vielleicht hätten wir sorgfältiger arbeiten können, aber oft endeten solche Änderungen mit Tippfehlern, die für das Release-Team zu Abenden wurden, um Skripte zu korrigieren und herauszufinden, welcher kluge Kerl diesen Befehl hinzugefügt hat und was er tatsächlich bewirkt. Im Allgemeinen kann nicht gesagt werden, dass die Skripte für die Assemblierung für eine Plattform zumindest einigermaßen ähnlich waren. Obwohl sie sicherlich das Gleiche getan haben.

Um einen Prozess für eine neue Anwendung zu starten, musste man einen Tag damit verbringen, eine „frische“ Version dieser Skripte auszuwählen, sie zu debuggen und zu sagen: „Ja, es funktioniert.“

Im Sommer 2018 richteten wir unseren Blick erneut auf die sich noch entwickelnde Fastlane.

Aufgabe Nr. 1: Fassen Sie alle Skriptschritte zusammen und schreiben Sie sie in Fastfile neu

Als wir anfingen, sahen unsere Skripte wie ein Fußtuch aus, das aus allen Schritten und Krücken in einem Shell-Skript in Jenkins bestand. Wir sind noch nicht stufenweise auf Pipeline und Division umgestiegen.

Wir haben uns angeschaut, was wir haben, und vier Schritte identifiziert, die zur Beschreibung unseres CI/CD passen:

  • Build – Abhängigkeiten installieren, Archiv zusammenstellen,
  • Test – Ausführen von Entwickler-Unit-Tests, Berechnen der Abdeckung,
  • Sonar – startet alle Linters und sendet Berichte an SonarQube,
  • Deploy – Senden eines Artefakts an Alpha (TestFlight).

Und wenn Sie nicht ins Detail gehen und die in Aktionen verwendeten Schlüssel weglassen, erhalten Sie dieses 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

Tatsächlich erwies sich unser erstes Fastfile als ungeheuerlich, wenn man bedenkt, welche Krücken wir noch brauchten und wie viele Parameter wir ersetzten:

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

Im obigen Beispiel müssen wir nur einen Teil der Parameter angeben: Dies sind die Build-Parameter – Schema, Konfiguration, Namen der Bereitstellungsprofile sowie Verteilungsparameter – Apple-ID des Entwicklerkontos, Passwort, Anwendungs-ID usw An. In erster Näherung legen wir alle diese Schlüssel in speziellen Dateien ab – Gymfile, Matchfile и Appfile.

Jetzt können Sie in Jenkins kurze Befehle aufrufen, die die Sicht nicht verwischen und für das Auge gut lesbar sind:

# fastlane ios <lane_name>

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

Hurra, wir sind großartig

Was hast du bekommen? Klare Befehle für jeden Schritt. Bereinigte Skripte, übersichtlich in Fastlane-Dateien angeordnet. Voller Freude rannten wir zu den Entwicklern und baten sie, alles, was sie brauchten, zu ihren Repositories hinzuzufügen.

Aber mit der Zeit wurde uns klar, dass wir auf die gleichen Schwierigkeiten stoßen würden – wir hätten immer noch 20 Assembler-Skripte, die auf die eine oder andere Weise beginnen würden, ihr eigenes Leben zu führen, es wäre schwieriger, sie zu bearbeiten, da die Skripte in Repositories verschoben würden, und wir hatten dort keinen Zugang. Und im Allgemeinen wird es nicht möglich sein, unseren Schmerz auf diese Weise zu lösen.

Mobile CICD-Erfahrung: ein Fastlane-Standard für viele mobile Anwendungen

Aufgabe Nr. 2: Holen Sie sich ein einzelnes Fastfile für N Anwendungen

Nun scheint es, dass die Lösung des Problems nicht mehr so ​​schwierig ist – stellen Sie die Variablen ein und los geht’s. Ja, tatsächlich wurde das Problem so gelöst. Aber in dem Moment, als wir es vermasselt haben, hatten wir weder Fachwissen in Fastlane selbst, noch in Ruby, in dem Fastlane geschrieben ist, noch nützliche Beispiele im Netzwerk – jeder, der damals über Fastlane schrieb, war auf ein Beispiel für eine Anwendung beschränkt ein Entwickler.

Fastlane kann mit Umgebungsvariablen umgehen, und wir haben dies bereits versucht, indem wir das Schlüsselbundkennwort festgelegt haben:

ENV['KEYCHAIN_PASSWORD']

Nachdem wir uns unsere Skripte angesehen hatten, identifizierten wir die gemeinsamen Teile:

#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

Um diese Schlüssel nun in Fastlane-Dateien verwenden zu können, mussten wir herausfinden, wie wir sie dort bereitstellen können. Fastlane hat hierfür eine Lösung: Laden von Variablen über dotenv. In der Dokumentation heißt es: Wenn es für Sie wichtig ist, Schlüssel für verschiedene Zwecke zu laden, erstellen Sie mehrere Konfigurationsdateien im Fastlane-Verzeichnis .env, .env.default, .env.development.

Und dann haben wir beschlossen, diese Bibliothek etwas anders zu nutzen. Platzieren wir im Entwickler-Repository nicht die Fastlane-Skripte und ihre Metainformationen, sondern die eindeutigen Schlüssel dieser Anwendung in der Datei .env.appName.

Als sie selbst Fastfile, Appfile, Matchfile и Gymfile, wir haben es in einem separaten Repository versteckt. Dort war eine zusätzliche Datei mit Passwortschlüsseln von anderen Diensten versteckt - .env.
Sie können ein Beispiel sehen hier.

Mobile CICD-Erfahrung: ein Fastlane-Standard für viele mobile Anwendungen

Bei CI hat sich am Aufruf nicht viel geändert; ein Konfigurationsschlüssel für eine bestimmte Anwendung wurde hinzugefügt:

# 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

Bevor wir die Befehle ausführen, laden wir unser Repository mit Skripten. Sieht nicht so schön aus:

git clone [email protected]/FastlaneCICD.git fastlane_temp

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

Ich habe diese Lösung vorerst belassen, obwohl Fastlane eine Lösung zum Herunterladen von Fastfile über hat Aktion import_from_git, aber es funktioniert nur für Fastfile, nicht aber für andere Dateien. Wenn Sie „wirklich schön“ wollen, können Sie Ihr eigenes schreiben action.

Ein ähnlicher Satz wurde für Android-Anwendungen und ReactNative erstellt. Die Dateien befinden sich im selben Repository, jedoch in unterschiedlichen Zweigen iOS, android и react_native.

Wenn das Release-Team einen neuen Schritt hinzufügen möchte, werden Änderungen im Skript über MR in Git aufgezeichnet, es besteht keine Notwendigkeit mehr, nach den Schuldigen fehlerhafter Skripte zu suchen, und im Allgemeinen müssen Sie jetzt versuchen, es zu beschädigen.

Nun, das ist es sicher

Zuvor haben wir viel Zeit damit verbracht, alle Skripte zu warten, sie zu aktualisieren und alle Konsequenzen von Updates zu beheben. Es war sehr enttäuschend, wenn die Gründe für Fehler und Ausfallzeiten in Releases einfache Tippfehler waren, die im Durcheinander der Shell-Skripte so schwer zu erkennen waren. Jetzt werden solche Fehler auf ein Minimum reduziert. Änderungen werden auf alle Anwendungen gleichzeitig übertragen. Und es dauert 15 Minuten, eine neue Anwendung in den Prozess einzubinden – richten Sie eine Vorlagenpipeline auf CI ein und fügen Sie die Schlüssel zum Repository des Entwicklers hinzu.

Es scheint, dass der Punkt mit Fastfile für Android und der Anwendungssignatur ungeklärt bleibt; wenn der Artikel interessant ist, werde ich eine Fortsetzung schreiben. Ich freue mich über Ihre Fragen oder Vorschläge „Wie würden Sie dieses Problem lösen?“ in den Kommentaren oder auf Telegram Baschkirowa.

Source: habr.com

Kommentar hinzufügen