Вопыт мабільнага CICD: адзін стандарт fastlane на шмат мабільных прыкладанняў

Вопыт мабільнага CICD: адзін стандарт fastlane на шмат мабільных прыкладанняў
Я б хацела пагаварыць аб бесперапыннай інтэграцыі і дастаўцы для мабільных прыкладанняў з дапамогай fastlane. Як мы ўкараняем CI/CD на ўсе мабільныя прыкладанні, як мы да гэтага ішлі і што атрымалася ў выніку.

У сетцы ўжо дастаткова матэрыялу па інструменце, якога так не хапала нам на старце, таму я наўмысна не буду падрабязна апісваць інструмент, а толькі спашлюся на тое, што было ў нас тады:

Артыкул складаецца з дзвюх частак:

  • Перадгісторыя з'яўлення мабільнага CI/CD у кампаніі
  • Тэхнічнае рашэнне раскочвання CI/CD на N-прыкладанняў

Першая частка - больш настальгія па былых часах, а другая ж - вопыт, які можна прымяніць у сябе.

Так гістарычна склалася

Год 2015

Мы толькі пачалі займацца распрацоўкай мабільных дадаткаў, тады яшчэ мы нічога не ведалі пра бесперапынную інтэграцыю, пра DevOps і іншыя модныя штукі. Кожнае абнаўленне прыкладання выкочваў сам распрацоўшчык са сваёй машыны. І калі для Android гэта дастаткова проста — сабраў, падпісаў .apk і закінуў у Google Developer Console, то для iOS тагачасны інструмент дыстрыбуцыі праз Xcode пакідаў нам шыкоўныя вечары – спробы загрузіць архіў часта заканчваліся памылкамі і даводзілася спрабаваць яшчэ раз. Атрымлівалася, што самы прапампаваны распрацоўшчык некалькі разоў у месяц не піша код, а займаецца рэлізам прыкладання.

Год 2016

Мы падраслі, за плячыма ўжо былі думкі аб тым, як вызваліць распрацоўшчыкаў ад цэлага дня для рэлізу, а гэтак жа з'явілася другое прыкладанне, што толькі мацней нас падштурхоўвала да аўтаматызацыі. У той жа год мы ўпершыню паставілі Jenkins і напісалі кучку страшненькіх скрыптоў, вельмі падобных да тых, што паказвае fastlane у сваёй дакументацыі.

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

Нажаль, аб тым як гэтыя скрыпты працуюць і для чаго патрэбны гэты бясконцы пачак ключоў дагэтуль ведалі толькі нашы распрацоўнікі, а калі нешта ў чарговы раз ламалася, "шыкоўныя вечары" для разбораў логаў ім жа і даставаліся.

Год 2017

У гэтым годзе мы даведаліся, што ёсць такая штука як fastlane. Было не так шмат інфармацыі, як цяпер - як завесці, як выкарыстоўваць. Ды і сам інструмент быў тады яшчэ сыраваты: пастаянныя памылкі, толькі расчароўвалі нас і ў чароўную аўтаматызацыю, якую яны абяцалі, верылася з цяжкасцю.

Аднак асноўныя ўтыліты, якія ўваходзяць у ядро ​​fastlane, - gym и pilot, у нас атрымалася завесці.

Нашы скрыпты крыху акультурыліся.

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

Акультурыліся хаця б таму, што не ўсе параметры, неабходныя для xcodebuild, трэба паказваць gym самастойна зразумее дзе і што ляжыць. А для больш тонкай наладкі можна паказаць тыя ж самыя ключы, што і ў xcodebuild, толькі нэймінг ключоў больш зразумела.

На гэты раз, дзякуючы gym і ўбудаванаму фарматар xcpretty, логі зборкі сталі нашмат пераборлівей. Гэта стала эканоміць час на папраўку зламанай зборкі, а часам у гэтым магла самастойна разабрацца рэліз-каманда.

Нажаль, замераў па хуткасці зборкі xcodebuild и gym мы не зрабілі, але будзем верыць дакументацыі - да 30% паскарэння.

Адзіны працэс на ўсе прыкладанні

Год 2018 і цяперашні час

Да 2018 году сам працэс зборкі і выкаткі прыкладанняў цалкам пераехаў на Jenkins, распрацоўнікі перасталі рэлізаваць са сваіх машын, права на рэліз мела толькі рэліз-каманда.

Нам ужо захацелася дакруціць запуск тэстаў і статычны аналіз, а нашыя скрыпты раслі і раслі. Раслі і мяняліся разам з нашымі праграмамі. На той момант прыкладанняў было каля 10. Улічваючы, што платформы ў нас дзве – гэта каля 20 "якія жывуць" скрыптоў.

Кожны раз, калі мы жадалі дадаць новы крок у скрыпт, прыходзілася капіпасціць кавалачкі ва ўсе shell-скрыпты. Магчыма, можна было працаваць і акуратней, але часта такія змены заканчваліся памылкамі друку, якія ўжо ператвараліся ў вечары рэліз-каманды на папраўку скрыптоў і высвятленні, хто з разумнікаў дадаў гэтую каманду і што яна наогул робіць. У цэлым нельга сказаць што скрыпты для зборкі пад адну платформу былі хоць колькі-небудзь падобнымі. Хаця безумоўна рабілі адно і тое ж.

Для таго каб завесці працэс для новага прыкладання - трэба было выдаткаваць дзень, каб падабраць "свежую" версію з гэтых скрыптоў, адладзіць і сказаць што "так, працуе".

Улетку 2018 мы яшчэ раз паглядзелі ў бок усё яшчэ развіваецца fastlane.

Задача №1: абагульніць усе крокі скрыптоў і перапісаць іх у Fastfile

Калі мы пачыналі, нашы скрыпты выглядалі анучай з усіх крокаў і мыліц у адным shell-скрыпце ў Jenkins. Мы яшчэ не перайшлі на pipeline і дзяленне па stage.

Паглядзелі на тое, што ёсць і вылучылі 4 кроку, падыходных пад апісанне нашага CI/CD:

  • build - усталёўка залежнасцяў, зборка архіва,
  • test - запуск unit-тэстаў распрацоўніка, падлік пакрыцця,
  • sonar - запуск усіх лінтэраў і адпраўка справаздач у SonarQube,
  • deploy - адпраўка артэфакта ў альфу (TestFlight).

І калі не ўдавацца ў падрабязнасці, апусціць выкарыстоўваныя ключы ў actions, атрымаецца вось такі 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

Насамрэч, першы Fastfile у нас атрымаўся монструозным, улічваючы некаторыя мыліцы, якія нам усё яшчэ былі патрэбныя, і колькасць параметраў, якія мы падстаўлялі:

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

На прыкладзе вышэй, толькі частка параметраў, якія нам трэба паказаць: гэта параметры зборкі - схема, канфігурацыя, назвы Provision Profile, а таксама параметры дыстрыбуцыі - Apple ID акаўнта распрацоўніка, пароль, ідэнтыфікатар прыкладання і гэтак далей. У першым набліжэнні мы паклалі ўсе гэтыя ключы ў спецыяльныя файлы. Gymfile, Matchfile и Appfile.

Зараз у Jenkins можна выклікаць кароткія каманды, якія не "замыльваюць" погляд і добра счытваюцца вокам:

# fastlane ios <lane_name>

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

Ура, мы малайцы

Што атрымалі? Зразумелыя каманды для кожнага кроку. Прычасаныя скрыпты, акуратна раскладзеныя ў файлы fastlane. Узрадаваўшыся, мы было пабеглі да распрацоўшчыкаў з просьбай дадаць усё што трэба ў свае рэпазітары.

Але своечасова ўсвядомілі, што сутыкнемся з тымі ж складанасцямі - у нас усё яшчэ будзе 20 скрыптоў зборкі, якія так ці інакш пачнуць жыць сваім жыццём, правіць іх будзе складаней, бо скрыпты пераедуць у рэпазітары, а ў нас доступу туды няма. І, увогуле, вырашыць наш боль такім чынам не атрымаецца.

Вопыт мабільнага CICD: адзін стандарт fastlane на шмат мабільных прыкладанняў

Задача №2: атрымаць адзіны Fastfile для N-прыкладанняў

Цяпер ужо здаецца, што вырашыць задачу не так ужо і складана - задайце зменныя, і паехалі. Так, уласна, так задачу і вырашылі. Але ў той момант, калі мы гэта ўкручвалі, у нас не было ні экспертызы ў самім fastlane, ні ў Ruby, на якім напісаны fastlane, ні карысных прыкладаў у сетцы — кожны, хто пісаў пра fastlane тады, абмяжоўваўся прыкладам для аднаго дадатку для аднаго распрацоўшчыка.

Fastlane умее ў зменныя асяроддзі, і гэта мы ўжо паспрабавалі, задаўшы пароль ад Keychain:

ENV['KEYCHAIN_PASSWORD']

Паглядзеўшы на нашыя скрыпты, мы вылучылі агульныя часткі:

#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

Цяпер для таго, каб пачаць выкарыстоўваць гэтыя ключы ў файлах fastlane'а, трэба было прыдумаць як іх туды дастаўляць. У Fastlane ёсць для гэтага рашэнне: загрузка зменных праз dotenv. У дакументацыі сказана, калі вам важна падгружаць ключы для розных мэт, напладзіце ў дырэкторыі fastlane некалькі канфігурацыйных файлаў .env, .env.default, .env.development.

І тады мы вырашылі выкарыстоўваць гэтую бібліятэку крыху па-іншаму. Змесцім у рэпазітары распрацоўнікаў не скрыпты fastlane і яго мэта інфармацыю, а ўнікальныя ключы гэтага прыкладання ў файле .env.appName.

Самі Fastfile, Appfile, Matchfile и Gymfile, мы схавалі ў асобны рэпазітар. Туды ж схавалі дадатковы файл з ключамі-паролямі ад іншых сэрвісаў. .env.
Прыклад, можна паглядзець тут.

Вопыт мабільнага CICD: адзін стандарт fastlane на шмат мабільных прыкладанняў

На CI выклік не моцна памяняўся, дадаўся ключ канфігурацыі канкрэтнага дадатку:

# 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

Перад тым, як запускаць каманды, мы падгружаем наш рэпазітар са скрыптамі. Выглядае не так прыгожа:

git clone [email protected]/FastlaneCICD.git fastlane_temp

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

Пакуль пакінулі гэтае рашэнне, хоць у Fastlane ёсць рашэнне для загрузкі Fastfile праз дзеянне import_from_git, але ён працуе толькі для Fastfile, для астатніх жа файлаў - не. Калі хочацца "прам зусім прыгожа", можна напісаць свой action.

Аналагічны набор зрабілі для Android прыкладанняў і ReactNative, файлы ляжаць у адным рэпазітары, але ў розных галінках iOS, android и react_native.

Калі рэліз каманда жадае дадаць які-небудзь новы крок, змены ў скрыпце фіксуюцца праз MR у git, больш не трэба шукаць вінаватых паламаных скрыптоў, ды і ў цэлым - зламаць зараз, гэта трэба паспрабаваць.

Цяпер сапраўды ўсё

Раней мы марнавалі час на падтрымку ўсіх скрыптоў, іх абнаўленне і папраўку ўсіх наступстваў абнаўленняў. Было вельмі крыўдна, калі чыннікі памылак і прастояў рэлізаў былі простымі памылкамі друку, за якімі так складана ўсачыць у мяшаніне shell-скрыпту. Цяпер жа такія памылкі зведзены да мінімуму. Змены накатваюцца адразу на ўсе прыкладанні. А новае прыкладанне завесці ў працэс каштуе 15 хвілін - наладзіць шаблонны pipeline на CI і дадаць ключы ў рэпазітар распрацоўніка.

Здаецца, застаўся неасветленым пункт з Fastfile для Android і подпіс прыкладанняў, калі артыкул будзе цікавая, напішу працяг. Буду рада вашым пытанням або прапановам "як бы вы вырашылі гэтую задачу" у каментарах або ў Telegram bashkirova.

Крыніца: habr.com

Дадаць каментар