Release and CI
On this page
Every game repo from the studio template has two GitHub Actions workflows. hygiene.yml runs on every push with no Unity licence. unity.yml runs tests and builds with GameCI (opens in a new tab), gated on the repository variable UNITY_CI=true.
#The Unity workflow
| Job | Action | What |
|---|---|---|
| Test | game-ci/unity-test-runner (v4.4.0) |
EditMode and PlayMode tests from Assets/Tests/ |
| Build | gully-labs/unity-builder (fork of game-ci/unity-builder v6.0.0) |
one build per target, in a matrix |
Build targets:
| Target | Used for |
|---|---|
StandaloneWindows64 |
desktop players, playtests |
StandaloneLinux64 |
dedicated servers (headless) |
Android |
mobile; signed when the keystore secrets exist |
WebGL |
browser builds |
Each build is uploaded as a workflow artifact. The Library/ folder is cached per target with actions/cache, which cuts repeat build times a lot.
#Pinned actions
Every action is pinned to a full commit SHA, with the version tag in a comment:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: game-ci/unity-test-runner@32e57712352b500e17974b245a6dce9e11a73213 # v4.4.0
- uses: gully-labs/unity-builder@eb1b9fba120c6e62c9fb7a7a81d6c107ce004c45 # v6.0.0Bump an action by pull request with the new SHA and tag. Never switch to a moving tag like @v6.
#Licence
Unity is proprietary: CI builds need a licence. The workflow reads it from secrets only.
| Secret | Licence type |
|---|---|
UNITY_EMAIL, UNITY_PASSWORD |
always |
UNITY_SERIAL |
Unity Pro or Unity Industry (required above the Personal revenue threshold) |
UNITY_LICENSE |
contents of a Personal .ulf, for prototyping |
Heads up
Never commit a .ulf file, a serial or a keystore. The hygiene workflow fails the build if a .ulf or .unitypackage file is committed.
Android signing uses ANDROID_KEYSTORE_BASE64, ANDROID_KEYSTORE_PASS, ANDROID_KEYALIAS_NAME and ANDROID_KEYALIAS_PASS. Without them the Android build is unsigned (fine for CI, not for a store).
#Hygiene workflow
Runs without a licence on every push and fails when:
- the repository is public;
- Force Text serialization or Visible Meta Files has drifted;
- a binary that belongs in LFS was committed as a normal blob;
- a
.unitypackageor.ulffile is in the tree.
#Runners
The hosted ubuntu-latest runner is enough for the empty template. Real games need more disk, memory and time than hosted runners give, so they build on a larger runner or a self-hosted runner on Gully infrastructure. Self-hosted runners follow the same rules as other Gully services: Gully boxes only, no public ports, secrets in the runner environment.
#Never committed
Library/, Temp/, Obj/, Build/, Logs/, UserSettings/ are in Unity's .gitignore and stay out of git. Builds go to artifacts or releases, never into the repo.
#Versioning and releases
- Version the game with tags:
v<major>.<minor>.<patch>. Set the same version in Player Settings before tagging. - Android needs an increasing
bundleVersionCodefor every store upload. - Build release candidates from tags, not from branch heads, so a build can be reproduced.
- Keep the editor version in
ProjectSettings/ProjectVersion.txtunchanged during a release; upgrade it in its own pull request.
#A release checklist
- All tests green on
main. - Asset Store licence audit up to date (
Assets/ThirdParty/README, see Asset Store licence). - Version bumped and tagged.
- Store policy re-checked for any crypto, reward or payment change (see Store policy).
- Server build deployed before clients that need it.
- Release notes written for players.