Сейчас я расскажу, как выглядит процесс разработки на Unity в маленькой gamedev компании и как мы его улучшаем и автоматизируем. Всё-таки 2020 год на дворе, хватит уже мышкой водить…
Про мышку, это не такая уж и шутка. До недавнего времени, процесс тестирования и разработки игр у нас выглядел примерно так:
Android
- Разработчики пишут код
- Собирается версия для Android
- Загружается в общую папку на Google Диск
- Тестировщик скачивает последний билд
- Тестирует и закидывает задачи в Redmine
- Goto: шаг 1
С версией для iOS немного сложнее:
iOS
- Разработчики пишут код
- Собирается Xcode проект
- Postbuild скрипт добавляет в него локализации, SDK и прочее
- Компилируется проект
- Архивируется
- Экспортируется в IPA файл (всё это делается не очень быстро, хаха)
- После этого можно залить IPA в общую папку на Google Диск
- Или загрузить в TestFlight, где можно прождать пару дней
- Тестировщик скачивает последний IPA
- Подключает девайс, устанавливает приложение
- Тестирует и закидывает задачи в Redmine
- Goto: шаг 1
Вот такая печаль. И если для Android, процесс ещё как-то приемлем (нет), то для iOS – всё очень долго, неудобно и неправильно. Кроме того, большинство операций делается вручную разработчиком (вот тут про мышку) и время которое может быть затрачено на разработку, уходит на ожидание компиляции, экспорта Xcode проекта и прочее. Это ещё надо учесть, что все находятся в одном офисе, т.е. можно подойти спросить/сказать что-то перед запуском всего этого адского процесса.
Но, в общем, оно как-то работает и выпускает игры :)
И тут недавно, с подачи Leopotam, ещё одного любителя велосипедостроения и оптимизации всего, я решил автоматизировать этот процесс. В результате получился BASH скрипт, который умеет:
- Скачивать последние изменения с GIT репозитория
- Запускать тесты
- Собирать APK для Android, в develop и release
- Собирать для iOS Xcode проект, в develop и release
- Компилировать его
- Архивировать и экспортировать IPA
- Генерировать для него манифест
- Генерировать HTML страницы для установки
- Загружать всё это на сервер с помощью sshpass
- Складывать все логи в отдельную папку
- Отсылать в Телеграмм уведомление
И всё это по нажатию одной кнопки. Без запуска Unity и Xcode. Как-то по-веселее, да?
Теперь, когда сборка завершена, тестировщику приходит уведомление в Телеграмм с двумя ссылками на Android и iOS версии. Остаётся перейти по ним и установить приложение прямо с устройства. Под iOS тоже работает, для этого и генерируется специальный манифест.
Вообще, это мой первый опыт в BASH программировании, поэтому не знаю, как оно, по фен-шую или нет, но работает исправно. В любом случае, скрипт будет допиливаться и изменяться под нужды проектов. Ниже несколько основных моментов, как что делается:
Как запустить сборку Unity проекта под определенную платформу
В мануале описаны все аргументы которые можно использовать для билда. Запускаем сборку APK:
$UNITY -batchmode -quit -projectPath "$PATH" -executeMethod Game.BuildActions.AndroidDevelopment -buildTarget android -logFile "$LOGS_PATH/android_development.log"
Тут -buildTarget как раз и задает платформу, а -executeMethod вызывает функцию в Unity проекте, которая запускает билд с заданными параметрами, выглядит это примерно так:
static void AndroidDevelopment () {
PlayerSettings.SetScriptingBackend (BuildTargetGroup.Android, ScriptingImplementation.IL2CPP);
PlayerSettings.SetScriptingDefineSymbolsForGroup (BuildTargetGroup.Android, "DEV");
EditorUserBuildSettings.SwitchActiveBuildTarget (BuildTargetGroup.Android, BuildTarget.Android);
EditorUserBuildSettings.development = true;
EditorUserBuildSettings.androidETC2Fallback = AndroidETC2Fallback.Quality32Bit;
BuildReport report = BuildPipeline.BuildPlayer (GetScenes (), ANDROID_DEVELOPMENT_FILE, BuildTarget.Android, BuildOptions.None);
int code = (report.summary.result == BuildResult.Succeeded) ? 0 : 1;
EditorApplication.Exit (code);
}
Тут можно задать свои параметры для билда, кондишны, и т.п. Результат возвращается в BASH скрипт.
Как скомпилировать и экспортировать Xcode проект
После того, как Unity успешно выдала Xcode проект, его надо скомпилировать, заархивировать и экспортировать в IPA файл, делается это примерно так:
xcodebuild -project "$IOS_PATH/Unity-iPhone.xcodeproj" -quiet > "$LOGS_PATH/ios_build_release.log" 2>&1
xcodebuild -project "$IOS_PATH/Unity-iPhone.xcodeproj" -scheme "Unity-iPhone" archive -archivePath "$IOS_RELEASE/Unity-iPhone.xcarchive" -quiet > "$LOGS_PATH/ios_archive_release.log" 2>&1
xcodebuild -exportArchive -archivePath "$IOS_RELEASE/Unity-iPhone.xcarchive" -exportOptionsPlist "$IOS_RELEASE/options.plist" -exportPath $IOS_RELEASE -allowProvisioningUpdates -quiet > "$LOGS_PATH/ios_export_release.log" 2>&1
Здесь options.plist – это специальный манифест в котором указывается, метод экспорта, TeamID и прочее. Все доступные параметры можно посмотреть по команде:
xcodebuild -help
При всех этих операциях указывается стандартный Unity-iPhone.xcodeproj, который генерирует Unity. Если у вас xcworkspace, то тут надо проверять его наличие и использовать.
Как отправить сообщение в Телеграмм из BASH скрипта
Нужно найти бота BotFather, написать ему /start, или /newbot, заполнить поля и получить сообщение с токеном и ссылкой на документацию. Чтобы отправить с помощью бота сообщение, надо выполнить такую команду:
curl $BOT_PROXY https://api.telegram.org/bot$BOT_TOKEN/sendMessage -m 60 -s -X POST -d chat_id=$CHAT_ID -d text="$1" > "$LOGS_PATH/bot.log" 2>&1
Тут CHAT_ID это идентификатор чата, куда отправлять сообщения. Можно например, добавить бота в группу где сидят тестировщики, дать ему права на чтение сообщений. После этого выполнить:
https://api.telegram.org/bot[BOT_TOKEN]/getUpdates
И получить ID группы и участников кто что-то писал. После этого указать ID группы или конкретного участника, кому отправлять. Как найти и настроить отправку через прокси, думаю не проблема.
Остальные функции, патчат шаблоны манифестов и HTML с помощью sed и заливают нужные файлы на сервер с помощью sshpass.
Параметры которые можно быстро поменять:
#
# PARAMS TO CHANGE
#
BRANCH='master'
COMPANY='my_company'
GAME_NAME='new_game'
BUNDLE='com.mygames.game'
TEAM='ios_team_id'
REMOTE_PATH='url_my_builds_server'
SSH_LOGIN='my_login'
SSH_PASS='my_pass'
SSH_HOST='my_builds_server.ru'
SSH_PATH='~/domains/my_builds_server.ru/builds'
TEMPLATE_FILE=$(PWD)'/template.html'
MANIFEST_FILE=$(PWD)'/manifest.plist'
VERSION_FILE=$(PWD)'/version.txt'
LOGS_PATH=$PROJECT_PATH'/Logs'
ANDROID_PATH=$PROJECT_PATH'/Builds/Android'
BUILDS_PATH=$PROJECT_PATH'/Builds'
IOS_PATH=$PROJECT_PATH'/Builds/iOS'
IOS_BUILD_PATH=$PROJECT_PATH'/Builds/iOS/build'
IOS_DEVELOPMENT=$PROJECT_PATH'/Builds/iOS/build/development'
IOS_RELEASE=$PROJECT_PATH'/Builds/iOS/build/release'
BOT_TOKEN='my_bot_token'
BOT_PROXY='--proxy 185.189.211.70:8080'
CHAT_ID='123456798'
UNITY='/Applications/Unity/Hub/Editor/2019.3.0f1/Unity.app/Contents/MacOS/Unity'
#
#
#
Демка и исходники
Конечно, всё это можно было сделать через тот же Gitlab CI/CD, уже есть готовые скрипты для этого, запускать раннеры, собирать и тестировать. Короче использовать готовую инфраструктуру и механизмы. Но на него надо переезжать и тоже настраивать. Может к этому и придём, пока так и это намного лучше того что было. Тем более, что весь скрипт был написал достаточно быстро.
Всем автоматизации в 2020 году! Вкалывают роботы, а не человек.
Leopotam
И где все это? :)
Про options.plist — TeamId не нужен, он берется из архива, а вот
сильно ускоряет подпись для dev-билдов.
Еще бы неплохо указать, что для сборки все-равно потребуется macos с xcode и установленными cli tools, а так же sshpass (через homebrew) если на sftp используются не ключи, а доступ по паролю.
Если используются нейтивные сдк, которые напрямую лезут к java и прочим штукам — скачиваемый jdk через unityhub не подойдет, т.к не прописаны в $PATH. Решение — прописывать их в $PATH перед запуском, либо поставить отдельно openjdk через homebrew. Второй способ удобнее, т.к юнити можно обновлять без проблем, а системный jdk останется и сам будет прописан в переменные окружения.
mopsicus Автор
Да, косячок :) предыдущую версию запушил, надо обновить.
Ну всё, понеслась… Я уже понял, у твоего велосипеда на 2 скорости больше)
Leopotam
Канешн, выбор бранча через через чат-бота (т.е можно прямо через него запрашивать билд нужного проекта с нужного бранча), автопрошивка хеша коммита с версией в сам билд и т.д и т.п :)
Реализация через gitlab-runner неудобна тем, что фиче-бранчей может быть много и надо мочь собирать каждую из них отдельно — приходится их мерджить в билд-бранч, либо вешать теги, что очень не удобно. Поэтому вот такой автобилд по запросу оказался довольно удобным решением.
kreo_OL
gitlab можно на мержреквесты настроить. И не принимать их)
или настроить один дополнительный конвеер на все ветки, но с ручным запуском. тогда в коммите можно нажать кнопку и сбилдить. Либо автоматическом но первым условием сделать проверку какого нибудь флага)
Последние два варианта правда будут засорят список работ в соответствующем разделе.
А, ну и запускать ci можно через api, по кнопочке где нибудь. У нас в тележке кнопка.
Leopotam
ну в результате все-равно либо чистить руками, либо запускать руками. Дополнительный конвейер — теряется имя ветки-источника билда, сейчас в билд вшивается хеш коммита и имя ветки — удобно. Бот висит в группе и реагирует на команду в виде
например
В ту же группу сообщает о пройденных стадиях (скачивание репа, билд юнити, билд xcode, подпись, закачка на хостинг) и конечные линки для скачивания.