Розгортаємо власний CI/CD сервер на M1* з GitHub Actions

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

Усім привіт, я Артем Дорош, Android-розробник, який протягом останніх трьох років займає неіснуючу позицію — Mobile DevOps. За моїм субʼєктивним визначенням Mobile DevOps — це людина, що любить писати bash скрипти, чекати по пів години на «зелений ліхтарик» після правки в YAML-файлі, і її завжди пінгують, коли хтось з її команди бачить в логах щось неочікуване.

Практично ніхто не любить мати справу з CI: він повільний, любить відвалюватись, чи, наприклад, затримувати ваш мердж реквест на пів години після зміни однієї стрічки, що привело до перезапуску всіх тестів, його важко дебажити (коли потрібно).

У даній статті я поділюсь власним досвідом в створенні власної CI/CD інфраструктури на M1*, проблемами що ми намагились вирішити таким чином, порадами і результатами наших тестів.

Щоб шо?

Нещодавно в нашій мобільній команді зʼявилась проблема: ми витрачаємо занадто багато коштів за наш мобільний CI. Так історично склалось, що інші команди майже перейшли одразу на використання GitHub Actions(скорочено GHA) після їх запуску в 2018. Інші команди були задоволені цим рішенням, окрім мобільних, тому вони пішли своїм шляхом.

В 2019 році ми переїхали зі старого CI/CD сервісу на інший, що на той час пропонував краще залізо і мав більше функціоналу. На противагу цьому були ціна за час виконання, неоптимальність кешування, досить довге початкове налаштування для кожного запуску. Не допомогало ситуації і те що, що кодбаза стабільно росла, а потужності їх інфраструктури не встигали за технічним прогресом.

Так ми почали шукати альтернативи. Першою думкою було здійснити переїзд на GitHub Actions через нативність рішення. Ми вже використовували їх для невеликих задач — міряння розмірів реквестів, підрахунок та логування кількості legacy коду тощо. Цього часу, як і до цього, ми досить швидко уперлися в недостатню потужність «з коробки». Але, дякуючи архітектурі GHA, ми не обмежені лише потужнсотями GitHub.

План дій

Далі ми спробували вирішити проблему системним підходом, а саме — створили план дій. В команді ми визначили наступні кроки:

  1. Визначити наші потреби. Ми маємо створити список вимог до нашого рішення: версію системи, обов’язкове програмне забезпечення, необхідні потужності та їх об’єм.
  2. Вивчити архітектуру GitHub Actions. Як воно працює, які є обмеження платформи та яким чином ми можемо додати власні потужності для наших потреб?
  3. Вибрати «залізо» для ваших потужностей. Для тестів ми можемо використовувати MacBook Air/Pro, на якому ви цілком можливо і читаєте даний допис. Якщо ж ми намагаємось будувати саме потужності для команди, то треба вивчити пропозиції на ринку та які саме конфігурації ми маємо розглядати для купівлі або оренди.
  4. Конфігурація та підтримка потужностей. Вивчити, як можна швидко розгорнути та оновити конфігурацію вашого сервера без потреби внесення змін в основний сервер, та як забезпечити ізоляцію. Для цього нам в нагоді будуть віртуальні машини.
  5. Тестування та аналіз. Відповівши на попередні пункти, ми маємо отримати PoC (proof of concept), який ми можемо використати для тестування. Саме на цьому етапі ми можемо допрацьовувати конфігурацію та фіналізувати наші потреби. Отримавши необхідні дані, ми можемо оцінити доцільність та ефективність такого рішення.

Визначаємо потреби

Як і завжди, ми почали з діалогу і зустрічі з нашою iOS-командою. Разом ми вивчили їх працюючий сетап і налаштування пайплайнів, та спитали що саме вони б хотіли би бачити в новому рішені. Ми зупинились на тому що нам потрібні сервери на macOS Ventura, декілька додаткових homebrew пакунків, XCode 14.0+ і 12 GB оперативної пам’яті на пайплайн. Ми не були впевнені в кількості ядер, тож ми визначали необхідну кількість пізніше шляхом спроб і помилок.

Важливим пунктом був і об’єм наших потужностей. У нас хоч і невелика команда (4+), нам потрібно щоб у кожного розробника була можливість в будь-який момент запустити перевірку на CI в межах мердж реквесту. Так ми прийшли до простої формули, що визначає необхідну кількість одночасних задач: (1.5~2) x розмір команди.

Ваші потреби можуть варіюватись, тож обов’язково проконсультуйтесь з вашою командою. Спробуйте дізнатись якнайбільше інформації та формалізувати її десь «на папері».

Про GitHub Actions Runner

GHA використовують ранери(runners), щоб виконувати ваші команди (steps) в межах задачі (job). Runner — це програма-агент, що банально очікує задачу, виконує її і відправляє логи на сервер. Цю програму можна запустити будь-де, хоч на мікрохвильовці. Один ранер може виконувати одну задачу за один раз. Є три типи ранерів, що ви можете використовувати в вашому проєкті:

  • GitHub-hosted — безкоштовні для open source, платні для закритих проєктів. Вони недостатньо потужні: так, macOS ранер має лише 3 віртуальні процесори та 7 GB оперативної памʼяті;
  • Self-hosted — ви можете запустити ваш ранер на вашому залізі. Власне платите ви за інтернет, електрику та залізо/ оренду заліза;
  • App-hosted — деякі сервіси пропнують вам кращі ранери за свою ціну. Зазвичай для цього ви додаєте їх додаток до проєкту і оформлюєте підписку.

У кожного з цих варіантів є свої переваги і недоліки. Ми досить швидко відмовились від ідеї використання GitHub-hosted ранерів, так як вони не відповідають нашим вимогам потужності та ціни. На час написання статті, 1 хвилина виконання на macOS коштує $0.08. Для порівняння, 1 хвилина на Linux ранері коштує $0.008.

Це вже не кажучи про те, що їхні ранери поки що не підтримують Apple Silicon, а відповідний тікет ще не отримав пріоритету чи дедлайну в роадмапі. Через це ми майже одразу вирішили експерементувати з self-hosted опцією.

Вибираємо залізо

Тут у нас не так багато опцій. На момент написання, Apple може нам запропонувати 3 десктопи на Apple Silicon:

  1. Mac Mini — M1 (8 cores, 4 performance cores, up to 16 GB RAM);
  2. iMac — те саме що Mac Mini, але навіщо?!
  3. Mac Studio — M1 Max (10 cores, 8 performance cores, up to 64 GB RAM), M1 Ultra (20 cores, 16 performance cores, up to 128 GB RAM).

Один Mac Mini буде хостити не більше ніж один ранер, якщо ви хочете оптимальний та не перевантажений сервер. Не варто економити на памʼяті, тож беріть одразу 16 GB RAM. 256 GB SSD також буде цілком достатньо.

Mac Studio своєю чергою має вдвічі або ж в чотири рази більше потужних ядер. Тут все просто: діліть кількість performance cores на 4 і ви отримаєте оптимальну кількість ранерів. Так, M1 Max з 32 GB RAM буде спокійно хостити 2 ранери, а M1 Ultra 64 GB RAM — 4. Можна і більше, але тоді ви будете втрачати в потужності. Також, резервуйте як мінімум 100 GB для кожного ранеру. Більше SSD — краще, хоча і 1 TB буде за очі.

Також рекомендую вам орендувати ці машини десь в хмарі недалеко від вашого регіону. Так вам не доведеться піклуватись про окремий старлінк та генератор для вашого CI на випадок постійних аварійних відключень електрохарчування. Невеличкий перелік місць, де ви можете їх орендувати:

  1. MacStadium;
  2. Amazon EC2;
  3. MacCloud.

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

Підготовка заліза

І от ви отримали доступ до вашого заліза. Далі нам треба підготувати його як хост для нашого майбутнього CI серверу. Зверніть увагу, що деякі пункти будуть опціональними в залежності від початкового стану ОС.

Для початку, встановлємо розширення Xcode та Homebrew:

xcode-select --install
/bin/bash  -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Далі потрібно увімкнути автологін в систему після включення. Для цього нам знадобиться утиліта kcpassword:

git clone https://github.com/xfreebird/kcpassword.git && cd kcpassword
./enable_autologin <user> <password>

Після цього нам треба вимкнути режим сну та автоблокування. На цьому етапі, якщо на девайсі увімкнутий MDM (Mobile Device Management), вам потрібно буде звернутись до адміністратора вашого MDM-сервісу, щоб вони дозволили модифікацію наступних налаштувань:

# Вимикаємо screensaver під час логіну
sudo defaults write /Library/Preferences/com.apple.screensaver loginWindowIdleTime 0
# Вимикаємо режим сну
sudo systemsetup -setdisplaysleep Off
sudo systemsetup -setsleep Off
sudo systemsetup -setcomputersleep Off

Ще в декстопних маках є можливість увімкнути перезапуск системи на випадок відключення електроенергії чи помилки системи.

sudo systemsetup -setrestartpowerfailure On
sudo systemsetup -setrestartfreeze On

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

sudo mdutil -a -i off 

Цього має буде достатньо для базового хосту. Також, увімкніть VNC та SSH в системних налаштуваннях. Це дозволить вам зекономити час.

Створюємо віртуальний ранер

Як раніше згадувалось, GHA можна запустити на будь-чому. Нічого не заважає вам просто встановити GHA ранер на вже підготовлену систему, але це може спричинити проблеми. Ваш CI може бути завʼязаний на якусь версію системи або Xcode. Банальне оновлення системи може викликати головний біль як у вас, так і у вашої команди. Не думаю що вам сподобається фіксити homebrew чи Xcode після купи приватних повідомлень від вашої команди.

Саме через це ми будемо використовувати віртуалізацію. Це допоможе нам з декількома проблемами:

  1. Ізоляція файлової системи — все потрібне для виконання буде встановлене у віртуальній машині.
  2. Ізоляція ресурсів — ми можемо виділити визначені ресурси на один ранер. Це дозволить запустити декілька ранерів на одному хості з окремо виділеними ядрами та памʼяттю.
  3. Простота міграції — для оновлення ранерів не знадобиться оновлювати хост. Потрібно лише запустити нову віртуальну машину і видалити стару.

З macOS 11 Apple додали Virtualization.Framework для роботи з віртуальними машинами на Apple Silicon. За допомогою нього можна створювати додатки, що можуть створювати та запускати віртуальні машини.

Його використовує Tart та Anka 3. Anka має свій реєстр віртуальних машин та контролер для управління нодами, але вона платна і потребує ліцензії. Зате Tart повністю безкоштовний, open source і підтримує всі OCІ. Єдиниий великий мінус — він не має утиліт для кластеризації, але вони вже думають над створенням схожого рішення.

Tart можна встановити або оновити однією командою:

brew install cirruslabs/cli/tart
# Оновити вже встановлений tart можна через upgrade
brew upgrade cirruslabs/cli/tart

Тепер нам потрібно визначитись з образом для нашого ранера. Для цього нам потрібно визначитись з версією Xcode та системи. Це може бути або macOS Monterey чи Ventura та Xcode 13 і новіше. Візьміть до уваги, що якісь пакунки та симулятори будуть відсутні «з коробки». Якщо вам потрібно створити власний образ, це можна досить просто зробити за допомогою Packer та спеціального плагіну для tart. CirrusCi також тримає офіційні шаблони для образів в Open Source, і ви можете використати їх як відправну точку для ваших образів. Далі його можна опублікувати в будь-який OCІ реєстр, як то GitHub Packages.

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

tart clone ghcr.io/cirruslabs/macos-ventura-xcode:14.1 ventura-runner-1

Це займе трохи часу, так як образи важать приблизно 40 GB. Після цього ви можете корегувати параметри віртуальної машини. Для прикладу, ми виділимо нашому ventura-runner-1 4 vCPU та 12 GB RAM:

tart set ventura-runner-1 --cpu 4 --memory 12228
# Перевірити зміни можна за допомогою tart get
tart get ventura-runner-1

Після цього можна запустити нашу віртуальну машину:

tart run ventura-runner-1
# Альтернативно, можна запустити машину без UI
tart run --no-graphics ventura-runner-1

Якщо ви запустите віртуальну машину з графічним вікном, то вона дисить швидко запуститься і ви зможете інтерактивно з нею взаємодіяти:

Незалежно від того? в якому режимі було запущено віртуальну машину, ви зможете приєднатись до неї по ssh. За замовчуванням, віртуальна машина буде мати користувача admin з паролем admin. Прямо як ваш роутер (серйозно, змініть пароль роутера).

ssh admin@$(tart ip ventura-runner-1)

Отримавши прямий доступ до віртуальної машини, маємо все необхідне для запуску вашого CI/CD агенту. Єдине про що варто пам’ятати — ваші віртуальні машини поки що не стартують автоматично разом із запуском фізичної машини. Це нескладно зробити через launchctl за доволі простою інструкцією.

Конфігурація GHA ранеру

Віртуальні машини на базі образів від Cirrus вже мають встановлений GHA runner agent, тому завантаження та встановлення можна пропустити. Але нам все ще потрібно авторизувати та налаштувати агент.

Для цього зайдіть в налаштування вашого GitHub репозиторію, знайдіть пункт «Runners» в налаштуваннях та натисніть «New self-hosted runner»:

На наступній сторінці нам знадобляться токен. Токен можна знайти в блоці «Configure». Його можна просто скопіювати, або ж можете скопіювати разом з командою конфігурації.

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

cd ~/actions-runner
# Довідка по параметрам конфігурації
./config.sh --help
# Авторизація
./config.sh --unattended \
    --url <repo_url> \
    --token <runner_token> \
    --name "ventura-runner-1" \
    --labels "xcode-14.1"
# Реєструємо агент як сервіс та активуємо його
./svc.sh install && ./svc.sh start

Якщо попередні дії були виконані без помилок, то в секції «Runners» в налаштуваннях ви побачите ваш ранер в статусі онлайн.

Тестуємо наш CI

Створимо базовий workflow, що буде запускатись на нашому сервері. Для цього нам треба вказати GitHub, на чому запускати наші задачі через теги та runs-on. Достатньо використати тег «self-hosted», що додається автоматично до кожного self-hosted ранера. Втім, якщо ваші ранери можуть мати різну конфігурацію, варто використати більш унікальний тег. Наприклад, наступний worflow клонує ваш репозиторій та перевіряє версію XCode, що встановлена у вашому ранері:

# .github/workflows/test.yaml
name: 'test'
on:
  pull_request:
  push:

jobs:
  test:
    runs-on: [self-hosted, xcode-14.1] 
    steps:
      - uses: actions/checkout@v3
      - run: xcodebuild -version

Додайте та запуште цей файл у ваш проєкт. GitHub на основі тригерів, що ви вказали у workflow, запустить необхідні задачі та буде звітувати про них вживу. Ви зможете побачити прогрес виконання цього пайплайну у вкладці «Actions» вашого репозиторію:

Дізнатись більше про синтаксис та можливості GitHub Actions ви можете з офіційної документації.

Про недоліки та результати

Головним недоліком даного рішення є відсутність підтримки вкладеної віртуалізації. На практиці це значить, що Android-емулятор чи часто потрібний Docker не будуть працювати всередині вашої віртуальної машини. Docker також використовується в деяких GitHub Action-ами, створеними спільнотою, що робить їх недоступними на ваших Apple Silicon ранерах. Це обмеження не лише Virtualization.Framework, але і самого чипу M1. M2 вже має підтримувати вкладену віртуалізацію, хоча сам фреймворк ще не було оновлено для цього.

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

Авжеж, є і переваги в даному рішенні. Так, в наших тестах подібна конфігурація показала суттєве зменшення затраченого часу на виконання workflow — з 20 хвилин до 8 хвилин на скриншот та Unit-тести нашого iOS-проєкту. Інші задачі показували приріст в швидкості в межах 30-50% в порівнянні з нашим попереднім CI.

Кінцеві результати будуть залежати від конфігурації віртуальних машин та саме від ваших пайплайнів. Не забувайте проводити власні тести та валідацію, перед тим як пропонувати робити щось cхоже у ваших проєктах. Може скластися так, що ціна підтримки стане вищою за зекономлені гроші чи хвилини. Якщо у вас в компанії є DevOps, то обовʼязково проконсультуйтеся з ними.

Висновки

Створити власний CI на Apple Silicon може бути непоганою ідеєю, якщо ви хочете отримати найефективніше рішення за власну ціну. В цьому дописі ви дізнались про вибір заліза, створення віртуальних машин за допомогою утиліти tart, та про налаштування агента для запуску ваших задач з GitHub Actions.

Дякую за перегляд! Сподіваюсь, ви дізналися щось нове з цього допису, а не просто витратили час. Буду радий відповісти на ваші питання в коментарях.

👍ПодобаєтьсяСподобалось18
До обраногоВ обраному8
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
рекомендую вам орендувати ці машини десь в хмарі недалеко від вашого регіону.
зʼявилась проблема: ми витрачаємо занадто багато коштів за наш мобільний CI.
256 GB SSD також буде цілком достатньо

у М1 256
а у М2 навіть 512 SSD один чіп впаяний
критерій достатньості важко сказати
можна було і доплатити за 16 Гб та 2 чіпи ссд замість одного

Дякую вам за статтю, допомогла. А я вірно розумію, що ви використовуєте ці образи і для зборі білдів під іос? Бо я щось застряг на тому етапі, що віртуалізація епл не підтримує логін з епл айді, а білд треба якось пдіписувати. Чи треба щось на кштал цього імплементувати docs.github.com/...​ers-for-xcode-development ?

Хмм, а в чому проблема купити мак про 2013 року на зеонах, і встановити серверну макось, нащо для цього використовувати не пристосований М1? А якщо гроші взагалі не проблема, є актуальна рекова прошка.

Хороше питання. Якщо коротко, то generational improvements. Ядер більше, вони гарячіші, і памʼять повільніше, ну і невідомо скільки ще буде підтримка саме цієї платформи. Але як варіант може бути доцільним.

а можна розкрити, як ви рахували доцільність?

--------------
Вартість:
Оренда Mac Studio на rentyourmac (ваше посилання): 229 EUR/month = 2748 EUR + це все налаштувати, + підтримка, + усе одно потрібні інші ранери для Android-емулятору чи Docker

GitHub Enterprise: 231 per user/year = 924 USD на команду з 4 людей
Якщо недостатньо GitHub Enterprise хвилин, можна докупити хвилини macOS за ціною $0.08
Та і GitHub Enterprise дає не тільки GitHub Actions.

-------------
Продуктивніть:

недостатню потужність «з коробки»

Якщо була саме проблема у тому, скільки часу виконуються ваші білди, то цікаво з яким саме GitHub runner ви порівнювали? І тоді порівняти приріст вартості нового рішення і приріст продуктивності.

Дефолтні:
Hardware specification for Windows and Linux virtual machines:

2-core CPU (x86_64)
7 GB of RAM
14 GB of SSD space
Hardware specification for macOS virtual machines:

3-core CPU (x86_64)
14 GB of RAM
14 GB of SSD space

Але на GitHub є можливіть запускати ранери на 64 cores 256 RAM 2040 GB. Тобто якщо питання лише в продуктивності, GitHub дає можливіть запискати надпотужні ранери.

GitHub Enterprise: 231 per user/year = 924 USD на команду з 4 людей

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

Якщо була саме проблема у тому, скільки часу виконуються ваші білди, то цікаво з яким саме GitHub runner ви порівнювали? І тоді порівняти приріст вартості нового рішення і приріст продуктивності.

В статті я не хотів лізти глибоко в фінансовий аспект. Спойлер: там все складно і, як завжди, ваші цифри можуть бути іншими. Спочатку ми переклали наш існуючуй пайплайн з нашого теперішнього CI провайдера на гітхабівський yaml. Відправна точка: один з основних пайплайнів на мердж реквест в нас займав на провайдері десь 28 хвилин з сетапом і всіма тестами.

Ми досить швидко почали замічати проблеми. У нас приблизно 20 модулів з тестами на симуляторі. Ми запушили, чекали, а пайплайн не закінчувався. Ми намагались траблшутити, але хотіли побачити хоч якийсь результат. Так, ми зменшили перелік до лише двух модулів, і після цього отримали результат для GH-hosted ранерів: 44 хвилини лише на 2 модулі. Ми навіть не рахували фінансовий аспект для GH-hosted, поки вони не «дадуть» потужніші ранери.

Але на GitHub є можливіть запускати ранери на 64 cores 256 RAM 2040 GB

Так, але це поки що в беті, і це не доступно для macOS ранерів. Але є відповідний тікет, і він начебто запланований на цей кварал (github.com/...​github/roadmap/issues/506). Тож можливо скоро стане краще, треба буде дивитись на прайсинг і ще ра міряти.

Дякую.

Я порівнював базуючись на даних, що були у статті: 4 людини. На 60 людей вам однієї Mac Studio теж не вистачить і теж буде інший чек.

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

Основна проблема макосі у GHA — це те що, актуальну OS ви будете чекати рік+, після паблік релізу. Тобто, наприклад Вентури ще немає на їх ранерах

У нас було 2 мак міні і на них дженкінз під мобайл.

В якийсь момент набридло їх менеджити і мігрували на azure dev ops.

github.com/foxlet/macOS-Simple-KVM
github.com/kholia/OSX-KVM

Macos у віртуалці під Linux і не треба купляти чи орендувати саме мак

Не дуже розумію чому тоді лінукс не викорстовувати замість макосі

Там є проблеми з OpenCore, та відповідно не усюди можна виконати нормально привілегійовані контейнери разом з віртуалками.

Загалом то ганяю docker-osx взагалі у кубах на Fastlane з TektonCD... просто в AWS m5ad на EPYC зазвичай дешевше та відповідно щоб то все нормально запустити треба пляски з бубном. Не усі до такого морально, та практично, готові.

Цікавий досвід, не хочете написати про нього?

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

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

Піратством те буде коли на кожен інстанс віртуалки у вас немає відповідного купленого яблучного пристрою... вже не раз піднімалось відповідне питання. А з віртуалками ніхто давно не грається бо вони запускаються як привілегійовані контейнери... от kext’и позбирати, пропатчити та попідписувати дійсно важко, й то може бути самостійним продуктом.

Якби то було піратством — в нас не було б взагалі мобільних СІ сервісів, або ж вони були суто яблучними з відповідною монополією.

мобільних СІ сервісів

Той же амазон має датацентр з мак міні

Просто там EULA дуже цікава, відповідно маючи просто яблучний пристрій в наявності — його завантажувач можна використовувати як для власних цілей, бо він вам належить. Відповідно навіть якщо мак міні застарів — його ліцензія дозволяє використовувати як віртуалку для більш потужного заліза... принаймні так було в 2019-2020 році.

This README.md documents the process of creating a Virtual Hackintosh system.

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

Так тут якраз в хакерство полізли. Якщо хочеться, щоб просто працювало — використовуєш дефолтні ранери.

А от аренда додаткового Мас, налаштування його, підтримка — це звичайно не Macos у віртуалці під Linux, але перший крок до цього.

Залежить від маштабів — сьогодні ліньки, а завтра вже пізно, й компанія витрачає по 5-6К в місяць на CI. Надбавки до зп просять усі, а гроші рахувати мало хто хоче/вміє.

я коли перший раз 900 доларів просив на тулзу теж такий був

а як побачив витрати компанії на шльондр та кокосик

то все фігня

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