image

Сейчас я расскажу, как выглядит процесс разработки на Unity в маленькой gamedev компании и как мы его улучшаем и автоматизируем. Всё-таки 2020 год на дворе, хватит уже мышкой водить…

Про мышку, это не такая уж и шутка. До недавнего времени, процесс тестирования и разработки игр у нас выглядел примерно так:

Android


  1. Разработчики пишут код
  2. Собирается версия для Android
  3. Загружается в общую папку на Google Диск
  4. Тестировщик скачивает последний билд
  5. Тестирует и закидывает задачи в Redmine
  6. Goto: шаг 1

С версией для iOS немного сложнее:

iOS


  1. Разработчики пишут код
  2. Собирается Xcode проект
  3. Postbuild скрипт добавляет в него локализации, SDK и прочее
  4. Компилируется проект
  5. Архивируется
  6. Экспортируется в IPA файл (всё это делается не очень быстро, хаха)
  7. После этого можно залить IPA в общую папку на Google Диск
  8. Или загрузить в TestFlight, где можно прождать пару дней
  9. Тестировщик скачивает последний IPA
  10. Подключает девайс, устанавливает приложение
  11. Тестирует и закидывает задачи в Redmine
  12. Goto: шаг 1

Вот такая печаль. И если для Android, процесс ещё как-то приемлем (нет), то для iOS – всё очень долго, неудобно и неправильно. Кроме того, большинство операций делается вручную разработчиком (вот тут про мышку) и время которое может быть затрачено на разработку, уходит на ожидание компиляции, экспорта Xcode проекта и прочее. Это ещё надо учесть, что все находятся в одном офисе, т.е. можно подойти спросить/сказать что-то перед запуском всего этого адского процесса.

Но, в общем, оно как-то работает и выпускает игры :)

И тут недавно, с подачи Leopotam, ещё одного любителя велосипедостроения и оптимизации всего, я решил автоматизировать этот процесс. В результате получился BASH скрипт, который умеет:

  • Скачивать последние изменения с GIT репозитория
  • Запускать тесты
  • Собирать APK для Android, в develop и release
  • Собирать для iOS Xcode проект, в develop и release
  • Компилировать его
  • Архивировать и экспортировать IPA
  • Генерировать для него манифест
  • Генерировать HTML страницы для установки
  • Загружать всё это на сервер с помощью sshpass
  • Складывать все логи в отдельную папку
  • Отсылать в Телеграмм уведомление

И всё это по нажатию одной кнопки. Без запуска Unity и Xcode. Как-то по-веселее, да?

image

Теперь, когда сборка завершена, тестировщику приходит уведомление в Телеграмм с двумя ссылками на 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 году! Вкалывают роботы, а не человек.

Комментарии (5)


  1. Leopotam
    12.12.2019 14:55
    +1

    • Скачивать последние изменения с GIT репозитория


    И где все это? :)


    • Про options.plist — TeamId не нужен, он берется из архива, а вот


      <key>compileBitcode</key>
      <false/>

      сильно ускоряет подпись для dev-билдов.


    • Еще бы неплохо указать, что для сборки все-равно потребуется macos с xcode и установленными cli tools, а так же sshpass (через homebrew) если на sftp используются не ключи, а доступ по паролю.


    • Если используются нейтивные сдк, которые напрямую лезут к java и прочим штукам — скачиваемый jdk через unityhub не подойдет, т.к не прописаны в $PATH. Решение — прописывать их в $PATH перед запуском, либо поставить отдельно openjdk через homebrew. Второй способ удобнее, т.к юнити можно обновлять без проблем, а системный jdk останется и сам будет прописан в переменные окружения.



    1. mopsicus Автор
      12.12.2019 15:03

      Да, косячок :) предыдущую версию запушил, надо обновить.

      Ну всё, понеслась… Я уже понял, у твоего велосипеда на 2 скорости больше)


      1. Leopotam
        12.12.2019 15:22

        Канешн, выбор бранча через через чат-бота (т.е можно прямо через него запрашивать билд нужного проекта с нужного бранча), автопрошивка хеша коммита с версией в сам билд и т.д и т.п :)


        Реализация через gitlab-runner неудобна тем, что фиче-бранчей может быть много и надо мочь собирать каждую из них отдельно — приходится их мерджить в билд-бранч, либо вешать теги, что очень не удобно. Поэтому вот такой автобилд по запросу оказался довольно удобным решением.


        1. kreo_OL
          17.12.2019 19:41

          gitlab можно на мержреквесты настроить. И не принимать их)
          или настроить один дополнительный конвеер на все ветки, но с ручным запуском. тогда в коммите можно нажать кнопку и сбилдить. Либо автоматическом но первым условием сделать проверку какого нибудь флага)
          Последние два варианта правда будут засорят список работ в соответствующем разделе.

          А, ну и запускать ci можно через api, по кнопочке где нибудь. У нас в тележке кнопка.


          1. Leopotam
            17.12.2019 20:29

            ну в результате все-равно либо чистить руками, либо запускать руками. Дополнительный конвейер — теряется имя ветки-источника билда, сейчас в билд вшивается хеш коммита и имя ветки — удобно. Бот висит в группе и реагирует на команду в виде


            /build имя-проекта имя-ветки

            например


            /build supergame feature/new-ui

            В ту же группу сообщает о пройденных стадиях (скачивание репа, билд юнити, билд xcode, подпись, закачка на хостинг) и конечные линки для скачивания.