Досвід інтеграції CLI-інструментів в роботу. Що було складно і як виправляли помилки

Усі статті, обговорення, новини про Mobile — в одному місці. Підписуйтеся на телеграм-канал!

Привіт! Мене звати Катерина Ніколаєва, iOS Software Engineer в Uklon. В матеріалі розповім про досвід використання CLI-інструментів для автоматизації рутинних дій. Що було складним та з якими «підводними каменями» ми зіштовхнулися в процесі роботи з ними.

Ця інформація буде корисною для всіх iOS-розробників, які працюють або планують працювати з CI (Continuous Integration) та мають намір самостійно її налаштовувати.

Command line interface tools

Це програми, які виконуються в командному рядку на локальній машині. Вони дозволяють взаємодіяти з операційною системою та іншим програмним забезпеченням без необхідності використовувати графічний інтерфейс.

Terminal — це найтиповіший інструмент для роботи з командним рядком на MacOS. Він дозволяє виконувати команди, запускати скрипти, змінювати налаштування системи, взаємодіяти з інтерфейсом Git та багато іншого.

Про що піде мова?

  • В першу чергу, розглянемо основні можливості CocoaPods.
  • Далі перейдемо до базового функціонала та особливостей налаштування Fastlane.
  • Наостанок найцікавіше — це процес інтеграції Fastlane з СІ.
  • І на завершення підіб’ємо підсумки щодо основних чекпоінтів.

CocoaPods

Напевно, майже не існує застосунків без сторонніх залежностей. І для налаштування дуже зручно використовувати менеджер залежностей.

Одним зі зручних та ефективних рішень для завантаження та контролю бібліотек і фреймворків є CocoaPods — це менеджер залежностей для розробки на мові Objective-C та Swift для платформи iOS та macOS.

CocoaPods дозволяє легко встановлювати та керувати залежностями в проєктах, оновлювати та зберігати їх конфігурації для забезпечення стабільності й надійності при розробці мобільних застосунків на платформі iOS.

У світі iOS розробки існують альтернативні менеджери залежностей, такі як Carthage та Swift Package Manager. Наразі ми активно використовуємо CocoaPods тому, що він досить зручний у використанні і задовольняє наші потреби.

Чому у 2023 році ми все ще віддаємо перевагу CocoaPods, а не стильному, модному, молодіжному Swift Package Manager? Відповідь дуже проста. Наразі не всі бібліотеки та фреймворки підтримують SPM. Потрібен час, щоб весь софт був адаптований до нового менеджера.

Основні компоненти CocoaPods

Базовий набір при роботі з CocoaPods — це Podfile, Podfile.lock та Podspec.

Podfile

Файл конфігурації для керування залежностями у проєктах мовою Swift та Objective-C, які використовують CocoaPods. Він знаходиться в кореневій папці проєкту та має ім’я «Podfile».

Щоб встановити залежність, після створення або зміни Podfile необхідно у терміналі перейти до папки з проєктом і виконати команду pod install.

CocoaPods завантажить та встановить усі зазначені залежності.

Podfile має такі основні можливості:

Target Dependencies. Якщо в проєкті, крім основного таргету, є ще, наприклад, тести або віджет, то для кожного з таргетів можна прописати свої залежності, які будуть встановлені безпосередньо в рамках його потреб.

Версії. В Podfile для кожної залежності можна вказати конкретну версію, яка вам потрібна, і при наступних командах pod install та pod update CocoaPods не буде встановлювати версію софта вище, ніж вона вказана.

Також замість версії можна вказати конкретну гілку або коміт, з якого потрібно витягнути оновлення. Це дуже гнучкий та зручний механізм при роботі з приватними подами.

Post install hooks. Це функціональність CocoaPods, яка дозволяє виконувати код користувача після встановлення залежностей за допомогою команди pod install.

Після встановлення залежностей CocoaPods виконує скрипти post-install, які можуть бути визначені в Podfile. Ці скрипти можуть використовуватися для автоматизації додаткових завдань, таких як налаштування конфігурації проєкту або встановлення файлів користувача та бібліотек.

Наприклад: ми використовуємо цю фічу для налаштування конфігурацій білда, а саме автоматичного встановлення Development Team для кожного таргету.

Podfile.lock

Це файл, який містить інформацію про версії фактично встановлених залежностей. Він створюється автоматично після виконання команди pod install та має бути обов’язково доданим до репозиторію.

Manifest.lock. Це файл, що створюється CocoaPods у процесі встановлення залежностей. Він містить інформацію про версії та конфігурацію всіх встановлених залежностей та використовується для забезпечення сумісності версій бібліотек між різними збірками проєкту.

Нічого не нагадує?) Простими словами — це локальний аналог Podfile.lock.

На етапі запуску проєкту Xcode виконує скрипт, який перевіряє, чи збігаються Manifest.lock і Podfile.lock за змістом. І якщо в них є розбіжності, запуск фейлиться і для розв’язання такого конфлікту можна просто видалити ці файли і виконати команду pod install для генерації нових.

В певний момент часу ми вирішили винести всі наші поди з репозиторію до .gitignore. Це дало великий профіт в тому, що наші мердж реквести стали компактнішими та читабельнішими без зайвої інформації. Також це знизило ризик виникнення конфліктів у подах.

Єдиним недоліком цього рішення було те, що для коректного запуску проєкту потрібно локально робити pod install кожного разу, якщо Manifest.lock не сходитися з Podfile.lock. Але зручність та швидкість проведення рев’ю МРів того вартувала.

Podspec

Podspec, скорочення від pod specification, — це файл, який визначає метадані та конфігурацію для конкретної бібліотеки або фреймворку, які можна додати до проєкту за допомогою CocoaPods.

Робота з ним, як правило, починається при створенні та конфігурації власних приватних подів.

Деякі ключові елементи цього файлу Podspec:

  • name: вказує назву бібліотеки або фреймворку;
  • version: вказує номер версії бібліотеки;
  • summary: надається короткий опис бібліотеки;
  • homepage: вказує URL-адресу домашньої сторінки або сховища бібліотеки;
  • license: вказує тип ліцензії та файл для бібліотеки;
  • author: вказує ім’я та електронну адресу автора;
  • source: вказує розташування вихідного коду, як правило, URL-адресу сховища Git;
  • source_files: визначає файли, які будуть включені до бібліотеки;
  • platform: визначає цільову платформу та її мінімальну версію;
  • swift_version: вказує мінімальну необхідну версію Swift;
  • dependency: визначає будь-які залежності, необхідні для бібліотеки.

Це лише базовий приклад, і є багато інших параметрів і конфігурацій, які можна додати до файлу Podspec на основі конкретних потреб бібліотеки.

Всі можливості Podspec можна розглянути в офіційній документації.

Fastlane

Інструмент автоматизації процесу розгортання мобільних застосунків. Надає набір заздалегідь побудованих дій, які розробники можуть використовувати для автоматизації загальних задач, таких як: збірка, тестування, підписання коду сертифікатами, збільшення версії, публікація в маркеті та багато іншого.

Крім того, Fastlane можна розширити за допомогою власних скриптів та плагінів, щоб додати функціональність, яка відповідає потребам розробників.

Fastlane для наших задач

В рамках щоденних викликів Fastlane стає у пригоді для таких речей.

Запуск тестів на відповідному симуляторі. Цей кейс не є тривіальним, оскільки, якщо над проєктом працює декілька розробників, у кожного з них симулятор може бути різним. Fastlane на нативному рівні вміє контролювати, який саме симулятор має бути запущений на локальній машині.

Повністю відповідає за code signing. Напевно, майже кожен iOS developer стикався з нюансами сертифікатів і профайлів для коректного запуску проєкту і деплою білдів. Всі ці дії автоматизуються за допомогою Fastlane.

Проставляє версії в .plist файлах. Замість інкременту версії програми вручну, Fastlane виконує скрипт, який автоматично виконує цю дію для кожного з таргетів у проєкті. Це значно пришвидшує процес підготовки релізів до випуску.

Збирає білди. Найтиповіший для багатьох розробників юзкейс використання Fastlane у повсякденному житті, й наша команда також не виключення.

Дуже зручний спосіб делегування відповідальності та зниження ризику людського фактора при формуванні .ipa архівів, а також подальша їх відправка в App Store Connect.

Особливості використання всього вище описаного при налаштуванні CI

Налаштовувати CI можна різними способами. Як CI ми використовуємо Gitlab Runner і для його налаштування під наші потреби ми обрали використання Ruby скриптів.

iOS деви, так чи інакше, у своїй роботі стикаються з Ruby як мінімум тому, що вище зазначені інструменти CocoaPods і Fastlane — програми на основі Ruby.

Одразу додам дисклеймер, що наступна інформація для Ruby розробників є тривіальною і простою, але для iOS девів, які напряму не працюють з Ruby, вона не є очевидною.

І бувають ситуації, коли починаєш запускати якісь скрипти, отримуєш помилки, шукаєш рішення в інтернеті й лікуєш проблему рандомними запропонованими командами, які, як правило, не тільки не допомагають, а можуть зробити тільки гірше.

І це проблема не точкова. Навіть існують платні сервіси, які обіцяють софт, що має полегшити життя при роботі з Ruby. Але, на жаль, тут теж може бути не все так однозначно. Такі програми теж можуть мати свої нюанси, з якими потім теж треба буде щось зробити, щоб їх уникнути.

І, щоб не розвʼязувати проблеми стороннього програмного забезпечення, ми пішли шляхом самостійного налаштування роботи з Ruby. Шляхом спроб і помилок були виявлені основні моменти:

  • Не слід використовувати системну версію Ruby.
  • Варто використовувати менеджер rbenv чи rvm.

Як правило нативна версія Ruby, яка поставляється разом з MacOS, є дуже старою і потрібна для підтримки системних налаштувань. Для наших задач потрібна свіжіша версія програмного забезпечення й оновлювати системну версію Ruby — дуже погана ідея, тому що це може спричинити непередбачувані наслідки в роботі самої системи.

Тому потрібно створити своєрідне «віртуальне оточення», за допомогою менеджерів rbenv чи rvm встановити потрібну версію Ruby (або навіть декілька) і працювати в рамках цього простору, не зачіпаючи системні налаштування.

Але як тепер зрозуміти, з якими сторонніми Ruby залежностями буде відбуватися взаємодія? І тут на допомогу приходить Bundler.

Bundler for Ruby

Bundler — це менеджер пакетів для Ruby, який забезпечує узгоджене середовище для керування залежностями програми Ruby.

Він керує залежностями програми, відстежуючи та встановлюючи точні версії gems (бібліотек Ruby), які потрібні. Bundler має аналогічну CocoaPods структуру та схожі компоненти. Він складається з:

  • Gemfile’у, що є аналогом Podfile’у;
  • Gemfile.lock, який є аналогом Podfile.lock;
  • bundle exec — виконання в контексті bundler.

Для запуску потрібного скрипта необхідно виконати команду bundle exec ruby script.rb. Це забезпечить правильну взаємодію з програмним забезпеченням, не використовуючи глобальні залежності.

Для налаштування нашої CI виявилося, що Ruby недостатньо, потрібно ще внести деякі конфігурації на Python.

venv for Python

Щоб завершити процес налаштування CI, потрібно було зробити ряд аналогічних дій, але вже на Python. За допомогою команди

python3 -m venv .env && source .env/bin/activate && pip3 install ...

теж створювалось умовне «віртуальне середовище» для подальшого виконання потрібних команд. Таким чином процес підготовки CI майже завершили.

Змінні оточення

Фінальним кроком є налаштування, прописані у змінних оточеннях. І тут теж криється підступний момент. Очевидним є прописання всіх потрібних конфігурацій в ~/. bash_profile. Але з певної версії MacOS змінився shell, за замовчуванням. І новий замість ~/.bash_profile дивиться на аналогічний йому ~/. zprofile.

Тому потрібні конфігурації необхідно вносити в обох місцях. Тому що ~/.zprofile — працюють у shell за замовчуванням, а ~/.bash_profile — це єдине, що на момент нашої конфігурації підтримувалося gitlab-runner’ом.

При внесенні потрібних змін білди будуть збиратися коректно як локально, так і за допомогою СI.

Робота над помилками

І от, коли все вже налаштовано, СІ готова, пайплайни запущені і вже відпрацьовують, раптом щось йде не по плану і ми отримуємо Fail.

Важливо в такому випадку правильно зрозуміти, з чим конкретно пов’язана помилка. Тому що остання помилка, яка знаходиться в простирадлі логів, рідко коли є «коренем зла».

Розглянемо приклад на основі логів однієї нашої job’и, яка зазнала поразки. Останньою помилкою була відсутність файлів до завантаження:

Але це не було фінальною помилкою, тому що існує причина, чому ми не змогли отримати файли. Піднімаючись далі вгору, натрапляємо на таку помилку:

яка говорить нам про те, що не вистачає derived data директорії. І це все ще не розв’язка нашої проблеми. Рухаємось далі, ще вище, і бачимо:

Щось пішло не так з передачею параметрів. Але це теж не є досить інформативним поясненням того, що насправді відбулося. Радує лише те, що це все ще не кінець історії. Ще вище натрапляємо на ось таку помилку:

Це означає, що у gitlab-runner’а консоль не є інтерактивною, тобто є Read only і працює лише на output. Як у звичайному терміналі, зробити ввід даних вручну не є можливим.

І ми все ще перебуваємо у фрустрації, бо досі не є зрозумілим як все це виправити, бо кореня проблеми все ще не знайдено.

А насправді справжня причина Fail’у була навіть не виділена яскраво червоним кольором і ми могли легко не звернути на неї увагу. Ось де собака була зарита:

Причиною було те, що у файлі Podfile.lock зафіксовано конкретний коміт з SHA, з якого команда pod install намагалась встановити приватний под. Але даний коміт у репозиторії напередодні було видалено force push’ем. Ось чому насправді пайплайн не зміг успішно завершити свою роботу.

З даного прикладу дуже гарно видно, що для того, щоб трабл-шутити помилки роботи CI, треба розібратися, як правильно читати інформацію в логах. Все працює за принципом «карткового будиночка».

Якщо зафейлився один скрипт, то й інші при виконанні зазнають невдачі. Через це треба знизу вверх перевірити логи і знайти ту найпершу команду, з якої щось пішло не так. Все геніальне — просто :)

Висновки

  1. Ruby-інструменти — то є скрипти, які допускають розширення.
  2. Для скриптів варто створювати віртуальне оточення, замість того, щоб покладатись на глобальні залежності.
  3. У логах важлива перша проблема, а не остання.

Дякую за увагу і буду дуже рада почитати про ваші неймовірно цікаві пригоди з налаштуванням CI у коментарях! 🙂

👍ПодобаєтьсяСподобалось5
До обраногоВ обраному1
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

Підписатись на коментарі