Понадобилось мне как-то раз сделать из PWA мобильные приложения. Приключение на 20 минут, зашли и вышли - подумал я и пропал на пару месяцев. В статье будет гремучая смесь из Java, C#, NodeJS, Swift, bash, Dockerfile и github actions. Но в результате возможность непрерывно поставлять свежие сборки в магазины приложений.
Погружаемся.
Первым делом я нашел сайт PWA builder который позволяет для любого PWA скачать сборки для iOS и Android. Интересное. В процессе изучения как оно работает я обнаружил два репозитория: bubblewrap для andoird и PWA builder iOS. Похоже, на их основе и работает PWA builder. Но использовать их в github actions как есть не представлялось возможным, поэтому пришлось вооружаться допильником.
Bubblewrap
Это консольная утилита, написанная на Typescript служащая оберткой для android build tools. Она умеет их скачивать, читать манифест с сайта, собирать apk и подписывать его. Звучит неплохо? Казалось бы. На голый раннер гитхаба bubblewrap будет скачивать джаву, android sdk, распаковывать их, а еще и просить согласиться с разными terms of service и EULA.
Чтобы сэкономить пару-тройку литров CO2 я собрал докер образ, в котором все уже готово.
В процессе сборки образа скачиваются необходимые компоненты и прогоняется процесс сборки для сайта webboard.app. Для этого используется заранее подготовленный keystore с простым паролем. Для чего это нужно, спросите вы? Да потому что в процессе bubblewrap init/build происходит дальнейшая установка системы сборки, которую не хотелось бы повторять на каждый пуш. Результат этой работы удаляется: rm /app/* -rf
Еще одним неудобством bubblewrap является то, что в процессе инициализации она читает manifest.json и на каждый параметр спрашивает, а правда ли использовать его? И если для холостой установки хватает утилиты yes, то в реальном мире все не так радужно: иногда хочется изменить фон или иконку, а гораздо чаще хочется поменять version code - play store очень не любит сборки с одним и тем же номером. Поэтому был написан скрипт answer.sh, беседующий с bubblewrap согласно переменным окружения.
Теперь нужен только keystore c вашим сертификатом - и тут у меня возникла проблема: я долго не мог понять, как завернуть в keystore гугловский сертификат или убедить гугл верить сгенеренному keystore. Оказалось, что первую сборку можно подписывать любым keystore, но не надо его после этого удалять, а использовать всю оставшуюся жизнь для этого приложения. Есть вариант сказать гуглу, что я забыл пароль, и сгенерить keystore заново, но пару дней вы не сможете публиковать приложение. Все еще не могу взять в толк, зачем нужно было устраивать такой цирк, а не использовать приватный ключ у разработчика и публичный у гугла.
Чтобы собрать github runner не хватает version code - каждый раз публиковать сборку под одним и тем же номером не очень хочется. На просторах гитхаба нашелся подходящий проект Google Play App Version Code - получает последнюю версию из google play console. Внутри все довольно сложно и на джаве, но делает то что нужно и даже есть готовый экшен для гитхаба, ребята красавцы, спасибо большое.
Все, собираем workflow и добавляем на PR в main. Свежая версия будет в консоли Google Play. А дальше уже можно раскатывать на тестовое окружение, или как у вас принято.
iOS PWA Builder
Тут все оказалось намного интереснее: почти во всех инструкциях от apple говорят нажать на кнопочку в xcode/appstoreconnect, а это не так просто в github runner. Окей, засучив рукава лезем в PWA builder iOS. Вау, C# web app, у которого в ресурсах лежит Swift обертка - там запускается WebView с нужным PWA. Окей, на скорую руку переделываем в консольную утилиту на том же C#. Оригинальный код на вход получал что-то похожее на манифест и подправлял Swift код согласно ему. Надо бы генерить этот манифест из нормального с сайта, но пока что лень, отдаем в консольную утилиту руками собраный конфиг.
После некоторого количества чертыханий эта хрень таки работает и выдает почти готовое Swift приложение. Осталось чуть-чуть его подправить, собрать, подменить версию, подписать, провалидировать и залить: workflow
Для получения версии из AppStore пришлось написать на NodeJS утилиту
При первой публикации приложения получили отказ - нужно добавить логин через Apple. Штош. Есть два пути - короткий через OAuth и длинный через нативную аутентификацию на девайсе. Второй мне показался более юзер-френдли, поэтому разворачиваем QEMU и изображая swift-кодера добавляем аутентификацию.
После этого приложение получилось опубликовать и все работает.
Всплываем
Погрзившись в особенности работы с мобильными приложениями возникает вопрос. А как вы это делаете-то? Неужели каждую сборку для iOS делают на реальном макбуке с установленным сертификатом разработчика нажимая кнопочку в IDE? Неужели у кого-то хранится keystore для публикации приложения в Google Play? Не знаю, я ведь ненастоящий мобильный разработчик, я только каску нашел. Расскажите в комментариях, как оно устроено на самом деле, и как можно упростить мой костыльный пайплайн.
naimjon94
Мобильная разработка это боль, особенно после веба, там совсем недавно завезли jetpack compose, я офигел