# 3. GameCI and GitHub Actions
Part of [[Build Automation and CI]]. Previous: [[2. Command Line Builds and a Build Script]]. Next: [[4. Unity Build Automation]].
> [!note] Verification status
> `game-ci/unity-builder@v4` and its `buildProfile` input (passed as Unity's `-activeBuildProfile`) come from GameCI's public Builder docs, fetched 2026-11-10. License/activation must follow [GameCI's current activation guide](https://game.ci/docs/github/activation) for your license type — **secret names and personal vs professional/educational flows can change; do not treat the env block below as a substitute for that page.** This workflow has **not** been run on GitHub.
## 3.1 What GameCI is
[GameCI](https://game.ci/) is a set of GitHub Actions (and siblings for other CI systems) that download a Unity Editor, activate a license, run tests, and build players inside a runner. For a class or a small studio already on GitHub ([[Setting Up GitHub for Unity]]), it is the most common self-hosted-pipeline path. You still need a **valid Unity license** the runner is allowed to use. There is no workaround, and the license file or serial belongs in **GitHub encrypted secrets**, never in the repo, never in a screenshot in a lecture slide.
## 3.2 Activation (read the official page)
Before a workflow will build, you activate Unity in CI. GameCI documents this separately because personal licenses, Professional/Enterprise serials, and floating licenses differ.
Do this, in order:
1. Open https://game.ci/docs/github/activation and follow the flow for **the license you actually have**.
2. Store whatever that page tells you to store as GitHub Actions secrets (typically a license file body and/or a serial, plus account credentials when the page requires them).
3. Reference those secrets only as `${{ secrets.NAME }}` in the workflow `env:`.
If activation fails, stop and fix activation. Do not paste a license into a log, a gist, or chat.
## 3.3 A starter workflow
`.github/workflows/unity-ci.yml` — tests first, then a Windows player from a Build Profile. Adjust Unity version, runner, and paths.
```yaml
name: Unity CI
on:
push:
branches: [main]
pull_request:
jobs:
test-editmode:
name: Edit Mode tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
lfs: true
- uses: actions/cache@v4
with:
path: Library
key: library-${{ runner.os }}-${{ hashFiles('Packages/packages-lock.json') }}
restore-keys: library-${{ runner.os }}-
- uses: game-ci/unity-test-runner@v4
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with:
unityVersion: 6000.5.0f1
testMode: editmode
checkName: Edit Mode tests
- uses: actions/upload-artifact@v4
if: always()
with:
name: test-results
path: artifacts
build-windows:
name: Windows player
needs: test-editmode
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
lfs: true
- uses: actions/cache@v4
with:
path: Library
key: library-${{ runner.os }}-${{ hashFiles('Packages/packages-lock.json') }}
restore-keys: library-${{ runner.os }}-
- uses: game-ci/unity-builder@v4
env:
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
with:
unityVersion: 6000.5.0f1
targetPlatform: StandaloneWindows64
buildProfile: Assets/Settings/Build Profiles/WindowsDemo.asset
- uses: actions/upload-artifact@v4
with:
name: windows-player
path: build
```
Read this as a **template**:
- Pin `unityVersion` to the **exact** Editor the project was made with (`6000.5.0f1` here is a placeholder — use the version in your ProjectSettings, do not invent a patch). GameCI has to offer an image for that version; check their version list if a tag is missing.
- `buildProfile` is relative to the project (and to `projectPath` if you set one). If you omit it, GameCI builds with `-buildTarget` only.
- `targetPlatform` is still required by the action even when a profile is present (per the Builder docs' required field). Keep it consistent with the profile's platform.
- A Linux runner can cross-compile many **Mono** standalone players; **IL2CPP** and platform SDKs (Android, iOS, Switch, …) need a runner with the right toolchain. Android / Quest IL2CPP often means a more specialized runner or Unity Build Automation (part 4).
- `lfs: true` matters if you followed [[2. Git LFS]].
- Caching `Library` speeds CI and can also poison a run if a cache is stale in a nasty way. If a job acts possessed, rerun with a fresh cache key.
The `UNITY_*` env names above are the ones GameCI's builder docs commonly show for a **license-file** flow. If you use a serial-based license, the activation page will tell you to pass a serial instead (and *not* to commit it). Prefer the official page over this paragraph.
## 3.4 Tests in CI
`game-ci/unity-test-runner@v4` is the usual pair for [[Unity Test Framework]]. Start with **Edit Mode** on every PR. Add Play Mode when the fixture scene is stable. Quest Player tests probably stay manual or nightly; a hosted runner will not have a headset.
Upload the XML / `artifacts` folder even on failure (`if: always()`).
## 3.5 Secrets hygiene for class
- One GitHub organization, one set of secrets, limited access.
- Students get fork PRs that **cannot** read the org's license secrets unless you design for that (GitHub's pull_request secrets rules). A common teaching setup is: CI runs on the class origin, not on every fork.
- Rotate the password or serial if it ever appears in a log.
## Official docs
- GameCI Builder: https://github.com/game-ci/documentation/blob/main/docs/03-github/04-builder.mdx
- GameCI activation: https://game.ci/docs/github/activation
- Unity command-line builds: https://docs.unity.com/en-us/engine/6000.5/manual/building-and-publishing/build-customize-build-pipeline/build-command-line