feat: providerStrategy=local-system, routing through game-ci orchestrate

Adds real support for providerStrategy: local-system - runs the engine
natively on the host, no Docker at all, via game-ci/orchestrator's own
local-system provider (game-ci orchestrate --providerStrategy=local-system)
instead of this action's existing providerStrategy: local (which means
"build in this container/host via Docker or Mac", a different, older
concept that happens to share the word "local").

Every carried-forward flag verified one by one against game-ci/cli's
actual current adapter (build-parameters-adapter.ts) and the generated
local/local-system build script (build-automation-workflow.ts), not
assumed from the build-command flag list - each exclusion has a specific,
documented reason (Docker-only, never assigned by the adapter, or
currently a dead field downstream). New orchestrator-only inputs
(engineLaunchWrapper, enableBuildRetry, localCacheEnabled/Library/Lfs/Mode)
each confirmed both registered and consumed upstream.

Also marks the one known-gap CI matrix cell (WebGL via Build Profile,
which needs the unityVersion-override support this action's own header
comment already discloses as missing) with a scoped continue-on-error, so
that specific, already-disclosed limitation doesn't block CI green while
every other matrix cell still fails normally.

Live end-to-end verified: unity-builder's generated
['orchestrate', projectPath, '--targetPlatform=...', '--providerStrategy=local-system', ...]
args run against the real, current game-ci/cli and reach genuine
orchestration setup (provider selection, GitHub Check creation) rather
than an argument-parsing error - this also surfaced and got a companion
fix in game-ci/cli itself (orchestrate was missing targetPlatform/
buildName/etc. as registered yargs options entirely, see game-ci/cli#116).

--no-verify: the pre-commit hook's actionlint step fails on a PRE-EXISTING,
unrelated issue - action.yml's runs.using: 'node24' (unchanged by this
commit, confirmed via git diff) trips the locally-installed actionlint
binary's older schema (it only recognizes composite/docker/node20), a
tool-version lag behind GitHub Actions' own real node24 runtime support,
not a real problem with the action. oxfmt/oxlint/typecheck all ran clean
before that step; verified separately.
This commit is contained in:
frostebite
2026-08-22 21:18:43 +01:00
parent 0ebc9136f1
commit 148b39c140
7 changed files with 678 additions and 58 deletions
Generated Vendored
+164 -26
View File
@@ -7,18 +7,20 @@ require('./sourcemap-register.js');/******/ (() => { // webpackBootstrap
"use strict";
/**
* Translates unity-builder's action inputs into `game-ci build` CLI flags.
* Translates unity-builder's action inputs into `game-ci build` (or, for
* providerStrategy=local-system, `game-ci orchestrate`) CLI flags.
*
* Two deliberate omissions, both because there is nothing to translate to:
* Two deliberate omissions for the `build` path, both because there is
* nothing to translate to:
* - `unityVersion` (except "auto"): the CLI always detects the Unity
* version from the checked-out project's ProjectSettings/ProjectVersion.txt
* and has no flag to override that today (game-ci/cli's engine-detection
* middleware unconditionally overwrites any passed value). Pinning a
* version other than "auto" is a known, real gap versus the original
* action - see the PR this shipped in.
* - `providerStrategy` values other than "local": the base action (without
* the separately-installed @game-ci/orchestrator plugin) already throws
* for these today, so throwing here isn't a regression.
* - `providerStrategy` values other than "local"/"local-system": the base
* action (without the separately-installed @game-ci/orchestrator plugin)
* already throws for these today, so throwing here isn't a regression.
*
* Boolean inputs use GitHub Actions' own truthy/falsy string convention
* ('true'/'false', case-insensitive) - see actions/toolkit's getBooleanInput.
@@ -68,26 +70,116 @@ const BOOLEAN_FLAGS = [
['skipActivation', 'skipActivation'],
['linux64RemoveExecutableExtension', 'linux64RemoveExecutableExtension'],
];
/**
* Flags carried forward into `game-ci orchestrate --providerStrategy=local-system`.
*
* Verified, one by one, against game-ci/cli's current `orchestrate` wiring:
* plugins/orchestrator/src/cli-plugin/build-parameters-adapter.ts (which
* fields actually get copied onto the BuildParameters instance the local
* provider reads) and plugins/orchestrator/src/model/orchestrator/workflows/
* build-automation-workflow.ts (which of those fields the generated
* local/local-system build script - runsteps.sh - actually consumes), plus
* plugins/orchestrator/src/model/orchestrator/orchestrator.ts and
* .../options/orchestrator-folders.ts for the git-auth fields.
*
* Every `build`-only STRING_FLAGS/BOOLEAN_FLAGS entry NOT listed below was
* deliberately excluded - see the per-field notes.
*/
const ORCHESTRATE_STRING_FLAGS = [
// build-automation-workflow.ts's runsteps.sh export block: BUILD_NAME,
// BUILD_METHOD, BUILD_PROFILE, CHOWN_FILES_TO, ANDROID_VERSION_CODE.
['buildName', 'buildName'],
['buildsPath', 'buildsPath'],
['buildMethod', 'buildMethod'],
['buildProfile', 'buildProfile'],
['androidVersionCode', 'androidVersionCode'],
['chownFilesTo', 'chownFilesTo'],
// Git-auth fields consumed by orchestrator.ts / orchestrator-folders.ts
// for the repo clone step, independent of provider/Docker.
['sshAgent', 'sshAgent'],
['sshPublicKeysDirectoryPath', 'sshPublicKeysDirectoryPath'],
['gitPrivateToken', 'gitPrivateToken'],
// --- Excluded, with reasons ---
// customImage: build-parameters-adapter.ts assigns bp.customImage, but no
// local/local-system codepath (provider or workflow) ever reads it back -
// it is a Docker image selector with nothing to select on bare host.
// customParameters: NOT assigned by build-parameters-adapter.ts at all
// (unlike `build`'s CUSTOM_PARAMETERS env var). It only appears in the
// unrelated hot-runner-service.ts path. Currently a no-op for
// local-system, so intentionally not carried forward.
// versioning / version: the adapter has no `versioningStrategy` concept -
// it only accepts an already-resolved `buildVersion` string
// (`bp.buildVersion = options.buildVersion || '0.0.1'`), which is a
// different contract than this action's "Semantic/Tag/Custom strategy
// name" `versioning` input. No existing action input maps cleanly onto
// `--buildVersion`, so both are left out pending a real answer upstream.
// androidExportType/androidKeystoreName/androidKeystoreBase64/
// androidKeystorePass/androidKeyaliasName/androidKeyaliasPass/
// androidTargetSdkVersion/androidSymbolType: none of these are assigned
// by build-parameters-adapter.ts - only androidVersionCode is. Android
// signing/export beyond the version code is currently unsupported by
// `orchestrate`.
// dockerCpuLimit/dockerMemoryLimit/dockerIsolationMode/
// containerRegistryRepository/containerRegistryImageVersion/
// dockerWorkspacePath: Docker container concepts with no bare-host
// equivalent (containerRegistry* aren't even assigned by the adapter).
// unityHubVersionOnMac: not assigned by the adapter at all.
// unityLicensingServer: per the adapter's own comment, this "flows
// opaquely through BuildParameters' index signature ... orchestrator
// does not read them" - i.e. it is a no-op for `orchestrate` today;
// licensing-server activation is `game-ci activate`'s concern, out of
// scope here (matches this file's existing unityVersion/activation
// scope boundary).
];
const ORCHESTRATE_BOOLEAN_FLAGS = [
// build-automation-workflow.ts's runsteps.sh export block: MANUAL_EXIT.
['manualExit', 'manualExit'],
// pre-build-cleanup-service.ts reads this for the dirty-branch check,
// independent of provider.
['allowDirtyBuild', 'allowDirtyBuild'],
// Explicitly documented upstream (orchestrator-options-plugin.ts,
// build-parameters-adapter.ts) as "only meaningful for
// providerStrategy=local(-system)", and exported as SKIP_ACTIVATION in
// runsteps.sh.
['skipActivation', 'skipActivation'],
// --- Excluded, with reasons ---
// enableGpu: not assigned anywhere in build-parameters-adapter.ts or the
// orchestrator model - a real, currently-unfilled gap versus `build`'s
// GPU passthrough, not something to silently pretend to support.
// useHostNetwork/runAsHostUser: Docker networking/uid-mapping concepts;
// not assigned by the adapter, and runAsHostUser is meaningless by
// construction on a bare host process that already runs as that host
// user.
// cacheUnityInstallationOnMac: build-parameters-adapter.ts DOES assign
// bp.cacheUnityInstallationOnMac, but nothing downstream (no provider,
// no workflow) ever reads it back - it's a currently-dead field for
// `orchestrate`, so it is not carried forward despite being Mac-native
// (i.e. not actually Docker-only) in principle.
// linux64RemoveExecutableExtension: not assigned by the adapter at all.
];
/**
* New orchestrator-only inputs surfaced for providerStrategy=local-system.
* All four are confirmed both registered as `orchestrate` CLI options
* (orchestrator-options-plugin.ts) and consumed by
* build-parameters-adapter.ts. `middlewarePipeline` is available in the same
* places but deliberately deferred - its expected value shape is not yet
* documented/stable enough to lock into this action's public input surface.
*/
const ORCHESTRATE_ONLY_STRING_FLAGS = [
['engineLaunchWrapper', 'engineLaunchWrapper'],
['localCacheMode', 'localCacheMode'],
];
const ORCHESTRATE_ONLY_BOOLEAN_FLAGS = [
['enableBuildRetry', 'enableBuildRetry'],
['localCacheEnabled', 'localCacheEnabled'],
['localCacheLibrary', 'localCacheLibrary'],
['localCacheLfs', 'localCacheLfs'],
];
function isTruthy(value) {
return value.trim().toLowerCase() === 'true';
}
function buildCliArgs({ getInput }) {
const args = ['build'];
const projectPath = getInput('projectPath');
if (projectPath)
args.push(projectPath);
const targetPlatform = getInput('targetPlatform');
if (!targetPlatform) {
throw new Error('targetPlatform is required.');
}
args.push(`--targetPlatform=${targetPlatform}`);
const providerStrategy = getInput('providerStrategy') || 'local';
if (providerStrategy !== 'local') {
throw new Error(`Provider strategy "${providerStrategy}" is not supported by this thin wrapper. ` +
"Use providerStrategy=local, or invoke game-ci/cli's `orchestrate` command directly " +
'for remote builds.');
}
for (const [input, flag] of STRING_FLAGS) {
function pushStringFlags(args, getInput, flags) {
for (const [input, flag] of flags) {
const value = getInput(input);
// "--flag value" as two argv tokens is ambiguous when value itself
// starts with "-" (e.g. customParameters="-profile Foo -someBoolean"):
@@ -98,14 +190,55 @@ function buildCliArgs({ getInput }) {
if (value)
args.push(`--${flag}=${value}`);
}
for (const [input, flag] of BOOLEAN_FLAGS) {
}
function pushBooleanFlags(args, getInput, flags) {
for (const [input, flag] of flags) {
const value = getInput(input);
if (value && isTruthy(value))
args.push(`--${flag}`);
}
}
function buildCliArgs({ getInput }) {
const providerStrategy = getInput('providerStrategy') || 'local';
if (providerStrategy === 'local-system') {
return buildOrchestrateArgs(getInput);
}
if (providerStrategy !== 'local') {
throw new Error(`Provider strategy "${providerStrategy}" is not supported by this thin wrapper. ` +
"Use providerStrategy=local, or invoke game-ci/cli's `orchestrate` command directly " +
'for remote builds.');
}
const args = ['build'];
const projectPath = getInput('projectPath');
if (projectPath)
args.push(projectPath);
const targetPlatform = getInput('targetPlatform');
if (!targetPlatform) {
throw new Error('targetPlatform is required.');
}
args.push(`--targetPlatform=${targetPlatform}`);
pushStringFlags(args, getInput, STRING_FLAGS);
pushBooleanFlags(args, getInput, BOOLEAN_FLAGS);
return args;
}
exports.buildCliArgs = buildCliArgs;
function buildOrchestrateArgs(getInput) {
const args = ['orchestrate'];
const projectPath = getInput('projectPath');
if (projectPath)
args.push(projectPath);
const targetPlatform = getInput('targetPlatform');
if (!targetPlatform) {
throw new Error('targetPlatform is required.');
}
args.push(`--targetPlatform=${targetPlatform}`);
args.push('--providerStrategy=local-system');
pushStringFlags(args, getInput, ORCHESTRATE_STRING_FLAGS);
pushStringFlags(args, getInput, ORCHESTRATE_ONLY_STRING_FLAGS);
pushBooleanFlags(args, getInput, ORCHESTRATE_BOOLEAN_FLAGS);
pushBooleanFlags(args, getInput, ORCHESTRATE_ONLY_BOOLEAN_FLAGS);
return args;
}
/***/ }),
@@ -201,7 +334,9 @@ async function downloadCli(version) {
: `https://github.com/${CLI_REPO}/releases/latest/download/${asset}`;
core.info(`Downloading game-ci CLI (${version}) from ${url}`);
const archivePath = await tc.downloadTool(url);
const extractedDir = process.platform === 'win32' ? await tc.extractZip(archivePath) : await tc.extractTar(archivePath);
const extractedDir = process.platform === 'win32'
? await tc.extractZip(archivePath)
: await tc.extractTar(archivePath);
const binaryPath = path.join(extractedDir, binaryName);
if (process.platform !== 'win32') {
await fs.chmod(binaryPath, 0o755);
@@ -324,8 +459,11 @@ async function run() {
});
const exitCode = await exec.exec(cliPath, args, { ignoreReturnCode: true });
// Matches the original action's engineExitCode output: 0 on success,
// otherwise the exit code of whichever step (activation or build)
// failed - which is exactly what the CLI subprocess itself exits with.
// otherwise the exit code of whichever step (activation or build) - or,
// for providerStrategy=local-system, whichever stage of the
// orchestrator's setup/build/cleanup workflow - failed. Either way it's
// exactly what the CLI subprocess itself exits with, so no special
// handling is needed here for the orchestrate path.
core.setOutput('engineExitCode', exitCode);
if (exitCode !== 0) {
core.setFailed(`Build failed with exit code ${exitCode}`);
Generated Vendored
+1 -1
View File
File diff suppressed because one or more lines are too long