Continuous Integration for Unity using CircleCI

Raju K
XRPractices
Published in
5 min readAug 11, 2019

Recently I’ve given a task of making CI work for a Unity project on which a distributed team of developers were working on. The code repository was in Github and the CI environment of choice was CircleCI. While I got some help from Gabriel Le Breton’s Github repository with a sample Unity project which had a CI implementation, I had to tailer it for my teams needs and will try to present the concepts in this article in a step by step manner.

There are 6 major parts, to get this work. Before even we start to work on the CI pipeline, we need to prepare our Unity project.

Part 1 — Preparing the Unity Build Pipeline

The key for CI integration is that the build should run without UI and invokable via command line. The unity editor can be launched in a silent mode for operations such as customised build. It is also possible to invoke an editor script in command line via it’s “-batchmode” and “-executeMethod” command line options, provided that the script which is being called has a static method.

So to make a build, we need to write an editor script with a static method. Like the one below

The PerformBuild static function calls the BuildPipeline.BuildPlayer with the parameters needed for the build. This function can be called from unity editor command line using “-executeMethod” option.

Find the complete BuildCommand.cs script in Gabriel Le Breton’s Github repository. This script need to be placed under “/Assets/Editor/” folder.

Part 2 — Obtain Unity License Info XML from the Docker image

Since we are going to use a docker image, this part is to extract the license information needed to activate the Unity instance in the docker container. This entire part need to be done in the local machine. Once we obtain the XML with the information needed to activate unity, this docker image may be removed from the local machine since this is a one time process.

If you don’t have docker in your machine, please go ahead download & install docker desktop.

We need to download a Unity docker container, you can find one here. Please refer to the tags page and select the appropriate docker with correct unity version and build target. In this sample, my unity version was 2018.4.5f1 and build target was Android. So to pull the docker to my local machine, used the following command.

docker pull gableroux/unity3d:2018.4.5f1-android

Now run the downloaded docker image, before running this command remember to set or replace the $UNITY_USERNAME and $UNITY_PASSWORD with appropriate values in the environment

docker run -it --rm -e "UNITY_USERNAME=$UNITY_USERNAME" \-e "UNITY_PASSWORD=$UNITY_PASSWORD" \-e "TEST_PLATFORM=android" \-e "WORKDIR=/root/project" \-v "$(pwd):/root/project" \gableroux/unity3d:2018.4.5f1 \bash

Once you are inside the docker image’s bash shell, execute the following command

xvfb-run --auto-servernum --server-args='-screen 0 640x480x24' \/opt/Unity/Editor/Unity \-logFile \-batchmode \-username "$UNITY_USERNAME" -password "$UNITY_PASSWORD"

Wait until you get an output like

LICENSE SYSTEM [2017723 8:6:38] Posting <?xml version="1.0" encoding="UTF-8"?><root><SystemInfo><IsoCode>en</IsoCode><UserName>[...]

Copy the XML content that appears in the output and save it as “unity3d.alf

Part 3 — Obtain license activation

Open Unity’s manual license activation process from here. Answer the questions and upload the “unity3d.alf” file that we prepared in Part 2 when asked in the page, download the “Unity_v2018.x.ulf”. This is an XML file which will contain the activation information.

Now we need to open this downloaded “Unity_v2018.x.ulf” in a text editor, copy the entire contents and convert that into a Base64 string representation using your preferred tool or using an online service. Once it is converted into Base64 representation, copy the Base64 string representation and save it to a local file namely “Unity.ulf.enc”. This we will use later in CircleCI to set an environment variable

Part 4 — CircleCI Setup

Within CircleCI, under the Project Settings -> Environment Variables, Create 3 Environment variables

BUILD_NAME — Name of the application or build

BUILD_TARGET — target platform (Android/iOS/etc)

UNITY_LICENSE_CONTENT — Set this variable with the Base64 string that we saved in Part 3 under “unity.ulf.enc

Part 5 — Build Scripts

Now it’s time to write few bash scripts, 3 to be specific. All 3 scripts must be kept under the “ci” folder of Unity project. If the “ci” folder doesn’t exist, create one. Note this folder should be directly under the project folder.

  1. unity_license.sh — the purpose of this script is to create the build cache folders and write the UNITY_LICENSE_CONTENT environment variable content to the activation file in the execution environment

2. build.sh — This is a local build script. You can run this script in local docker and confirm there are no build errors because of wrong parameter or option. Remember to set the environment variables before running the script.

3. docker_unity_build.sh — This is the build script meant to be executed in the unity docker within CircleCI. Internally it calls both unity_license.sh and build.sh

Part 6 — CircleCI YML

The circleci build jobs are configured and controlled by the config.yml file available under “.circleci” folder, note that this folder is directly under the project root folder. If you don’t have this folder, create one under the unity project and create an config.yml as shown below.

Remember to update the docker image in line 6 according to your unity version and build. Update the Build name in line 8.

The “executors” section defines the execution environment for the build, so here we mention our docker image.

The “build -> steps” section defines the sequence of operations that are going to be executed for the given build name.

The “Jobs” section defines the build jobs along with any environment variable overrides if any.

The “Workflows -> Jobs” section defines which are all jobs that are going to be executed. If this section is empty, then nothing gets executed even if you have defined “Jobs”. Defining the job doesn’t execute it. They get executed only when you list the defined job under workflows section.

That’s all to it. If you push your next set of changes to your repository, this CircleCI build will kick-in. You can view the results in “Workflows” tab in your CircleCI

--

--

Raju K
XRPractices

Innovator | XR | AR | VR| Robotics Enthusiast | Thoughtworks