diff --git a/.github/workflows/cats.yml b/.github/workflows/cats.yml index ef94df5c..4b653d61 100644 --- a/.github/workflows/cats.yml +++ b/.github/workflows/cats.yml @@ -10,6 +10,7 @@ jobs: aCatForCreatingThePullRequest: name: A cat for your effort! runs-on: ubuntu-latest + timeout-minutes: 5 steps: - name: Action Cats uses: ruairidhwm/action-cats@1.0.2 diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 74715cd1..41e0cea5 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -8,6 +8,7 @@ jobs: codeFormatting: name: Code formatting runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@v4 - name: Read Node version from mise.toml @@ -25,7 +26,7 @@ jobs: id: yarn-config run: echo "cacheFolder=$(yarn config get cacheFolder)" >> "$GITHUB_OUTPUT" - name: Restore yarn install cache (node_modules + cacheFolder + install-state) - uses: actions/cache@v4 + uses: actions/cache/restore@v4 with: path: | node_modules @@ -42,10 +43,24 @@ jobs: run: | case "$(yarn --version)" in 1.*) echo 'expected up-to-date yarn version'; exit 1 ;; esac yarn install --immutable + - name: Save yarn install cache (node_modules + cacheFolder + install-state) + if: always() + uses: actions/cache/save@v4 + with: + path: | + node_modules + ${{ steps.yarn-config.outputs.cacheFolder }} + .yarn/install-state.gz + key: + yarn-${{ runner.os }}-node${{ steps.node.outputs.version }}-${{ hashFiles('yarn.lock') + }} + timeout-minutes: 5 + continue-on-error: true - run: yarn format:check codeStyles: name: Code styles runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@v4 - name: Read Node version from mise.toml @@ -63,7 +78,7 @@ jobs: id: yarn-config run: echo "cacheFolder=$(yarn config get cacheFolder)" >> "$GITHUB_OUTPUT" - name: Restore yarn install cache (node_modules + cacheFolder + install-state) - uses: actions/cache@v4 + uses: actions/cache/restore@v4 with: path: | node_modules @@ -80,10 +95,24 @@ jobs: run: | case "$(yarn --version)" in 1.*) echo 'expected up-to-date yarn version'; exit 1 ;; esac yarn install --immutable + - name: Save yarn install cache (node_modules + cacheFolder + install-state) + if: always() + uses: actions/cache/save@v4 + with: + path: | + node_modules + ${{ steps.yarn-config.outputs.cacheFolder }} + .yarn/install-state.gz + key: + yarn-${{ runner.os }}-node${{ steps.node.outputs.version }}-${{ hashFiles('yarn.lock') + }} + timeout-minutes: 5 + continue-on-error: true - run: yarn lint types: name: Types check runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@v4 - name: Read Node version from mise.toml @@ -101,7 +130,7 @@ jobs: id: yarn-config run: echo "cacheFolder=$(yarn config get cacheFolder)" >> "$GITHUB_OUTPUT" - name: Restore yarn install cache (node_modules + cacheFolder + install-state) - uses: actions/cache@v4 + uses: actions/cache/restore@v4 with: path: | node_modules @@ -118,10 +147,24 @@ jobs: run: | case "$(yarn --version)" in 1.*) echo 'expected up-to-date yarn version'; exit 1 ;; esac yarn install --immutable + - name: Save yarn install cache (node_modules + cacheFolder + install-state) + if: always() + uses: actions/cache/save@v4 + with: + path: | + node_modules + ${{ steps.yarn-config.outputs.cacheFolder }} + .yarn/install-state.gz + key: + yarn-${{ runner.os }}-node${{ steps.node.outputs.version }}-${{ hashFiles('yarn.lock') + }} + timeout-minutes: 5 + continue-on-error: true - run: yarn typecheck tests: name: Tests runs-on: ubuntu-latest + timeout-minutes: 15 steps: - uses: actions/checkout@v4 - name: Read Node version from mise.toml @@ -139,7 +182,7 @@ jobs: id: yarn-config run: echo "cacheFolder=$(yarn config get cacheFolder)" >> "$GITHUB_OUTPUT" - name: Restore yarn install cache (node_modules + cacheFolder + install-state) - uses: actions/cache@v4 + uses: actions/cache/restore@v4 with: path: | node_modules @@ -156,6 +199,19 @@ jobs: run: | case "$(yarn --version)" in 1.*) echo 'expected up-to-date yarn version'; exit 1 ;; esac yarn install --immutable + - name: Save yarn install cache (node_modules + cacheFolder + install-state) + if: always() + uses: actions/cache/save@v4 + with: + path: | + node_modules + ${{ steps.yarn-config.outputs.cacheFolder }} + .yarn/install-state.gz + key: + yarn-${{ runner.os }}-node${{ steps.node.outputs.version }}-${{ hashFiles('yarn.lock') + }} + timeout-minutes: 5 + continue-on-error: true - run: yarn test --coverage - run: bash <(curl -s https://codecov.io/bash) env: @@ -163,6 +219,7 @@ jobs: e2e: name: E2E tests runs-on: ubuntu-latest + timeout-minutes: 20 steps: - uses: actions/checkout@v4 - name: Read Node version from mise.toml @@ -180,7 +237,7 @@ jobs: id: yarn-config run: echo "cacheFolder=$(yarn config get cacheFolder)" >> "$GITHUB_OUTPUT" - name: Restore yarn install cache (node_modules + cacheFolder + install-state) - uses: actions/cache@v4 + uses: actions/cache/restore@v4 with: path: | node_modules @@ -197,6 +254,19 @@ jobs: run: | case "$(yarn --version)" in 1.*) echo 'expected up-to-date yarn version'; exit 1 ;; esac yarn install --immutable + - name: Save yarn install cache (node_modules + cacheFolder + install-state) + if: always() + uses: actions/cache/save@v4 + with: + path: | + node_modules + ${{ steps.yarn-config.outputs.cacheFolder }} + .yarn/install-state.gz + key: + yarn-${{ runner.os }}-node${{ steps.node.outputs.version }}-${{ hashFiles('yarn.lock') + }} + timeout-minutes: 5 + continue-on-error: true - run: yarn build - run: npx playwright install --with-deps chromium - run: yarn test:e2e diff --git a/.github/workflows/firebase-hosting-merge.yml b/.github/workflows/firebase-hosting-merge.yml index bb97d812..3e15c753 100644 --- a/.github/workflows/firebase-hosting-merge.yml +++ b/.github/workflows/firebase-hosting-merge.yml @@ -9,6 +9,7 @@ name: Deploy Live jobs: build_and_deploy: runs-on: ubuntu-latest + timeout-minutes: 20 steps: # Checkout - uses: actions/checkout@v4 @@ -29,7 +30,7 @@ jobs: id: yarn-config run: echo "cacheFolder=$(yarn config get cacheFolder)" >> "$GITHUB_OUTPUT" - name: Restore yarn install cache (node_modules + cacheFolder + install-state) - uses: actions/cache@v4 + uses: actions/cache/restore@v4 with: path: | node_modules @@ -46,6 +47,19 @@ jobs: run: | case "$(yarn --version)" in 1.*) echo 'expected up-to-date yarn version'; exit 1 ;; esac yarn install --immutable + - name: Save yarn install cache (node_modules + cacheFolder + install-state) + if: always() + uses: actions/cache/save@v4 + with: + path: | + node_modules + ${{ steps.yarn-config.outputs.cacheFolder }} + .yarn/install-state.gz + key: + yarn-${{ runner.os }}-node${{ steps.node.outputs.version }}-${{ hashFiles('yarn.lock') + }} + timeout-minutes: 5 + continue-on-error: true - name: Build website run: yarn build diff --git a/.github/workflows/firebase-hosting-pull-request.yml b/.github/workflows/firebase-hosting-pull-request.yml index 1e214c85..c67b947a 100644 --- a/.github/workflows/firebase-hosting-pull-request.yml +++ b/.github/workflows/firebase-hosting-pull-request.yml @@ -7,6 +7,7 @@ jobs: build_and_preview: if: '${{ github.event.pull_request.head.repo.full_name == github.repository }}' runs-on: ubuntu-latest + timeout-minutes: 20 steps: # Checkout - uses: actions/checkout@v4 @@ -27,7 +28,7 @@ jobs: id: yarn-config run: echo "cacheFolder=$(yarn config get cacheFolder)" >> "$GITHUB_OUTPUT" - name: Restore yarn install cache (node_modules + cacheFolder + install-state) - uses: actions/cache@v4 + uses: actions/cache/restore@v4 with: path: | node_modules @@ -44,6 +45,19 @@ jobs: run: | case "$(yarn --version)" in 1.*) echo 'expected up-to-date yarn version'; exit 1 ;; esac yarn install --immutable + - name: Save yarn install cache (node_modules + cacheFolder + install-state) + if: always() + uses: actions/cache/save@v4 + with: + path: | + node_modules + ${{ steps.yarn-config.outputs.cacheFolder }} + .yarn/install-state.gz + key: + yarn-${{ runner.os }}-node${{ steps.node.outputs.version }}-${{ hashFiles('yarn.lock') + }} + timeout-minutes: 5 + continue-on-error: true - name: Build website run: yarn build diff --git a/.github/workflows/search-trigger.yml b/.github/workflows/search-trigger.yml index 615cd1ba..abcfd73d 100644 --- a/.github/workflows/search-trigger.yml +++ b/.github/workflows/search-trigger.yml @@ -5,6 +5,7 @@ jobs: updateSearchIndex: name: Update search index runs-on: ubuntu-latest + timeout-minutes: 10 steps: - uses: actions/checkout@v4 - uses: darrenjennings/algolia-docsearch-action@da2ed379c147b356d60dbfec68bdcfacb2791a98 diff --git a/docs/03-github-cli/02-build.mdx b/docs/03-github-cli/02-build.mdx index 2d708744..e8c1974b 100644 --- a/docs/03-github-cli/02-build.mdx +++ b/docs/03-github-cli/02-build.mdx @@ -6,6 +6,14 @@ slug: /cli/build # Engine Commands +:::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`](/docs/cli/remote-builds), specifically its +[advanced topics](/docs/cli/orchestrate-advanced). +::: + 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. @@ -32,34 +40,62 @@ game-ci build ./my-unity-project \ Common 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` | `UnityBuilderAction.Builder.BuildProject` | 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). | +| 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. | +| `--container-os` | `auto` | `auto`, `linux`, or `windows`. Which Docker image tag, workdir, and entrypoint shape to use for local builds - see below. | +| `--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. +### Container OS Detection + +For local builds, the CLI needs to know whether the target container is Linux or Windows-based to +pick the right image tag, workdir path, and entrypoint. By default (`--container-os auto`), it asks +the Docker daemon directly (`docker version --format '{{.Server.Os}}'`) rather than assuming the +container OS matches the host OS. This matters on Windows: Docker Desktop can run either Windows or +Linux containers, and a Windows host running Docker Desktop in **Linux containers** mode still needs +Linux-style image tags and paths - the container runtime is Linux regardless of the host. If the +daemon can't be reached, the CLI falls back to the host OS. Pass `--container-os linux` or +`--container-os windows` to skip detection and force one explicitly. + +### Windows-Only-Editor Native Plugin Warning + +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. + ### Custom Unity Methods Use `--build-method` to execute a static Unity method instead of the default GameCI builder method. @@ -117,6 +153,11 @@ game-ci build ./my-godot-project \ | `--output-path` | `build/game` | Build output path. | | `--custom-image` | `barichello/godot-ci:` | Godot-capable Docker image. | +If the project has no `export_presets.cfg` (no export presets configured — common for projects that +treat it like a machine-specific file, similar to `.env`), `game-ci build` falls back to +`godot --headless --verbose --import` to validate that the project imports cleanly, instead of +failing outright. + The default image tag uses the detected Godot version when available, otherwise `4.3`. ### Choosing an image and a runner @@ -206,6 +247,40 @@ game-ci test ./my-unity-project --unity-cli-args "--mode editmode --output resul 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. +#### Classic Docker Test Flow + +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. + +```bash +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: + +```bash +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\`), 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. | + ## Versioning Unity builds support version generation. @@ -234,17 +309,59 @@ version. ## Global Flags -| Option | Description | -| ----------------------- | ------------------------------------ | -| `--config` | Read CLI options from a config file. | -| `--plugin` | Load an external plugin. | -| `--plugins` | Alias for plugin arrays in config. | -| `--quiet`, `-q` | Suppress output. | -| `--verbose`, `-v` | Enable verbose logging. | -| `--veryVerbose`, `--vv` | Enable very verbose logging. | -| `--maxVerbose`, `--vvv` | Enable debug logging. | +| 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](#named-profiles) below. | +| `--quiet`, `-q` | Suppress output. | +| `--verbose`, `-v` | Enable verbose logging. | +| `--veryVerbose`, `--vv` | Enable very verbose logging. | +| `--maxVerbose`, `--vvv` | Enable debug logging. | + +## Named Profiles + +`.game-ci.yml` can define a `profiles` map alongside its base `cliOptions`, and `--profile ` +selects one: + +```yaml +cliOptions: + targetPlatform: StandaloneLinux64 + buildsPath: build + +profiles: + android-release: + targetPlatform: Android + androidExportType: androidAppBundle + buildsPath: build/android + ios-debug: + targetPlatform: iOS + buildsPath: build/ios +``` + +```bash +game-ci build ./my-project --profile android-release +``` + +Precedence, highest first: + +1. Explicit CLI flags (e.g. `--target-platform` passed directly on the command line) +2. The selected profile's options (`profiles.`) +3. The base `cliOptions` block + +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 +``` ## See Also - [Orchestrated jobs](/docs/cli/remote-builds) +- [Orchestrate: advanced topics](/docs/cli/orchestrate-advanced) - [Configuration and plugins](/docs/cli/configuration-and-plugins) diff --git a/docs/03-github-cli/03-remote-builds.mdx b/docs/03-github-cli/03-remote-builds.mdx index b77d06a5..36a170ee 100644 --- a/docs/03-github-cli/03-remote-builds.mdx +++ b/docs/03-github-cli/03-remote-builds.mdx @@ -6,6 +6,15 @@ slug: /cli/remote-builds # Orchestrated Jobs +:::info Core stays lean — advanced capability lives here +`game-ci build`, `game-ci test`, and `game-ci activate` are deliberately thin, engine-invocation +wrappers and stay that way on purpose. Caching, retry/recovery, extensibility hooks, and wrapping +the engine's own process launch are `orchestrate`'s job, not core's — see +[Orchestrate: advanced topics](./orchestrate-advanced) for [local caching](./orchestrate-advanced/local-caching), +[middleware/hooks](./orchestrate-advanced/middleware), [build retry](./orchestrate-advanced/build-retry), +and the [engine launch wrapper](./orchestrate-advanced/launch-wrapper). +::: + `game-ci orchestrate` schedules a provider-backed engine job. Providers can run standard builds, test workflows, custom engine commands, or a fully custom job definition. @@ -37,42 +46,40 @@ package determines the command surface. | Direct standalone Orchestrator provider execution | `game-ci orchestrate` | | Executable provider protocol integration | `game-ci serve` in the provider tool | -## Orchestrator Plugin +## Built-In Orchestrator -The Orchestrator is the default provider backend for provider-backed execution. Load it as a CLI -plugin: +The Orchestrator is the default provider backend for provider-backed execution, and it ships as a +built-in plugin — `game-ci` registers it automatically, the same way it registers the built-in +Unity, Godot, and Unreal engine plugins. You do not need to pass `--plugin` to use +`game-ci orchestrate` or any `--provider-strategy`: ```bash -game-ci \ - --plugin @game-ci/orchestrator-plugin \ - orchestrate ./my-unity-project \ +game-ci orchestrate ./my-unity-project \ --provider-strategy local-docker \ --target-platform StandaloneLinux64 ``` Available Orchestrator provider types include: -| Provider type | Strategy value | Description | -| ----------------- | ------------------- | ---------------------------------------------- | -| Local Docker | `local-docker` | Run the job in Docker on the current machine. | -| Local System | `local-system` | Run directly on the current machine. | -| AWS | `aws` | Run on AWS ECS/Fargate. | -| Kubernetes | `k8s` | Run as a Kubernetes job. | -| Google Cloud Run | `gcp-cloud-run` | Run on Google Cloud Run. | -| Azure ACI | `azure-aci` | Run on Azure Container Instances. | -| GitHub Actions | `github-actions` | Dispatch to a GitHub Actions workflow. | -| GitLab CI | `gitlab-ci` | Trigger a GitLab CI pipeline. | -| Remote PowerShell | `remote-powershell` | Run on a remote Windows host. | -| Ansible | `ansible` | Run through an Ansible inventory and playbook. | -| CLI protocol | `cli` | Delegate to a custom provider executable. | -| Config-defined | `config:` | Map lifecycle commands from YAML or JSON. | +| Provider type | Strategy value | Description | +| ----------------- | ----------------------- | ---------------------------------------------------------------------------------------- | +| Local Docker | `local-docker` | Run the job in Docker on the current machine. | +| Local System | `local`, `local-system` | Run directly on the current machine, no Docker. See [Local System](#local-system) below. | +| AWS | `aws` | Run on AWS ECS/Fargate. | +| Kubernetes | `k8s` | Run as a Kubernetes job. | +| Google Cloud Run | `gcp-cloud-run` | Run on Google Cloud Run. | +| Azure ACI | `azure-aci` | Run on Azure Container Instances. | +| GitHub Actions | `github-actions` | Dispatch to a GitHub Actions workflow. | +| GitLab CI | `gitlab-ci` | Trigger a GitLab CI pipeline. | +| Remote PowerShell | `remote-powershell` | Run on a remote Windows host. | +| Ansible | `ansible` | Run through an Ansible inventory and playbook. | +| CLI protocol | `cli` | Delegate to a custom provider executable. | +| Config-defined | `config:` | Map lifecycle commands from YAML or JSON. | ## Local Docker ```bash -game-ci \ - --plugin @game-ci/orchestrator-plugin \ - orchestrate ./my-unity-project \ +game-ci orchestrate ./my-unity-project \ --provider-strategy local-docker \ --target-platform StandaloneLinux64 ``` @@ -80,15 +87,52 @@ game-ci \ Use this when you want Orchestrator behavior, such as provider hooks and workspace services, without starting with a cloud account. +## Local System + +`--provider-strategy local` (the alias `local-system` resolves to the same provider) runs the build +directly on this machine — no Docker, no cloud account: + +```bash +game-ci orchestrate ./my-unity-project \ + --provider-strategy local \ + --target-platform StandaloneLinux64 +``` + +This drives the host activate → build/test → return-license step-script chain. It is comparable to +the public CLI's classic `game-ci test --docker --local` host path, but is sourced from +Orchestrator build parameters. The core `game-ci build` command uses Docker on Linux and Windows. +Unlike `local-docker` (and the cloud providers above), the +`local` strategy does not clone the repository or pull Git LFS content for you — it assumes the +project at the invocation directory is already checked out and hydrated. That's the point of the +strategy: it targets a persistent, self-hosted runner where the workspace already exists between +runs, rather than a fresh container or VM. + +Common Local System options: + +| Option | Default | Description | +| ------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `--skip-activation` | `false` | Skip the per-run Unity license activation/return steps. For a self-hosted runner with an already-licensed, long-lived Unity Hub session, rather than one that activates and deactivates on every run. | +| `--engine-launch-wrapper` | empty | Prefix the engine process with a host command such as `flock`. See [Engine launch wrapper](./orchestrate-advanced/launch-wrapper). | + +The `local`/`local-system` strategy is also where the rest of the advanced, self-hosted-runner +surface lives: + +- **[Local caching](./orchestrate-advanced/local-caching)** — persist the Unity `Library` folder and + Git LFS objects across runs (`--local-cache-enabled` and friends). +- **[Middleware and hooks](./orchestrate-advanced/middleware)** — run your own commands/containers + around pipeline phases (`--middleware-pipeline`, `--middleware-files`). +- **[Build retry and recovery](./orchestrate-advanced/build-retry)** — opt-in classify/decide/retry + recovery for known-transient Unity build failures (`--enable-build-retry`). +- **[Engine launch wrapper](./orchestrate-advanced/launch-wrapper)** — wrap the engine's own process + launch, e.g. with a launch-serialization lock (`--engine-launch-wrapper`). + ## AWS ```bash export AWS_PROFILE=my-profile export AWS_DEFAULT_REGION=us-east-1 -game-ci \ - --plugin @game-ci/orchestrator-plugin \ - orchestrate ./my-unity-project \ +game-ci orchestrate ./my-unity-project \ --provider-strategy aws \ --target-platform StandaloneLinux64 \ --container-cpu 2048 \ @@ -114,9 +158,7 @@ profiles, SSO sessions, and runner roles. ## Kubernetes ```bash -game-ci \ - --plugin @game-ci/orchestrator-plugin \ - orchestrate ./my-unity-project \ +game-ci orchestrate ./my-unity-project \ --provider-strategy k8s \ --target-platform StandaloneLinux64 \ --kube-config "$KUBE_CONFIG_BASE64" \ @@ -139,9 +181,7 @@ Use `--custom-job` when the provider should run commands that do not map to a bu or test command. ```bash -game-ci \ - --plugin @game-ci/orchestrator-plugin \ - orchestrate ./my-godot-project \ +game-ci orchestrate ./my-godot-project \ --provider-strategy local-docker \ --custom-job '- name: godot-export image: barichello/godot-ci:4.3 @@ -166,13 +206,11 @@ For protocol details, see ## Config-Defined Providers -When the Orchestrator plugin is loaded, provider strategies can also point at YAML or JSON provider -configuration files: +Because the Orchestrator is a built-in plugin, provider strategies can also point at YAML or JSON +provider configuration files without any extra setup: ```bash -game-ci \ - --plugin @game-ci/orchestrator-plugin \ - orchestrate ./my-project \ +game-ci orchestrate ./my-project \ --provider-strategy config:./.game-ci/providers/local-shell.yml ``` @@ -183,12 +221,10 @@ format, runtime environment variables, and examples. ## Configuration Files -Orchestrated jobs can load the provider plugin and common options from `.game-ci.yml`: +Orchestrated jobs can load common options from `.game-ci.yml`: ```yaml cliOptions: - plugins: - - '@game-ci/orchestrator-plugin' providerStrategy: local-docker targetPlatform: StandaloneLinux64 ``` @@ -199,5 +235,12 @@ Then the command can stay short: game-ci orchestrate ./my-project ``` -Provider-specific options are registered by the loaded plugin, so run with `--help` after loading -the plugin to inspect the exact option set available in your installed version. +Provider-specific options are registered by the built-in Orchestrator plugin, so run with `--help` +to inspect the exact option set available in your installed version. + +## See Also + +- [Orchestrate: advanced topics](./orchestrate-advanced) — caching, middleware/hooks, build retry, + and the engine launch wrapper +- [Engine commands](/docs/cli/build) — core `build`/`test`/`activate` +- [Configuration and plugins](/docs/cli/configuration-and-plugins) diff --git a/docs/03-github-cli/04-configuration-and-plugins.mdx b/docs/03-github-cli/04-configuration-and-plugins.mdx deleted file mode 100644 index 4e3c51fc..00000000 --- a/docs/03-github-cli/04-configuration-and-plugins.mdx +++ /dev/null @@ -1,152 +0,0 @@ ---- -sidebar_position: 4 -slug: /cli/configuration-and-plugins ---- - -# Configuration and Plugins - -The `game-ci` CLI can read options from `.game-ci.yml` and load plugins from command-line flags or -config. Those plugins can add engine detection, build commands, test commands, custom commands, -options, and provider-backed job execution. - -## Config File - -The CLI looks for `.game-ci.yml` in the current working directory. You can also pass an explicit -config path: - -```bash -game-ci --config ./ci/game-ci.yml build ./my-project -``` - -Options live under `cliOptions`. - -```yaml -cliOptions: - plugin: - - '@game-ci/orchestrator-plugin' - verbose: true - targetPlatform: StandaloneLinux64 - buildsPath: dist -``` - -The config file uses the same option names as the CLI's parsed options. In practice, that means -camelCase names such as `targetPlatform`, `providerStrategy`, `customImage`, and `buildsPath`. - -## Plugin Sources - -Plugins can provide engine detectors, build or test command handlers, custom commands, options, and -provider implementations. - -| Source type | Example | -| ---------------- | ---------------------------------------- | -| NPM package | `--plugin @game-ci/orchestrator-plugin` | -| Local file/path | `--plugin ./plugins/my-plugin.ts` | -| Executable | `--plugin executable:./my-provider` | -| GitHub shorthand | `--plugin github:game-ci/example-plugin` | - -Direct GitHub loading is reserved for future plugin loader work. Publish the plugin to npm or use a -local path for now. - -## Orchestrator as a Plugin - -The Orchestrator ships provider implementations for the public CLI. - -```bash -game-ci \ - --plugin @game-ci/orchestrator-plugin \ - orchestrate ./my-project \ - --provider-strategy aws -``` - -You can also add the plugin to `.game-ci.yml`: - -```yaml -cliOptions: - plugins: - - '@game-ci/orchestrator-plugin' - providerStrategy: local-docker - targetPlatform: StandaloneLinux64 -``` - -Then run: - -```bash -game-ci orchestrate ./my-project -``` - -Provider strategies loaded through the Orchestrator plugin can also be config-defined providers: - -```yaml -cliOptions: - plugins: - - '@game-ci/orchestrator-plugin' - providerStrategy: config:./.game-ci/providers/local-shell.yml -``` - -Use this when YAML or JSON can describe how to call your existing provider scripts or automation -APIs. See [Config-defined providers](/docs/github-orchestrator/providers/config-defined-providers). - -## Built-In Engine Support - -The CLI includes built-in plugins for: - -| Engine | Detection signal | Built-in command surface | -| ------ | ------------------------------------ | ------------------------ | -| Unity | `ProjectSettings/ProjectVersion.txt` | Engine command options | -| Godot | Godot project files | Engine command options | -| Unreal | `.uproject` files | Engine command options | - -External plugins can add new engine support or replace command behavior without changing the CLI -core. - -## Plugin API - -A plugin exports a `GameCIPlugin` object. It can provide any combination of engine detectors, build -commands, test commands, option registration, providers, and an `onLoad` hook. - -```ts -export default { - name: 'my-engine', - version: '1.0.0', - engineDetectors: [ - { - name: 'my-engine', - detect(projectPath) { - return { engine: 'my-engine', engineVersion: '1.2.3' }; - }, - }, - ], - commands: [ - { - engine: 'my-engine', - createCommand(command, subCommands) { - if (command === 'build') return new MyBuildCommand(command); - if (command === 'test') return new MyTestCommand(command); - return null; - }, - }, - ], - providers: { - 'my-provider': MyProvider, - }, -}; -``` - -Provider plugins register provider strategy names. `game-ci orchestrate` then creates the provider -selected by `--provider-strategy`. `game-ci remote run` and `game-ci remote build` are kept as -compatibility aliases for older workflows. - -Provider implementations can also live outside the public CLI. The Orchestrator supports -configuration-driven providers, executable providers, and TypeScript/JavaScript provider modules. -Use the public CLI plugin API when you are adding command surface or engine behavior to `game-ci` -itself; use the Orchestrator provider extension points when you are changing where jobs run. - -## Local Config Folder - -Use `config open` to open the local GameCI folder: - -```bash -game-ci config open -``` - -The CLI stores its user-level files under `~/.game-ci`. diff --git a/docs/03-github-cli/04-orchestrate-advanced/00-overview.mdx b/docs/03-github-cli/04-orchestrate-advanced/00-overview.mdx new file mode 100644 index 00000000..fcb83c85 --- /dev/null +++ b/docs/03-github-cli/04-orchestrate-advanced/00-overview.mdx @@ -0,0 +1,28 @@ +--- +sidebar_position: 0 +sidebar_label: Why This Lives Here +slug: /cli/orchestrate-advanced +--- + +# Orchestrate: Advanced Topics + +`game-ci build`, `game-ci test`, and `game-ci activate` stay deliberately thin: resolve the engine, +set up the environment, run it once, activate/return a license when needed. That is a structural +choice, not a temporary gap. It keeps the core command surface small enough to read in one sitting +and safe to depend on. + +Everything on this page and its sub-pages is capability that goes beyond invoking the engine once: + +- **[Local caching](/docs/cli/orchestrate-advanced/local-caching)** — persisting the Unity `Library` + folder and Git LFS objects across runs on a self-hosted runner. +- **[Middleware and hooks](/docs/cli/orchestrate-advanced/middleware)** — trigger-aware commands and + containers wrapped around pipeline phases, for extensibility without forking a provider. +- **[Build retry and recovery](/docs/cli/orchestrate-advanced/build-retry)** — opt-in + classify/decide/retry recovery for known transient Unity build failures. +- **[Engine launch wrapper](/docs/cli/orchestrate-advanced/launch-wrapper)** — wrapping the engine's + own process launch, not the whole build step. + +All of it lives under `game-ci orchestrate` (`--provider-strategy local` / `local-system`, unless +noted otherwise) rather than on `build`/`test`/`activate`, because each one is a real behavior +change or a standing infrastructure concern — the kind of thing you opt into deliberately for a +specific runner or pipeline, not something every `game-ci build` invocation should carry implicitly. diff --git a/docs/03-github-cli/04-orchestrate-advanced/01-local-caching.mdx b/docs/03-github-cli/04-orchestrate-advanced/01-local-caching.mdx new file mode 100644 index 00000000..3f9ae5ad --- /dev/null +++ b/docs/03-github-cli/04-orchestrate-advanced/01-local-caching.mdx @@ -0,0 +1,121 @@ +--- +sidebar_position: 1 +slug: /cli/orchestrate-advanced/local-caching +--- + +# Local Caching + +The `local`/`local-system` provider strategy has its own filesystem-based cache for the Unity +`Library` folder and Git LFS objects. It is separate from the cache path used by the `aws`, `k8s`, +and `local-docker` provider strategies, which have their own pre-existing S3/rclone-backed caching +and ignore all of the options below. + +```bash +game-ci orchestrate ./my-unity-project \ + --provider-strategy local \ + --target-platform StandaloneLinux64 \ + --local-cache-enabled \ + --local-cache-mode move-directory +``` + +| Option | Default | Description | +| ----------------------------- | ------- | ------------------------------------------------------------------------------------------------ | +| `--local-cache-enabled` | `false` | Enable local filesystem Library/LFS caching for the `local`/`local-system` provider. | +| `--local-cache-library` | `true` | Cache the engine `Library` folder locally (requires `--local-cache-enabled`). | +| `--local-cache-lfs` | `false` | Cache `.git/lfs` locally (requires `--local-cache-enabled`). | +| `--local-cache-root` | empty | Root directory for the local cache. Defaults to `RUNNER_TEMP/game-ci-cache` or `.game-ci/cache`. | +| `--local-cache-fallback` | `false` | Allow restoring from a fallback cache key when the exact key misses. | +| `--local-cache-fallback-keys` | empty | Comma-separated explicit fallback cache keys to try, in order. | +| `--local-cache-mode` | `tar` | Local cache save/restore mode: `tar`, `move-directory`, or `copy-directory`. | + +These options only affect the `local`/`local-system` provider strategy. `aws`, `k8s`, and +`local-docker` use a separate, pre-existing Library/LFS caching path and ignore them entirely. + +## Choosing A Cache Mode + +| Mode | Behavior | +| ---------------- | -------------------------------------------------------------------------------------------------------------------------- | +| `tar` | Archive/extract the engine cache folder (`Library` for Unity). Portable, but pays an archive/extract cost every run. | +| `move-directory` | An O(1) same-volume move/rename swap of a per-runner `Library` backup into place. No copy, no compression — just a rename. | +| `copy-directory` | Plain recursive directory copy, no archive step. Simpler than `tar`, still pays a full-copy cost. | + +`move-directory` is a destructive transfer rather than a shared cache copy: restore moves the +cached directory into the workspace, and save moves it back. It requires the cache root and the +project's `Library` folder to be on the same filesystem volume. A cross-volume rename fails; there +is no automatic copy fallback. Use `copy-directory` when the paths may be on different volumes or +when multiple builds need the same cache entry concurrently. + +Git LFS caching is independent of this mode and uses a tar archive when enabled. + +## Fallback Keys + +`--local-cache-fallback` (with optional `--local-cache-fallback-keys`) lets a run restore from a +prior, non-exact cache key when the exact key misses — for example falling back to yesterday's +`Library` cache on the first build of a new branch, rather than starting from an empty `Library` and +paying a full reimport. + +The exact key is `{targetPlatform}-{unityVersion}-{branch}` with unsupported filename characters +replaced by underscores. Explicit fallback keys are tried first in the order supplied. Automatic +candidates then prefer the same platform and Unity version, followed by the same platform, then the +same Unity version. Fallback restores are copied even in `move-directory` mode so the seed remains +available. + +## Cache Root + +`--local-cache-root` defaults to `RUNNER_TEMP/game-ci-cache` when `RUNNER_TEMP` is set (matching +GitHub Actions self-hosted runner conventions), otherwise `.game-ci/cache` relative to the working +directory. Set it explicitly when your self-hosted runner's persistent cache volume lives somewhere +else, or when `move-directory` mode needs to share a volume with the project checkout. + +## Cache Floor On Import Success + +By default, a failed build/test leaves the local cache untouched — only a successful run saves a +new cache. `--local-cache-save-on-failure` opts into a more forgiving policy, based on a pattern +designed to preserve useful import work: if asset import completed before a later, +non-corruption failure (a crash, a license error, or a nonzero exit downstream of import), +the `Library` the import produced is still valuable and gets banked as a cache "floor" — so the +next run starts from a warm `Library` instead of an empty one, even though this run failed. + +```bash +game-ci orchestrate ./my-unity-project \ + --provider-strategy local \ + --target-platform StandaloneLinux64 \ + --local-cache-enabled \ + --local-cache-save-on-failure +``` + +| Option | Default | Description | +| ------------------------------------------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `--local-cache-save-on-failure` | `false` | On a failed build/test, attempt to bank the local cache as a floor if asset import had already completed. Requires `--local-cache-enabled`. | +| `--local-cache-floor-corruption-categories` | `COMPILE,PACKAGE` | Comma-separated failure categories treated as corruption-specific — these always block the floor save, regardless of import completion. Unrecognized entries are ignored (with a warning) and the built-in default is used if the override leaves no recognized categories. | + +This is off by default: banking a cache from a _failed_ run is a real behavior change beyond +simply enabling caching, matching the same caution already applied to +[`--enable-build-retry`](/docs/cli/orchestrate-advanced/build-retry). + +### How The Decision Is Made + +On failure, the same diagnostics the [build retry feature](/docs/cli/orchestrate-advanced/build-retry) +already computes are reused — no separate detector. The run's log is classified into a failure +category (`CRASH`, `LICENSE`, `COMPILE`, `PACKAGE`, `EXIT_NEG1`, `GENERIC`, ...) and checked for +whether asset import completed (a log pattern match, or `Library/ArtifactDB`'s modification time +advancing past a pre-run baseline). The floor is banked only when both hold: + +```text +shouldBankAsFloor = importCompleted && !isCorruptionSpecificCategory(failureCategory) +``` + +- **Generic, process-level failures** (`CRASH`, `LICENSE`, `EXIT_NEG1`, `GENERIC`) — bankable if + import completed. This preserves the imported `Library`, subject to the diagnostic heuristic. +- **Corruption-specific failures** (`COMPILE`, `PACKAGE` by default) — blocked unconditionally, + even if import completed. These indicate the `Library`/`PackageCache` content itself may be + broken, not just that something crashed after a clean import. + +Override which categories count as corruption-specific with +`--local-cache-floor-corruption-categories` if your project's failure signatures differ from the +built-in default — for example, narrowing it to just `COMPILE` if your `PACKAGE` failures are +reliably unrelated to `Library` content, or widening it to also block `CRASH`. An unset or entirely +unrecognized override falls back to the built-in `COMPILE,PACKAGE` default. + +The floor-save attempt never masks the original failure: if it fails or is skipped, the build's +real error is still what gets thrown/reported. diff --git a/docs/03-github-cli/04-orchestrate-advanced/02-middleware.mdx b/docs/03-github-cli/04-orchestrate-advanced/02-middleware.mdx new file mode 100644 index 00000000..2fa16b4a --- /dev/null +++ b/docs/03-github-cli/04-orchestrate-advanced/02-middleware.mdx @@ -0,0 +1,155 @@ +--- +sidebar_position: 2 +slug: /cli/orchestrate-advanced/middleware +--- + +# Middleware And Hooks + +Middleware is the extensibility story for `game-ci orchestrate`: a composable, trigger-aware way to +run your own commands or containers around pipeline phases, without forking a provider or writing a +full plugin. + +Each middleware definition wraps a pipeline phase with `before`/`after` command or container blocks, +and only activates when its trigger conditions match. + +## Loading Middleware + +Two ways to supply middleware definitions, and they combine: + +- **Inline YAML** via `--middleware-pipeline`, a YAML document (single object or array) passed + directly on the command line or through `.game-ci.yml`. +- **Files** via `--middleware-files`, a comma-separated allowlist of base file names (no `.yml`/ + `.yaml` extension) to load from a `game-ci/middleware/` directory relative to the working + directory. Only files whose base name is in the allowlist are loaded — the directory is not + loaded wholesale. + +```bash +game-ci orchestrate ./my-unity-project \ + --provider-strategy local \ + --target-platform StandaloneLinux64 \ + --middleware-files discord-notify,disk-space-guard +``` + +with `game-ci/middleware/discord-notify.yaml` in the project: + +```yaml +name: discord-notify +type: command +priority: 100 +trigger: + phase: [build] + when: env.DISCORD_WEBHOOK_URL +before: + commands: | + curl -s -X POST "$DISCORD_WEBHOOK_URL" -d '{"content":"Build starting..."}' +after: + commands: | + curl -s -X POST "$DISCORD_WEBHOOK_URL" -d '{"content":"Build finished."}' +``` + +## Middleware Schema + +| Field | Type | Default | Description | +| -------------- | ------------------------ | --------- | ----------------------------------------------------------------------------- | +| `name` | string | `unnamed` | Identifies the middleware in logs and generated hook names. | +| `description` | string | — | Free-text, informational only. | +| `type` | `command` \| `container` | `command` | Resolves to a command hook or a container hook. | +| `priority` | number | `100` | Ordering — see [Priority Ordering](#priority-ordering) below. | +| `trigger` | object | — | See [Triggers](#triggers) below. Required. | +| `image` | string | `ubuntu` | Default container image for `type: container`, overridable per-phase. | +| `before` | string or object | — | Commands to run before the phase. Shorthand string, or `{ commands, image }`. | +| `after` | string or object | — | Commands to run after the phase. Same shape as `before`. | +| `allowFailure` | boolean | `false` | Container middleware only. If `true`, a failing container hook is tolerated. | +| `secrets` | array | `[]` | Named secrets to resolve into environment variables for the hook. | +| `outputs` | string[] | — | Reserved for future output capture. | + +At least one of `before`/`after` is expected — a middleware with neither has nothing to resolve to +a hook. + +## Phases + +Middleware can trigger on four pipeline phases: + +| Phase | Hook kind | Wired into | +| ------------ | --------------- | -------------------------------------------------------------------- | +| `setup` | command hooks | Before/after the provider's environment setup step. | +| `build` | command hooks | Before/after the actual engine build/test invocation. | +| `pre-build` | container hooks | Before/after, run as a container step ahead of the build container. | +| `post-build` | container hooks | Before/after, run as a container step following the build container. | + +The supported type/phase pairs are `type: command` with `setup` or `build`, and `type: container` +with `pre-build` or `post-build`. The `type` field is authoritative. A mismatched pair, an unknown +phase, or one definition that mixes command and container phases is rejected with a configuration +error rather than being silently omitted. List multiple compatible phases in `trigger.phase` if the +same middleware should activate at more than one point. + +## Priority Ordering + +Middleware executes in a "wrapping" pattern around the phase it targets: + +- **`before` hooks run in ascending priority order** — lower priority numbers run earlier, so a + `priority: 10` middleware's `before` runs before a `priority: 100` middleware's `before`. +- **`after` hooks run in descending priority order** — the reverse: the `priority: 100` + middleware's `after` runs before the `priority: 10` middleware's `after`. + +The net effect: the _outermost_ middleware (lowest priority number) has its `before` run first and +its `after` run last, exactly like nested wrapping. Middleware definitions across inline YAML and +files are all merged and sorted together by priority before any phase runs. + +## Triggers + +```yaml +trigger: + phase: [build] # required — one or more of setup/build/pre-build/post-build + provider: [local, local-system] # optional — restrict to specific providerStrategy values + platform: [StandaloneLinux64] # optional — restrict to specific --target-platform values + when: "env.ENABLE_NOTIFY == 'true'" # optional — expression condition +``` + +All specified conditions must pass (AND logic) for the middleware to activate for a given phase. +`provider` and `platform` accept a single string or an array. When omitted, a condition is treated +as "matches anything." + +### `when` Expression Syntax + +`when` supports a small, deliberately limited expression grammar evaluated against `process.env` — +not a general expression language: + +| Form | Meaning | +| -------------------- | --------------------------------------------------------------------- | +| `env.VAR == 'value'` | True when the environment variable equals the quoted value. | +| `env.VAR != 'value'` | True when the environment variable does not equal the quoted value. | +| `env.VAR` | Truthy check — true when set, non-empty, and not the literal `false`. | +| `!env.VAR` | Falsy check — true when unset, empty, or the literal `false`. | + +An expression that matches none of these forms logs a warning and defaults to `true`. + +## Command Vs Container Middleware + +- `type: command` middleware runs its `before`/`after` `commands` as shell commands on the host + running the pipeline step. Use it with `setup`/`build` phase hooks. Setting `allowFailure: true` + on command middleware is rejected because command hooks do not support that failure mode. +- `type: container` middleware runs its `before`/`after` `commands` inside a container using + `image` (top-level default, or overridden per-phase). Use it with `pre-build`/`post-build` phase + hooks. Container hooks support `allowFailure` and can use tooling that is absent from the host. + +## Secrets + +```yaml +secrets: + - name: DISCORD_WEBHOOK_URL +``` + +Each entry resolves its value from an explicit `value`, or falls back to `process.env[name]` / +`process.env[UPPER_SNAKE_CASE(name)]`, and is exposed to the hook's commands as an environment +variable. The normalized uppercase name is used for the environment variable when `name` is not +already in environment-variable form. + +The `trigger.when` expression is evaluated against the process environment before these secret +entries are hydrated. An explicit YAML `value` therefore cannot make a `when` condition true unless +the same variable is already present in the process environment. + +Avoid placing secret values directly in middleware YAML: they remain plaintext in the config, and +this middleware path does not automatically register them with GitHub Actions log masking. Prefer +injecting values through your CI secret store or the runner environment, and never echo them from a +hook. diff --git a/docs/03-github-cli/04-orchestrate-advanced/03-build-retry.mdx b/docs/03-github-cli/04-orchestrate-advanced/03-build-retry.mdx new file mode 100644 index 00000000..768e5727 --- /dev/null +++ b/docs/03-github-cli/04-orchestrate-advanced/03-build-retry.mdx @@ -0,0 +1,65 @@ +--- +sidebar_position: 3 +slug: /cli/orchestrate-advanced/build-retry +--- + +# Build Retry And Recovery + +`--enable-build-retry` (default **off**) wraps the `local`/`local-system` provider's engine build in +a classify → decide → retry loop for known-transient Unity failures. + +```bash +game-ci orchestrate ./my-unity-project \ + --provider-strategy local \ + --target-platform StandaloneLinux64 \ + --enable-build-retry +``` + +| Option | Default | Description | +| ---------------------- | ------- | -------------------------------------------------------------------------------------------------- | +| `--enable-build-retry` | `false` | Enable automatic classify/decide/retry recovery for failed Unity builds on `local`/`local-system`. | + +## Why It Is Off By Default + +A single failed attempt behaves exactly as it always has — it throws — unless you opt in. That is +deliberate: the recovery path this feature drives can back up or **delete** the project's `Library` +folder, or clear specific subfolders inside it, as part of recovering from a failure class it +recognizes. Mutating `Library` automatically is a real behavior change from "a build either succeeds +or fails" to "a build may retry itself and modify local state along the way," so existing users have +to turn it on explicitly rather than inherit it from an upgrade. + +## How It Works + +Three cooperating services drive the loop: + +- **`UnityBuildDiagnosticsService`** classifies a failed run's output/exit behavior into a known + failure class (or none). +- **`UnityRecoveryService`** decides the recovery action for that class: whether to retry, whether + to preserve or nuke `Library`, which subfolders (if any) to clear first, and how long to delay + before retrying. +- **`UnityRetryService`** drives the loop itself, executing the decided recovery action and + re-running the build up to that failure class's retry budget. + +### Recognized Failure Classes + +| Failure class | Recovery action | +| -------------------------------------------------- | ------------------------------------------------------ | +| LFS pointer files instead of real DLLs | Hydrate LFS objects, then retry — `Library` untouched. | +| Unity licensing startup race | Wait, then retry — `Library` untouched. | +| `PackageCache` GUID / immutable-asset corruption | Clear `Library/PackageCache`, then retry. | +| Unity API updater ran mid-build | Retry against the already-updated `Library`. | +| Crash before import completed | Retry with an import-only pass, then build. | +| Unity exited `0` without invoking the build method | Clear `Library/SourceAssetDB`, then retry. | +| Crash evidence found after import completed | Back up/nuke the whole `Library` folder, then retry. | + +Each failure class has its own **retry budget** (most allow 1 retry; the licensing race allows 2) +using built-in defaults — there is no CLI surface yet for configuring these budgets per-project. +Once a class's budget is exhausted, the run fails rather than retrying again. + +## When To Use It + +Turn this on for a self-hosted `local`/`local-system` runner that has a track record of the specific +transient failures above — most commonly licensing races on a runner that activates/returns a +license every run, or `PackageCache`/`Library` corruption after an interrupted prior build. It is not +a general-purpose "retry on any failure" switch, and it does not apply to `aws`, `k8s`, +`local-docker`, or core `game-ci build`/`test`. diff --git a/docs/03-github-cli/04-orchestrate-advanced/04-launch-wrapper.mdx b/docs/03-github-cli/04-orchestrate-advanced/04-launch-wrapper.mdx new file mode 100644 index 00000000..3385eb7f --- /dev/null +++ b/docs/03-github-cli/04-orchestrate-advanced/04-launch-wrapper.mdx @@ -0,0 +1,48 @@ +--- +sidebar_position: 4 +slug: /cli/orchestrate-advanced/launch-wrapper +--- + +# Engine Launch Wrapper + +`--engine-launch-wrapper` prefixes the engine's own process invocation with a command of your +choice — for example a self-hosted runner's own launch-serialization lock, so only one Unity Editor +process launches at a time on a machine that runs multiple runners. + +```bash +game-ci orchestrate ./my-unity-project \ + --provider-strategy local \ + --target-platform StandaloneLinux64 \ + --engine-launch-wrapper "flock /tmp/unity-launch.lock --" +``` + +| Option | Default | Description | +| ------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------- | +| `--engine-launch-wrapper` | empty | Command to prefix the engine's process invocation with. Only meaningful for `providerStrategy=local`/`local-system`. | + +## Scope: The Engine Launch, Not The Build Step + +This wraps precisely the point where the engine process itself is spawned — not the surrounding +build step, and not the whole pipeline phase the way a [middleware](./middleware) hook does. That +distinction matters: a middleware `before`/`after` hook runs adjacent to a phase (setup, build, +pre-build, post-build), while the launch wrapper is applied at the single call site that actually +invokes the engine binary, inside the host-mode step scripts. + +Use middleware when you want to run something before/after a whole phase (uploading logs, notifying +a webhook, warming a cache). Use the launch wrapper when you need something wrapped tightly around +the engine process launch itself — a lock, a resource limiter, or a process-level instrumentation +tool that needs to see the engine's own exit code directly. + +## Only Meaningful For `local`/`local-system` + +The launch wrapper only affects the bare-host `local`/`local-system` provider strategy's step +scripts. Providers that run inside containers/cloud infrastructure (`local-docker`, `aws`, `k8s`, +and the rest) don't invoke the engine through this call site the same way, so the option has no +effect there. + +The wrapper is passed to the local host step scripts through `ENGINE_LAUNCH_WRAPPER`. Quote it as a +single CLI value, and test the command on the runner's shell: Linux host steps invoke it through +their shell, while Windows host steps use PowerShell command semantics. + +`--engine-launch-wrapper` is deliberately exposed only by `game-ci orchestrate`. Core +`game-ci build`/`test`/`activate` do not register this option. diff --git a/docs/03-github-cli/04-orchestrate-advanced/_category_.yaml b/docs/03-github-cli/04-orchestrate-advanced/_category_.yaml new file mode 100644 index 00000000..b492a0c6 --- /dev/null +++ b/docs/03-github-cli/04-orchestrate-advanced/_category_.yaml @@ -0,0 +1,4 @@ +position: 4 +label: 'Orchestrate: Advanced Topics' +collapsible: true +collapsed: false diff --git a/docs/03-github-cli/05-configuration-and-plugins.mdx b/docs/03-github-cli/05-configuration-and-plugins.mdx new file mode 100644 index 00000000..eb2609a6 --- /dev/null +++ b/docs/03-github-cli/05-configuration-and-plugins.mdx @@ -0,0 +1,235 @@ +--- +sidebar_position: 5 +slug: /cli/configuration-and-plugins +--- + +# Configuration and Plugins + +The `game-ci` CLI can read options from `.game-ci.yml` and load plugins from command-line flags or +config. Those plugins can add engine detection, build commands, test commands, custom commands, +options, and provider-backed job execution. + +## Config File + +The CLI looks for `.game-ci.yml` in the current working directory. You can also pass an explicit +config path: + +```bash +game-ci --config ./ci/game-ci.yml build ./my-project +``` + +Options live under `cliOptions`. + +```yaml +cliOptions: + verbose: true + targetPlatform: StandaloneLinux64 + buildsPath: dist +``` + +The config file uses the same option names as the CLI's parsed options. In practice, that means +camelCase names such as `targetPlatform`, `providerStrategy`, `customImage`, and `buildsPath`. + +The file can also define a `profiles` map alongside `cliOptions`, selected with `--profile ` +to layer profile-specific overrides on top of the base options. See +[Named Profiles](/docs/cli/build#named-profiles) for the merge order and an example. + +## Plugin Sources + +Plugins can provide engine detectors, build or test command handlers, custom commands, options, and +provider implementations. + +| Source type | Example | +| --------------- | ----------------------------------- | +| NPM package | `--plugin @game-ci/example-plugin` | +| Local file/path | `--plugin ./plugins/my-plugin.ts` | +| Executable | `--plugin executable:./my-provider` | + +## Orchestrator (Built-In) + +The Orchestrator ships provider implementations for the public CLI, and it is registered as a +built-in plugin — the same way the built-in Unity, Godot, and Unreal engine plugins are. You don't +need to load it with `--plugin`; it's already available: + +```bash +game-ci orchestrate ./my-project \ + --provider-strategy aws +``` + +You can set the provider strategy in `.game-ci.yml` too: + +```yaml +cliOptions: + providerStrategy: local-docker + targetPlatform: StandaloneLinux64 +``` + +Then run: + +```bash +game-ci orchestrate ./my-project +``` + +Provider strategies can also be config-defined providers: + +```yaml +cliOptions: + providerStrategy: config:./.game-ci/providers/local-shell.yml +``` + +Use this when YAML or JSON can describe how to call your existing provider scripts or automation +APIs. See [Config-defined providers](/docs/github-orchestrator/providers/config-defined-providers). + +## Built-In Engine Support + +The CLI includes built-in plugins for: + +| Engine | Detection signal | Built-in command surface | +| ------ | ------------------------------------ | ------------------------ | +| Unity | `ProjectSettings/ProjectVersion.txt` | Engine command options | +| Godot | Godot project files | Engine command options | +| Unreal | `.uproject` files | Engine command options | +| Bevy | `bevy` dependency in `Cargo.toml` | Engine command options | + +External plugins can add new engine support or replace command behavior without changing the CLI +core. + +## Plugin API + +A plugin exports a `GameCIPlugin` object. It can provide any combination of engine detectors, build +commands, test commands, option registration, providers, and an `onLoad` hook. + +```ts +export default { + name: 'my-engine', + version: '1.0.0', + engineDetectors: [ + { + name: 'my-engine', + detect(projectPath) { + return { engine: 'my-engine', engineVersion: '1.2.3' }; + }, + }, + ], + commands: [ + { + engine: 'my-engine', + createCommand(command, subCommands) { + if (command === 'build') return new MyBuildCommand(command); + if (command === 'test') return new MyTestCommand(command); + return null; + }, + }, + ], + providers: { + 'my-provider': MyProvider, + }, +}; +``` + +Provider plugins register provider strategy names. `game-ci orchestrate` then creates the provider +selected by `--provider-strategy`. `game-ci remote run` and `game-ci remote build` are kept as +compatibility aliases for older workflows. + +Provider implementations can also live outside the public CLI. The Orchestrator supports +configuration-driven providers, executable providers, and TypeScript/JavaScript provider modules. +Use the public CLI plugin API when you are adding command surface or engine behavior to `game-ci` +itself; use the Orchestrator provider extension points when you are changing where jobs run. + +## Plugin Catalog + +Beyond the built-in Unity/Godot/Unreal/Bevy engine plugins and the built-in Orchestrator, a set of +plugins add engine support and cross-cutting capabilities. + +:::warning Every plugin below is experimental + +None of them are published to npm, and each one warns at runtime when used. Several of them +(`steam-deploy`, `runtime-test-framework`, `itch-deploy`, `steam-workshop`, `github-release-deploy`, +`code-signing`, `pseudo-localization`) are implemented and loaded by default, but their options may +still change without a major version bump. **The rest are structural drafts: the plugin shape is +real, but the domain logic is not written, so any command they claim will throw.** Load a draft only +with an explicit `--plugin @game-ci/` (or a `plugins:` entry in `.game-ci.yml`). + +::: + +| Plugin | Kind | Status | +| ---------------------------------------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| `@game-ci/steam-deploy` | Deploy command | **Implemented**, loaded by default. `game-ci deploy steam ` - VDF generation, local/Docker SteamCMD. | +| `@game-ci/runtime-test-framework` | Command | **Implemented**, loaded by default. `game-ci test-runtime ` - see [below](#runtime-test-framework). | +| `@game-ci/itch-deploy` | Deploy command | **Implemented**, loaded by default. Mirrors `steam-deploy`'s shape. | +| `@game-ci/steam-workshop` | Deploy command | **Implemented**, loaded by default. Mods/maps via `workshop_build_item.vdf`, distinct from a full-game upload. | +| `@game-ci/github-release-deploy` | Deploy command | **Implemented**, loaded by default. Attaches artifacts to a GitHub/GitLab Release. | +| `@game-ci/code-signing` | Command | **Implemented**, loaded by default. | +| `@game-ci/pseudo-localization` | Command | **Implemented**, loaded by default. | +| `@game-ci/gamemaker` | Engine | Draft - registration shape only, build logic not implemented. | +| `@game-ci/rpg-maker` | Engine | Draft. | +| `@game-ci/renpy` | Engine | Draft. | +| `@game-ci/save-data-compat` | Command | Draft - command not yet registered in core either. | +| `@game-ci/screen-capture` | Command, GPU | Draft - visual-regression comparison is real and tested (digest-based, not perceptual), the `capture` command is not. | +| `@game-ci/dedicated-server-provisioning` | Command | Draft - docker-compose/systemd/firewall generation is real and tested, the `provision-server` command is not. | +| `@game-ci/dev-tunnel` | Command | Draft - the exposed-service directory (with public/private disclosure rules) is real and tested, the `tunnel` command is not. | +| `@game-ci/anti-cheat` | Options | Draft - hooks into an existing build rather than adding a command; no vendor SDK integration written yet. | + +Several of the command-based drafts also need a small core change to register their command name +with the CLI at all (the same change `deploy` itself once needed) before they can be invoked, even +once their logic exists. Each plugin's own `README.md` under `plugins//` in the `game-ci/cli` +repo states exactly what is real versus planned. + +Crash-symbol collection is not on this list - unlike the plugins above, it lives in the +Orchestrator itself as a built-in output type, since debug symbols have to be captured at build +time or they're gone for good. See +[Output Collection](/docs/github-orchestrator/advanced-topics/output-collection). + +### Runtime Test Framework + +`game-ci test-runtime ` is a distinct capability from `game-ci test`: it launches the +actual _built player_ your build step produced (not the Editor, and not Unity's own Test Framework +player, which `game-ci test`'s `-runTests` path uses) and reports on whatever tests its in-game +harness ran. + +```bash +game-ci test-runtime ./build/StandaloneLinux64 --timeout 60000 +``` + +`buildPath` can point at the executable or at a directory containing it - the plugin looks for the +single matching candidate (one `.exe` on Windows, one `.app` bundle on macOS, one executable-bit +file on Linux) and errors clearly if it finds none or several, rather than guessing. + +**This plugin never runs test code itself.** Your project's own in-game harness does, against a +small results contract: + +1. The plugin launches the player with `GAME_CI_RUNTIME_TEST_MODE=1` and + `GAME_CI_RUNTIME_TEST_RESULTS_PATH=` set. +2. Your harness checks for `GAME_CI_RUNTIME_TEST_MODE`, runs whatever tests it likes, and writes a + JSON file to `GAME_CI_RUNTIME_TEST_RESULTS_PATH` before exiting: + + ```json + { + "schemaVersion": 1, + "tests": [ + { "name": "player spawns at origin", "passed": true, "durationMs": 12 }, + { + "name": "inventory persists across scene load", + "passed": false, + "message": "expected 3 items, got 2" + } + ] + } + ``` + +3. The plugin reads that file after the process exits (or kills it and fails the run if it does not + exit within `--timeout`) and fails the step on any `passed: false` entry, or if the file was + never written. + +The results file, not the exit code, is authoritative - a player that writes valid results but +happens to exit non-zero for an unrelated reason still has its real results honored. + +## Local Config Folder + +Use `config open` to open the local GameCI folder: + +```bash +game-ci config open +``` + +The CLI stores its user-level files under `~/.game-ci`. diff --git a/docs/03-github-cli/05-github-action.mdx b/docs/03-github-cli/06-github-action.mdx similarity index 64% rename from docs/03-github-cli/05-github-action.mdx rename to docs/03-github-cli/06-github-action.mdx index aa32b662..fce26883 100644 --- a/docs/03-github-cli/05-github-action.mdx +++ b/docs/03-github-cli/06-github-action.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 5 +sidebar_position: 6 slug: /cli/github-action --- @@ -24,7 +24,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: game-ci/cli@v0.1.0 + - uses: game-ci/cli@v0.1.48 with: args: build . --target-platform StandaloneLinux64 ``` @@ -32,7 +32,7 @@ jobs: Run tests the same way: ```yaml -- uses: game-ci/cli@v0.1.0 +- uses: game-ci/cli@v0.1.48 with: args: test . ``` @@ -42,7 +42,7 @@ Run tests the same way: Leave `args` empty when later workflow steps should call `game-ci` directly. ```yaml -- uses: game-ci/cli@v0.1.0 +- uses: game-ci/cli@v0.1.48 - run: game-ci --help ``` @@ -55,37 +55,38 @@ control over quoting than a single `args` input provides. ## Version Selection -When the action ref is a version tag such as `v0.1.0`, the action installs the matching CLI release. +When the action ref is a version tag such as `v0.1.48`, the action installs the matching CLI release. When the action ref is a branch, such as `main`, it installs the latest CLI release unless you pass `version`. ```yaml - uses: game-ci/cli@main with: - version: v0.1.0 + version: v0.1.48 args: --help ``` -Pinning `uses: game-ci/cli@v0.1.0` is preferred for repeatable workflows. +Pinning `uses: game-ci/cli@v0.1.48` is preferred for repeatable workflows. ## Inputs -| Input | Default | Description | -| ------------------- | ------- | ----------------------------------------------------------------------------- | -| `args` | empty | Arguments passed to `game-ci`. Leave empty to install only. | -| `version` | empty | CLI release to install, for example `v0.1.0`. Overrides action-ref detection. | -| `working-directory` | `.` | Directory where the `game-ci` command runs when `args` is set. | +| Input | Default | Description | +| ------------------- | ------- | ------------------------------------------------------------------------------ | +| `args` | empty | Arguments passed to `game-ci`. Leave empty to install only. | +| `version` | empty | CLI release to install, for example `v0.1.48`. Overrides action-ref detection. | +| `working-directory` | `.` | Directory where the `game-ci` command runs when `args` is set. | ## Orchestrated Jobs -The action can run provider-backed jobs by loading provider plugins, the same as the terminal CLI. +The Orchestrator is built into every `game-ci` release, but for provider-backed GitHub Actions jobs +specifically, use the dedicated Orchestrator action - it wires up the provider-specific inputs this +generic `args`-passthrough action doesn't have its own inputs for: ```yaml -- uses: game-ci/cli@v0.1.0 +- uses: game-ci/orchestrator@v1.0.0 with: - args: >- - --plugin @game-ci/orchestrator-plugin orchestrate . --provider-strategy local-docker - --target-platform StandaloneLinux64 + providerStrategy: local-docker + targetPlatform: StandaloneLinux64 ``` For provider-specific setup, see [orchestrated jobs](/docs/cli/remote-builds). @@ -94,11 +95,11 @@ For provider-specific setup, see [orchestrated jobs](/docs/cli/remote-builds). The action installs the released CLI binary for the current runner: -| Runner OS | Installed asset | -| --------- | ----------------------------------------------- | -| Linux | `game-ci-linux-x64` or compatible release asset | -| macOS | `game-ci-macos-x64` or `game-ci-macos-arm64` | -| Windows | `game-ci-windows-x64.exe` | +| Runner OS | Installed asset | +| --------- | --------------------------------------------------------------- | +| Linux | `game-ci-linux-x64` or compatible release asset | +| macOS | `game-ci-macos-x64` or `game-ci-macos-arm64` | +| Windows | `game-ci-windows-x64.zip` (extracted, with its `dist/` sibling) | Linux and macOS runners use the repository installer script. Windows runners download and verify the Windows release asset directly. @@ -108,7 +109,7 @@ Windows release asset directly. Set `working-directory` when the project is not at the repository root: ```yaml -- uses: game-ci/cli@v0.1.0 +- uses: game-ci/cli@v0.1.48 with: working-directory: ./clients/unity args: build . --build-method Company.CI.RunValidation --target-platform StandaloneLinux64 diff --git a/docs/03-github-cli/07-experimental-plugins.mdx b/docs/03-github-cli/07-experimental-plugins.mdx new file mode 100644 index 00000000..daec5d52 --- /dev/null +++ b/docs/03-github-cli/07-experimental-plugins.mdx @@ -0,0 +1,148 @@ +--- +sidebar_position: 7 +slug: /cli/experimental-plugins +--- + +# Experimental Plugins + +These CLI plugins are functional but new. They're registered by default in every `game-ci` binary - +no `--plugin` flag needed, and nothing extra to install - but expect rough edges, and verify against +a test target (a test repo, a test itch.io channel) before pointing any of them at something real. + +## Deploy to a GitHub Release + +`@game-ci/github-release-deploy` attaches a built output to a GitHub Release. + +```bash +GITHUB_TOKEN=... game-ci deploy github-release ./build --repo owner/repo --tag v1.2.3 +``` + +`buildPath` may be a single file (uploaded as-is, optionally renamed via `--assetName`) or a +directory - every top-level file inside it is uploaded as a separate asset, named after itself. +Re-running against the same tag is idempotent: an existing release for that tag is reused, and an +asset that already exists on it is replaced rather than failing. + +| Option | Description | +| ------------------- | --------------------------------------------------------------------- | +| `--repo` | `owner/repo`. Defaults to `$GITHUB_REPOSITORY`. | +| `--tag` | Release tag. Required. | +| `--releaseNotes` | Release body/description. | +| `--draft` | Create the release as a draft. | +| `--prerelease` | Mark the release as a prerelease. | +| `--assetName` | Override the uploaded asset's file name (single-file buildPath only). | +| `--targetCommitish` | Commit/branch to create the tag from, if it doesn't already exist. | + +GitLab Release support isn't included in this first version. + +## Deploy to itch.io + +`@game-ci/itch-deploy` wraps itch.io's official `butler push` CLI. + +```bash +BUTLER_API_KEY=... game-ci deploy itch ./build --user myuser --game mygame --channel windows +``` + +Requires `butler` already installed and on `PATH` (or pass `--butlerPath` explicitly - recommended +for CI determinism). This plugin doesn't install butler for you. + +| Option | Description | +| --------------- | ------------------------------------------------------------- | +| `--user` | itch.io username or organization. Required. | +| `--game` | itch.io game slug. Required. | +| `--channel` | Channel to push to, e.g. `windows`, `linux`, `web`. Required. | +| `--butlerPath` | Explicit path to the butler executable. | +| `--userVersion` | Custom version string shown in itch.io's build history. | +| `--ignore` | Comma-separated glob patterns excluded from the push. | + +## Publish a Steam Workshop item + +`@game-ci/steam-workshop` uploads a Workshop item (a mod, map, or asset pack) via SteamCMD's +`workshop_build_item.vdf` path - distinct from `@game-ci/steam-deploy`'s full-game upload. + +```bash +STEAM_USERNAME=... STEAM_PASSWORD=... game-ci deploy steam-workshop ./my-mod --appId 480 --title "My Mod" +``` + +Omit `--publishedFileId` to publish a new item; pass it to update an existing one. + +## Sign and notarize a build + +`@game-ci/code-signing` signs (and, on macOS, notarizes and staples) a built player. + +```bash +APPLE_ID=... APPLE_TEAM_ID=... APPLE_APP_SPECIFIC_PASSWORD=... game-ci \ + sign ./build/Game.app --platform macos --identity "Developer ID Application: Studio Name (TEAM123)" +``` + +Windows signing goes through `signtool sign` instead, via `--platform windows` and either +`--certificatePath` or `--certificateThumbprint`. + +## Pseudo-localization QA + +`@game-ci/pseudo-localization` injects pseudo-loc strings pre-translation, to catch UI +overflow/truncation and missing-localization bugs before real translation work starts. + +```bash +game-ci pseudo-localize ./Localization +``` + +Reads a flat key→string localization table - `/.json` or `.csv` - and +writes the pseudo-localized result to `/.`. Engine-specific +structured formats (e.g. Unity's binary StringTable assets) aren't supported yet. + +## Bevy engine support + +`@game-ci/bevy` detects a [Bevy](https://bevyengine.org/) project - a real `bevy` dependency in +`Cargo.toml`, not just any Cargo project - and builds/tests it via `cargo build +--release`/`cargo test --release`. Detection is automatic; point `build`/`test` at the project like +you would for any other engine: + +```bash +game-ci build ./my-game +game-ci test ./my-game +``` + +`--target` is only needed for cross-compilation (e.g. `--target x86_64-pc-windows-gnu` to build a +Windows binary from a Linux runner) - omit it to build for the runner's own host toolchain, which is +what most CI jobs want. + +Detection isn't specific to Bevy - `build`/`test` inspect the project directory for every engine +this way. If you need to skip that (e.g. a project whose structure confuses auto-detection, or you +just want to be explicit), pass `--engine=bevy` to force it: + +```bash +game-ci build ./my-game --engine=bevy +``` + +| Option | Applies to | Description | +| -------------- | ----------- | --------------------------------------------------------------- | +| `--target` | build, test | Rust target triple. Builds for the host toolchain when omitted. | +| `--features` | build, test | Comma-separated cargo features to enable. | +| `--locked` | build, test | Fail instead of updating `Cargo.lock`. Default `true`. | +| `--debug` | build | Build in debug mode instead of `--release`. Default `false`. | +| `--outputPath` | build | Directory to copy the built binary into. | + +Cross-compilation via `--target` assumes the target's toolchain and any required linkers are +already installed (e.g. via `rustup target add`, or a `cross`-based custom Docker image) - this +plugin doesn't set that up for you. Doesn't yet resolve workspace-inherited dependencies +(`bevy.workspace = true`) for detection - a known gap for workspace-structured projects. + +## Planned plugins + +These are registered as structural drafts only - the command shape exists, but no domain logic is +written yet, and invoking them throws. Not usable today; listed here so the roadmap is visible. + +| Plugin | What it will do | +| ---------------------------------------- | -------------------------------------------------------------------------------------------------- | +| `@game-ci/anti-cheat` | Wrap a build with an anti-cheat SDK's own packaging step. | +| `@game-ci/dedicated-server-provisioning` | Provision/deploy a built dedicated server to hosting infrastructure. | +| `@game-ci/dev-tunnel` | Expose a local dev build through a public tunnel for remote playtesting. | +| `@game-ci/gamemaker` | Build a GameMaker project via Igor (needs a real licensed GameMaker install to verify against). | +| `@game-ci/renpy` | Build a Ren'Py visual novel project. | +| `@game-ci/rpg-maker` | Build an RPG Maker project (no official CLI exists yet to verify a real invocation shape against). | +| `@game-ci/save-data-compat` | Validate save-data compatibility across engine/build versions. | +| `@game-ci/screen-capture` | Capture gameplay footage/screenshots during an automated test run (GPU-required). | + +Interested in one of these? Open an issue on [game-ci/cli](https://github.com/game-ci/cli/issues) - +the plugin interface is stable, so implementing the domain logic for any of these is a scoped, +self-contained contribution. diff --git a/docs/03-github-cli/index.mdx b/docs/03-github-cli/index.mdx index 84063f7f..d1517f42 100644 --- a/docs/03-github-cli/index.mdx +++ b/docs/03-github-cli/index.mdx @@ -11,8 +11,8 @@ custom engine methods, and provider-backed jobs. Lower-level engine and provider are loaded as plugins. Unity is the primary supported package in GameCI, but the CLI itself is not Unity-only. It ships -with built-in engine detection and engine command implementations for Unity, Godot, and Unreal -Engine. External plugins can add more engines, tests, custom commands, options, and remote +with built-in engine detection and engine command implementations for Unity, Godot, Unreal +Engine, and Bevy. External plugins can add more engines, tests, custom commands, options, and remote providers. Use this CLI when you want a stable command surface such as: @@ -21,7 +21,7 @@ Use this CLI when you want a stable command surface such as: game-ci build ./my-project game-ci test ./my-project game-ci build ./my-unity-project --build-method Company.CI.RunValidation -game-ci --plugin @game-ci/orchestrator-plugin orchestrate ./my-project --provider-strategy local-docker +game-ci orchestrate ./my-project --provider-strategy local-docker game-ci config open ``` @@ -31,15 +31,39 @@ installation, so the command set you see depends on which package installed the ## Install -Standalone binaries do not require Node.js, Bun, or a package manager. +Standalone binaries do not require Node.js, Bun, or a Node package manager such as npm or Yarn. -### Linux / macOS +Homebrew and Scoop are the recommended options for most people. They put `game-ci` on your `PATH` +for you and handle upgrades and uninstall cleanly. The install scripts do not do either of those; +they print `PATH` instructions you have to follow yourself. + +The install scripts remain fully supported. Use them for CI, containers, pinning an exact version +with `GAME_CI_VERSION`, or any environment without Homebrew or Scoop. + +### Homebrew (macOS and Linux) + +```bash +brew install game-ci/tap/game-ci +``` + +Upgrade with `brew upgrade game-ci`. The formula lives in the `game-ci/homebrew-tap` repository. + +### Scoop (Windows) + +```powershell +scoop bucket add game-ci https://github.com/game-ci/scoop-bucket +scoop install game-ci +``` + +Upgrade with `scoop update game-ci`. The manifest lives in the `game-ci/scoop-bucket` repository. + +### Install Script (Linux / macOS) ```bash curl -fsSL https://raw.githubusercontent.com/game-ci/cli/main/install.sh | sh ``` -### Windows PowerShell +### Install Script (Windows PowerShell) ```powershell irm https://raw.githubusercontent.com/game-ci/cli/main/install.ps1 | iex @@ -52,7 +76,10 @@ irm https://raw.githubusercontent.com/game-ci/cli/main/install.ps1 | iex | `GAME_CI_VERSION` | Pin a release, for example `v0.1.0`. | latest | | `GAME_CI_INSTALL` | Install directory for the `game-ci` executable. | `~/.game-ci/bin` | -After installation, make sure `~/.game-ci/bin` is on your `PATH`. +These variables are read by the install scripts only. Homebrew and Scoop do not use them; upgrade +through the package manager instead. + +After running an install script, make sure `~/.game-ci/bin` is on your `PATH`. ## From Source @@ -83,11 +110,12 @@ The built-in plugins provide: | Unity | `ProjectSettings/ProjectVersion.txt` | Engine command options | Supports custom static methods through `--build-method`. | | Godot | Godot project files | Engine command options | Uses `barichello/godot-ci` by default. | | Unreal | `.uproject` files | Engine command options | Requires a licensed Unreal-capable Docker image. | +| Bevy | `bevy` dependency in `Cargo.toml` | Engine command options | A regular Cargo dependency, not a separate build tool. | | Other | Plugin-defined | Plugin-defined commands | Plugins can add build, test, provider, or custom command behavior. | Provider types such as local Docker, local system, AWS, Kubernetes, GitHub Actions dispatch, and -custom provider executables are registered by provider plugins. The Orchestrator plugin is the -intended backend for GameCI provider-backed execution. +custom provider executables are registered by provider plugins. The Orchestrator is the built-in, +default backend for GameCI provider-backed execution — no `--plugin` flag needed. ## First Commands @@ -111,13 +139,13 @@ game-ci build ./my-project --engine unity --engine-version 2022.3.20f1 ## Public CLI vs Orchestrator CLI -| Use case | Recommended entry point | -| ----------------------------------------------- | --------------------------------------------------------- | -| Local or CI engine commands with a friendly API | `game-ci` from `game-ci/cli` | -| Provider-backed jobs | `game-ci orchestrate` with `@game-ci/orchestrator-plugin` | -| Provider protocol development | Standalone `@game-ci/orchestrator` CLI | -| GitHub Actions workflows with the CLI | `game-ci/cli` GitHub Action | -| Unity-specific GitHub Actions workflows | `game-ci/unity-builder` with Orchestrator inputs | +| Use case | Recommended entry point | +| ----------------------------------------------- | ----------------------------------------------------- | +| Local or CI engine commands with a friendly API | `game-ci` from `game-ci/cli` | +| Provider-backed jobs | `game-ci orchestrate` (built-in Orchestrator backend) | +| Provider protocol development | Standalone `@game-ci/orchestrator` CLI | +| GitHub Actions workflows with the CLI | `game-ci/cli` GitHub Action | +| Unity-specific GitHub Actions workflows | `game-ci/orchestrator` for provider-backed builds | ## Command Names At A Glance @@ -138,26 +166,24 @@ surface and plugin behavior. Reach for standalone Orchestrator only when you ins `@game-ci/orchestrator` directly or you are debugging Orchestrator/provider behavior outside the public CLI. -## Command Names At A Glance +## Core Stays Lean, Orchestrate Is Where Advanced Capability Lives -| Command | Where it exists | Use it for | -| ---------------------- | --------------------------- | -------------------------------------------------------------------------- | -| `game-ci build` | Public GameCI CLI | User-facing local or CI engine builds, including custom Unity methods. | -| `game-ci test` | Public GameCI CLI | Engine test workflows when the selected engine plugin provides test logic. | -| `game-ci remote run` | Public GameCI CLI | Provider-backed jobs through Orchestrator or another provider plugin. | -| `game-ci remote build` | Public GameCI CLI | Compatibility alias for `game-ci remote run`. | -| `game-ci orchestrate` | Standalone Orchestrator CLI | Direct low-level Orchestrator provider runs and provider debugging. | -| `game-ci build` | Standalone Orchestrator CLI | A narrow remote Orchestrator build shortcut, not the public CLI build API. | -| `game-ci serve` | Standalone Orchestrator CLI | JSON provider protocol mode for executable provider plugins. | +`game-ci build`, `game-ci test`, and `game-ci activate` are deliberately thin, engine-invocation +wrappers: resolve the engine, set up the environment, run the engine, activate/return a license +when needed. They stay that way on purpose, so the core command surface is easy to read, easy to +audit, and unlikely to change out from under you. -If you are reading public CLI docs, prefer `remote run` for provider-backed work. Reach for -standalone `orchestrate` only when you installed `@game-ci/orchestrator` directly or you are -debugging Orchestrator/provider behavior outside the public CLI. +Anything that goes beyond invoking the engine once — caching, retry/recovery, extensibility hooks, +wrapping the engine's own process launch — belongs to `game-ci orchestrate` instead, not to core. +See [Orchestrate](/docs/cli/remote-builds) and its +[advanced topics](/docs/cli/orchestrate-advanced/local-caching) for that surface. ## Next Steps - [Engine commands](/docs/cli/build) - run builds, tests, and custom engine methods -- [Orchestrated jobs](/docs/cli/remote-builds) - load provider plugins and run provider-backed jobs +- [Orchestrate](/docs/cli/remote-builds) - load provider plugins and run provider-backed jobs +- [Orchestrate: advanced topics](/docs/cli/orchestrate-advanced/local-caching) - caching, + middleware/hooks, build retry, and the engine launch wrapper - [Configuration and plugins](/docs/cli/configuration-and-plugins) - configure `.game-ci.yml` and plugins - [GitHub Action](/docs/cli/github-action) - install and run the CLI in GitHub Actions diff --git a/docs/03-github-orchestrator/01-introduction-to-orchestrator/01-introduction.mdx b/docs/03-github-orchestrator/01-introduction-to-orchestrator/01-introduction.mdx index d8f17ab7..544e720e 100644 --- a/docs/03-github-orchestrator/01-introduction-to-orchestrator/01-introduction.mdx +++ b/docs/03-github-orchestrator/01-introduction-to-orchestrator/01-introduction.mdx @@ -73,12 +73,11 @@ Orchestrator is not meant to replace the simple path. It exists for projects whe import cost, cache reuse, workspace size, runner availability, hardware needs, or infrastructure consistency have become real engineering problems. -:::info Built into Unity Builder +:::info GitHub Action -For GitHub Actions, Orchestrator is available through -[`game-ci/unity-builder`](https://github.com/game-ci/unity-builder). It activates when you choose a -non-local `providerStrategy` or enable Orchestrator-backed services. You do not need a separate -standalone install for normal GitHub Actions usage. +For GitHub Actions, use [`game-ci/orchestrator`](https://github.com/game-ci/orchestrator). The action +wraps Unity Builder and exposes the provider and service inputs documented in this section. The +standalone CLI is not required for normal GitHub Actions usage. The standalone [`@game-ci/orchestrator`](https://github.com/game-ci/orchestrator) CLI remains useful for provider development, debugging, and direct backend usage. diff --git a/docs/03-github-orchestrator/01-introduction-to-orchestrator/02-game-ci-vs-orchestrator.mdx b/docs/03-github-orchestrator/01-introduction-to-orchestrator/02-game-ci-vs-orchestrator.mdx index 52e453ad..4e32c340 100644 --- a/docs/03-github-orchestrator/01-introduction-to-orchestrator/02-game-ci-vs-orchestrator.mdx +++ b/docs/03-github-orchestrator/01-introduction-to-orchestrator/02-game-ci-vs-orchestrator.mdx @@ -18,6 +18,13 @@ execution, and game-specific performance features. Those benefits are not limite setups; a single host with multiple runners, multiple projects, or expensive cache reuse can also benefit. +This split is a deliberate architectural line, not just a feature gap: core `game-ci` +`build`/`test`/`activate` stay thin, engine-invocation wrappers on purpose, so they stay easy to +read and safe to depend on. Anything beyond invoking the engine once — caching, retry/recovery, +extensibility hooks, wrapping the engine's own process launch — is Orchestrator's job. See the +CLI's own [Orchestrate: advanced topics](/docs/cli/orchestrate-advanced) for the `game-ci +orchestrate`-side surface (local caching, middleware, build retry, the engine launch wrapper). + ```mermaid flowchart LR subgraph foundation["Standard GameCI foundation"] diff --git a/docs/03-github-orchestrator/01-introduction-to-orchestrator/03-getting-started.mdx b/docs/03-github-orchestrator/01-introduction-to-orchestrator/03-getting-started.mdx index c8749610..fae98885 100644 --- a/docs/03-github-orchestrator/01-introduction-to-orchestrator/03-getting-started.mdx +++ b/docs/03-github-orchestrator/01-introduction-to-orchestrator/03-getting-started.mdx @@ -4,11 +4,9 @@ import TabItem from '@theme/TabItem'; # Getting Started Orchestrator lets you run engine builds through the provider target that fits your workflow: cloud, -on-premise, local Docker, or your local machine. Unity is the primary built-in package in -`game-ci/unity-builder`; the public `game-ci` CLI also includes built-in configurations for other -engines, and the plugin API can add new engine providers. Orchestrator works as an optional plugin -for `game-ci/unity-builder` in GitHub Actions and as a provider implementation for the public -`game-ci` CLI. +on-premise, local Docker, or your local machine. In GitHub Actions, `game-ci/orchestrator` wraps +Unity Builder and adds provider-backed execution. The public `game-ci` CLI includes built-in engine +and Orchestrator plugins, and its plugin API can add more. ## Prerequisites @@ -35,7 +33,7 @@ Set up your AWS credentials as GitHub secrets (`AWS_ROLE_ARN` for OIDC or `AWS_A role-to-assume: ${{ secrets.AWS_ROLE_ARN }} aws-region: eu-west-2 -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws targetPlatform: StandaloneLinux64 @@ -52,7 +50,7 @@ See the [AWS provider page](../providers/aws) for full setup. Base64-encode your kubeconfig and store it as a GitHub secret (`KUBE_CONFIG`): ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: k8s targetPlatform: StandaloneLinux64 @@ -72,7 +70,7 @@ Requires Docker and a self-hosted GitHub Actions runner: ```yaml # runs-on: self-hosted -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: local-docker targetPlatform: StandaloneLinux64 @@ -88,13 +86,14 @@ See [Provider Types](../providers/overview) for the full list and detailed setup ## GitHub Actions -When you set `providerStrategy` in `game-ci/unity-builder`, the orchestrator activates -automatically. No separate install step is needed. +Use the `game-ci/orchestrator` action when a workflow needs `providerStrategy` or another +Orchestrator input. It invokes Unity Builder as part of the action; no separate builder step is +needed. ### Basic example ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws targetPlatform: StandaloneLinux64 @@ -113,7 +112,7 @@ jobs: steps: - uses: actions/checkout@v4 - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 env: UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} @@ -126,8 +125,7 @@ jobs: ## Command Line -For command-line builds, use the public [GameCI CLI](/docs/cli) and load Orchestrator as a provider -plugin. +For command-line builds, use the public [GameCI CLI](/docs/cli). Orchestrator is built in. ### 1. Install GameCI CLI @@ -151,7 +149,6 @@ export AWS_SECRET_ACCESS_KEY="..." ```bash game-ci \ - --plugin @game-ci/orchestrator-plugin \ orchestrate ./my-project \ --provider-strategy aws \ --target-platform StandaloneLinux64 diff --git a/docs/03-github-orchestrator/01-introduction-to-orchestrator/04-examples/01-command-line.mdx b/docs/03-github-orchestrator/01-introduction-to-orchestrator/04-examples/01-command-line.mdx index 30f335a1..a2137c7e 100644 --- a/docs/03-github-orchestrator/01-introduction-to-orchestrator/04-examples/01-command-line.mdx +++ b/docs/03-github-orchestrator/01-introduction-to-orchestrator/04-examples/01-command-line.mdx @@ -1,8 +1,8 @@ # Command Line -Use the public `game-ci` CLI for command-line builds and other engine automation. Orchestrator is -loaded as a provider plugin, so the user-facing command is `game-ci orchestrate` while -provider-specific behavior stays in the Orchestrator package. +Use the public `game-ci` CLI for command-line builds and other engine automation. Its built-in +Orchestrator plugin provides `game-ci orchestrate`, while provider-specific behavior stays in the +Orchestrator package. ## Install GameCI CLI @@ -27,7 +27,6 @@ The standalone Orchestrator CLI can also be installed directly for provider deve ```bash game-ci \ - --plugin @game-ci/orchestrator-plugin \ orchestrate ./my-project \ --provider-strategy local-docker \ --target-platform StandaloneLinux64 @@ -37,7 +36,6 @@ game-ci \ ```bash game-ci \ - --plugin @game-ci/orchestrator-plugin \ orchestrate ./my-project \ --provider-strategy aws \ --target-platform StandaloneLinux64 \ @@ -48,7 +46,6 @@ game-ci \ ```bash game-ci \ - --plugin @game-ci/orchestrator-plugin \ orchestrate ./my-project \ --provider-strategy k8s \ --target-platform StandaloneLinux64 \ @@ -86,7 +83,6 @@ Avoid long CLI flags for credentials by using environment variables or the ```bash game-ci \ - --plugin @game-ci/orchestrator-plugin \ orchestrate ./my-project \ --provider-strategy aws \ --target-platform StandaloneLinux64 \ @@ -97,5 +93,5 @@ game-ci \ ## Further Reading - [GameCI CLI](/docs/cli) - public command-line interface -- [Orchestrated jobs](/docs/cli/remote-builds) - provider plugin usage +- [Orchestrated jobs](/docs/cli/remote-builds) - provider-backed execution - [Standalone Orchestrator CLI](../../cli/getting-started) - lower-level Orchestrator entry point diff --git a/docs/03-github-orchestrator/01-introduction-to-orchestrator/04-examples/02-github-actions.mdx b/docs/03-github-orchestrator/01-introduction-to-orchestrator/04-examples/02-github-actions.mdx index 9ba561bd..c7b6f6f2 100644 --- a/docs/03-github-orchestrator/01-introduction-to-orchestrator/04-examples/02-github-actions.mdx +++ b/docs/03-github-orchestrator/01-introduction-to-orchestrator/04-examples/02-github-actions.mdx @@ -40,7 +40,7 @@ jobs: # For static keys fallback, use aws-access-key-id/aws-secret-access-key instead aws-region: eu-west-2 - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws targetPlatform: StandaloneLinux64 @@ -86,7 +86,7 @@ jobs: # For static keys fallback, use aws-access-key-id/aws-secret-access-key instead aws-region: eu-west-2 - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 id: build with: providerStrategy: aws @@ -137,7 +137,7 @@ jobs: with: lfs: true - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 id: build with: providerStrategy: k8s @@ -184,7 +184,7 @@ jobs: with: lfs: true - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: local-docker targetPlatform: StandaloneLinux64 @@ -199,7 +199,7 @@ For long builds, use async mode so the GitHub Action returns immediately. Monito GitHub Checks. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws targetPlatform: StandaloneLinux64 @@ -241,7 +241,7 @@ jobs: # For static keys fallback, use aws-access-key-id/aws-secret-access-key instead aws-region: eu-west-2 - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws mode: garbage-collect @@ -291,7 +291,7 @@ jobs: # For static keys fallback, use aws-access-key-id/aws-secret-access-key instead aws-region: eu-west-2 - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws targetPlatform: ${{ matrix.targetPlatform }} @@ -308,7 +308,7 @@ For large projects, keep the entire project folder cached between builds. Dramat rebuilds at the cost of more storage. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws targetPlatform: StandaloneLinux64 @@ -326,7 +326,7 @@ See [Retained Workspaces](../../advanced-topics/caching#retained-workspaces) and Chain multiple container hooks to export builds to S3 and deploy to Steam in a single workflow. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws targetPlatform: StandaloneLinux64 diff --git a/docs/03-github-orchestrator/01-introduction-to-orchestrator/04-examples/03-aws.mdx b/docs/03-github-orchestrator/01-introduction-to-orchestrator/04-examples/03-aws.mdx index e424357d..2bc6622d 100644 --- a/docs/03-github-orchestrator/01-introduction-to-orchestrator/04-examples/03-aws.mdx +++ b/docs/03-github-orchestrator/01-introduction-to-orchestrator/04-examples/03-aws.mdx @@ -28,7 +28,7 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: eu-west-2 - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws targetPlatform: StandaloneLinux64 @@ -74,7 +74,7 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: eu-west-2 - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 id: build with: providerStrategy: aws @@ -92,7 +92,7 @@ jobs: Specify CPU/memory and export build artifacts to S3. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 id: aws-build with: providerStrategy: aws @@ -124,7 +124,7 @@ AWS Fargate only accepts specific combinations (`1024 = 1 vCPU`, memory in MB): For long builds, use async mode so the GitHub Action returns immediately. Monitor via GitHub Checks. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws targetPlatform: StandaloneLinux64 @@ -138,7 +138,7 @@ For long builds, use async mode so the GitHub Action returns immediately. Monito Keep the entire project cached between builds for dramatically faster rebuilds. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws targetPlatform: StandaloneLinux64 @@ -153,7 +153,7 @@ Keep the entire project cached between builds for dramatically faster rebuilds. Chain container hooks to export to S3 and deploy to Steam in one step. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws targetPlatform: StandaloneLinux64 @@ -189,7 +189,7 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws-region: eu-west-2 - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws mode: garbage-collect diff --git a/docs/03-github-orchestrator/01-introduction-to-orchestrator/04-examples/04-kubernetes.mdx b/docs/03-github-orchestrator/01-introduction-to-orchestrator/04-examples/04-kubernetes.mdx index b3d15b18..e12656b1 100644 --- a/docs/03-github-orchestrator/01-introduction-to-orchestrator/04-examples/04-kubernetes.mdx +++ b/docs/03-github-orchestrator/01-introduction-to-orchestrator/04-examples/04-kubernetes.mdx @@ -23,7 +23,7 @@ jobs: with: lfs: true - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: k8s targetPlatform: StandaloneLinux64 @@ -61,7 +61,7 @@ jobs: with: lfs: true - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 id: build with: providerStrategy: k8s @@ -80,7 +80,7 @@ jobs: Specify CPU/memory and persistent volume size for the build workspace. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 id: k8s-build with: providerStrategy: k8s @@ -113,7 +113,7 @@ Kubernetes uses the same unit format as AWS (`1024 = 1 vCPU`, memory in MB): Use S3-backed caching for the Library folder to speed up rebuilds. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: k8s targetPlatform: StandaloneLinux64 @@ -133,7 +133,7 @@ Hook execution order matters - `aws-s3-pull-cache` restores the cache before the Keep the build workspace persistent between builds for large projects. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: k8s targetPlatform: StandaloneLinux64 diff --git a/docs/03-github-orchestrator/04-jobs.mdx b/docs/03-github-orchestrator/04-jobs.mdx index 7754f2e6..08cc1259 100644 --- a/docs/03-github-orchestrator/04-jobs.mdx +++ b/docs/03-github-orchestrator/04-jobs.mdx @@ -28,7 +28,7 @@ The standard job - runs the Unity Editor to produce a build artifact. This is wh about. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: targetPlatform: StandaloneLinux64 providerStrategy: aws @@ -48,7 +48,7 @@ The build job: Run Unity tests without producing a build. Use a custom `buildMethod` that runs tests and exits: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: targetPlatform: StandaloneLinux64 buildMethod: MyNamespace.TestRunner.RunEditModeTests @@ -71,7 +71,7 @@ Run any static C# method in the Unity Editor. Useful for: - Code generation ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: targetPlatform: StandaloneLinux64 buildMethod: MyNamespace.Pipeline.ProcessAssets @@ -88,7 +88,7 @@ Replace the entire build workflow with your own container steps. Useful for non- fully custom pipelines that still benefit from Orchestrator's cloud infrastructure. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws customJob: | @@ -108,7 +108,7 @@ For long-running builds, Orchestrator can dispatch the job and return immediatel continues in the cloud. Progress is reported via GitHub Checks. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: targetPlatform: StandaloneLinux64 providerStrategy: aws @@ -137,7 +137,7 @@ The `remote-cli-pre-build` phase handles: You can inject additional pre-build steps: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: preBuildSteps: | - name: install-dependencies @@ -156,7 +156,7 @@ The `remote-cli-post-build` phase handles: You can inject additional post-build steps: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: postBuildSteps: | - name: upload-to-steam diff --git a/docs/03-github-orchestrator/05-providers/02-aws.mdx b/docs/03-github-orchestrator/05-providers/02-aws.mdx index 83101830..6c260851 100644 --- a/docs/03-github-orchestrator/05-providers/02-aws.mdx +++ b/docs/03-github-orchestrator/05-providers/02-aws.mdx @@ -62,7 +62,7 @@ Common combinations: ## Example Workflow ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 id: aws-fargate-unity-build with: providerStrategy: aws diff --git a/docs/03-github-orchestrator/05-providers/03-kubernetes.mdx b/docs/03-github-orchestrator/05-providers/03-kubernetes.mdx index de657cfb..a6d61365 100644 --- a/docs/03-github-orchestrator/05-providers/03-kubernetes.mdx +++ b/docs/03-github-orchestrator/05-providers/03-kubernetes.mdx @@ -30,7 +30,7 @@ vCPU or GB suffix. ## Example Workflow ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 id: k8s-unity-build with: providerStrategy: k8s @@ -64,7 +64,7 @@ Inject arbitrary config files into K8s build containers using `configFiles`. Fil read-only at `/game-ci/config/` via a Kubernetes ConfigMap: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: k8s configFiles: | diff --git a/docs/03-github-orchestrator/05-providers/04-github-actions-dispatch.mdx b/docs/03-github-orchestrator/05-providers/04-github-actions-dispatch.mdx index 1751b4a4..fcd6c493 100644 --- a/docs/03-github-orchestrator/05-providers/04-github-actions-dispatch.mdx +++ b/docs/03-github-orchestrator/05-providers/04-github-actions-dispatch.mdx @@ -71,7 +71,7 @@ jobs: Set `providerStrategy: github-actions` and supply the required inputs: ```yaml -- uses: game-ci/unity-builder@main +- uses: game-ci/orchestrator@main with: providerStrategy: github-actions targetPlatform: StandaloneLinux64 @@ -132,7 +132,7 @@ jobs: with: lfs: true - - uses: game-ci/unity-builder@main + - uses: game-ci/orchestrator@main with: providerStrategy: github-actions targetPlatform: ${{ matrix.targetPlatform }} diff --git a/docs/03-github-orchestrator/05-providers/04-local-docker.mdx b/docs/03-github-orchestrator/05-providers/04-local-docker.mdx index 33938109..c315f7db 100644 --- a/docs/03-github-orchestrator/05-providers/04-local-docker.mdx +++ b/docs/03-github-orchestrator/05-providers/04-local-docker.mdx @@ -11,7 +11,7 @@ Runs the build workflow inside a Docker container on the local machine. No cloud ### GitHub Actions (self-hosted runner) ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: local-docker targetPlatform: StandaloneLinux64 @@ -33,7 +33,7 @@ Inject config files into the Docker container workspace using `configFiles`. Fil `game-ci-config/` in the workspace before the container starts: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: local-docker configFiles: | diff --git a/docs/03-github-orchestrator/05-providers/05-gitlab-ci-dispatch.mdx b/docs/03-github-orchestrator/05-providers/05-gitlab-ci-dispatch.mdx index 3183599d..0491427a 100644 --- a/docs/03-github-orchestrator/05-providers/05-gitlab-ci-dispatch.mdx +++ b/docs/03-github-orchestrator/05-providers/05-gitlab-ci-dispatch.mdx @@ -54,7 +54,7 @@ unity-build: Set `providerStrategy: gitlab-ci` and supply the required inputs: ```yaml -- uses: game-ci/unity-builder@main +- uses: game-ci/orchestrator@main with: providerStrategy: gitlab-ci targetPlatform: StandaloneLinux64 @@ -123,7 +123,7 @@ jobs: with: lfs: true - - uses: game-ci/unity-builder@main + - uses: game-ci/orchestrator@main with: providerStrategy: gitlab-ci targetPlatform: ${{ matrix.targetPlatform }} diff --git a/docs/03-github-orchestrator/05-providers/05-local.mdx b/docs/03-github-orchestrator/05-providers/05-local.mdx index 4dc12e77..bbeb4a70 100644 --- a/docs/03-github-orchestrator/05-providers/05-local.mdx +++ b/docs/03-github-orchestrator/05-providers/05-local.mdx @@ -13,7 +13,7 @@ for development and testing. ### GitHub Actions (self-hosted runner) ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: local targetPlatform: StandaloneLinux64 diff --git a/docs/03-github-orchestrator/05-providers/06-custom-providers.mdx b/docs/03-github-orchestrator/05-providers/06-custom-providers.mdx index 8f92d891..022325f3 100644 --- a/docs/03-github-orchestrator/05-providers/06-custom-providers.mdx +++ b/docs/03-github-orchestrator/05-providers/06-custom-providers.mdx @@ -23,19 +23,19 @@ Set `providerStrategy` to a provider source instead of a built-in name: ```yaml # GitHub repository -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: 'https://github.com/your-org/my-provider' targetPlatform: StandaloneLinux64 # GitHub shorthand -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: 'your-org/my-provider' targetPlatform: StandaloneLinux64 # Specific branch -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: 'your-org/my-provider@develop' targetPlatform: StandaloneLinux64 diff --git a/docs/03-github-orchestrator/05-providers/06-remote-powershell.mdx b/docs/03-github-orchestrator/05-providers/06-remote-powershell.mdx index 581f91a7..1562cafb 100644 --- a/docs/03-github-orchestrator/05-providers/06-remote-powershell.mdx +++ b/docs/03-github-orchestrator/05-providers/06-remote-powershell.mdx @@ -56,7 +56,7 @@ Set `providerStrategy: remote-powershell` and supply the connection details: ### WinRM Transport (Default) ```yaml -- uses: game-ci/unity-builder@main +- uses: game-ci/orchestrator@main with: providerStrategy: remote-powershell targetPlatform: StandaloneWindows64 @@ -69,7 +69,7 @@ Set `providerStrategy: remote-powershell` and supply the connection details: ### SSH Transport ```yaml -- uses: game-ci/unity-builder@main +- uses: game-ci/orchestrator@main with: providerStrategy: remote-powershell targetPlatform: StandaloneWindows64 @@ -137,7 +137,7 @@ jobs: with: lfs: true - - uses: game-ci/unity-builder@main + - uses: game-ci/orchestrator@main with: providerStrategy: remote-powershell targetPlatform: StandaloneWindows64 diff --git a/docs/03-github-orchestrator/05-providers/07-ansible.mdx b/docs/03-github-orchestrator/05-providers/07-ansible.mdx index 5656af70..91a85eeb 100644 --- a/docs/03-github-orchestrator/05-providers/07-ansible.mdx +++ b/docs/03-github-orchestrator/05-providers/07-ansible.mdx @@ -46,7 +46,7 @@ Or use a runner image that includes Ansible pre-installed. Set `providerStrategy: ansible` and supply the required inputs: ```yaml -- uses: game-ci/unity-builder@main +- uses: game-ci/orchestrator@main with: providerStrategy: ansible targetPlatform: StandaloneLinux64 @@ -195,7 +195,7 @@ For sensitive variables (license keys, credentials), use ```yaml - name: Write vault password run: echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > /tmp/vault-pass - - uses: game-ci/unity-builder@main + - uses: game-ci/orchestrator@main with: providerStrategy: ansible ansibleVaultPassword: /tmp/vault-pass @@ -224,7 +224,7 @@ jobs: - name: Write vault password run: echo "${{ secrets.ANSIBLE_VAULT_PASSWORD }}" > /tmp/vault-pass - - uses: game-ci/unity-builder@main + - uses: game-ci/orchestrator@main with: providerStrategy: ansible targetPlatform: StandaloneLinux64 diff --git a/docs/03-github-orchestrator/05-providers/08-github-integration.mdx b/docs/03-github-orchestrator/05-providers/08-github-integration.mdx index 9b22691f..ccd19ead 100644 --- a/docs/03-github-orchestrator/05-providers/08-github-integration.mdx +++ b/docs/03-github-orchestrator/05-providers/08-github-integration.mdx @@ -10,7 +10,7 @@ By enabling the [`githubCheck`](../api-reference#github-integration) parameter, will create a GitHub check for each step. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws githubCheck: true @@ -42,7 +42,7 @@ sequenceDiagram ``` ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws asyncOrchestrator: true diff --git a/docs/03-github-orchestrator/05-providers/10-gcp-cloud-run.mdx b/docs/03-github-orchestrator/05-providers/10-gcp-cloud-run.mdx index a53f00de..a6764934 100644 --- a/docs/03-github-orchestrator/05-providers/10-gcp-cloud-run.mdx +++ b/docs/03-github-orchestrator/05-providers/10-gcp-cloud-run.mdx @@ -11,7 +11,7 @@ This provider is experimental. APIs and behavior may change between releases. ```mermaid graph LR - Runner["GitHub Actions Runner
unity-builder
providerStrategy: gcp-cloud-run
gcpStorageType:
gcs-fuse / gcs-copy /
nfs / in-memory"] + Runner["GitHub Actions Runner
game-ci/orchestrator
providerStrategy: gcp-cloud-run
gcpStorageType:
gcs-fuse / gcs-copy /
nfs / in-memory"] API["Cloud Run Jobs API"] Job["Job: unity-build
Image: unityci
Storage: ..."] Runner -->|gcloud CLI| API --> Job @@ -72,7 +72,7 @@ trade-offs for performance, persistence, and complexity. ### GCS FUSE - mount bucket as filesystem ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: gcp-cloud-run gcpProject: my-project @@ -83,7 +83,7 @@ trade-offs for performance, persistence, and complexity. ### NFS - Filestore for fast Library caching ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: gcp-cloud-run gcpProject: my-project @@ -97,7 +97,7 @@ trade-offs for performance, persistence, and complexity. ### Copy - simple artifact upload/download ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: gcp-cloud-run gcpProject: my-project diff --git a/docs/03-github-orchestrator/05-providers/11-azure-aci.mdx b/docs/03-github-orchestrator/05-providers/11-azure-aci.mdx index b8c16462..99231827 100644 --- a/docs/03-github-orchestrator/05-providers/11-azure-aci.mdx +++ b/docs/03-github-orchestrator/05-providers/11-azure-aci.mdx @@ -12,7 +12,7 @@ This provider is experimental. APIs and behavior may change between releases. ```mermaid graph LR - Runner["GitHub Actions Runner
unity-builder
providerStrategy: azure-aci
azureStorageType:
azure-files / blob-copy /
azure-files-nfs / in-memory"] + Runner["GitHub Actions Runner
game-ci/orchestrator
providerStrategy: azure-aci
azureStorageType:
azure-files / blob-copy /
azure-files-nfs / in-memory"] API["Container Instances API"] Container["Container: unity-build
Image: unityci
Storage: ..."] Runner -->|Azure CLI| API --> Container @@ -72,7 +72,7 @@ Set `azureStorageType` to control how the build accesses large files. ### Azure Files - SMB mount (default) ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: azure-aci azureResourceGroup: my-rg @@ -83,7 +83,7 @@ Set `azureStorageType` to control how the build accesses large files. ### NFS - better POSIX performance ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: azure-aci azureResourceGroup: my-rg @@ -96,7 +96,7 @@ Set `azureStorageType` to control how the build accesses large files. ### Blob copy - simple artifact upload/download ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: azure-aci azureResourceGroup: my-rg diff --git a/docs/03-github-orchestrator/05-providers/12-cli-provider-protocol.mdx b/docs/03-github-orchestrator/05-providers/12-cli-provider-protocol.mdx index 82e2d317..e4084cd8 100644 --- a/docs/03-github-orchestrator/05-providers/12-cli-provider-protocol.mdx +++ b/docs/03-github-orchestrator/05-providers/12-cli-provider-protocol.mdx @@ -22,7 +22,7 @@ graph LR Set `providerExecutable` to the path of your executable: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerExecutable: ./my-provider targetPlatform: StandaloneLinux64 @@ -163,7 +163,7 @@ esac Make it executable and point `providerExecutable` at it: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerExecutable: ./my-provider.sh targetPlatform: StandaloneLinux64 diff --git a/docs/03-github-orchestrator/05-providers/13-config-defined-providers.mdx b/docs/03-github-orchestrator/05-providers/13-config-defined-providers.mdx index d5807293..7f6af372 100644 --- a/docs/03-github-orchestrator/05-providers/13-config-defined-providers.mdx +++ b/docs/03-github-orchestrator/05-providers/13-config-defined-providers.mdx @@ -61,7 +61,7 @@ lifecycle: Use it as the provider strategy: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: config:./.game-ci/providers/local-shell.yml targetPlatform: StandaloneLinux64 @@ -98,7 +98,7 @@ providers: ``` ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: config:./.game-ci/providers.yml#lab-windows targetPlatform: StandaloneWindows64 diff --git a/docs/03-github-orchestrator/06-secrets.mdx b/docs/03-github-orchestrator/06-secrets.mdx index dc30c05c..dc5f8519 100644 --- a/docs/03-github-orchestrator/06-secrets.mdx +++ b/docs/03-github-orchestrator/06-secrets.mdx @@ -26,7 +26,7 @@ Set `secretSource` to use a premade integration or custom command. This is the r Specify `secretSource` and `pullInputList` (comma-separated list of secret keys to fetch): ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 env: pullInputList: UNITY_LICENSE,UNITY_SERIAL,UNITY_EMAIL,UNITY_PASSWORD secretSource: aws-parameter-store diff --git a/docs/03-github-orchestrator/07-advanced-topics/01-caching.mdx b/docs/03-github-orchestrator/07-advanced-topics/01-caching.mdx index 58ba09da..2a74a3ec 100644 --- a/docs/03-github-orchestrator/07-advanced-topics/01-caching.mdx +++ b/docs/03-github-orchestrator/07-advanced-topics/01-caching.mdx @@ -80,7 +80,7 @@ flowchart LR Set `cacheCheckpointInterval` to save the Library folder periodically while Unity is running: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws targetPlatform: StandaloneLinux64 @@ -94,7 +94,7 @@ on the configured cache upload hook to push them after the build container stops Use `cacheSaveOnFailure` for builds that may exit non-zero because of OOMs, crashes, or assertions: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws targetPlatform: StandaloneLinux64 @@ -121,7 +121,7 @@ Library folder is tens of gigabytes and checkpoint archives consume too much I/O Use `cacheRetentionDays` to automatically remove old cache entries from storage: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws cacheRetentionDays: 30 @@ -134,6 +134,11 @@ Use `cacheRetentionDays` to automatically remove old cache entries from storage: | `30` | Main and develop branch caches | | `90` | Release branch caches | +When `childWorkspacesEnabled` is set, `cacheRetentionDays` also age-sweeps cached child workspaces +under `childWorkspaceCacheRoot` — a cached child workspace is the same kind of disk-space liability +as a local Library cache entry, so it shares the same retention setting rather than needing a +separate one. + You can vary retention by branch: ```yaml @@ -168,7 +173,7 @@ Set `maxRetainedWorkspaces` to control how many full workspaces are kept: | `> 0` | Keep at most N workspaces. Additional jobs fall back to standard caching. | ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws maxRetainedWorkspaces: 3 @@ -228,7 +233,7 @@ Use both when interrupted imports are common and import results need to survive single Library archive: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 env: UNITY_ACCELERATOR_ENDPOINT: '127.0.0.1:10080' with: @@ -336,6 +341,32 @@ clear error. Serialise output-emitting steps. If a job needs to emit several outputs from parallel sub-jobs, collect them into a single output-emitting step at the end. +### Retained-workspace lock outliving a failed build + +`maxRetainedWorkspaces` locks are plain marker objects in the storage backend (S3 or rclone remote) +with no TTL or liveness check — a lock is considered held for as long as the marker object exists, +regardless of whether the process that created it is still running. A build that throws before it +reaches its own cleanup step used to leave that marker behind permanently, shrinking the retained +workspace pool by one on every crash until someone deleted the object by hand. + +Release the lock on every exit path, not just the success path — including the one a thrown error +takes. Releasing an already-released (or never-acquired) lock should be a no-op, so the release call +is safe to make unconditionally rather than only after confirming a lock is actually held. + +### Orphaned background cache-save lock from a killed process + +A background (detached, `copy-directory`) cache save writes a PID-stamped lock file before it starts +and removes it on completion. If the process saving the cache is killed mid-copy (OOM, runner +crash, forced job cancellation), the lock file survives it. A lock check that only runs reactively — +triggered by a later save/restore call that happens to target the same cache key — never revisits a +cache key this run doesn't touch, so that lock can outlive the process that created it indefinitely. + +Sweep lock files proactively at the start of a build, across every cache key under the cache root, +not only the one this run is about to use. Treat a lock as stale when the PID it names is no longer +alive. An incomplete or unparseable marker is kept for a five-minute grace period because the +writer briefly stores `pending` before it can record the child PID. Permission errors during PID +liveness checks are not treated as proof that the process is dead. + ### Path-filter on the workflow entrypoint to skip docs-only commits Docs-only pushes (markdown changes, README updates) shouldn't trigger Unity CI runs. A @@ -368,4 +399,4 @@ caching strategies above — fewer wasted builds means less cache churn. | `minCacheEntries` | Minimum cache entries to keep during age-based GC (floor) | | `skipCache` | Skip cache restore entirely | | `useCompressionStrategy` | Use LZ4 compression for cache archives | -| `localCacheMode` | One of `move-directory` (default), `copy-directory`, `tar` | +| `localCacheMode` | One of `tar` (default), `move-directory`, `copy-directory` | diff --git a/docs/03-github-orchestrator/07-advanced-topics/05-hooks/03-command-hooks.mdx b/docs/03-github-orchestrator/07-advanced-topics/05-hooks/03-command-hooks.mdx index 5e0a71cc..19c61d6c 100644 --- a/docs/03-github-orchestrator/07-advanced-topics/05-hooks/03-command-hooks.mdx +++ b/docs/03-github-orchestrator/07-advanced-topics/05-hooks/03-command-hooks.mdx @@ -26,7 +26,7 @@ Pass hooks inline via the `commandHooks` parameter or reference files from the ` directory via `customHookFiles`. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws customHookFiles: my-hook diff --git a/docs/03-github-orchestrator/07-advanced-topics/05-hooks/04-container-hooks.mdx b/docs/03-github-orchestrator/07-advanced-topics/05-hooks/04-container-hooks.mdx index 67929a73..4c9192f4 100644 --- a/docs/03-github-orchestrator/07-advanced-topics/05-hooks/04-container-hooks.mdx +++ b/docs/03-github-orchestrator/07-advanced-topics/05-hooks/04-container-hooks.mdx @@ -33,7 +33,7 @@ Define container hooks inline via `preBuildContainerHooks` / `postBuildContainer files from `.game-ci/container-hooks/` via `containerHookFiles`. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws containerHookFiles: aws-s3-upload-build diff --git a/docs/03-github-orchestrator/07-advanced-topics/07-load-balancing.mdx b/docs/03-github-orchestrator/07-advanced-topics/07-load-balancing.mdx index 79a774e1..51d55c23 100644 --- a/docs/03-github-orchestrator/07-advanced-topics/07-load-balancing.mdx +++ b/docs/03-github-orchestrator/07-advanced-topics/07-load-balancing.mdx @@ -47,7 +47,7 @@ automatically. Three mechanisms work together: The most common pattern: prefer your self-hosted runner, but offload to the cloud when it's busy. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: local-docker fallbackProviderStrategy: aws @@ -66,7 +66,7 @@ to the best available provider and returns immediately - the GitHub runner is fr regardless of which provider handles the build. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: local-docker fallbackProviderStrategy: aws @@ -87,7 +87,7 @@ Enable `retryOnFallback` to automatically retry on the alternate provider when t This is useful for long builds where transient cloud failures are common. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws fallbackProviderStrategy: local-docker @@ -104,7 +104,7 @@ Cloud providers sometimes take a long time to provision infrastructure. Set `pro swap to the alternate provider if startup takes too long. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: k8s fallbackProviderStrategy: aws @@ -126,7 +126,7 @@ The built-in load balancing is designed to never block a build: Use the outputs to track which provider was selected and why: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 id: build with: providerStrategy: local-docker @@ -177,7 +177,7 @@ jobs: with: lfs: true - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: ${{ matrix.provider }} targetPlatform: ${{ matrix.targetPlatform }} @@ -210,7 +210,7 @@ jobs: echo "memory=3072" >> "$GITHUB_OUTPUT" fi - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: ${{ steps.provider.outputs.strategy }} targetPlatform: StandaloneLinux64 @@ -253,7 +253,7 @@ jobs: with: lfs: true - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: ${{ needs.check-runner.outputs.provider }} targetPlatform: StandaloneLinux64 @@ -327,7 +327,7 @@ jobs: with: lfs: true - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 if: steps.check.outputs.idle != '0' with: providerStrategy: local-docker @@ -359,7 +359,7 @@ jobs: ref: ${{ inputs.ref }} lfs: true - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws targetPlatform: ${{ inputs.targetPlatform }} @@ -406,7 +406,7 @@ jobs: with: lfs: true - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: ${{ inputs.providerStrategy }} targetPlatform: ${{ inputs.targetPlatform }} @@ -492,7 +492,7 @@ jobs: with: lfs: true - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: ${{ matrix.provider }} targetPlatform: ${{ matrix.targetPlatform }} diff --git a/docs/03-github-orchestrator/07-advanced-topics/08-storage.mdx b/docs/03-github-orchestrator/07-advanced-topics/08-storage.mdx index 4dcd9bbe..0591a0c5 100644 --- a/docs/03-github-orchestrator/07-advanced-topics/08-storage.mdx +++ b/docs/03-github-orchestrator/07-advanced-topics/08-storage.mdx @@ -72,7 +72,7 @@ can upload and process each category consistently. Declare output categories in your workflow or project configuration: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: targetPlatform: StandaloneLinux64 outputTypes: build,test-results,metrics,images @@ -149,7 +149,7 @@ automatically as part of the CloudFormation base stack. For other providers, ens and region are set in the environment. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws # storageProvider defaults to "s3" @@ -163,7 +163,7 @@ and region are set in the environment. when you want to store caches and artifacts somewhere other than S3. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: k8s storageProvider: rclone diff --git a/docs/03-github-orchestrator/07-advanced-topics/10-build-services.mdx b/docs/03-github-orchestrator/07-advanced-topics/10-build-services.mdx index ba83103b..0fde366a 100644 --- a/docs/03-github-orchestrator/07-advanced-topics/10-build-services.mdx +++ b/docs/03-github-orchestrator/07-advanced-topics/10-build-services.mdx @@ -25,7 +25,7 @@ by the built-in build command, smoke tests, asset processing, or fully custom au benefits from Orchestrator providers, logs, hooks, storage, and cleanup. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws customJob: | @@ -44,7 +44,6 @@ For command-line usage, pass the same YAML as `--custom-job`: ```bash game-ci \ - --plugin @game-ci/orchestrator-plugin \ orchestrate ./my-project \ --provider-strategy local-docker \ --custom-job '- name: smoke @@ -107,7 +106,7 @@ submodules: ### Example ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: local submoduleProfilePath: config/submodule-profiles/game/client/profile.yml @@ -154,7 +153,7 @@ profile-dependent are cleared before the build: You can also provide explicit fallback keys with `localCacheFallbackKeys`. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: local localCacheEnabled: true @@ -166,36 +165,35 @@ You can also provide explicit fallback keys with `localCacheFallbackKeys`. `localCacheMode` controls how the local cache stores and restores the Unity Library: -| Mode | Behavior | Best for | -| ---------------- | ------------------------ | --------------------------------------------------------------------- | -| `move-directory` | Directory move / rename | Default; O(1) atomic rename, fastest for large Libraries | -| `copy-directory` | Recursive directory copy | Shared seed caches that must remain available to other builds | -| `tar` | Portable tar archive | Distributing cache entries via the rclone/S3 built-in container hooks | +| Mode | Behavior | Best for | +| ---------------- | ------------------------ | ------------------------------------------------------------------- | +| `move-directory` | Directory move / rename | O(1) same-volume rename for a cache key used by one build at a time | +| `copy-directory` | Recursive directory copy | Shared seed caches that must remain available to other builds | +| `tar` | Portable tar archive | Default; works across filesystem volumes | -`move-directory` (the default since [game-ci/orchestrator#41](https://github.com/game-ci/orchestrator/pull/41)) -is much faster than tar extraction for very large Libraries because it renames a directory rather -than copying file contents. It works best when the cache root and workspace are on the same -volume; if a rename fails across filesystem/volume boundaries (`EXDEV`), it automatically falls -back to a copy instead of failing the build. If `localCacheFallback` restores a fallback key while -`localCacheMode` is `move-directory`, the fallback is copied instead of moved so the shared -fallback seed remains available to other branches. +`move-directory` can be much faster than tar extraction for very large Libraries because it +renames a directory rather than copying file contents. The cache root and workspace must be on the +same volume; a cross-volume rename (`EXDEV`) fails rather than falling back to a copy. Restore moves +the exact cache entry into the workspace and save moves it back, so do not share one exact key +between concurrent builds. If `localCacheFallback` restores a fallback key, that fallback is copied +instead of moved so the seed remains available to other branches. ### Inputs -| Input | Default | Description | -| ------------------------ | ---------------- | -------------------------------------------------------- | -| `localCacheEnabled` | `false` | Enable filesystem caching | -| `localCacheRoot` | - | Cache directory override | -| `localCacheLibrary` | `true` | Cache Unity Library folder | -| `localCacheLfs` | `true` | Cache LFS objects | -| `localCacheFallback` | `false` | Try compatible local cache keys after exact-key miss | -| `localCacheFallbackKeys` | - | Comma-separated explicit fallback keys | -| `localCacheMode` | `move-directory` | Cache mode: `move-directory`, `copy-directory`, or `tar` | +| Input | Default | Description | +| ------------------------ | ------- | -------------------------------------------------------- | +| `localCacheEnabled` | `false` | Enable filesystem caching | +| `localCacheRoot` | - | Cache directory override | +| `localCacheLibrary` | `true` | Cache Unity Library folder | +| `localCacheLfs` | `true` | Cache LFS objects | +| `localCacheFallback` | `false` | Try compatible local cache keys after exact-key miss | +| `localCacheFallbackKeys` | - | Comma-separated explicit fallback keys | +| `localCacheMode` | `tar` | Cache mode: `move-directory`, `copy-directory`, or `tar` | ### Example ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: local localCacheEnabled: true @@ -235,7 +233,7 @@ The agent name is derived from the executable filename (e.g. `elastic-git-storag ### Example ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: local lfsTransferAgent: ./tools/elastic-git-storage @@ -271,7 +269,7 @@ enable when your build pipeline depends on hooks running. ```yaml # Enable hooks but skip pre-commit -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: local gitHooksEnabled: true diff --git a/docs/03-github-orchestrator/07-advanced-topics/10-lfs-agents.mdx b/docs/03-github-orchestrator/07-advanced-topics/10-lfs-agents.mdx index 6294b9ff..52f18cdc 100644 --- a/docs/03-github-orchestrator/07-advanced-topics/10-lfs-agents.mdx +++ b/docs/03-github-orchestrator/07-advanced-topics/10-lfs-agents.mdx @@ -18,7 +18,7 @@ When you set `lfsTransferAgent: elastic-git-storage`, Orchestrator will: ### Basic Usage ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: targetPlatform: StandaloneLinux64 lfsTransferAgent: elastic-git-storage diff --git a/docs/03-github-orchestrator/07-advanced-topics/11-test-workflow-engine.mdx b/docs/03-github-orchestrator/07-advanced-topics/11-test-workflow-engine.mdx index 0d594aa1..958f3465 100644 --- a/docs/03-github-orchestrator/07-advanced-topics/11-test-workflow-engine.mdx +++ b/docs/03-github-orchestrator/07-advanced-topics/11-test-workflow-engine.mdx @@ -183,7 +183,7 @@ extensible_groups: Standard Unity Test Framework tests that run in the editor without entering play mode: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: testSuitePath: .game-ci/test-suites/pr-suite.yml testFilterRefs: smoke,ci @@ -221,7 +221,7 @@ runs: Test results are output in machine-readable formats: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: testSuitePath: .game-ci/test-suites/pr-suite.yml testResultFormat: junit # junit, json, or both @@ -238,7 +238,7 @@ for PR workflows, quarantines, branch-specific smoke tests, or community-maintai ### Inline injection ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: testSuitePath: .game-ci/test-suites/pr-suite.yml testFilterRefs: smoke @@ -254,7 +254,7 @@ for PR workflows, quarantines, branch-specific smoke tests, or community-maintai ### File-based injection ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: testSuitePath: .game-ci/test-suites/pr-suite.yml testFilterInjectionPath: .game-ci/test-filters/nightly.yml diff --git a/docs/03-github-orchestrator/07-advanced-topics/12-hot-runner-protocol.mdx b/docs/03-github-orchestrator/07-advanced-topics/12-hot-runner-protocol.mdx index 42c54254..9dc69955 100644 --- a/docs/03-github-orchestrator/07-advanced-topics/12-hot-runner-protocol.mdx +++ b/docs/03-github-orchestrator/07-advanced-topics/12-hot-runner-protocol.mdx @@ -52,7 +52,7 @@ workspace delta. ### GitHub Runner Transport ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: runnerTransport: github runnerLabels: unity-2022,linux,hot @@ -62,7 +62,7 @@ workspace delta. ### WebSocket Transport ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: runnerTransport: websocket runnerEndpoint: wss://build.example.com/runners @@ -72,7 +72,7 @@ workspace delta. ### File Transport ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: runnerTransport: file runnerEndpoint: /mnt/shared/build-jobs/ @@ -91,7 +91,7 @@ Persistent mode saves editor startup time, keeps the Library folder warm, and le only changed assets when paired with incremental sync. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: editorMode: persistent runnerTransport: websocket @@ -113,7 +113,7 @@ cache archive, Orchestrator sends the runner enough information to update the ex ### Git Delta ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: editorMode: persistent syncStrategy: git-delta @@ -127,7 +127,7 @@ engine process reuse its warm state. Use direct input when a job should validate files that have not been pushed to git. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: editorMode: persistent syncStrategy: direct-input diff --git a/docs/03-github-orchestrator/07-advanced-topics/15-large-projects.mdx b/docs/03-github-orchestrator/07-advanced-topics/15-large-projects.mdx index 2940de68..852c18c6 100644 --- a/docs/03-github-orchestrator/07-advanced-topics/15-large-projects.mdx +++ b/docs/03-github-orchestrator/07-advanced-topics/15-large-projects.mdx @@ -22,20 +22,18 @@ Standard CI assumptions break down at this scale: - **CI timeouts** - Default job timeouts of 6 hours are insufficient for cold clones followed by full imports on large projects. -Orchestrator addresses each of these with a two-level workspace architecture, move-centric caching, -selective LFS hydration, and submodule profile filtering. +Orchestrator addresses these costs with child workspaces, optional move-centric local caching, +custom LFS transfer-agent integration, incremental sync, and submodule profile filtering. ## Two-Level Workspace Architecture Orchestrator manages workspaces at two levels: -**Root workspace** - A lean, long-lived clone of the repository. It contains the full git history -and index, a minimal set of LFS objects (only those needed for compilation), and no Unity Library -folder. The root workspace is cached across builds and updated incrementally. +**Root workspace** - A long-lived source workspace used to derive cached children. Its exact Git LFS +contents depend on the configured Git/LFS workflow and transfer agent. -**Child workspaces** - Per-build-target workspaces derived from the root. Each child is LFS-hydrated -for its specific asset paths, contains the Library folder for its platform target, and is retained -between builds of the same target. +**Child workspaces** - Named workspaces derived from the root. A child can retain its own engine +cache folder and be reused by subsequent builds assigned the same name. ``` root-workspace/ @@ -55,18 +53,26 @@ child-workspaces/ Library/ ``` -The orchestrator manages this layout automatically when `retainedWorkspaces: true` is set. Child -workspaces are created on first build and reused on subsequent builds of the same target. Only -changed files from git delta sync are applied to each child. +The orchestrator manages this layout when `childWorkspacesEnabled: true` is set. You must provide a +stable `childWorkspaceName`; using the matrix target keeps platforms isolated. Children are cached +under `childWorkspaceCacheRoot`, created on first use, and reused by later builds with the same +name. Only `syncStrategy: git-delta` limits synchronization to Git changes. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: - retainedWorkspaces: true - workspaceRoot: /mnt/build-storage/my-game + childWorkspacesEnabled: true + childWorkspaceName: ${{ matrix.targetPlatform }} + childWorkspaceCacheRoot: /mnt/build-storage/my-game/workspaces targetPlatform: StandaloneLinux64 ``` +`childWorkspacePreserveGit` (default `true`) keeps `.git` in the cached child workspace so +incremental sync strategies (see [Incremental Sync](#incremental-sync) below) keep working across +builds. `childWorkspaceSeparateLibrary` (default `true`) caches each engine cache folder (e.g. +`Library`) independently from the rest of the workspace, so it restores and saves on its own move +rather than moving with the whole workspace. + ## Move-Centric Caching Traditional caching copies files: archive → upload → download → extract. For a 50 GB Library folder @@ -77,10 +83,11 @@ is an O(1) metadata operation regardless of how many files it contains. A 50 GB in milliseconds. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: + localCacheEnabled: true localCacheRoot: /mnt/build-storage/cache - cacheStrategy: move + localCacheMode: move-directory ``` The cache lifecycle for a Library folder: @@ -91,49 +98,44 @@ The cache lifecycle for a Library folder: 4. Next build - Library is already warm at cache location This eliminates the archive/upload/download/extract cycle entirely for builds running on retained -storage. Remote cache fallback (S3, GCS, Azure Blob via rclone) is still available for cold runners -that do not have local cache access. - -| Cache Strategy | Library Move Time | Suitable For | -| -------------- | ------------------------ | ------------------------------- | -| `move` | Milliseconds | Retained storage, build farms | -| `rclone` | Minutes (size-dependent) | Remote cache, ephemeral runners | -| `github-cache` | Minutes (10 GB limit) | Small projects only | +storage. Remote cache fallback (S3, GCS, Azure Blob via rclone) is a separate mechanism (see +[Storage](storage)) for cold runners that do not have local cache access. + +| `localCacheMode` | Library Restore/Save Time | Suitable For | +| ---------------- | --------------------------------- | --------------------------------------------------- | +| `move-directory` | Milliseconds (same-volume rename) | Retained storage and one build at a time per key | +| `copy-directory` | Seconds-minutes (size-dependent) | Cache root on a different volume than the workspace | +| `tar` | Minutes (archive + extract) | Portable default | + +`move-directory` requires the cache root and the workspace to be on the same filesystem volume — a +same-volume rename is what makes the move O(1). A cross-volume rename fails at the OS level, so +point `localCacheRoot` at a path on the same drive as the workspace, or use `copy-directory` when +that cannot be guaranteed. Fallback-key restores (see +[Fallback Keys](build-services#fallback-keys)) are always copied rather than moved, regardless of +`localCacheMode`, so the original cache entry is left intact for other builds. ## Custom LFS Transfer Agents -Standard Git LFS transfers every object through a single HTTP endpoint. For large projects this -creates a bottleneck, especially when only a subset of assets is needed for a given build. - Orchestrator supports alternative LFS transfer agents via the `lfsTransferAgent` input. A transfer -agent is a binary that Git invokes in place of the standard LFS client. Agents can implement partial -transfer, parallel streams, resumable downloads, and custom storage backends. +agent is a binary that Git invokes in place of the standard LFS client. The agent — not +Orchestrator — defines its transfer, filtering, and storage behavior. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: lfsTransferAgent: elastic-git-storage lfsTransferAgentArgs: '--verbose' - lfsStoragePaths: 'Assets/LargeAssets,Assets/Cinematics' + lfsStoragePaths: '/mnt/fast-lfs;/mnt/archive-lfs' ``` -`lfsStoragePaths` limits LFS hydration to the specified asset directories. Files outside these paths -are not downloaded, reducing transfer volume to only what the build target needs. - -**elastic-git-storage** is the recommended agent for large projects. It supports: - -- Parallel multi-stream transfers -- Resumable downloads after network interruption -- Content-addressed deduplication across workspaces -- Direct object storage access (S3, GCS, Azure Blob) without a relay server - -**rclone-based agents** are an alternative when the LFS server cannot be replaced. They proxy -transfers through any rclone-supported backend, enabling caching and bandwidth throttling. +`lfsStoragePaths` is a semicolon-separated list passed to the agent as the +`LFS_STORAGE_PATHS` environment variable. Orchestrator does not interpret these values as repository +path filters; consult the selected agent's documentation for their meaning. To use a custom agent, provide the agent binary path: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: lfsTransferAgent: /usr/local/bin/my-lfs-agent lfsTransferAgentArgs: '--threads 8 --cache /mnt/lfs-cache' @@ -183,14 +185,14 @@ pipeline), the runner pulls only the changed files from a generic storage remote git-delta so that code changes and asset changes are both handled incrementally. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: syncStrategy: git-delta - retainedWorkspaces: true + childWorkspacesEnabled: true ``` -Together, retained workspaces and git-delta sync deliver the minimal possible import work on every -build: Unity sees only the files that actually changed. +Together, retained child workspaces and git-delta sync deliver the minimal possible import work on +every build: Unity sees only the files that actually changed. ## Build Performance Tips @@ -210,17 +212,24 @@ strategy: target platform during off-hours. Retained workspaces mean subsequent PR builds start with warm Library folders. -**Use LFS partial clone patterns.** Structure the repository so assets are grouped by build target -under predictable paths (`Assets/Platforms/Linux/`, `Assets/Platforms/WebGL/`). This makes -`lfsStoragePaths` filtering straightforward and predictable. +**Design LFS layout for the selected agent.** Grouping assets by build target can help agents that +support selective hydration, but that filtering is agent-specific rather than an Orchestrator +guarantee. -**Reserve timeouts generously.** Set `buildTimeout` to account for cold-start scenarios, even when -warm builds are expected. The first build after a runner restart will be cold. +**Reserve timeouts generously.** There is no orchestrator-level build timeout input — set +`timeout-minutes` on the GitHub Actions job itself to account for cold-start scenarios, even when +warm builds are expected. The first build after a runner restart will be cold. Separately, +`gcTimeoutMinutes` forces a garbage-collection pass if a build overruns that many minutes (see +[Build Reliability](build-reliability)) — it is a cache-hygiene safety net, not a job timeout. ```yaml -- uses: game-ci/unity-builder@v4 - with: - buildTimeout: 360 # minutes +jobs: + build: + timeout-minutes: 360 + steps: + - uses: game-ci/orchestrator@v1.0.0 + with: + targetPlatform: StandaloneLinux64 ``` **Monitor Library folder health.** Occasional full reimports are necessary when Unity upgrades or @@ -229,13 +238,16 @@ mid-sprint when a runner's Library becomes stale. ## Inputs Reference -| Input | Description | -| ---------------------- | ------------------------------------------------------- | -| `retainedWorkspaces` | Keep child workspaces between builds (`true` / `false`) | -| `workspaceRoot` | Base path for root and child workspace storage | -| `localCacheRoot` | Local filesystem path for move-centric Library cache | -| `cacheStrategy` | Cache approach: `move`, `rclone`, `github-cache` | -| `lfsTransferAgent` | Name or path of a custom LFS transfer agent binary | -| `lfsTransferAgentArgs` | Additional arguments passed to the LFS transfer agent | -| `lfsStoragePaths` | Comma-separated asset paths to limit LFS hydration | -| `buildTimeout` | Maximum build duration in minutes | +| Input | Description | +| ------------------------------- | --------------------------------------------------------------------- | +| `childWorkspacesEnabled` | Enable per-build-target child workspaces (`true` / `false`) | +| `childWorkspaceName` | Cache slot name for this child workspace, usually the target platform | +| `childWorkspaceCacheRoot` | Base path for cached child workspaces | +| `childWorkspacePreserveGit` | Keep `.git` in the cached child workspace (default `true`) | +| `childWorkspaceSeparateLibrary` | Cache each engine cache folder independently (default `true`) | +| `localCacheEnabled` | Enable the local filesystem cache (`true` / `false`) | +| `localCacheRoot` | Local filesystem path for the move-centric Library cache | +| `localCacheMode` | Restore/save strategy; `tar` is the default | +| `lfsTransferAgent` | Name or path of a custom LFS transfer agent binary | +| `lfsTransferAgentArgs` | Additional arguments passed to the LFS transfer agent | +| `lfsStoragePaths` | Semicolon-separated storage values passed to the custom LFS agent | diff --git a/docs/03-github-orchestrator/07-advanced-topics/16-monorepo-support.mdx b/docs/03-github-orchestrator/07-advanced-topics/16-monorepo-support.mdx index b824a5d4..c443c0c4 100644 --- a/docs/03-github-orchestrator/07-advanced-topics/16-monorepo-support.mdx +++ b/docs/03-github-orchestrator/07-advanced-topics/16-monorepo-support.mdx @@ -101,7 +101,7 @@ overridden; all others inherit from the base. Specify profiles and variants as action inputs: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: submoduleProfilePath: config/submodule-profiles/my-game/ci/profile.yml submoduleVariantPath: config/submodule-profiles/my-game/ci/server.yml @@ -142,7 +142,7 @@ jobs: buildMethod: MyOtherGame.BuildScripts.BuildGame targetPlatform: StandaloneWindows64 steps: - - uses: game-ci/unity-builder@v4 + - uses: game-ci/orchestrator@v1.0.0 with: submoduleProfilePath: ${{ matrix.profile }} submoduleVariantPath: ${{ matrix.variant }} diff --git a/docs/03-github-orchestrator/07-advanced-topics/17-build-reliability.mdx b/docs/03-github-orchestrator/07-advanced-topics/17-build-reliability.mdx index 45dceceb..5af72af0 100644 --- a/docs/03-github-orchestrator/07-advanced-topics/17-build-reliability.mdx +++ b/docs/03-github-orchestrator/07-advanced-topics/17-build-reliability.mdx @@ -37,7 +37,7 @@ Enable verbose diagnostics with `orchestratorDebug: true` when investigating pro resource allocation, environment variables, disk usage, or cache behavior. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws orchestratorDebug: true @@ -60,7 +60,7 @@ flowchart LR Run garbage collection from GitHub Actions: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws mode: garbage-collect @@ -78,7 +78,7 @@ of the `constantGarbageCollection` setting. This prevents resource leaks from lo builds. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: gcTimeoutMinutes: 120 # Force GC if build exceeds 2 hours garbageMaxAge: 24 @@ -92,7 +92,7 @@ By default, Orchestrator keeps the 2 most recent cache snapshots per folder. Use to adjust how many tar archives are retained: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: maxCacheEntries: 5 # Keep 5 most recent cache snapshots per folder ``` @@ -106,7 +106,7 @@ to set a floor — the GC will never remove entries below this count, even if th threshold: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: cacheRetentionDays: 30 minCacheEntries: 1 # Always keep at least 1 cache entry per key @@ -144,7 +144,7 @@ The integrity check runs three validations in sequence: ### Configuration ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: gitIntegrityCheck: 'true' ``` @@ -162,7 +162,7 @@ This is a last-resort recovery. It works because the orchestrator's checkout ste the repository from the remote after re-initialization. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: gitIntegrityCheck: 'true' gitAutoRecover: 'true' # this is the default when gitIntegrityCheck is enabled @@ -171,7 +171,7 @@ the repository from the remote after re-initialization. To run integrity checks without automatic recovery (report-only mode), disable it explicitly: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: gitIntegrityCheck: 'true' gitAutoRecover: 'false' @@ -203,7 +203,7 @@ cross-platform contributions. ### Solution ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: cleanReservedFilenames: 'true' ``` @@ -231,7 +231,7 @@ Enable `unityProcessCleanup` on Windows self-hosted runners to clean up stale Un the local build starts: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: unityProcessCleanup: 'true' ``` @@ -310,7 +310,7 @@ managed with a count-based retention policy. ### Configuration ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: buildArchiveEnabled: 'true' buildArchivePath: '/mnt/build-archives' @@ -377,7 +377,7 @@ This is applied automatically and does not require any configuration. For self-hosted runners with persistent workspaces: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: gitIntegrityCheck: 'true' gitAutoRecover: 'true' @@ -393,7 +393,7 @@ since the workspace is created fresh each time. Reserved filename cleanup is sti repository contains cross-platform contributions: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: cleanReservedFilenames: 'true' ``` diff --git a/docs/03-github-orchestrator/07-advanced-topics/18-engine-plugins.mdx b/docs/03-github-orchestrator/07-advanced-topics/18-engine-plugins.mdx index ab3849dc..c411e013 100644 --- a/docs/03-github-orchestrator/07-advanced-topics/18-engine-plugins.mdx +++ b/docs/03-github-orchestrator/07-advanced-topics/18-engine-plugins.mdx @@ -46,7 +46,7 @@ load a community or custom engine provider: ```yaml # GitHub Actions with a built-in engine configuration -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: engine: godot targetPlatform: StandaloneLinux64 @@ -63,7 +63,7 @@ Use `enginePlugin` only when you need a custom engine provider or when you want built-in configuration: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: engine: custom-engine enginePlugin: '@your-org/custom-engine-provider' diff --git a/docs/03-github-orchestrator/07-advanced-topics/19-unity-accelerator.mdx b/docs/03-github-orchestrator/07-advanced-topics/19-unity-accelerator.mdx index 5d0a445d..ee9e7f1d 100644 --- a/docs/03-github-orchestrator/07-advanced-topics/19-unity-accelerator.mdx +++ b/docs/03-github-orchestrator/07-advanced-topics/19-unity-accelerator.mdx @@ -104,7 +104,7 @@ Create two files in your repository under `game-ci/container-hooks/`: ### 2. Configure the workflow ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 env: UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} @@ -166,7 +166,7 @@ Ensure the security group allows inbound TCP 10080 from your Fargate task securi Pass the accelerator's private IP or DNS name as an environment variable: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 env: UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} diff --git a/docs/03-github-orchestrator/07-advanced-topics/20-self-hosting-and-orchestrator.mdx b/docs/03-github-orchestrator/07-advanced-topics/20-self-hosting-and-orchestrator.mdx index 109281ad..202c9b6a 100644 --- a/docs/03-github-orchestrator/07-advanced-topics/20-self-hosting-and-orchestrator.mdx +++ b/docs/03-github-orchestrator/07-advanced-topics/20-self-hosting-and-orchestrator.mdx @@ -347,7 +347,7 @@ flowchart TD For persistent self-hosted runners using Orchestrator: ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: gitIntegrityCheck: 'true' gitAutoRecover: 'true' diff --git a/docs/03-github-orchestrator/07-advanced-topics/21-failures-and-diagnostics.mdx b/docs/03-github-orchestrator/07-advanced-topics/21-failures-and-diagnostics.mdx index cb8984a7..b0698593 100644 --- a/docs/03-github-orchestrator/07-advanced-topics/21-failures-and-diagnostics.mdx +++ b/docs/03-github-orchestrator/07-advanced-topics/21-failures-and-diagnostics.mdx @@ -253,7 +253,7 @@ Enable `githubCheck` when you also want Orchestrator step status reflected as Gi ```yaml - name: Build - uses: game-ci/unity-builder@v4 + uses: game-ci/orchestrator@v1.0.0 with: targetPlatform: StandaloneLinux64 # Diagnostics are written to Step Summary automatically diff --git a/docs/03-github-orchestrator/07-advanced-topics/22-unity-log-collection.mdx b/docs/03-github-orchestrator/07-advanced-topics/22-unity-log-collection.mdx index 4b7d8516..5a756c6f 100644 --- a/docs/03-github-orchestrator/07-advanced-topics/22-unity-log-collection.mdx +++ b/docs/03-github-orchestrator/07-advanced-topics/22-unity-log-collection.mdx @@ -22,16 +22,16 @@ diagnostic paths in the future. ## Quick Start -Add `collectUnityLogs: true` to your existing `unity-builder` (or `unity-orchestrator`) step. -Everything else is automatic: +Add `collectUnityLogs: true` to your `game-ci/orchestrator` step: ```yaml - name: Build with Unity - uses: game-ci/unity-builder@v4 + uses: game-ci/orchestrator@v1.0.0 with: targetPlatform: StandaloneLinux64 collectUnityLogs: true streamUnityLogs: true # optional — live tail Editor.log to GHA log + streamUnityLogPaths: '' # optional comma-separated override - name: Upload Unity diagnostic logs if: always() @@ -226,9 +226,8 @@ with `[UnityLogs] Editor.log:` so the streamed lines stay greppable in a busy GH ## Inputs -These inputs are accepted by `game-ci/unity-builder` (which forwards to the Orchestrator plugin) -and by `game-ci/unity-orchestrator` directly. They also work as `--collectUnityLogs` etc. flags on -the standalone `game-ci` CLI. +These inputs are accepted by `game-ci/orchestrator`. They also work as +`--collectUnityLogs`-style flags on the standalone Orchestrator CLI. | Input | Default | Description | | --------------------------- | ------- | -------------------------------------------------------- | @@ -251,7 +250,7 @@ After the build, the Orchestrator emits two GitHub Actions outputs: ```yaml - id: build - uses: game-ci/unity-builder@v4 + uses: game-ci/orchestrator@v1.0.0 with: targetPlatform: StandaloneLinux64 collectUnityLogs: true diff --git a/docs/03-github-orchestrator/07-advanced-topics/23-output-collection.mdx b/docs/03-github-orchestrator/07-advanced-topics/23-output-collection.mdx new file mode 100644 index 00000000..ff778217 --- /dev/null +++ b/docs/03-github-orchestrator/07-advanced-topics/23-output-collection.mdx @@ -0,0 +1,91 @@ +--- +sidebar_position: 23 +--- + +# Output Collection + +The Orchestrator classifies everything a build produces - the player itself, test results, +coverage reports, logs, crash symbols - into **output types**, each with a default path and a +description. Requesting an output type by name means you don't have to remember where a given +kind of file lands; the Orchestrator does. + +## Built-in types + +| Type | Default path | What it is | +| -------------- | ----------------------------- | ---------------------------------------------- | +| `build` | `./Builds/{platform}/` | Standard game build artifact | +| `test-results` | `./TestResults/` | NUnit/JUnit XML test results | +| `server-build` | `./Builds/{platform}-server/` | Dedicated server build artifact | +| `data-export` | `./Exports/` | Exported data files (CSV, JSON, binary) | +| `images` | `./Captures/` | Screenshots, render captures, atlas previews | +| `logs` | `./Logs/` | Structured build and test logs | +| `metrics` | `./Metrics/` | Build performance metrics and asset statistics | +| `coverage` | `./Coverage/` | Code coverage reports | +| `symbols` | `./Symbols/` | Debug symbols for crash symbolication | + +Request one or more types with the `artifactOutputTypes` Action input - a comma-separated list, +defaulting to `build,logs,test-results`: + +```yaml +- uses: game-ci/orchestrator@vX + with: + artifactOutputTypes: build,test-results,coverage +``` + +Unknown type names are skipped with a warning rather than failing the run - a typo in this list +should not fail an otherwise-successful build. + +Related inputs: `artifactUploadTarget` (`github-artifacts` by default, or `storage`/`local`/`none`), +`artifactUploadPath`, `artifactCompression`, `artifactRetentionDays`, and `artifactCustomTypes` for +registering a custom type inline (a JSON array of `{ name, defaultPath, description }`) without +writing TypeScript. + +## Crash-symbol collection + +Debug symbols have to be captured **at build time** or they are gone for good - once the build +machine is torn down, every future crash report from that build is unsymbolicatable. That makes +symbol collection an output-collection concern rather than a build step you opt into separately. + +Requesting `symbols` finds: + +- `.dSYM` bundles (macOS/iOS) - reported as a single bundle entry, never descended into, since + the symbolicator needs the bundle structure intact +- `.pdb` (Windows) +- `.sym` (Breakpad-format, used by most third-party crash reporters) +- `.so.dbg` / `.dbg` (Linux DWARF) +- Unity's `.symbols.json` (IL2CPP method-name maps) + +```yaml +- uses: game-ci/orchestrator@vX + with: + artifactOutputTypes: build,symbols +``` + +The collector only finds and classifies symbol files; it does not upload them anywhere +crash-reporter-specific. Uploading uses the same artifact upload path as every other output type +(see below) - there is deliberately no vendor-specific (Sentry/Backtrace/Crashlytics) upload +logic baked in, since that step is a plain file upload once the symbols are located. + +## Custom output types + +Register a type the built-ins don't cover: + +```ts +import { OutputTypeRegistry } from '@game-ci/orchestrator'; + +OutputTypeRegistry.registerType({ + name: 'replays', + defaultPath: './Replays/', + description: 'Recorded gameplay sessions for QA review', + builtIn: false, +}); +``` + +A custom type cannot override a built-in name - `registerType` logs a warning and leaves the +built-in definition in place, rather than silently shadowing it. + +## Uploading + +Collected outputs are uploaded through `ArtifactUploadHandler`, which supports GitHub Artifacts, +generic storage, or a local path - the same destination options regardless of which output types +were collected. diff --git a/docs/03-github-orchestrator/08-cli/01-getting-started.mdx b/docs/03-github-orchestrator/08-cli/01-getting-started.mdx index 610f2ba1..427f7e06 100644 --- a/docs/03-github-orchestrator/08-cli/01-getting-started.mdx +++ b/docs/03-github-orchestrator/08-cli/01-getting-started.mdx @@ -14,12 +14,12 @@ uses the executable name `game-ci`, but it exposes a different command surface. ## When To Use It -| Use case | Entry point | -| ------------------------------------------- | -------------------------------------------------- | -| General local or CI commands | [GameCI CLI](/docs/cli) | -| Provider-backed jobs through a friendly CLI | `game-ci orchestrate` with the Orchestrator plugin | -| GitHub Actions Unity builds | `game-ci/unity-builder@v4` | -| Provider protocol development or debugging | Standalone Orchestrator CLI | +| Use case | Entry point | +| ------------------------------------------- | ------------------------------------------------ | +| General local or CI commands | [GameCI CLI](/docs/cli) | +| Provider-backed jobs through a friendly CLI | `game-ci orchestrate` from the public GameCI CLI | +| GitHub Actions Unity builds | `game-ci/orchestrator@v1.0.0` | +| Provider protocol development or debugging | Standalone Orchestrator CLI | ## Command Surface @@ -38,22 +38,6 @@ The public CLI commands `game-ci orchestrate` and `game-ci test` are not standal commands, even though standalone Orchestrator also exposes an `orchestrate` verb. Use the public CLI when you want the higher-level command model and plugin loading behavior. -## Command Surface - -| Standalone command | Purpose | -| --------------------------- | --------------------------------------------------------------------------- | -| `game-ci orchestrate` | Full direct Orchestrator provider command for remote, Docker, or host runs. | -| `game-ci build` | Narrow remote build shortcut for Orchestrator-compatible parameters. | -| `game-ci orchestrate cache` | Cache inspection helpers under the direct Orchestrator command. | -| `game-ci serve` | JSON provider protocol mode used by executable provider integrations. | -| `game-ci activate` | Unity license preflight helper. | -| `game-ci status` | Local environment diagnostics. | -| `game-ci version` | Print standalone Orchestrator CLI version information. | -| `game-ci update` | Update the standalone Orchestrator binary. | - -The public CLI commands `game-ci remote run` and `game-ci test` are not standalone Orchestrator -commands. Use the public CLI when you want that higher-level command model. - ## Install ### Linux / macOS @@ -83,7 +67,6 @@ This command talks directly to the Orchestrator package. The public CLI equivale ```bash game-ci \ - --plugin @game-ci/orchestrator-plugin \ orchestrate ./my-project \ --provider-strategy aws \ --target-platform StandaloneLinux64 @@ -91,11 +74,11 @@ game-ci \ ## Relationship To Unity Builder -When you use `game-ci/unity-builder@v4` with `providerStrategy`, unity-builder loads Orchestrator as -an optional plugin. You do not need to install the standalone Orchestrator CLI in that path. +The `game-ci/orchestrator` action wraps Unity Builder and exposes Orchestrator's provider and service +inputs. You do not need to install the standalone CLI when using the action. ```yaml -- uses: game-ci/unity-builder@v4 +- uses: game-ci/orchestrator@v1.0.0 with: providerStrategy: aws targetPlatform: StandaloneLinux64 diff --git a/docs/03-github-orchestrator/08-cli/03-orchestrate-command.mdx b/docs/03-github-orchestrator/08-cli/03-orchestrate-command.mdx index ab12e99b..81a1cb9e 100644 --- a/docs/03-github-orchestrator/08-cli/03-orchestrate-command.mdx +++ b/docs/03-github-orchestrator/08-cli/03-orchestrate-command.mdx @@ -16,7 +16,7 @@ and retrieves artifacts. game-ci orchestrate [options] ``` -This is the standalone CLI equivalent of using `game-ci/unity-builder` with a `providerStrategy` in +This is the standalone CLI equivalent of using `game-ci/orchestrator` with a `providerStrategy` in GitHub Actions. It is mainly useful for provider development, debugging, and direct Orchestrator usage. The public CLI uses the same command verb, but it loads providers through the public plugin API and keeps the higher-level GameCI command model. diff --git a/docs/03-github/04-builder.mdx b/docs/03-github/04-builder.mdx index bea139b5..6ef2a6ee 100644 --- a/docs/03-github/04-builder.mdx +++ b/docs/03-github/04-builder.mdx @@ -138,13 +138,39 @@ Platform that the build should target. Must be one of the [allowed values](https://docs.unity3d.com/ScriptReference/BuildTarget.html) listed in the Unity scripting manual. -_**required:** `true`_ +_**required:** `true`_ _**example:** `StandaloneWindows64`_ + +`targetPlatform` selects the Unity build target; it does not by itself prescribe `runs-on`. +`unity-builder` supports Linux and Windows editor containers and the macOS host path. Many Mono +targets can be cross-compiled — for example, a Linux runner can produce a Windows Mono player — +while targets that need a platform SDK or native IL2CPP toolchain require a compatible host. + +Choose the runner OS together with the project's scripting backend and target requirements. A +multi-platform workflow can use separate jobs or a matrix whose `runs-on` value matches each +target/toolchain combination; see the +[Advanced IL2CPP example](/docs/github/getting-started#advanced-il2cpp-example). + +**Mono vs IL2CPP** is configured in Unity Player Settings (`Project Settings > Player > Other +Settings > Scripting Backend`), not through a `unity-builder` `scriptingBackend` input. The action +uses the runner OS, target platform, Unity version, and project configuration to select an +appropriate editor image/module. If a target's required toolchain is unavailable on the chosen +runner, move that matrix entry to a compatible OS. #### unityVersion Version of Unity to use for building the project. Use "auto" to get from your ProjectSettings/ProjectVersion.txt +The exact string from Unity Hub/the editor's own version display, including the release tag +letter and build number - e.g. `2021.3.16f1`, `2022.3.7f1`, `6000.0.23f1`. Not just the numeric +part (`2021.3.16` alone will not resolve). + +```yaml +- uses: game-ci/unity-builder@v4 + with: + unityVersion: 2021.3.16f1 +``` + _**required:** `false`_ _**default:** `auto`_ #### customImage diff --git a/docs/03-github/06-deployment/steam.mdx b/docs/03-github/06-deployment/steam.mdx index ac6d719b..385cf7cd 100644 --- a/docs/03-github/06-deployment/steam.mdx +++ b/docs/03-github/06-deployment/steam.mdx @@ -165,3 +165,91 @@ The branch within steam that this build will be automatically put live on. Note that the `default` branch [has been observed to not work](https://github.com/game-ci/steam-deploy/issues/19) as a release branch, presumably because it is potentially dangerous. + +#### extraExclusions + +Comma-separated extra file-exclusion glob patterns for the primary depot, on top of the built-in +defaults (`*.pdb`, `*.log`, `*.vdf`, and Unity's Burst debug/backup folders). Useful for anything +Valve's [depot `FileExclusion` +rules](https://partner.steamgames.com/doc/sdk/uploading#FileMapping_and_FileExclusion) need to +strip that isn't already covered. + +```yaml +- uses: game-ci/steam-deploy@v3 + with: + extraExclusions: '*.tmp,Docs/*' + # ... +``` + +:::note + +Finer-grained depot control — multiple `FileMapping` blocks per depot, and `FileProperties` +(`userconfig`/`versionedconfig`, for files a player modifies locally that updates shouldn't +overwrite) — is implemented in [game-ci/cli](https://github.com/game-ci/cli) but not yet exposed as +inputs on this action. Track [game-ci/steam-deploy#67](https://github.com/game-ci/steam-deploy/issues/67) +for progress. + +::: + +### 6. Multiple Steam accounts or apps in one workflow + +You don't need anything special for this — `username`/`configVdf`/`appId` are all per-step, so just +add one `game-ci/steam-deploy` step per account or app, each pointed at its own secrets: + +```yaml +steps: + - uses: game-ci/steam-deploy@v3 + with: + username: ${{ secrets.STEAM_USERNAME_APP1 }} + configVdf: ${{ secrets.STEAM_CONFIG_VDF_APP1 }} + appId: 111111 + rootPath: build/app1 + + - uses: game-ci/steam-deploy@v3 + with: + username: ${{ secrets.STEAM_USERNAME_APP2 }} + configVdf: ${{ secrets.STEAM_CONFIG_VDF_APP2 }} + appId: 222222 + rootPath: build/app2 +``` + +### 7. Bundling extra files with a build (e.g. the Steamworks SDK) + +Anything under the path(s) you point `rootPath`/`depot[X]Path` at gets uploaded as part of that +depot — there's no separate "extra files" input. Copy whatever you need into the build output +before the deploy step runs: + +```yaml +- run: cp -r steamworks-sdk-files/ build/StandaloneWindows64/ +- uses: game-ci/steam-deploy@v3 + with: + rootPath: build + depot1Path: StandaloneWindows64 +``` + +Use [`extraExclusions`](#extraExclusions) if you need to keep specific files out instead. + +### 8. Avoiding the artifact upload/download round-trip + +`actions/upload-artifact` + `download-artifact` (as used in the example above) is only necessary +when the build and deploy steps run in **different jobs** — jobs don't share a filesystem. If you +build and deploy in the **same job**, the build output is already on disk when the deploy step +runs, so skip the artifact steps entirely: + +```yaml +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + - uses: game-ci/unity-builder@v4 + with: + targetPlatform: StandaloneWindows64 + - uses: game-ci/steam-deploy@v3 + with: + rootPath: build + depot1Path: StandaloneWindows64 +``` + +Splitting build and deploy into separate jobs is only worth the artifact-transfer cost when you +want to parallelize multiple platform builds ahead of a single deploy job, as in the [step 2 +example](#2-add-jobs-to-mainyml) above.