| sidebar_position | 2 |
|---|---|
| sidebar_label | Engine Commands |
| slug | /cli/build |
:::info Looking for caching, retry, or hooks?
build, test, and activate are deliberately thin, engine-invocation wrappers, and stay that way
on purpose. Caching, build retry/recovery, middleware/hooks, and wrapping the engine's own process
launch are not core capabilities — they live under
game-ci orchestrate, specifically its
advanced topics.
:::
The CLI resolves engine commands from the project at the current directory, or the project path
passed as the first argument. game-ci build is the most common command, but it is not the only
workflow the CLI can run.
game-ci build [projectPath] [options]
game-ci test [projectPath] [options]The CLI detects the engine before registering the final command options. Built-in detection covers Unity, Godot, and Unreal Engine projects. Plugins can add more engines, test commands, custom commands, and command-specific options.
You can pass options in kebab-case on the command line, for example --target-platform. Config
files use the parsed camelCase option names, for example targetPlatform.
game-ci build ./my-unity-project \
--target-platform StandaloneLinux64 \
--build-name MyGameCommon Unity options:
| Option | Default | Description |
|---|---|---|
--target-platform, -t |
StandaloneLinux64 |
Unity target platform. |
--build-name |
target platform | Output build name. |
--builds-path, -o |
build |
Output folder for builds. |
--build-method, -m |
empty (built-in method selected automatically) | Static build method to run. |
--custom-image |
GameCI Unity editor image | Override the Docker image. |
--custom-parameters |
empty | Extra arguments passed to Unity. |
--docker-workspace-path |
/github/workspace |
Container mount path for the workspace. |
--unity-email, -u |
empty | Unity account email. |
--unity-password, -p |
empty | Unity account password. |
--unity-serial, -s |
empty | Unity Pro or Plus serial. |
--unity-license, -l |
empty | Contents of, or path to, a Unity .ulf file. |
--unity-licensing-server |
empty | Unity floating licensing server. |
--ssh-agent |
empty | SSH agent path to forward into the container. |
--git-private-token |
empty | Token used for private Git dependencies. |
--chown-files-to |
empty | User or user:group for build artifact owner. |
--build-profile |
empty | Path to a Unity 6 Build Profile asset (relative to the project). When set, this determines the build's target instead of --target-platform. |
--manual-exit |
false |
Skip passing -quit to the editor, so it stays open after the build method returns. Your build method must call EditorApplication.Exit(0) itself, otherwise the build hangs until it times out. Use this if your build method needs to enter play mode before exiting. |
--skip-activation |
false |
Skip the license activation and return-license steps entirely. Useful when a license is already active in a long-lived container. |
--run-as-host-user |
false |
Linux only. Run the build as a user matching the host's UID/GID instead of the container's default root user, so build artifacts aren't left root-owned on the host. |
--enable-gpu |
false |
Windows only. Installs a Mesa llvmpipe software graphics driver before the build, for GPU-less compute-shader/graphics testing. |
--git-config-extensions |
empty | Linux only. Newline-separated list of extra git config entries in key=value form (e.g. for LFS/submodule auth setups --git-private-token doesn't cover). |
--skip-native-plugin-check |
false |
Skip the Windows-only-Editor native plugin scan (see below) before a Linux container build. |
On Linux and Windows, Unity builds run through Docker. On macOS, the CLI uses the host Unity
installation path handled by the macOS builder setup. --run-as-host-user and
--git-config-extensions are Linux-only; --enable-gpu is Windows-only.
Before a Unity build that runs inside a Linux Docker container, game-ci build scans the project's
Assets folder for .dll.meta files whose PluginImporter restricts Editor availability to
Windows hosts only. Such a plugin isn't just excluded from the built player the way you'd expect
from a platform-scoped native plugin — it becomes invisible to the Editor entirely on a Linux host,
which can silently break compilation of any code that references it unconditionally.
When the scan finds one or more matches, the build logs a warning naming the affected .dll paths
and continues — it never fails the build on its own. Pass --skip-native-plugin-check to skip the
scan entirely, for example if you've already accounted for the plugin's availability in your project
setup. The scan only runs for Unity builds targeting the Linux container path; it does not run for
the Windows container path or for the macOS host build path, since neither of those hosts the Editor
on Linux.
Use --build-method to execute a static Unity method instead of the default GameCI builder method.
This is useful for validation, packaging, asset processing, test bootstrap code, or project-specific
automation that should still run inside the same GameCI environment.
game-ci build ./my-unity-project \
--target-platform StandaloneLinux64 \
--build-method Company.CI.RunValidation \
--custom-parameters "-suite smoke -publishReport true"The method must be available to the Unity editor as a static C# method. Values passed through
--custom-parameters are appended to the Unity command line for your method to parse.
game-ci build ./my-unity-project \
--target-platform Android \
--android-export-type androidAppBundle \
--android-keystore-base64 "$ANDROID_KEYSTORE_BASE64" \
--android-keystore-password "$ANDROID_KEYSTORE_PASSWORD" \
--android-key-alias release \
--android-key-alias-password "$ANDROID_KEY_PASSWORD"Android options:
| Option | Default | Description |
|---|---|---|
--android-export-type |
androidPackage |
androidPackage, androidAppBundle, or androidStudioProject. |
--android-symbol-type |
none |
none, public, or debugging. |
--android-keystore-name |
empty | Keystore file name. |
--android-keystore-base64 |
empty | Base64 encoded keystore contents. |
--android-keystore-password |
empty | Keystore password. |
--android-key-alias |
empty | Keystore alias. |
--android-key-alias-password |
empty | Keystore alias password. |
--android-target-sdk-version |
empty | Android SDK version number. |
Godot builds use a Docker image by default.
game-ci build ./my-godot-project \
--export-preset "Linux/X11" \
--output-path build/game| Option | Default | Description |
|---|---|---|
--export-preset |
Linux/X11 |
Godot export preset name. |
--output-path |
build/game |
Build output path. |
--custom-image |
barichello/godot-ci:<version> |
Godot-capable Docker image. |
The default image tag uses the detected Godot version when available, otherwise 4.3.
Unlike Unreal Engine (below), the default barichello/godot-ci image is small — an Ubuntu base
plus standard build tooling, with Godot's editor and export templates fetched at build time rather
than baked into a multi-GB image. Standard GitHub-hosted runners (~22GB free space by default)
handle it comfortably; there's no need for a self-hosted runner or a larger-runner tier purely for
image size the way there is for Unreal.
Unreal Engine builds require a Docker image that you are licensed to use.
game-ci build ./MyProject.uproject \
--target-platform Linux \
--build-config Shipping \
--custom-image ghcr.io/epicgames/unreal-engine:dev-slim-5.4| Option | Default | Description |
|---|---|---|
--target-platform |
Linux |
Unreal target platform. |
--build-config |
Shipping |
Unreal build configuration. |
--custom-image |
required | Unreal-capable Docker image. |
The built-in command uses Unreal Automation Tool inside the container. The image must provide
/home/ue4/UnrealEngine/Engine/Build/BatchFiles/RunUAT.sh, or you should wrap your own Unreal
workflow through a custom job or plugin.
Real Unreal Engine images are large, which affects where you can practically run this:
| Image | Size | Notes |
|---|---|---|
ghcr.io/epicgames/unreal-engine:dev-slim-5.4 |
~35 GB | Requires Epic GitHub org access |
| Community UE5 images (e.g. ue5-docker) | ~40 GB (from ~120 GB) | Self-built from your own UE license |
| Full UE dev image | 50-120 GB | Rarely practical for CI |
Standard GitHub-hosted runners have ~22GB free space by default (up to ~52GB with a disk-reclaim step like jlumbroso/free-disk-space) — even the smallest realistic UE image is tight or won't fit there, and pulling a 35GB+ image alone can take 10-20+ minutes. Larger GitHub-hosted runners (a paid tier) scale storage with core count — from ~75GB on the smallest (2-core) size up to ~2TB on the largest (64-core) — so they're a real option for UE images if you don't want to manage a self-hosted runner. For anything beyond a quick smoke test on a standard runner, a self-hosted runner with local image caching (so you're not re-pulling the image every run) is the practical default choice. unrealcontainers is a good resource for building and sizing your own image. See Orchestrator's provider docs for self-hosted and cloud provider options beyond plain GitHub-hosted runners.
game-ci test is part of the CLI command model. The exact behavior and options are provided by the
detected engine plugin, so test support can evolve per engine without changing the CLI core.
game-ci test ./my-projectFor Unity projects, game-ci test runs Unity's own official test runner (part of the
Unity CLI, first released by Unity in April 2026 and
still marked experimental) directly against the installed editor - it is not a reimplementation
and does not replace game-ci/unity-test-runner's custom NUnit harness (package-mode testing,
code coverage, scoped registries).
| Option | Default | Description |
|---|---|---|
--unity-cli-args |
empty | Raw arguments passed straight through to unity test. |
--unity-cli-args is a raw pass-through rather than typed flags, because Unity's own CLI reference
page documents test with a one-line description and no formal parameter table, pointing to
unity test --help on the installed binary as the authoritative source instead. Hardcoding guessed
flag names would mean shipping unverified assumptions. Unity's release notes do name specific flags
added to test over time (--mode, --output, --filter, --editor-version, --editor-path,
--architecture, --allow-install, --timeout as of CLI 0.1.0-beta.7) - treat these as a
starting point, not a guarantee for your installed version; confirm with --help.
game-ci test ./my-unity-project --unity-cli-args "--mode editmode --output results.xml"For engines or projects that need a different test runner, use a plugin-provided test command, a
Unity custom method via game-ci build --build-method, or a remote custom job.
Pass --docker to run the classic Docker/Hub-image-driven batchmode test flow (-runTests)
instead of Unity's own unity test CLI — this is the same flow game-ci/unity-test-runner's action
uses: editmode/playmode/standalone/package-mode testing, coverage, and artifact collection.
game-ci test ./my-unity-project --docker --test-platforms "playmode;editmode"Add --local to run that same batchmode flow directly on this machine instead of inside a
container — for a self-hosted runner with Unity already installed and licensed, no Docker required.
This runs through the same host-mode step-script chain (runsteps → activate → test →
return_license) that Orchestrator's local/local-system provider strategy uses:
game-ci test ./my-unity-project --docker --local --test-platforms "playmode;editmode"--docker --local works natively on both Linux and Windows self-hosted runners. On Windows, Unity's
install location is resolved automatically from Unity Hub's default install directory
(C:\Program Files\Unity\Hub\Editor\<version>), or you can point at a non-default install with the
UNITY_PATH environment variable.
Known limits of --docker --local on Windows: standalone-player tests (--test-platforms standalone)
have no headless/no-display equivalent to Linux's xvfb-run, so they are not supported on a
headless Windows Server runner. Without --local, --docker is currently only supported on Linux
hosts — Windows' container-side scripts only implement the build flow, not the classic test flow.
| Option | Default | Description |
|---|---|---|
--docker |
false |
Run the classic Docker/Hub-image-driven batchmode test flow instead of Unity's own unity test CLI. |
--local |
false |
Only meaningful with --docker. Run the same batchmode test flow directly on this host instead of inside a container. |
Unity builds support version generation.
| Option | Default | Description |
|---|---|---|
--versioning-strategy |
Semantic |
Version strategy. |
--version |
empty | Explicit version when using a custom strategy. |
--android-version-code |
empty | Android-specific version code override. |
--allow-dirty-build |
false |
Allow a build with uncommitted changes. |
Versioning is currently implemented for Unity builds.
Normally the CLI detects the engine from the project files. You can override detection with global options:
| Option | Description |
|---|---|
--engine |
Engine name used for command selection. |
--engine-version |
Engine version used by the command, image tag, or build setup. |
Use this when a repository contains more than one engine project or when detection cannot infer the version.
| Option | Description |
|---|---|
--config |
Read CLI options from a config file. |
--plugin |
Load an external plugin. |
--plugins |
Alias for plugin arrays in config. |
--profile |
Select a named profile from the config file. See Named Profiles below. |
--quiet, -q |
Suppress output. |
--verbose, -v |
Enable verbose logging. |
--veryVerbose, --vv |
Enable very verbose logging. |
--maxVerbose, --vvv |
Enable debug logging. |
.game-ci.yml can define a profiles map alongside its base cliOptions, and --profile <name>
selects one:
cliOptions:
targetPlatform: StandaloneLinux64
buildsPath: build
profiles:
android-release:
targetPlatform: Android
androidExportType: androidAppBundle
buildsPath: build/android
ios-debug:
targetPlatform: iOS
buildsPath: build/iosgame-ci build ./my-project --profile android-releasePrecedence, highest first:
- Explicit CLI flags (e.g.
--target-platformpassed directly on the command line) - The selected profile's options (
profiles.<name>) - The base
cliOptionsblock
The profile's options are merged over the base cliOptions before either is compared against
explicit flags, so a profile only needs to specify what's different from the base — it does not
need to repeat every option.
Requesting a profile name that isn't defined fails loudly, listing the profiles that are available:
Unknown profile "android-relese" passed via --profile. Available profiles in .game-ci.yml: android-release, ios-debug