Esperienza CICD mobile: uno standard fastlane per molte applicazioni mobili

Esperienza CICD mobile: uno standard fastlane per molte applicazioni mobili
Vorrei parlare di integrazione continua e distribuzione di app mobili tramite fastlane. Come implementiamo CI/CD su tutte le applicazioni mobili, come ci siamo arrivati ​​e cosa è successo alla fine.

In rete c'è già abbastanza materiale sullo strumento, cosa che all'inizio ci mancava, quindi volutamente non descriverò lo strumento in dettaglio, ma mi riferirò solo a ciò che avevamo allora:

L'articolo è composto da due parti:

  • Contesto dell'emergere di CI/CD mobile in azienda
  • Soluzione tecnica per l'implementazione di CI/CD per applicazioni N

La prima parte è più nostalgia per i vecchi tempi, mentre la seconda è un'esperienza che puoi applicare a te stesso.

Così è successo storicamente

2015 anno

Abbiamo appena iniziato a sviluppare applicazioni mobili, quindi non sapevamo nulla di integrazione continua, di DevOps e altre cose alla moda. Ogni aggiornamento dell'applicazione è stato implementato dallo sviluppatore stesso dal suo computer. E se per Android è abbastanza semplice: assemblato, firmato .apk e lo abbiamo caricato nella Console per gli sviluppatori di Google, poi per iOS l'allora strumento di distribuzione tramite Xcode ci ha lasciato delle serate fantastiche: i tentativi di scaricare l'archivio spesso finivano con errori e abbiamo dovuto riprovare. Si è scoperto che lo sviluppatore più avanzato non scrive il codice più volte al mese, ma piuttosto rilascia l'applicazione.

2016 anno

Siamo cresciuti, pensavamo già a come liberare gli sviluppatori da un'intera giornata per il rilascio, ed è apparsa anche una seconda applicazione, che ci ha solo spinto maggiormente verso l'automazione. Nello stesso anno abbiamo installato Jenkins per la prima volta e scritto una serie di script spaventosi, molto simili a quelli mostrati da Fastlane nella sua documentazione.

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

Sfortunatamente, fino ad ora solo i nostri sviluppatori sapevano come funzionano questi script e perché è necessaria questa pila infinita di chiavi, e quando qualcosa si è rotto di nuovo, hanno avuto delle “serate meravigliose” per analizzare i log.

2017 anno

Quest'anno abbiamo imparato che esiste una cosa chiamata fastlane. Non c'erano tante informazioni quante ce ne sono adesso: come avviarne uno, come usarlo. E lo strumento stesso a quel tempo era ancora rozzo: gli errori costanti non facevano altro che deluderci ed era difficile credere nella magica automazione che promettevano.

Tuttavia, le principali utilità incluse nel core Fastlane lo sono gym и pilot, siamo riusciti ad avviarlo.

I nostri script sono stati leggermente migliorati.

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

Sono stati migliorati, se non altro perché non tutti i parametri necessari per xcodebuild, devi indicare - gym capirà in modo indipendente dove e cosa si trova. E per una regolazione più precisa, puoi specificare le stesse chiavi di in xcodebuild, solo la denominazione dei tasti è più chiara.

Questa volta, grazie a Gym e al formattatore xcpretty integrato, i registri di compilazione sono diventati molto più leggibili. Ciò ha iniziato a far risparmiare tempo nella riparazione di assiemi danneggiati e talvolta il team di rilascio è riuscito a capirlo da solo.

Sfortunatamente, le misurazioni della velocità di assemblaggio xcodebuild и gym Non l'abbiamo fatto, ma ci fideremo della documentazione: fino al 30% di velocità in più.

Processo unico per tutte le applicazioni

Anno 2018 e presenti

Nel 2018, il processo di creazione e implementazione delle applicazioni è stato completamente spostato su Jenkins, gli sviluppatori hanno smesso di rilasciare dalle loro macchine e solo il team di rilascio aveva il diritto di rilasciare.

Volevamo già migliorare il lancio dei test e dell'analisi statica, e i nostri script crescevano sempre di più. Cresciuto e cambiato insieme alle nostre applicazioni. All’epoca le applicazioni erano circa 10. Considerando che abbiamo due piattaforme, si tratta di circa 20 script “vivi”.

Ogni volta che volevamo aggiungere un nuovo passaggio allo script, dovevamo copiare e incollare i pezzi in tutti gli script di shell. Forse avremmo potuto lavorare con più attenzione, ma spesso tali modifiche finivano con errori di battitura, che si trasformavano in serate in cui il team di rilascio passava a correggere gli script e scoprire quale ragazzo intelligente ha aggiunto questo comando e cosa fa effettivamente. In generale, non si può dire che gli script per l'assemblaggio per una piattaforma fossero almeno in qualche modo simili. Anche se sicuramente hanno fatto la stessa cosa.

Per avviare il processo per una nuova applicazione, era necessario dedicare un giorno alla selezione di una versione “fresca” di questi script, eseguirne il debug e dire che “sì, funziona”.

Nell’estate del 2018 abbiamo guardato ancora una volta alla fastlane ancora in fase di sviluppo.

Attività n. 1: riepilogare tutte le istruzioni di script e riscriverle in Fastfile

Quando abbiamo iniziato, i nostri script sembravano una coperta composta da tutti i passaggi e le stampelle in un unico script di shell in Jenkins. Non siamo ancora passati alla pipeline e alla divisione per fasi.

Abbiamo esaminato ciò che abbiamo e identificato 4 passaggi che si adattano alla descrizione del nostro CI/CD:

  • build: installazione delle dipendenze, assemblaggio dell'archivio,
  • test: esecuzione di test unitari per sviluppatori, calcolo della copertura,
  • sonar: avvia tutti i linter e invia report a SonarQube,
  • deploy: invio di un artefatto ad alpha (TestFlight).

E se non entri nei dettagli, omettendo i tasti usati nelle azioni, otterrai questo 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

In effetti, il nostro primo Fastfile si è rivelato mostruoso, considerando alcune delle stampelle di cui avevamo ancora bisogno e il numero di parametri che abbiamo sostituito:

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

Nell'esempio sopra, solo una parte dei parametri che dobbiamo specificare: questi sono i parametri di build - schema, configurazione, nomi dei profili di fornitura, nonché parametri di distribuzione - ID Apple dell'account sviluppatore, password, ID dell'applicazione e così via SU. In prima approssimazione, inseriamo tutte queste chiavi in ​​file speciali: Gymfile, Matchfile и Appfile.

Ora in Jenkins puoi richiamare comandi brevi che non offuscano la vista e siano facilmente leggibili a occhio nudo:

# fastlane ios <lane_name>

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

Evviva, siamo fantastici

Cosa hai preso? Comandi chiari per ogni passaggio. Script ripuliti, disposti ordinatamente in file fastlane. Rallegrandoci, siamo corsi dagli sviluppatori chiedendo loro di aggiungere tutto ciò di cui avevano bisogno nei loro repository.

Ma ci siamo resi conto in tempo che avremmo incontrato le stesse difficoltà: avremmo ancora 20 script di assembly che in un modo o nell'altro avrebbero iniziato a vivere la propria vita, sarebbe stato più difficile modificarli, poiché gli script si sarebbero spostati nei repository, e non abbiamo avuto accesso lì. E, in generale, non sarà possibile risolvere il nostro dolore in questo modo.

Esperienza CICD mobile: uno standard fastlane per molte applicazioni mobili

Attività n.2: ottieni un singolo Fastfile per N applicazioni

Ora sembra che risolvere il problema non sia così difficile: imposta le variabili e andiamo. Sì, in effetti, è così che è stato risolto il problema. Ma nel momento in cui abbiamo sbagliato tutto, non avevamo né esperienza nella fastlane stessa, né in Ruby, in cui è scritto fastlane, né esempi utili sulla rete: tutti quelli che scrivevano su fastlane allora si limitavano a un esempio per un'applicazione per uno sviluppatore.

Fastlane può gestire variabili d'ambiente e lo abbiamo già provato impostando la password del portachiavi:

ENV['KEYCHAIN_PASSWORD']

Dopo aver esaminato i nostri script, abbiamo identificato le parti comuni:

#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

Ora, per iniziare a utilizzare queste chiavi nei file fastlane, dovevamo capire come consegnarle lì. Fastlane ha una soluzione per questo: caricamento delle variabili tramite dotenv. La documentazione dice che se per te è importante caricare le chiavi per scopi diversi, crea diversi file di configurazione nella directory fastlane .env, .env.default, .env.development.

E poi abbiamo deciso di utilizzare questa libreria in modo leggermente diverso. Inseriamo nel repository degli sviluppatori non gli script fastlane e le relative meta informazioni, ma le chiavi univoche di questa applicazione nel file .env.appName.

se stessi Fastfile, Appfile, Matchfile и Gymfile, lo abbiamo nascosto in un repository separato. Lì era nascosto un file aggiuntivo con chiavi password di altri servizi: .env.
Puoi vedere un esempio qui.

Esperienza CICD mobile: uno standard fastlane per molte applicazioni mobili

Su CI la chiamata non è cambiata molto; è stata aggiunta una chiave di configurazione per un'applicazione specifica:

# 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

Prima di eseguire i comandi, carichiamo il nostro repository con gli script. Non sembra così carino:

git clone [email protected]/FastlaneCICD.git fastlane_temp

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

Per ora abbiamo lasciato questa soluzione, anche se Fastlane ha una soluzione per scaricare Fastfile tramite azione import_from_git, ma funziona solo per Fastfile, ma non per altri file. Se vuoi “davvero bello”, puoi scriverne uno tuo action.

Un set simile è stato creato per le applicazioni Android e ReactNative, i file si trovano nello stesso repository, ma in rami diversi iOS, android и react_native.

Quando il team di rilascio vuole aggiungere qualche nuovo passaggio, le modifiche allo script vengono registrate tramite MR in git, non c'è più bisogno di cercare i colpevoli degli script non funzionanti e, in generale, ora devi provare a romperli.

Questo è certo

In precedenza, dedicavamo del tempo alla manutenzione di tutti gli script, all'aggiornamento e alla correzione di tutte le conseguenze degli aggiornamenti. È stato molto deludente quando le ragioni degli errori e dei tempi di inattività nei rilasci erano semplici errori di battitura di cui era così difficile tenere traccia nel caos degli script di shell. Ora tali errori sono ridotti al minimo. Le modifiche vengono implementate in tutte le applicazioni contemporaneamente. Inoltre, sono necessari 15 minuti per inserire una nuova applicazione nel processo: impostare una pipeline di modelli su CI e aggiungere le chiavi al repository dello sviluppatore.

Sembra che il problema con Fastfile per Android e la firma dell'applicazione rimanga inspiegato, se l'articolo è interessante scriverò il seguito. Sarò felice di vedere le tue domande o suggerimenti "come risolveresti questo problema" nei commenti o su Telegram Bashkirova.

Fonte: habr.com

Aggiungi un commento