Builder
Building the project as part of a workflow may help to create mind-space and focus on the project itself.
Use Unity - Builder to automatically build Unity projects for different platforms.
Basic setup
Credentials
Make sure you have set up these variables in the activation step:
UNITY_EMAIL(the email address for your Unity account)UNITY_PASSWORD(the password that you use to login to Unity)
NOTE: Issues have been observed when using a UNITY_PASSWORD with special characters. It is
recommended to use a password without any special characters (mixed-case alphanumeric characters
only).
GameCI does not acquire nor store your Unity email or password. They are required for reactivating the license during build and test steps.
Workflow file setup
Create or edit the file called .github/workflows/main.yml and add a job to it.
Personal license
Personal licenses require a one-time manual activation step.
Make sure you acquire and activate your license file, and add it as a secret.
Then, define the build step as follows:
- uses: game-ci/unity-builder@v4
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with:
targetPlatform: WebGL
Professional license
Make sure you have set up these variables in the activation step:
UNITY_EMAIL(should contain the email address for your Unity account)UNITY_PASSWORD(the password that you use to login to Unity)UNITY_SERIAL(the serial provided by Unity)
Then, define the build step as follows:
- uses: game-ci/unity-builder@v4
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
with:
targetPlatform: WebGL
License Server
If you host your own Unity license server internally you can provide its url using
unityLicensingServer. A floating license will be acquired before the build, and returned after.
Example of use:
- uses: game-ci/unity-builder@v4
with:
targetPlatform: WebGL
unityLicensingServer: [url to your license server]
That is all you need to build your project.
By default, the enabled scenes from the project's settings will be built.
Storing the build
To be able to access your built files, they need to be uploaded as artifacts. To do this it is recommended to use Github Actions official upload artifact action after any build action.
By default, Builder outputs it's builds to a folder named build.
Example:
- uses: actions/upload-artifact@v4
with:
name: Build
path: build
Builds can now be downloaded as Artifacts in the Actions tab.
Caching
In order to make builds run faster, you can cache Library files from previous builds. To do so simply add Github Actions official cache action before any unity steps.
Example:
- uses: actions/cache@v3
with:
path: path/to/your/project/Library
key: Library-MyProjectName-TargetPlatform
restore-keys: |
Library-MyProjectName-
Library-
This simple addition could speed up your build by more than 50%.
Configuration options
Below options can be specified under with: for the unity-builder action.
targetPlatform
Platform that the build should target.
Must be one of the allowed values listed in the Unity scripting manual.
required: true
unityVersion
Version of Unity to use for building the project. Use "auto" to get from your ProjectSettings/ProjectVersion.txt
required: false default: auto
customImage
Specific docker image that should be used for building the project.
- uses: game-ci/unity-builder@v4
with:
customImage: 'unityci/editor:2020.1.14f1-base-0'
required: false default: ""
projectPath
Specify the path to your Unity project to be built. The path should be relative to the root of your repository.
required: false default: <your repository root>
buildProfile
Provide a path to a build profile. The path should be relative to the root of your Unity project
(i.e. relative to projectPath). See the
Unity documentation for more information on
using build profiles.
- uses: game-ci/unity-builder@v4
with:
buildProfile: 'Assets/Settings/Build Profiles/WindowsDemo.asset'
required: false default: Unity will do a platform build for the targetPlatform.
buildName
Name of the build. Also the folder in which the build will be stored within buildsPath.
required: false default: <build_target>
buildsPath
Path where the builds should be stored.
In this folder a folder will be created for every targetPlatform.
required: false default: build
buildMethod
Custom command to run your build.
There are two conditions for a custom buildMethod:
- Must reference a valid path to a
staticmethod. - The class must reside in the
Assets/Editordirectory (or in an Editor Assembly).
Example:
- uses: game-ci/unity-builder@v4
with:
buildMethod: EditorNamespace.BuilderClassName.StaticBuildMethod
To get started with a modified version of the default Unity Builder build script, you can copy
BuildScript.cs to your
Assets/Editor/UnityBuilderAction directory and reference it:
- uses: game-ci/unity-builder@v4
with:
buildMethod: UnityBuilderAction.BuildScript.Build
If you need to pass custom parameters to this build method, see customParameters below.
required: false default: Built-in script that will run a build out of the box.
customParameters
Custom parameters to configure the build.
Parameters must start with a hyphen (-) and may be followed by a value (without hyphen).
Parameters without a value will be considered booleans (with a value of true).
- uses: game-ci/unity-builder@v4
with:
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue
There are 2 main use cases:
- To pass your own custom parameters to be used with
buildMethodabove. Note that environment variables set in the workflow file will not be available in the environment running Unity, so use this mechanism instead. - To pass Unity Build Options (for
example,
customParameters: -standaloneBuildSubtarget Serverwill do server builds)
required: false default: ""
versioning
Configure a specific versioning strategy
- uses: game-ci/unity-builder@v4
with:
versioning: Semantic
Find the available strategies below:
Semantic
Versioning out of the box! (recommended)
Compatible with all platforms. Does not modify your repository. Requires zero configuration.
How it works:
Generates a version based on semantic versioning. Follows
<major>.<minor>.<patch>for example0.17.2. The latest tag dictates<major>.<minor>(defaults to 0.0 for no tag). The number of commits (since the last tag, if any) is used for<patch>. To increment major or minor version, manually add a tag to a commit that follows the<major>.<minor>versioning convention. Optionally, the version number can have avprefix, for examplev0.17.
No configuration required.
Tag
Generates the version based on the git tag of the latest commit (HEAD). For example 0.17.2 will
generate the version 0.17.2. A leading "v" is stripped, so a version of v1.3.3 will generate a
version of 1.3.3. (advanced users)
Compatible with all platforms. Does not modify your repository.
Custom
Allows specifying a custom version in the version field. (advanced users)
This strategy is useful when your project or pipeline has some kind of orchestration that determines the versions.
None
No version will be set by Builder. (not recommended)
Not recommended unless you generate a new version in a pre-commit hook. Manually setting versions is error-prone.
androidVersionCode
Configure the android versionCode.
When not specified, the version code is generated from the version using the
major * 1000000 + minor * 1000 + patch scheme;
androidExportType
Set this flag to androidPackage to build an apk, androidAppBundle for an aab, or
androidStudioProject to build an Android Studio Project.
- uses: game-ci/unity-builder@v4
with:
androidExportType: 'androidAppBundle'
androidKeystoreName: user.keystore
androidKeystoreBase64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
androidKeystorePass: ${{ secrets.ANDROID_KEYSTORE_PASS }}
androidKeyaliasName: ${{ secrets.ANDROID_KEYALIAS_NAME }}
androidKeyaliasPass: ${{ secrets.ANDROID_KEYALIAS_PASS }}
You should also set all the Android Keystore options (see below). Refer to this section for keystore setup.
required: false default: androidPackage
androidKeystoreName
Configure the android keystoreName. Must be provided if configuring the below keystore options.
For this to take effect, you must enable Custom Keystore in your
Android Player settings. The
default build script overrides the other keystore settings with these keystore options.
required: false default: ""
androidKeystoreBase64
Configure the base64 contents of the android keystore file. You should get this with
base64 $androidKeystoreName
The contents will be decoded from base64 using
echo $androidKeystoreBase64 | base64 --decode > $projectPath/$androidKeystoreName
It is recommended to use GitHub Secrets.
required: false default: ""
androidKeystorePass
Configure the android keystorePass. Recommended to use GitHub Secrets.
required: false default: ""
androidKeyaliasName
Configure the android keyaliasName. Recommended to use GitHub Secrets.
required: false default: ""
androidKeyaliasPass
Configure the android keyaliasPass. Recommended to use GitHub Secrets.
required: false default: ""
androidTargetSdkVersion
Configure the android target API level. If used, must be one of Unity's AndroidSdkVersions.
required: false default: ""
androidSymbolType
Export android symbols alongside the build. Can be set to none, public, or debugging.
required: false default: "none"
sshAgent
SSH Agent path to forward to the container.
This is useful if your manifest has a dependency on a private GitHub repo.
required: false default: ""
sshPublicKeysDirectoryPath
SSH directory path to mount in the container on ~/.ssh. Must be used with sshAgent.
It is recommended to have only public keys in this directory, and rely on sshAgent to manage
private keys.
This is useful if your manifest has a dependency on multiple private GitHub repos and you need to use multiple SSH deploy keys.
required: false default: ``
gitPrivateToken
Github private token to pull from github.
This is useful if your manifest has a dependency on a private GitHub repo.
required: false default: ""
runAsHostUser
Run the container as the host user based on the file permissions of the cloned project that gets
mounted to the container. This is useful if you are using a self-hosted runner and need the
generated files from the container to be owned by the same user as the runner host. This solves most
permission errors without requiring the runner agent to run as root. This option shouldn't be needed
on Github Hosted runners as the systems are automatically wiped each run so we default to false.
Self-Hosted runners generally will want to set this to true.
required: false default: false
chownFilesTo
User and optionally group (user or user:group or uid:gid) to give ownership of the resulting build artifacts.
required: false default: ""
allowDirtyBuild
Allows the branch of the build to be dirty, and still generate the build.
- uses: game-ci/unity-builder@v4
with:
allowDirtyBuild: true
Note that it is generally bad practice to modify your branch in a CI Pipeline. However there are exceptions where this might be needed. (use with care).
required: false default: false
unityLicensingServer
Sets the url to a unity license server for acquiring floating licenses.
required: false default: ""
cacheUnityInstallationOnMac
Enables caching the Unity Hub and Editor installation for MacOS runners. This can significantly reduce project build times if you have enough available cache on Github Actions.
required: false default: false
unityHubVersionOnMac
Allows pinning the Unity Hub version used on MacOS runners. Should be of format Major.Minor.Patch,
ie 3.4.0. An empty string represents the latest available version on homebrew.
required: false default: ""
dockerWorkspacePath
Allows customizing the build path within the container in case there are hardcoded paths generated during the build. For example building an IOS XCode project on Linux and moving to a new path on MacOS occasionally leads to broken paths requiring this override to ensure the paths match.
Paths should be of format /path/to/build, ie /tmp/build. On Windows, leave off the drive letter
specification. For example C:\build becomes /build. There should be no trailing slash in the
path and the path should be absolute. The path will automatically be created within the container if
it does not exist. It is recommended to use a path that doesn't already exist within the container
to avoid any conflicts.
required: false default: "/github/workspace"
dockerCpuLimit
Number of CPU cores to assign to the Docker container. Defaults to all available cores when no value is specified. Can accept fractional values, e.g., 0.5 for half of a CPU core's execution time.
required: false default: ""
dockerMemoryLimit
Amount of memory to assign to the Docker container. Defaults to 95% of total system memory rounded down to the nearest megabyte on Linux and 80% on Windows. If the platform is unrecognized, it defaults to 75% of total system memory. To manually specify a value, use the format \<number>\<unit>. The units can be:
m: Megabytes (e.g., 512m = 512 megabytes)g: Gigabytes (e.g., 4g = 4 gigabytes)
required: false default: ""
dockerIsolationMode
Isolation mode to use for the Docker container when running on Windows. Can be one of:
processhypervdefault: This mode will choose the recommended mode as described by Microsoft. Server versions will useprocess, while desktop versions will usehyperv.
required: false default: "default"
containerRegistryRepository
Container registry and repository to pull the image from. Only applicable if customImage is not
set.
This allows you to host your own images and still leverage the logic to automatically determine the
editor version and target platform. Accepts images from other registries too, for example
ghcr.io/namespace/imagename for Github Container Registry.
required: false default: "unityci/editor"
containerRegistryImageVersion
Container registry image version. Only applicable if customImage is not set.
This allows you to roll back the image version to a previous version if needed or even use your own version numbering for custom built and maintained registries.
required: false default: "3"
skipActivation
Skips the activation step. This is useful for Mac self-hosted runners as the Unity process runs on the host so activation/deactivation can conflict with parallel running jobs. This should only be used for Mac self-hosted runners that already have a license activated. Running in any other configuration will result in a failed build due to inactive licenses.
required: false default: false
enableGpu
Omits passing the -nographics parameter when launching Unity to perform the build. Useful when
using runners that have GPU available, and your build process utilizes Unity's graphics API during
build. MacOS only.
required: false default: false
Outputs
Below are outputs that can be accessed by using ${{ steps.myBuildStep.outputs.outputName }}, where
myBuildStep is the id of the Builder step, and outputName is the name of the output.
buildVersion
Returns the version that was generated by Builder, following the strategy configured for
versioning.
- uses: game-ci/unity-builder@v4
id: myBuildStep
- run: echo 'Project Version: ${{ steps.myBuildStep.outputs.buildVersion }}'
androidVersionCode
Returns the version code that was generated by Builder for Android builds.
- uses: game-ci/unity-builder@v4
id: myBuildStep
- run: echo 'Android Version Code: ${{ steps.myBuildStep.outputs.androidVersionCode }}'
engineExitCode
Returns the exit code from the build scripts. This code is 0 if the build was successful. If there was an error during activation, the code is from the activation step. If activation is successful, the code is from the project build step.
- uses: game-ci/unity-builder@v4
id: myBuildStep
- run: echo 'Build Step Exit Code: ${{ steps.myBuildStep.outputs.engineExitCode }}'
Private Github repositories
If you use private git repository in your packages/manifest.json, you need to create SSH pub/private
keys for your project and then add to the webfactory/ssh-agent
- Generate public,private key for github's SSH using
this instruction
and make sure that you you use
[email protected]:USER/PROJECT.gitinstead of email for-Cparameter because we're going to use it as deploy key for a single project andwebfactory/ssh-agentgoing to use that for checking which repository this key is related to . - Add the public_key to Deploy settings of your private git repository (https://github.com/USER/PROJECT/settings/keys)
- Add private_key to Github secrets of the project that you want to build (https://github.com/USER/PROJECT/settings/secrets/actions) (key name should be SSH_PRIVATE_KEY )
- Add github to ssh known hosts :
name: 'Add GitHub to the SSH known hosts file'
run: |
mkdir -p -m 0700 /home/runner/.ssh
curl --silent https://api.github.com/meta | jq --raw-output '"github.com "+.ssh_keys[]' >> /home/runner/.ssh/known_hosts
chmod 600 /home/runner/.ssh/known_hosts
- Finally, add
webfactory/ssh-agentto your Github action :
name: Accessing private repos
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: webfactory/ssh-[email protected]
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- uses: game-ci/unity-builder@v4
with:
sshAgent: ${{ env.SSH_AUTH_SOCK }}
Multiple private Github repositories
GitHub Deploy Keys
are SSH keys that can only be set up on repository level. If you have multiple private repositories
in your packages/manifest.json, you will need to add a Github Deploy Key for each one, and use the
webfactory/ssh-agent action to manage them and use the right SSH key for each repository.
In order for webfactory/ssh-agent to work inside the container, you will need to upload the public
keys, SSH config and set the git url mirroring configurations in the container. You can do that by
using sshPublicKeysDirectoryPath and setting the GIT_CONFIG_EXTENSIONS environment variable:
- uses: webfactory/ssh-[email protected]
with:
ssh-private-key: |
${{ secrets.FIRST_KEY }}
${{ secrets.NEXT_KEY }}
${{ secrets.ANOTHER_KEY }}
- name: Prepare SSH config for unity builder
run: |
mkdir $HOME/.ssh_docker
cp $HOME/.ssh/config $HOME/.ssh_docker/
cp $HOME/.ssh/key* $HOME/.ssh_docker/
cp $HOME/.ssh/known_hosts $HOME/.ssh_docker/
sed -i 's/\/home\/runner/\/root/g' $HOME/.ssh_docker/config
sudo chown -R root:root $HOME/.ssh_docker
GIT_CONFIG_EXTENSIONS=$(git config --list | grep '^url\.')
{
echo 'GIT_CONFIG_EXTENSIONS<<EOF'
echo "$GIT_CONFIG_EXTENSIONS"
echo EOF
} >> "$GITHUB_ENV"
shell: bash
- uses: game-ci/unity-builder@v4
with:
sshAgent: ${{ env.SSH_AUTH_SOCK }}
sshPublicKeysDirectoryPath: ${{ env.HOME }}/.ssh_docker
UPM authentication using .upmconfig.toml
Unity requires an .upmconfig.toml to exist in the home directory to authenticate and download
packages from private UPM registries. To create this file, first create the
/home/runner/work/_temp/_github_home directory, and then create the .upmconfig.toml file inside
that directory. This action must be done before running the game-ci/unity-builder@v4 action.
- name: Create .upmconfig.toml UPM authentication file
run: |
mkdir /home/runner/work/_temp/_github_home
cd /home/runner/work/_temp/_github_home
echo "[npmAuth.\"https://upm.example.com\"]" >> .upmconfig.toml
echo "alwaysAuth = true" >> .upmconfig.toml
echo "token = \"${{ secrets.NPM_TOKEN }}\"" >> .upmconfig.toml
Using preprocessor macros
If needed, Unity preprocessor directives can be set by adding a step to the build.
steps:
- name: Apply csc.rsp (pre-processor symbols)
run: echo -define:MY_SYMBOL >> Assets/csc.rsp
To avoid warnings about "unapplied changes", add Assets/csc.rsp in your .gitignore
Once set, you can then reference these preprocessor directives in code:
void Start() {
#if MY_SYMBOL
GetComponent<TMP_Text>().text = "I have a symbol!";
#else
GetComponent<TMP_Text>().text = "I do not have a symbol...";
#endif
}
Complete example
A complete workflow that builds every available platform could look like this:
name: Build project
on: [push, pull_request]
jobs:
buildForAllSupportedPlatforms:
name: Build for ${{ matrix.targetPlatform }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
targetPlatform:
- StandaloneOSX # Build a macOS standalone (Intel 64-bit).
- StandaloneWindows # Build a Windows standalone.
- StandaloneWindows64 # Build a Windows 64-bit standalone.
- StandaloneLinux64 # Build a Linux 64-bit standalone.
- iOS # Build an iOS player.
- Android # Build an Android .apk standalone app.
- WebGL # WebGL.
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
lfs: true
- uses: actions/cache@v3
with:
path: Library
key: Library-${{ matrix.targetPlatform }}
restore-keys: Library-
- if: matrix.targetPlatform == 'Android'
uses: jlumbroso/free-disk-[email protected]
- uses: game-ci/unity-builder@v4
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with:
targetPlatform: ${{ matrix.targetPlatform }}
- uses: actions/upload-artifact@v4
with:
name: Build-${{ matrix.targetPlatform }}
path: build/${{ matrix.targetPlatform }}
Next steps
You can find more workflow examples in Getting Started.