Пай­плайн CI/CD — это, по сути, прос­то HTTP-зап­росы в пра­виль­ном поряд­ке. Запус­тить сбор­ку, опро­сить ста­тус, ска­чать арте­факт, опуб­ликовать в Google Play — все это дела­ется по REST API. Тут и curl спра­вит­ся! Давай пос­мотрим, мож­но ли обой­тись без Jenkins, плат­ных плат­форм и Python-фрей­мвор­ков с десят­ками зависи­мос­тей.

Я соб­рал CI/CD-сер­вер для Android-при­ложе­ний на голом Bash. Он запус­кает сбор­ки через облачный CI или Gradle нап­рямую, пуб­лику­ет резуль­тат в Google Play, монито­рит уве­дом­ления о пуб­ликации через IMAP. В качес­тве интерфей­са управле­ния я взял Telegram, но на его мес­те мог бы быть Slack, Discord, ntfy, email или обыч­ный SSH — тран­спорт здесь смен­ная деталь.

Воз­можно, ты ска­жешь, что делать все на Bash стран­но. Но ведь это как раз язык для склей­ки инс­тру­мен­тов: фай­ловый ввод‑вывод, работа с перемен­ными окру­жения, HTTP-зап­росы через curl, изме­нение JSON через jq. Получа­ется, что мы прос­то при­меним клас­сичес­кий «путь Unix» к веб-API, а не толь­ко к локаль­ным прог­раммам. И заод­но обой­дем­ся без докеров и кубер­нетесов.

А глав­ное, изме­нить мож­но будет что угод­но и в любой момент без стра­даний с деп­лоем. Теперь идея кажет­ся более здра­вой? Давай опро­буем ее на при­мере, и ста­нет понят­но, при­мени­мо ли это в жиз­ни.

Для начала пос­мотрим, как будет работать пол­ный цикл пуб­ликации. Что­бы его запус­тить, прос­то нажима­ешь «Publish Beta» — и через нес­коль­ко минут смот­ришь, что появи­лось в чате.

Полный цикл: скачивание артефакта, загрузка в Google Play, уведомление о публикации
Пол­ный цикл: ска­чива­ние арте­фак­та, заг­рузка в Google Play, уве­дом­ление о пуб­ликации

Бот ска­чал AAB с Codemagic, заг­рузил его в Google Play, зафик­сировал edit-тран­закцию — и потом, ког­да Google завер­шил модера­цию, поч­товый монитор пой­мал пись­мо и прис­лал финаль­ное уве­дом­ление. Все без учас­тия челове­ка.

А теперь рас­ска­жу, как я это пос­тро­ил. Если захочешь пов­торить, вот, что тебе понадо­бит­ся:

  • то­кен Telegram-бота (зап­роси у @BotFather);
  • то­кен Codemagic API или локаль­ный Android SDK с Gradle;
  • Google Play Service Account JSON с пра­вами Release Manager;
  • поч­товый ящик с дос­тупом по IMAP (Gmail подой­дет);
  • Docker или любой Linux-хост;
  • за­виси­мос­ти: curl, jq, openssl, mbsync;
 

Архитектура

Сис­тема сос­тоит из двух незави­симых про­цес­сов в одном Docker-кон­тей­нере.

Ор­кес­тра­тор сбо­рок (publish_bot.sh) — при­нима­ет коман­ды от опе­рато­ра, запус­кает сбор­ку через CI API или локаль­ный Gradle, отда­ет арте­факт, пуб­лику­ет в Google Play, отве­чает на зап­росы ста­туса. Обслу­жива­ет нес­коль­ко про­ектов одновре­мен­но — у каж­дого свой вор­кер‑про­цесс.

Мо­нитор поч­ты (imap_to_telegram.sh) — каж­дые 30 секунд чита­ет вхо­дящие по IMAP, ищет пись­ма от Google Play Console, пересы­лает уве­дом­ления в нуж­ный канал.

Меж­ду ними — файл channel_ids.json: мап­пинг «package name → иден­тифика­тор канала». Оба про­цес­са чита­ют его незави­симо.

Архитектура сервиса: два процесса, общий файл маппинга, сменный транспорт
Ар­хитек­тура сер­виса: два про­цес­са, общий файл мап­пинга, смен­ный тран­спорт

Тран­спорт — вер­хний блок (коман­ды) и ниж­ний (уве­дом­ления) — намерен­но отде­лен от логики. Хочешь пере­ехать со Slack на Telegram или добавить парал­лель­ную отправ­ку в ntfy — пра­вишь три фун­кции, не зат­рагивая ядро.

Каж­дый осталь­ной блок тоже заменя­ем незави­симо: поменять CI-сер­вис — одна фун­кция, добавить трек пуб­ликации — один case в switch. Архи­тек­тура намерен­но плос­кая: никаких абс­трак­ций, прос­то фун­кции.

Деп­лой — одна коман­да:

docker run -d \
--name cicd-server \
--restart unless-stopped \
-e IMAP_PASSWORD='пароль' \
cicd-server

Скрипт start.sh запус­кает оба про­цес­са:

./publish_bot.sh &
./imap_to_telegram.sh &
wait

Даль­ше раз­берем кон­крет­ные задачи по поряд­ку.

 

Транспорт

Преж­де чем пог­ружать­ся в детали — важ­ный архи­тек­турный момент. В сис­теме есть ров­но три точ­ки вза­имо­дей­ствия с опе­рато­ром:

  1. Вхо­дящие коман­ды — отку­да сер­вис получа­ет задание (запус­тить сбор­ку, показать ста­тус).
  2. Ис­ходящие уве­дом­ления — куда сооб­щать о резуль­тате.
  3. Дос­тавка арте­фак­тов — куда отда­вать соб­ранный APK/AAB.

Все это реали­зова­но в трех‑четырех фун­кци­ях. Поменять тран­спорт — зна­чит перепи­сать эти фун­кции.

 

Только уведомления

Ес­ли инте­рак­тив не нужен — вари­анты прос­тые.

ntfy.sh — минима­лис­тичный вари­ант. Пуб­личный сер­вис, бес­плат­ный, с мобиль­ным при­ложе­нием. Отпра­вить уве­дом­ление:

curl -d "✅ Сборка ${PACKAGE} завершена" \
"https://ntfy.sh/my-secret-build-topic"

То­пик — прос­то стро­ка в URL. Под­писыва­ешь­ся в при­ложе­нии — получа­ешь push. Никакой регис­тра­ции, никаких токенов. Мож­но под­нять собс­твен­ный ntfy-сер­вер для при­ват­ности.

Slack/Discord webhooks — если коман­да уже исполь­зует его. Нам дос­таточ­но сде­лать один POST-зап­рос с JSON:

curl -X POST "$SLACK_WEBHOOK_URL" \
-H "Content-Type: application/json" \
-d "{"text": "Сборка *${PACKAGE}* завершена"}"

У Discord поле называ­ется content вмес­то text, URL дру­гой, но пат­терн тот же.

Email через msmtp — работа­ет сов­сем без зависи­мос­ти от сто­рон­них сер­висов:

echo -e "Subject: Сборка готова\n\n${PACKAGE} собрался успешно" \
| msmtp recipient@example.com
 

Двусторонний канал

Ес­ли нуж­ны и коман­ды, и уве­дом­ления, то выбор сле­дующий.

Telegram — самый удоб­ный вари­ант для этой задачи: бес­плат­ный API, long polling без пуб­лично­го IP, фай­лы до 50 Мбайт мож­но слать пря­мо в чат, кноп­ки дела­ются одним JSON. Под­робно раз­берем ниже.

Slack slash-коман­ды — рабочий вари­ант для коман­дной раз­работ­ки. Slash-коман­да отправ­ляет POST на твой endpoint — зна­чит нужен пуб­личный URL, в отли­чие от Telegram polling. Отве­ты при­нима­ем через webhook.

Matrix/Element — откры­тый про­токол с REST API, под­держи­вает polling (/sync), мож­но захос­тить сер­вер у себя. Схе­ма та же что у Telegram: loop, затем curl /sync, обра­бот­ка события, и сле­дом curl /send.

SSH — для лич­ного исполь­зования экзо­тич­но, но зато зависи­мос­ти нулевые:

ssh user@cicd-host "./publish_bot.sh build_release com.example.app"

cron — подой­дет, если инте­рак­тив вооб­ще не нужен. Если у тебя ноч­ные сбор­ки и еже­недель­ные бета‑релизы, то мож­но вооб­ще всё авто­мати­зиро­вать и обой­тись без мес­сен­дже­ров.

Что дей­стви­тель­но слож­но заменить — это отправ­ку фай­лов. Telegram при­нима­ет APK пря­мо в чат, файл ска­чива­ется одним тапом. У Slack лимит на заг­рузку в бес­плат­ном пла­не, у ntfy фай­лов нет. Аль­тер­натива: S3 или Cloudflare R2 как хра­нили­ще, а ссыл­ка будет при­ходить в уве­дом­лении.

 

Привязка проекта к каналу уведомлений

Сер­вис обслу­жива­ет нес­коль­ко при­ложе­ний. Как он понима­ет, для какого про­екта приш­ла коман­да и в какой канал слать уве­дом­ление?

Продолжение доступно только участникам

Материалы из последних выпусков становятся доступны по отдельности только через два месяца после публикации. Чтобы продолжить чтение, необходимо стать участником сообщества «Xakep.ru».

Присоединяйся к сообществу «Xakep.ru»!

Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее

  • Подпишись на наc в Telegram!

    Только важные новости и лучшие статьи

    Подписаться

  • Подписаться
    Уведомить о
    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии