Mobilne środowisko CICD: jeden standard Fastlane dla wielu aplikacji mobilnych

Mobilne środowisko CICD: jeden standard Fastlane dla wielu aplikacji mobilnych
Chciałbym porozmawiać o ciągłej integracji i dostarczaniu aplikacji mobilnych za pomocą Fastlane. Jak wdrażamy CI/CD we wszystkich aplikacjach mobilnych, jak do tego dotarliśmy i co ostatecznie się wydarzyło.

W sieci jest już wystarczająco dużo materiału na temat narzędzia, którego tak bardzo nam brakowało na początku, dlatego celowo nie będę szczegółowo opisywał narzędzia, a jedynie odniosę się do tego, co wtedy mieliśmy:

Artykuł składa się z dwóch części:

  • Kontekst pojawienia się mobilnego CI/CD w firmie
  • Rozwiązanie techniczne umożliwiające wdrażanie CI/CD dla aplikacji typu N

Pierwsza część to bardziej nostalgia za dawnymi czasami, a druga to doświadczenie, które możesz zastosować do siebie.

Tak to się działo historycznie

2015 rok

Dopiero zaczęliśmy tworzyć aplikacje mobilne, wtedy nie wiedzieliśmy nic o ciągłej integracji, DevOps i innych modnych rzeczach. Każda aktualizacja aplikacji była wdrażana przez samego programistę ze jego komputera. A jeśli dla Androida jest to dość proste - zmontowane, podpisane .apk i przesłałem go do Google Developer Console, następnie w przypadku iOS ówczesne narzędzie do dystrybucji za pośrednictwem Xcode pozostawiło nam wspaniałe wieczory - próby pobrania archiwum często kończyły się błędami i musieliśmy spróbować ponownie. Okazało się, że najbardziej zaawansowany programista nie pisze kodu kilka razy w miesiącu, tylko wydaje aplikację.

2016 rok

Dorastaliśmy, myśleliśmy już o tym, jak zwolnić programistów z całego dnia na wydanie, pojawiła się też druga aplikacja, co tylko popchnęło nas bardziej w stronę automatyzacji. W tym samym roku po raz pierwszy zainstalowaliśmy Jenkinsa i napisaliśmy kilka przerażających skryptów, bardzo podobnych do tych, które Fastlane pokazuje w swojej dokumentacji.

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

Niestety, do tej pory tylko nasi programiści wiedzieli, jak działają te skrypty i do czego potrzebny jest ten niekończący się stos kluczy, a gdy znów coś się zepsuło, mieli „cudowne wieczory” na analizowanie logów.

2017 rok

W tym roku dowiedzieliśmy się, że istnieje coś takiego jak fastlane. Nie było tak wielu informacji jak teraz – jak zacząć, jak to wykorzystać. A samo narzędzie było wówczas jeszcze prymitywne: ciągłe błędy tylko nas rozczarowały i trudno było uwierzyć w magiczną automatyzację, którą obiecywały.

Jednakże głównymi narzędziami zawartymi w rdzeniu Fastlane są gym и pilot, udało nam się to rozpocząć.

Nasze skrypty zostały nieco ulepszone.

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

Zostały ulepszone choćby dlatego, że nie wszystkie parametry potrzebne xcodebuild, musisz wskazać - gym samodzielnie zrozumie, gdzie i co leży. Aby uzyskać dokładniejsze dostrojenie, możesz określić te same klawisze, co w xcodebuild, tylko nazewnictwo klawiszy jest jaśniejsze.

Tym razem, dzięki Gym i wbudowanemu formaterowi xcpretty, logi kompilacji stały się znacznie bardziej czytelne. Zaczęło to oszczędzać czas na naprawie uszkodzonych zespołów i czasami zespół wydający mógł rozwiązać ten problem samodzielnie.

Niestety pomiary szybkości montażu xcodebuild и gym Nie zrobiliśmy tego, ale zaufamy dokumentacji - przyspieszenie do 30%.

Jeden proces dla wszystkich zastosowań

Rok 2018 i obecnie

Do 2018 roku proces tworzenia i wdrażania aplikacji został całkowicie przeniesiony na Jenkins, programiści przestali wypuszczać aplikacje ze swoich maszyn i tylko zespół wydający miał prawo do wydawania aplikacji.

Chcieliśmy już usprawnić uruchamianie testów i analiz statycznych, a nasze skrypty rosły i rosły. Rośnie i zmienia się wraz z naszymi aplikacjami. Aplikacji było wówczas około 10. Biorąc pod uwagę, że mamy dwie platformy, to około 20 „żywych” skryptów.

Za każdym razem, gdy chcieliśmy dodać nowy krok do skryptu, musieliśmy skopiować i wkleić fragmenty do wszystkich skryptów powłoki. Być może moglibyśmy pracować ostrożniej, ale często takie zmiany kończyły się literówkami, co zamieniało się w wieczory, w których zespół ds. wydania zajmował się poprawianiem skryptów i dowiadywaniem się, który mądry facet dodał to polecenie i co faktycznie robi. Generalnie nie można powiedzieć, że skrypty do asemblera dla jednej platformy były choć trochę podobne. Chociaż z pewnością zrobili to samo.

Aby rozpocząć proces tworzenia nowej aplikacji, trzeba było poświęcić jeden dzień na wybranie „świeżej” wersji tych skryptów, zdebugowanie jej i stwierdzenie „tak, to działa”.

Latem 2018 roku ponownie spojrzeliśmy w stronę wciąż rozwijającego się fastlane’u.

Zadanie nr 1: podsumuj wszystkie kroki skryptu i przepisz je w Fastfile

Kiedy zaczynaliśmy, nasze skrypty wyglądały jak obrus składający się ze wszystkich stopni i kul w jednym skrypcie skorupowym w Jenkins. Nie przeszliśmy jeszcze na rurociąg i podział etapowy.

Przyjrzeliśmy się temu, co mamy i zidentyfikowaliśmy 4 kroki, które pasują do opisu naszego CI/CD:

  • build - instalowanie zależności, składanie archiwum,
  • test — uruchamianie testów jednostkowych dewelopera, obliczanie pokrycia,
  • sonar - uruchamia wszystkie lintery i wysyła raporty do SonarQube,
  • wdrożyć — wysłanie artefaktu do wersji alfa (TestFlight).

A jeśli nie wdasz się w szczegóły, pomijając klawisze używane w akcjach, otrzymasz ten 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

W rzeczywistości nasz pierwszy Fastfile okazał się potworny, biorąc pod uwagę niektóre podpory, których wciąż potrzebowaliśmy i liczbę parametrów, które zastąpiliśmy:

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

W powyższym przykładzie tylko część parametrów, które musimy określić: są to parametry kompilacji - schemat, konfiguracja, nazwy profili prowizyjnych, a także parametry dystrybucji - Apple ID konta programisty, hasło, identyfikator aplikacji i tak dalej NA. W pierwszym przybliżeniu umieścimy wszystkie te klucze w specjalnych plikach - Gymfile, Matchfile и Appfile.

Teraz w Jenkinsie możesz wywoływać krótkie polecenia, które nie zamazują widoku i są czytelne dla oka:

# fastlane ios <lane_name>

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

Hurra, jesteśmy wspaniali

Co dostałeś? Jasne polecenia dla każdego kroku. Oczyszczone skrypty, starannie ułożone w plikach Fastlane. Radując się, pobiegliśmy do programistów, prosząc ich, aby dodali wszystko, czego potrzebują do swoich repozytoriów.

Ale z czasem zdaliśmy sobie sprawę, że napotkamy te same trudności - nadal mielibyśmy 20 skryptów asemblerowych, które tak czy inaczej zaczęłyby żyć własnym życiem, trudniej byłoby je edytować, ponieważ skrypty przeniosłyby się do repozytoriów, i nie mieliśmy tam dostępu. I ogólnie rzecz biorąc, nie będzie możliwe rozwiązanie naszego bólu w ten sposób.

Mobilne środowisko CICD: jeden standard Fastlane dla wielu aplikacji mobilnych

Zadanie nr 2: zdobądź pojedynczy plik Fastfile dla N aplikacji

Teraz wydaje się, że rozwiązanie problemu nie jest takie trudne - ustaw zmienne i jedziemy. Tak, faktycznie, w ten sposób problem został rozwiązany. Ale w momencie, gdy to schrzaniliśmy, nie mieliśmy ani wiedzy o samym fastlane, ani o Ruby, w którym napisany jest fastlane, ani przydatnych przykładów w sieci - każdy, kto pisał wtedy o fastlane, ograniczał się wówczas do przykładu dla jednej aplikacji dla jednego dewelopera.

Fastlane może obsługiwać zmienne środowiskowe i już tego próbowaliśmy, ustawiając hasło pęku kluczy:

ENV['KEYCHAIN_PASSWORD']

Po przejrzeniu naszych skryptów zidentyfikowaliśmy wspólne części:

#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

Teraz, aby zacząć używać tych kluczy w plikach Fastlane, musieliśmy wymyślić, jak je tam dostarczyć. Fastlane ma na to rozwiązanie: ładowanie zmiennych poprzez dotenv. Dokumentacja mówi, że jeśli ważne jest, aby załadować klucze do różnych celów, utwórz kilka plików konfiguracyjnych w katalogu Fastlane .env, .env.default, .env.development.

A potem postanowiliśmy wykorzystać tę bibliotekę nieco inaczej. Umieśćmy w repozytorium programistów nie skrypty Fastlane i ich metainformacje, ale unikalne klucze tej aplikacji w pliku .env.appName.

Sami Fastfile, Appfile, Matchfile и Gymfile, ukryliśmy go w osobnym repozytorium. Ukryto tam dodatkowy plik z kluczami haseł z innych serwisów - .env.
Możesz zobaczyć przykład tutaj.

Mobilne środowisko CICD: jeden standard Fastlane dla wielu aplikacji mobilnych

Na CI wywołanie nie uległo większym zmianom, dodano klucz konfiguracyjny dla konkretnej aplikacji:

# 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

Przed uruchomieniem poleceń ładujemy nasze repozytorium skryptami. Nie wygląda to zbyt ładnie:

git clone [email protected]/FastlaneCICD.git fastlane_temp

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

Na razie porzuciłem to rozwiązanie, chociaż Fastlane ma rozwiązanie do pobierania Fastfile przez akcja import_from_git, ale działa tylko w przypadku Fastfile, ale nie w przypadku innych plików. Jeśli chcesz „naprawdę piękne”, możesz napisać własne action.

Podobny zestaw został wykonany dla aplikacji Android i ReactNative, pliki znajdują się w tym samym repozytorium, ale w różnych gałęziach iOS, android и react_native.

Kiedy zespół wydawniczy chce dodać jakiś nowy krok, zmiany w skrypcie są rejestrowane poprzez MR w git, nie ma już potrzeby szukania winowajców zepsutych skryptów i w ogóle teraz trzeba próbować to złamać.

Teraz to już na pewno

Wcześniej spędzaliśmy czas na utrzymywaniu wszystkich skryptów, aktualizowaniu ich i naprawianiu wszystkich konsekwencji aktualizacji. Było to bardzo rozczarowujące, gdy przyczyną błędów i przestojów w wydaniach były zwykłe literówki, które tak trudno było wyśledzić w gąszczu skryptów powłoki. Teraz tego typu błędy zostały zredukowane do minimum. Zmiany są wdrażane we wszystkich aplikacjach jednocześnie. A włączenie nowej aplikacji do procesu zajmuje 15 minut — skonfiguruj potok szablonów w CI i dodaj klucze do repozytorium programisty.

Wygląda na to, że kwestia Fastfile dla Androida i sygnatury aplikacji pozostaje niewyjaśniona, jeśli artykuł będzie ciekawy, napiszę kontynuację. Z przyjemnością zobaczę Twoje pytania lub sugestie „Jak rozwiązać ten problem” w komentarzach lub na Telegramie baszkirowa.

Źródło: www.habr.com

Dodaj komentarz