Kompletny projekt przykładowy – obejmujący projekt Unity, konfigurację Jenkins, szablony konteneryzacji w Dockerze oraz skrypty — dostępny jest tutaj: https://github.com/AndreiMaksimovich/Unity-Build-and-Test-Automation
Dlaczego warto automatyzować buildy?
Oszczędność czasu
W trakcie pracy nad projektem tworzy się setki, jeśli nie tysiące buildów dla QA, testów wewnętrznych I publikacji. Automatyzacja tych kroków pozwala zaoszczędzić ogromne ilości czasu, znacznie większe niż wymaga stworzenie samego systemu automatyzacji.
Zapewnienie spójności
Ręczne procesy są obciążone błędami ludzkimi. Pipeline buildów gwarantuje, że każdy build przechodzi przez te same etapy w odpowiedniej kolejności.
Automatyzacja kosztownych zadań
Pipeline to idealne miejsce, by przenieść procesy czasochłonne lub obciążające zasoby:
- kompresja i optymalizacja assetów
- analiza statyczna kodu / bezpieczeństwa
- łączenie meshy i materiałów
- generowanie shaderów / kodu
- pełnej jakości wypiekanie oświetlenia (light baking)
- analiza scen i prefabów
Przykładowy prosty workflow CI/CD
Gdy jesteś zadowolony z lokalnych zmian i je commitujesz, nadchodzi czas na testowy build. Z automatyzacją, pojedynczy klik w interfejsie WWW uruchamia maszynę buildową, która:
- Klonuje repozytorium Git
- Przepala (rebake) oświetlenie
- Uruchamia unit i PlayMode testy, raportując błędy
- Buduje wersje na Android, iOS i Web
- Wysyła buildy na lokalne urządzenia testowe
- Publikuje testowe buildy do Google Play i App Store (TestFlight)
Dla uproszczenia pomijam szczegóły etapu 6 – kwestie podpisywania kluczy, profilów provisioning, zarządzania sekretami to temat na osobny artykuł.
Przyjrzyjmy się teraz API Unity i narzędziom systemowym niezbędnym do zbudowania takiego systemu.
API Unity i narzędzia wspierające automatyzację
Uruchamianie skryptów edytora przez CLI
Możesz uruchomić Unity Editor z CLI, wskazując statyczną metodę w skrypcie edytora, która zostanie wykonana, a potem editor sam się zamknie:
{UNITY_EDITOR_PATH} -batchmode -quit -projectPath {UNITY_PROJECT_PATH} {EDITOR_CLASS_NAME}.{EDITOR_CLASS_METHOD}
Dokumentacja: https://docs.unity3d.com/2020.1/Documentation/Manual/CommandLineArguments.html
Manipulacja scenami Unity bez GUI
Sceny można otwierać, edytować i zapisywać przez skrypt, bez interakcji z GUI:
EditorSceneManager.OpenScene(scenePath);
// modyfikacje sceny
EditorSceneManager.SaveOpenScenes();
Dokumentacja: https://docs.unity3d.com/6000.2/Documentation/ScriptReference/SceneManagement.EditorSceneManager.html
Light Baking
Oświetlenie można wypiekać automatycznie, programowo, na aktualnie otwartej scenie:
Lightmapping.Bake();
Dokumentacja: https://docs.unity3d.com/6000.2/Documentation/ScriptReference/Lightmapping.Bake.html
Zmiana targetu builda
Można na bieżąco zmieniać aktywny target builda przez skrypt:
EditorUserBuildSettings.SwitchActiveBuildTarget(namedBuildTarget, target);
Dokumentacja: https://docs.unity3d.com/6000.2/Documentation/ScriptReference/EditorUserBuildSettings.SwitchActiveBuildTarget.html
Addressables
Możesz zbudować Addressables przez skrypt:
AddressableAssetSettings.BuildPlayerContent();
Dokumentacja: https://docs.unity3d.com/Packages/com.unity.addressables@1.15/manual/BuildPlayerContent.html
Ustawienia Playera
Można programowo zmieniać ustawienia builda, np. załadować keystore dla Androida ze storage’u, użyć go w procesie, a potem odrzucić te zmiany.
PlayerSettings.Android.keystoreName =
"PathToKeystore";
PlayerSettings.Android.keystorePass =
"";
PlayerSettings.Android.keyaliasName =
"KeyaliasName";
PlayerSettings.Android.keyaliasPass =
"";
Dokumentacja:
Budowanie projektu Unity przez kod
Build Options:
varbuildPlayerOptions =
newBuildPlayerOptions {
locationPathName = {PATH}, target = {BUILD_TARGET}, extraScriptingDefines = {SCRIPT_DEFINES}, options = {BUILD_OPTIONS}, scenes = {SCENES}};
Dokumentacja: https://docs.unity3d.com/ScriptReference/BuildPlayerOptions.html
Build Pipeline:
BuildPipeline.BuildPlayer(buildPlayerOptions);
Dokumentacja: https://docs.unity3d.com/6000.2/Documentation/ScriptReference/BuildPipeline.BuildPlayer.html
Uruchamianie testów
Możesz automatycznie uruchamiać testy jednostkowe (unit / EditMode) i PlayMode przez kod.
PlayMode Testy przez skrypt:
vartestRunner = ScriptableObject.CreateInstance<TestRunnerApi>();
varfilter =
newFilter {
targetPlatform = {PLATFORM}, testMode = TestMode.PlayMode }; varexecutionSettings =
newExecutionSettings {
filters =
new[] { filter }
};testRunner.Execute(executionSettings);
Dokumentacja: https://docs.unity3d.com/Packages/com.unity.test-framework@2.0/api/UnityEditor.TestTools.TestRunner.Api.TestRunnerApi.html
Unit Testy przez CLI:
${UNITY_PATH}-runTests -batchmode -projectPath
${UNITY_PROJECT_PATH}-testPlatform EditMode -logfile stdout -testResults {TEST_RESULTS_PATH}
Dokumentacja: https://docs.unity3d.com/Packages/com.unity.test-framework@1.1/manual/reference-command-line.html
Budowanie projektu iOS z linii poleceń
Możesz zbudować i spakować projekt iOS bezpośrednio z linii poleceń, korzystając z xcodebuild
.
1. Utwórz archiwum XCArchive
xcodebuild -workspace {WORKSPACE_PATH} -scheme {SCHEME_NAME} -archivePath {XCARCHIVE_PATH} archive
2. Przygotuj opcje eksportu (.plist)
Utwórz plik .plist
z wybranymi ustawieniami eksportu:
<?xml version="1.0"encoding=
"UTF-8"?>
<!DOCTYPE plistPUBLIC"-//Apple//DTD PLIST 1.0//EN""http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plistversion=
"1.0">
<dict>
<key
>method
</key>
<string
>app-store
</string>
<key
>teamID
</key>
<string
>{YOUR_TEAM_ID}
</string>
<key
>uploadBitcode
</key>
<true
/>
<key
>uploadSymbols
</key>
<true
/>
</dict>
</plist>
3. Zbuduj plik .ipa
xcodebuild -exportArchive -archivePath {XCARCHIVE_PATH} -exportPath {EXPORT_PATH} -exportOptionsPlist {OPTIONS_PLIST_PATH}
Dokumentacja: Apple Technical Note TN2339
Instalowanie testowych buildów na lokalnych urządzeniach iOS
Najprostszym sposobem instalacji plików .ipa
lokalnie jest użycie Apple Configurator, który zawiera narzędzie cfgutil
:
1. Zainstaluj Apple Configurator 2 – App Store link
2. Dodaj cfgutil
do zmiennej PATH:
sudo
ln-s
"/Applications/Apple Configurator 2.app/Contents/MacOS/cfgutil"/usr/local/bin/cfgutil
3. Zainstaluj plik .ipa
na podłączonym urządzeniu (przez USB):
cfgutil install-app {IPA_PATH}
Instalacja bezprzewodowa na urządzeniach iOS jest uciążliwa i mało niezawodna. W większości przypadków znacznie łatwiej jest zautomatyzować uploady do TestFlight i dystrybuować buildy tą drogą.
Instalowanie testowych buildów na lokalnych urządzeniach z Androidem
Możesz instalować pliki .apk
bezpośrednio na urządzeniach z Androidem podłączonych kablem USB lub przez lokalne połączenie bezprzewodowe.
Konfiguracja bezprzewodowa:
adb pair {IP}:{PORT}
adb connect {IP}:{PORT}
adb install {APK_PATH}
adb shell am start -n {APP_PACKAGE}/{ENTRY_POINT}
Dokumentacja: https://developer.android.com/tools/adb
Automatyzacja uploadu do App Store TestFlight i Google Play Internal Testing
Proste przykłady użycia, podane bez kontekstu …
App Store
xcrun notarytool submit {IPA_PATH} \
--apple-id {APPLE_ID_EMAIL} \
--team-id {TEAM_ID} \
--password {APP_SPECIFIC_PASSWORD}
Google Play
gcloud auth activate-service-account \
--key-file={SERVICE_ACCOUNT_JSON} \
--project={GCP_PROJECT_ID}
gcloud firebase appdistribution:distribute {AAB_PATH} \
--app {FIREBASE_APP_ID} \
--testers {TESTERS} \
--release-notes {RELEASE_NOTE}
Podsumowanie
Powyższe API i przykłady obejmują większość tego, co potrzebujesz, by stworzyć własny pipeline do automatyzacji buildów w Unity. Dołóż integrację z Gitem, powiadomienia (np. Slack Webhooks), i masz prosty, ale potężny workflow.
Ostateczny krok to wybór narzędzia, które to wszystko połączy. W moim przykładzie używam Jenkins. Choć może wydawać się nieco przestarzały, ogrom dostępnych pluginów oraz możliwość wykorzystania zdalnych maszyn roboczych (np. do wypiekania oświetlenia na Windowsie z dedykowanym GPU) sprawia, że w przypadku lokalnie hostowanych systemów nadal jest dobrym wyborem.
Jednak praktycznie każde nowoczesne rozwiązanie CI/CD będzie działało – a API i skrypty pozostają takie same.