Matchingham Games CI/CD Documentation
As Matchingham Games we are using Gitlab CI to automate our Unity Test and Release Builds using the continuous methodologies. What are these methodologies?
Continuous Integration
For every push to repository, you can create a set of scripts to build and test your game automatically. This practice is known as Continuous Integration. Each change submitted to an application, even to development branches, is built and tested automatically and continuously..
Continuous Delivery
This is a step beyond the continuous integration. Game is also deployed continuously to AppStore and Google Play Store, but it requires human intervention to manually strategically trigger the Releases.
GitFlow
In order to use the CI/CD methodologies, we have to work with GitFlow and synchronize development cycles within our team. To do that we choose GitFlow as a branching strategy. GitFlow is a widely used branching model in the industry. There are lots of variants of this workflow but simplified version will work with our CI/CD workflow.
By default, there will be two important branches named main
and development
and a bunch of other feature branches.
-
main
: Protected branch. Every merge/push to main branch will trigger release pipeline automatically. At the end of the release pipeline, the release configured builds will be delivered to both AppStore and Google Play Store. So, direct pushes to main branch are not be permitted, rather you have to send merge requests to main branch to trigger release pipeline. -
development
: every merge/push to development branch will trigger test pipeline automatically. At the end of the pipeline the development configured builds will be delivered to Firebase App Distribution. -
feature/my-awesome-feature
: we have to develop our game with feature branches where every feature branch can contain bunch of commits.- These pushes will not trigger any of the pipelines. After development cycle ends, a Merge Request (aka: Pull Request / PR) should be created to merge feature branch into development branch.
- After merge ends, development build pipeline will automatically be triggered and deliver test builds to Firebase App Distribution. After some development cycle like this, once we are happy about our builds, a new merge request should be created to merge development branch into main branch.
- After merge request to the main branch is accepted, release configured build pipeline will automatically work and deliver release builds to App Store and Google Play Store.
How CI Script Works
- Our CI script will fetch the latest version of gitlab-ci.yml file from the AWS.
- Your code will fetch from the git repository with 50 depth by gitlab runner automatically
- If you have a previous build, pipeline will get the previous
Library
files from build machine's local cache. - Run the MGBuilder Unity Build Script.
- Update local cache by moving new
Library
folder to the build machine's local cache folder. - Get correct credentials from local cache
- Get Fastlane setup from AWS
- Run Fastlane to deploy.
Step by Step Implementation
-
gitignore
Use this privided gitignore file
❗ Do not add
ProjectVersion.txt
file to .gitignore file. This is a required file for CI/CD Workflow. Every team member in project should be on the same version of Unity Editor, including build machines
-
UpmConfig Authentication
Authenticate with Matchingham Games Unity Package Manager Registry by following this documentation. After authentication you should be able to see 'MGBuilder Package' in Unity Editor.
-
MGBuilder Package
Install the latest version of MGBuilder package. This package contains Build Scripts for specific targets and establishes the communication between Pipeline and Unity. Always keep this package up to date.
-
.gitlab-ci.yml
file
.gitlab-ci.yml
is a file in the root of your repository, which contains the CI/CD configuration.
Gitlab CI uses the YAML (Yet Another Markup Language) language to run CI operations.
Create .gitlab-ci.yml
file in the root of the repository and add this line on the top of yml file. This will add our base ci script to this project.
include: https://...../base-ci.yml
Fill out the required environment variables. Like in the example below. All detailed environment variables are down below this documentation.
Normally you shouldn't edit this file frequently. If you want to change environment variables start a manual pipeline and edit those variables from Gitlab Web UI. You can find detailed information about how to start manual pipeline down below.
include: https:/...../base-ci.yml
variables:
ACCOUNT: mg
APP_NAME: ""
SLACK_CHANNEL_ID: ""
FIREBASE_TESTER_GROUP_NAME: "" # Firebase App Distribution Tester group alias.
MACHINE_TAG: "mg-3"
.default.dev:
variables:
EUBS_Development: "1" # Enables development build for firebase builds.
APP_NAME: "" # App name will be displayed in the Slack channel only
.ios:
variables:
iOSBundleIdentifier: "com.matchingham.awesomegame"
IOS_UNITY_VERSION: "2020.3.16f1" # Build machine Unity version
IOS_APP_FIREBASE_ID: ""
.android:
variables:
ANDROID_APP_FIREBASE_ID: ""
ANDROID_UNITY_VERSION: "2020.3.16f1" # Build machine Unity version
AndroidPackageName: "com.matchingham.awesomegame"
ANDROID_APP_FIREBASE_ID: ""
keystoreName: "Relative/Path/to/key.keystore"
keyaliasName: ""
keystorePass: ""
keyaliasPass: ""
-
Compile flags
There is a main compile flag to be used within the CI/CD Pipeline.
-
MG_DEBUG
: UseMG_DEBUG
compile flag to differentiate Test or Release Builds. For Example if you are using some Debugger like SRDebugger, you can include the initialization by using this flag. This flag will be defined automatically in MGBuilder package, only for test builds, ie,development
branch pushes.#if MG_DEBUG
SRDebug.Init();
#endif -
MG_PIPE
: Set before compilation for every pipeline builds. -
MG_APPLE
: Set before compilation for every.ios
jobs. -
MG_GOOGLE
: Set before compilation for every.android
jobs. -
MG_HUAWEI
: Set before compilation for every.huawei
jobs. -
DISABLE_SRDEBUGGER
: If release/main pipeline is running andIS_SR_DEBUGGER_FLAG_ACTIVE
is set to 1, this flag will be added to the build. This flag will be defined automatically in the MGBuilder package, only for release (main) builds. It prevents SRDebugger from being included in the release build. -
Keystore relative path
In order to publish Android Builds to Google Play Store, keystore file name, alias and password must be entered correctly in .gitlab-ci.yml
file.
If you want, you can specify different keystore for different environments.
.android-dev:
variables:
...
keystoreName: "relative/path/to/development.keystore"
keyaliasName: ""
keystorePass: ""
keyaliasPass: ""
...
.android.release:
variables:
...
keystoreName: "relative/path/to/production.keystore"
keyaliasName: ""
keystorePass: ""
keyaliasPass: ""
...
-
Which machine to run pipeline. (Runners)
Currently we have only one machine, but this may change.
mgi-3
Tags can be assigned to variable MACHINE_TAG
variables:
MACHINE_TAG: "mg-3"
-
Versioning
Version Number
AndroidVersion
&VERSION
defines the version number for android and iOS relatively.
To create Semantic Versioning use the Unity Build Settings panel in Editor.
Version number string needs to be in X.X.X format, where every X is an integer. You should manage app version within the player tab under project settings panel.
If there is a version number defined in the pipeline variables, pipeline will use the value from pipeline variables, not from the Unity project settings. This helps developer to increment version number without being required to make another commit with these values are changed.
Build Number
Version Code
&Build Number
defines the build number for android and iOS relatively.
Build numbers are automatically incremented by the pipeline, you don't need to increment in every for every single build.It is developers responsibility to set this version number in unity editor. Pipeline will handle build number for you. This way pipeline will ensure every single build is unique.
When development pipeline is triggered, pipeline will get the build number from the Firebase App Distribution and increment the latest number by one.
When release pipeline is triggered, pipeline will get the build number value from the App Store and Google Play Store. Then, script will make the comparison between values from stores and project settings then will assign the bigger one. If the biggest value is what it is on store page then it will increment build number by 1 from latest build number.
-
Unity Versions
Every team member on the project, should be working on the same Unity version including build machines to make development cycle less error-prone. Define which version of Unity you want for the builds like below.
.android:
variables:
...
ANDROID_UNITY_VERSION: "2020.3.25f1"
...
.ios:
variables:
...
IOS_UNITY_VERSION: "2020.3.25f1"
...
-
Release Notes
If you place
release-notes.md
file in the root folder of your project, Pipeline will read its contents and forward it to Slack, Firebase and Gitlab Releases. To tag someone in slack using release-notes.md, use slack user id in release-notes.md file. Use Slack Markdown for formatting -
Slack Integration
Integrate Devops Team App
to your slack channel by following Channel Info > Integrations > Add An App
. In Search box search for Devops Team and add to channel.
Define the Slack channel ID to which notifications will be sent in the .gitlab-ci.yml
file. To get the channel ID, go to your slack channel and type /sdt whoami
then press enter. Here the current conversation ID is your channel id.
.default:
variables:
SLACK_CHANNEL_ID: ""
Our Slack Configuration will keep the channel updated for every successful development or main branch build deliveries.
-
Unity Connect Integration
In order to work with Unity Connect Services, project needs to be linked with the Unity Dashboard.
Once you linked project, open desired services. Unity will update PojectSetting.asset
file and add the information such as cloudProjectId
, projectName
, organizationId
as well as enabled services. You should push these project settings changes to the repository.
Manual Pipeline Start
To Start a pipeline go to gitlab repository of your project. Tap CI/CD > Pipelines on the left pane. In the pipelines page click the "Run Pipeline" button.
After that you should select the correct branch of desired build. If you want release build use main
branch or if you want development build use the development
branch. You can see there are bunch of environment variables that you need to set. On the bottom of the page you can set your predefined variables and their values.
Click on run pipeline button and you will see the related jobs. After clicking the play button, related job will start running.
Create New Pipeline from any previous commit
It is possible to create a new pipeline from any previous commit. To do so you need to create a new tag pointing to desired commit. Tag name should not be contain ios
, android
or huawei
. Then you can create a new pipeline using the Gitlab Web Interface CI/CD > Pipelines > Run Pipeline and choose the tag from dropdown menu.
Prevent autostart jobs
Pipeline will automatically start once a new push has been received to configured branches. This behavior can be prevented by passing [ci skip]
of [skip ci]
to the latest commit message. Pipeline will iterate exactly this words, location doesn't matter.
Crashlytics Debug Symbol Processing
Both iOS and Android Debug Symbols are processed after the build stage is finished.
Projects with Asset Bundles
Google Asset Delivery with Asset Bundles
If your project uses Google Asset Delivery , pipeline can build assets bundles according to your AssetBundleConfig
. You can pass your configuration as a json file path to the pipeline. To do so use AssetBundleConfigSerialization to serialize your AssetBundleConfig and save your json file , pass the path to the pipeline using SERIALIZED_ASSET_BUNDLE_CONFIG_PATH
parameter, like so:
.android:
variables:
...
`SERIALIZED_ASSET_BUNDLE_CONFIG_PATH`: "Assets/AssetBundles/Android/assetBundleConfig"
...
Split Application Binary
If your project uses Split Application Binary then you must enable this settings in Unity Editor. Pipeline will automatically create an aab for release pipelines. But for development pipelines you must enable aab build for developement pipelines in yml file as so:
.android.dev:
variables:
BUNDLE_BUILD: "1"
Finally link your application with Firebase App Distribution and Google Play Console.
Play Asset Delivery with Addressables
For use Play asset delivery with addressables please use this document as a reference After implementing this structure, please move the Adressable Asset setting you just created , to the top of the Build and Play Mode scripts
Pipeline Decisions and Differences between development
and main
builds
Pipeline will make decision that you can not override right now
Feature | Variable Name | main - release | development - app distribution |
---|---|---|---|
Scripting Backend | - | IL2CPP | - |
Target Architecture | - | ARMv7 & ARM64 | - |
Pipeline will make decision that you can override by variables, in yml file
Feature | Variable Name | main - release | development - app distribution |
---|---|---|---|
Target API Level | AndroidTargetSdkVersion | 33 | 33 |
Compression Method | BO_Compress | LZ4HC | LZ4HC |
Unity Logo Splash Screen
Use Draw Mode to Unity Logo Below
to hide Unity Logo in builds.
Xcode Version
Now you can set project specific Xcode Version by using :
.ios:
variables:
XCODE_VERSION: 14.1.0
Custom PreProcess
Sometimes Pre Process Scripts in Unity does not work, especially building asset bundles. So there is another option to execute pre process scripts using in yml so:
variables:
CUSTOM_PRE_BUILD_SCRIPT_METHOD_NAME: "MyCustomClass.MyCustomStaticMethod"
Your method signature should be static
. Pipeline will execute your script, in batchmode.
Jobs and Job Hierarchy
You can find the job inheritance image below.
Base Jobs
We use job inheritance to assign variables we want for specific structures. For example if you want some changes that reflects only for ios release builds you should use like this:
.ios.release:
variables:
...
.default:
every job inherits..dev:
every development build related job inherits.release:
every release build related job inherits.ios:
every iOS related jobs inherits..android:
every Android related jobs inherits..ios.release:
every iOS release related jobs inherits..android.release:
every Android release related jobs inherits..ios.dev:
every iOS development related jobs inherits..android.dev:
every Android development related jobs inherits.
Action Jobs
These jobs are action jobs. They execute the build process in the correct order.
ios:release:build:
creates iOS release build and deploys to App Store Connect.android:release:build:
creates Android build and deploys to Google Play Console.ios:dev:build:
creates iOS development build and deploys to Firebase App Distribution.android:dev:build:
creates Android development build and deploys to Firebase App Distribution.android:release:publish:
creates release tags for Android build to Gitlab.ios:release:publish:
creates release tags for iOS build to Gitlab.ios:crashlytics:
sends debug symbols for iOS builds to Firebase Crashlytics.android:crashlytics:
sends debug symbols for Android builds to Firebase Crashlytics.
Environment Variables
List of Environment Variables
MACHINE_TAG
Variable Name | Default Value | Required |
---|---|---|
MACHINE_TAG | mg | ✅ |
Runner Machine to be used where pipeline executes builds. Currently we have 3 machine mg-1
, mg-2
and mg-3
.mg-3
is the most up to date machine.
ACCOUNT
Variable Name | Default Value | Required |
---|---|---|
ACCOUNT | mg | ✅ |
Account to be used for publishing on the App Store and Google Play Store. use mg
for Matchingham Games Account, use onur
for Onur Account.
AndroidPackageName
Variable Name | Default Value | Required |
---|---|---|
AndroidPackageName | ✅ |
Android package name with inverse domain convention eg. com.matchingham.ourbestgameever
iOSBundleIdentifier
Variable Name | Default Value | Required |
---|---|---|
iOSBundleIdentifier | ✅ |
iOS Bundle Identifier with inverse domain convention eg. com.matchingham.ourbestgameever
ANDROID_APP_FIREBASE_ID
Variable Name | Default Value | Required |
---|---|---|
ANDROID_APP_FIREBASE_ID | ✅ |
AndroidApp Firebase App ID . It can be obtained via Firebase console in Project Settings. Follow this tutorial to get
ANDROID_TARGET_SDK_VERSION
Variable Name | Default Value | Required |
---|---|---|
ANDROID_TARGET_SDK_VERSION | 30 | ✅ |
IOS_APP_FIREBASE_ID
Variable Name | Default Value | Required |
---|---|---|
IOS_APP_FIREBASE_ID | ✅ |
iOS App Firebase App ID . It can be obtained via Firebase console in Project Settings. Follow this tutorial to get
FIREBASE_TESTER_GROUP_NAME
Variable Name | Default Value | Required |
---|---|---|
FIREBASE_TESTER_GROUP_NAME | ✅ |
Firebase App Distribution tester group alias. For detail firebase documentation.
For multiple groups, use comma-separated values. For example group1,group2
.
SLACK_CHANNEL_ID
Variable Name | Default Value | Required |
---|---|---|
SLACK_CHANNEL_ID | ✅ |
Slack channel ID that notifications will be sent to. To get the channel ID go to channel type /sdt whoami
keystoreName
Variable Name | Default Value | Required | Only On |
---|---|---|---|
keystoreName | ✅ | Android |
Relative path to Android Keystore file. Required for Android Builds.
keyaliasName
Variable Name | Default Value | Required | Only On |
---|---|---|---|
keyaliasName | ✅ | Android |
Key alias name. Required for Android Builds.
keystorePass
Variable Name | Default Value | Required | Only On |
---|---|---|---|
keystorePass | ✅ | Android |
Keystore password. Required for Android Builds.
keyaliasPass
Variable Name | Default Value | Required | Only On |
---|---|---|---|
keyaliasPass | ✅ | Android |
Keyalias password. Required for Android Builds.
APP_NAME
Variable Name | Default Value | Required |
---|---|---|
APP_NAME | ✅ |
App Name will be passed to Slack notifications.
UNITY_COMMAND_LINE_ARGUMENTS
Variable Name | Default Value | Required |
---|---|---|
UNITY_COMMAND_LINE_ARGUMENTS | -batchmode -username $UNITY_USER_NAME -password $UNITY_USER_PASSWORD -projectPath . -logFile - -quit -executeMethod | 🚫 |
Arguments for when running unity from command line. For details follow documentation
BO_Compress
Variable Name | Default Value | Required |
---|---|---|
BO_Compress | LZ4 | 🚫 |
(Value Can Be = LZ4, LZ4HC and None) Use chunk-based LZ4 or LZ4 high-compression or None when building the Player.
EUBS_Development
Variable Name | Default Value | Required |
---|---|---|
EUBS_Development | 0 | 🚫 |
(Value Can Be = 0, 1) Enables development build. For Detail documentation
Creates development build includes debug symbols and enables the Profiler.
.default.dev:
variables:
EUBS_Development: "1"
EUBS_BuildWithDeepProfilingSupport
Variable Name | Default Value | Required |
---|---|---|
EUBS_BuildWithDeepProfilingSupport | 0 | 🚫 |
"(Value Can Be = 0, 1) Enables Deep Profiling support in the player. For Detail
EUBS_WaitForManagedDebugger
Variable Name | Default Value | Required |
---|---|---|
EUBS_WaitForManagedDebugger | 0 | 🚫 |
(Value Can Be = 0, 1) Instructs the player to wait for managed debugger to attach before executing any script code. For Detail documentation
EUBS_ConnectProfiler
Variable Name | Default Value | Required |
---|---|---|
EUBS_ConnectProfiler | 0 | 🚫 |
(Value Can Be = 0, 1) When the build is started, an open Profiler Window will automatically connect to the Player and start profiling. For Detail documentation
EUBS_AllowDebugging
Variable Name | Default Value | Required |
---|---|---|
EUBS_AllowDebugging | 0 | 🚫 |
(Value Can Be = 0, 1) Enable source-level debuggers to connect. For Detail documentation
EUBS_IOSBuildConfigType
Variable Name | Default Value | Required | Only on |
---|---|---|---|
EUBS_IOSBuildConfigType | Debug | 🚫 | iOS |
ONLY iOS (Value Can Be = Debug, Release) Scheme with which the project will be run in Xcode. For Detail documentation
ShowSplashScreen
Variable Name | Default Value | Required |
---|---|---|
ShowSplashScreen | 1 | 🚫 |
Set this to 1 to display the Splash Screen be shown when the application is launched. Set it to 0 to disable the Splash Screen. For Detail documentation
AndroidBuildPath
Variable Name | Default Value | Required |
---|---|---|
AndroidBuildPath | Builds/Android | 🚫 |
The path where the android application will be built.
AndroidVersion
Variable Name | Default Value | Required | Only on |
---|---|---|---|
AndroidVersion | 🚫 | Android |
ONLY Android Version Number. Format must be X.X.X and all X must be integer
AndroidBundleVersionCode
Variable Name | Default Value | Required | Only on |
---|---|---|---|
AndroidBundleVersionCode | 🚫 | Android |
ONLY Android BundleVersion Code. Value must be integer.
iOSBuildPath
Variable Name | Default Value | Required |
---|---|---|
iOSBuildPath | Builds/iOS | 🚫 |
VERSION
Variable Name | Default Value | Required | Only on |
---|---|---|---|
VERSION | 🚫 | iOS |
ONLY iOS Version Number. If not be set then automatically it will be set.
BUILD
Variable Name | Default Value | Required | Only on |
---|---|---|---|
BUILD | 🚫 | iOS |
"ONLY iOS Build Number. If not be set then automatically it will be set.
ONLY_ON
Variable Name | Default Value | Required |
---|---|---|
ONLY_ON | 🚫 |
It is used to specify which branch the auto-build script should run on.
For example, if your branch names are master
and dev
you should specify this branch name as follows.
.default:
variables:
...
ONLY_ON: "master"
...
.default.dev:
variables:
...
ONLY_ON: "dev"
...
DISABLE
Variable Name | Default Value | Required |
---|---|---|
DISABLE | 🚫 |
Disables the auto-build script. (Value Can Be = 1) For example, in some project we want to shutdown ios app production.
.ios
variables:
DISABLE: "1"
CLEAN_BUILD
Variable Name | Default Value | Required |
---|---|---|
CLEAN_BUILD | 0 | 🚫 |
Clears the Library cache before building. (Value can be set to 1.)
Each build will then rebuild the Library folder, which increases the build time.
Use only if you are changing the Unity version or want a completely clean build.
CLEAN_PREVIOUS_BUILD
Variable Name | Default Value | Required |
---|---|---|
CLEAN_PREVIOUS_BUILD | 0 | 🚫 |
Clears the previous Android build artifacts from Library/Bee/Android before building. (Value can be set to 1.)
This ensures you start fresh for your Android builds.
LC_ALL
Variable Name | Default Value | Required |
---|---|---|
LC_ALL | en_US.UTF-8 | 🚫 |
Sets the locale for the build machine. Must be en_US.UTF-8 for Fastlane to work properly.
LANG
Variable Name | Default Value | Required |
---|---|---|
LANG | en_US.UTF-8 | 🚫 |
Sets the locale for the build machine. Must be en_US.UTF-8 for Fastlane to work properly.
AndroidScriptingBackend
Variable Name | Default Value | Required | Only on |
---|---|---|---|
AndroidScriptingBackend | IL2CPP | 🚫 | Android |
Set the scripting backend for Android. Possible values: Mono or IL2CPP.
AndroidTextureCompression
Variable Name | Default Value | Required | Only on |
---|---|---|---|
AndroidTextureCompression | ETC2 | 🚫 | Android |
Set the default texture compression before building the project. Supported formats: dxt, pvrtc, atc, etc, etc2, astc.
AndroidETC2Fallback
Variable Name | Default Value | Required | Only on |
---|---|---|---|
AndroidETC2Fallback | 1 | 🚫 | Android |
Allowed values: 32-bit, 16-bit, or 32-bit half resolution. Unity uses this to fall back on devices that don’t support ETC2.
SKIP_WAITING_FOR_IOS_PROCESSING
Variable Name | Default Value | Required | Only on |
---|---|---|---|
SKIP_WAITING_FOR_IOS_PROCESSING | 0 | 🚫 | iOS |
Set this to true (or 1) to skip waiting for App Store Connect to finish processing iOS builds.
Useful if you want to build Android and iOS in parallel. A Slack message will notify you while iOS is still processing.
ANDROID_RELEASE_STATUS
Variable Name | Default Value | Required | Only on |
---|---|---|---|
ANDROID_RELEASE_STATUS | completed | 🚫 | Android |
Defines the release status for the Android build. Supported values: completed, draft, halted, inProgress.
Use completed
to auto-start internal testing after Google Play’s initial review.
Use draft
to upload but not start testing automatically.
PRE_BUILD_MERGE_REQUESTER_SCRIPT
Variable Name | Default Value | Required |
---|---|---|
PRE_BUILD_MERGE_REQUESTER_SCRIPT | 🚫 |
Specifies a static method (e.g., MyCustomClass.MyStaticFunction) to run only when you start the pipeline manually from GitLab’s web interface.
After the method finishes, the pipeline automatically commits changes (if any) in a new branch, merges it into the starting branch, and opens a Merge Request.
You’ll see a Slack notification for the changes.
IS_SR_DEBUGGER_FLAG_ACTIVE
Variable Name | Default Value | Required |
---|---|---|
IS_SR_DEBUGGER_FLAG_ACTIVE | 0 | 🚫 |
If the release/main pipeline is running and this is set to 1, the DISABLE_SRDEBUGGER compile flag is added.
Prevents SRDebugger from being included in the release build.
SHOULD_BUILD_ADDRESSABLES
Variable Name | Default Value | Required |
---|---|---|
SHOULD_BUILD_ADDRESSABLES | 1 | 🚫 |
If set to 0, the MGBuilder package skips the Addressables build (AddressableAssetSettings.BuildPlayerContent).
Set to 1 to build addressables as normal.