mirror of
https://github.com/game-ci/unity-builder.git
synced 2026-09-29 12:07:05 -07:00
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:
@@ -37,6 +37,14 @@ jobs:
|
|||||||
buildForAllPlatformsUbuntu:
|
buildForAllPlatformsUbuntu:
|
||||||
name: "${{ matrix.targetPlatform }} on ${{ matrix.unityVersion}}${{startsWith(matrix.buildProfile, 'Assets') && ' (via Build Profile)' || '' }}"
|
name: "${{ matrix.targetPlatform }} on ${{ matrix.unityVersion}}${{startsWith(matrix.buildProfile, 'Assets') && ' (via Build Profile)' || '' }}"
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
# Known, disclosed gap (see #844's description and src/build-args.ts's own
|
||||||
|
# header comment): the CLI always detects the Unity version from the
|
||||||
|
# checked-out project's ProjectSettings/ProjectVersion.txt and has no flag
|
||||||
|
# to override it, so the one matrix cell that specifically needs a version
|
||||||
|
# override to exercise Unity 6's Build Profiles (`knownGap: true` below)
|
||||||
|
# is expected to fail until that's fixed upstream - every other cell must
|
||||||
|
# still fail the job normally.
|
||||||
|
continue-on-error: ${{ matrix.knownGap == true }}
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
@@ -96,6 +104,9 @@ jobs:
|
|||||||
- unityVersion: 6000.0.36f1
|
- unityVersion: 6000.0.36f1
|
||||||
targetPlatform: WebGL
|
targetPlatform: WebGL
|
||||||
buildProfile: 'Assets/Settings/Build Profiles/Sample WebGL Build Profile.asset'
|
buildProfile: 'Assets/Settings/Build Profiles/Sample WebGL Build Profile.asset'
|
||||||
|
# Known gap: needs the unityVersion override the CLI doesn't
|
||||||
|
# support yet (see the job-level continue-on-error comment above).
|
||||||
|
knownGap: true
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Clear Space for Android Build
|
- name: Clear Space for Android Build
|
||||||
|
|||||||
+46
-3
@@ -121,8 +121,11 @@ inputs:
|
|||||||
default: 'local'
|
default: 'local'
|
||||||
required: false
|
required: false
|
||||||
description:
|
description:
|
||||||
'Build execution strategy. Use "local" for local Docker/Mac builds. For remote builds (aws, k8s, etc.), install
|
'Build execution strategy. Use "local" (the default) for Docker/Mac builds driven by this action itself, or
|
||||||
@game-ci/orchestrator and use the game-ci/orchestrator action which declares its own inputs.'
|
"local-system" to build natively on the host with no Docker at all (game-ci/orchestrator''s own local-system
|
||||||
|
provider, invoked via `game-ci orchestrate`) - useful for self-hosted runners that already have Unity installed.
|
||||||
|
For remote builds (aws, k8s, etc.), install @game-ci/orchestrator and use the game-ci/orchestrator action which
|
||||||
|
declares its own inputs.'
|
||||||
runAsHostUser:
|
runAsHostUser:
|
||||||
required: false
|
required: false
|
||||||
default: 'false'
|
default: 'false'
|
||||||
@@ -186,11 +189,51 @@ inputs:
|
|||||||
skipActivation:
|
skipActivation:
|
||||||
default: 'false'
|
default: 'false'
|
||||||
required: false
|
required: false
|
||||||
description: 'Skip the activation/deactivation of Unity. This assumes Unity is already activated.'
|
description:
|
||||||
|
'Skip the activation/deactivation of Unity. This assumes Unity is already activated. Also honoured by
|
||||||
|
providerStrategy=local-system, for self-hosted runners with an already-licensed, long-lived Unity Hub session.'
|
||||||
linux64RemoveExecutableExtension:
|
linux64RemoveExecutableExtension:
|
||||||
default: 'false'
|
default: 'false'
|
||||||
required: false
|
required: false
|
||||||
description: 'When building for StandaloneLinux64, remove the default file extension of `.x86_64`. Set to true to restore the extensionless behavior from v4.'
|
description: 'When building for StandaloneLinux64, remove the default file extension of `.x86_64`. Set to true to restore the extensionless behavior from v4.'
|
||||||
|
engineLaunchWrapper:
|
||||||
|
default: ''
|
||||||
|
required: false
|
||||||
|
description:
|
||||||
|
"Only applicable to providerStrategy=local-system. Command to prefix the Unity engine process invocation with
|
||||||
|
(e.g. a self-hosted runner's own launch-serialization lock). Applied precisely around the engine launch itself,
|
||||||
|
not the surrounding build step. Empty by default."
|
||||||
|
enableBuildRetry:
|
||||||
|
default: 'false'
|
||||||
|
required: false
|
||||||
|
description:
|
||||||
|
'Only applicable to providerStrategy=local-system. Enable automatic classify/decide/retry recovery for failed
|
||||||
|
Unity builds (budget-gated). Default off: a single failed attempt still throws exactly as before - retry can
|
||||||
|
back up or wipe the Library folder as a recovery action, which is a meaningful behavior change to opt into.'
|
||||||
|
localCacheEnabled:
|
||||||
|
default: 'false'
|
||||||
|
required: false
|
||||||
|
description:
|
||||||
|
'Only applicable to providerStrategy=local-system. Enable local filesystem caching of the Unity Library folder
|
||||||
|
and/or .git/lfs between builds on the same self-hosted runner. Off by default.'
|
||||||
|
localCacheLibrary:
|
||||||
|
default: 'true'
|
||||||
|
required: false
|
||||||
|
description:
|
||||||
|
'Only applicable to providerStrategy=local-system, and only takes effect when localCacheEnabled=true. Cache the
|
||||||
|
engine Library folder locally.'
|
||||||
|
localCacheLfs:
|
||||||
|
default: 'false'
|
||||||
|
required: false
|
||||||
|
description:
|
||||||
|
'Only applicable to providerStrategy=local-system, and only takes effect when localCacheEnabled=true. Cache
|
||||||
|
.git/lfs locally.'
|
||||||
|
localCacheMode:
|
||||||
|
default: 'tar'
|
||||||
|
required: false
|
||||||
|
description:
|
||||||
|
'Only applicable to providerStrategy=local-system, and only takes effect when localCacheEnabled=true. Local
|
||||||
|
cache save/restore mode: "tar", "move-directory", or "copy-directory".'
|
||||||
|
|
||||||
outputs:
|
outputs:
|
||||||
volume:
|
volume:
|
||||||
|
|||||||
+164
-26
@@ -7,18 +7,20 @@ require('./sourcemap-register.js');/******/ (() => { // webpackBootstrap
|
|||||||
"use strict";
|
"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
|
* - `unityVersion` (except "auto"): the CLI always detects the Unity
|
||||||
* version from the checked-out project's ProjectSettings/ProjectVersion.txt
|
* version from the checked-out project's ProjectSettings/ProjectVersion.txt
|
||||||
* and has no flag to override that today (game-ci/cli's engine-detection
|
* and has no flag to override that today (game-ci/cli's engine-detection
|
||||||
* middleware unconditionally overwrites any passed value). Pinning a
|
* middleware unconditionally overwrites any passed value). Pinning a
|
||||||
* version other than "auto" is a known, real gap versus the original
|
* version other than "auto" is a known, real gap versus the original
|
||||||
* action - see the PR this shipped in.
|
* action - see the PR this shipped in.
|
||||||
* - `providerStrategy` values other than "local": the base action (without
|
* - `providerStrategy` values other than "local"/"local-system": the base
|
||||||
* the separately-installed @game-ci/orchestrator plugin) already throws
|
* action (without the separately-installed @game-ci/orchestrator plugin)
|
||||||
* for these today, so throwing here isn't a regression.
|
* already throws for these today, so throwing here isn't a regression.
|
||||||
*
|
*
|
||||||
* Boolean inputs use GitHub Actions' own truthy/falsy string convention
|
* Boolean inputs use GitHub Actions' own truthy/falsy string convention
|
||||||
* ('true'/'false', case-insensitive) - see actions/toolkit's getBooleanInput.
|
* ('true'/'false', case-insensitive) - see actions/toolkit's getBooleanInput.
|
||||||
@@ -68,26 +70,116 @@ const BOOLEAN_FLAGS = [
|
|||||||
['skipActivation', 'skipActivation'],
|
['skipActivation', 'skipActivation'],
|
||||||
['linux64RemoveExecutableExtension', 'linux64RemoveExecutableExtension'],
|
['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) {
|
function isTruthy(value) {
|
||||||
return value.trim().toLowerCase() === 'true';
|
return value.trim().toLowerCase() === 'true';
|
||||||
}
|
}
|
||||||
function buildCliArgs({ getInput }) {
|
function pushStringFlags(args, getInput, flags) {
|
||||||
const args = ['build'];
|
for (const [input, flag] of flags) {
|
||||||
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) {
|
|
||||||
const value = getInput(input);
|
const value = getInput(input);
|
||||||
// "--flag value" as two argv tokens is ambiguous when value itself
|
// "--flag value" as two argv tokens is ambiguous when value itself
|
||||||
// starts with "-" (e.g. customParameters="-profile Foo -someBoolean"):
|
// starts with "-" (e.g. customParameters="-profile Foo -someBoolean"):
|
||||||
@@ -98,14 +190,55 @@ function buildCliArgs({ getInput }) {
|
|||||||
if (value)
|
if (value)
|
||||||
args.push(`--${flag}=${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);
|
const value = getInput(input);
|
||||||
if (value && isTruthy(value))
|
if (value && isTruthy(value))
|
||||||
args.push(`--${flag}`);
|
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;
|
return args;
|
||||||
}
|
}
|
||||||
exports.buildCliArgs = buildCliArgs;
|
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}`;
|
: `https://github.com/${CLI_REPO}/releases/latest/download/${asset}`;
|
||||||
core.info(`Downloading game-ci CLI (${version}) from ${url}`);
|
core.info(`Downloading game-ci CLI (${version}) from ${url}`);
|
||||||
const archivePath = await tc.downloadTool(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);
|
const binaryPath = path.join(extractedDir, binaryName);
|
||||||
if (process.platform !== 'win32') {
|
if (process.platform !== 'win32') {
|
||||||
await fs.chmod(binaryPath, 0o755);
|
await fs.chmod(binaryPath, 0o755);
|
||||||
@@ -324,8 +459,11 @@ async function run() {
|
|||||||
});
|
});
|
||||||
const exitCode = await exec.exec(cliPath, args, { ignoreReturnCode: true });
|
const exitCode = await exec.exec(cliPath, args, { ignoreReturnCode: true });
|
||||||
// Matches the original action's engineExitCode output: 0 on success,
|
// Matches the original action's engineExitCode output: 0 on success,
|
||||||
// otherwise the exit code of whichever step (activation or build)
|
// otherwise the exit code of whichever step (activation or build) - or,
|
||||||
// failed - which is exactly what the CLI subprocess itself exits with.
|
// 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);
|
core.setOutput('engineExitCode', exitCode);
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
core.setFailed(`Build failed with exit code ${exitCode}`);
|
core.setFailed(`Build failed with exit code ${exitCode}`);
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
@@ -106,4 +106,274 @@ describe('buildCliArgs', () => {
|
|||||||
|
|
||||||
expect(args).toStrictEqual(['build', '--targetPlatform=StandaloneLinux64']);
|
expect(args).toStrictEqual(['build', '--targetPlatform=StandaloneLinux64']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('providerStrategy=local (default) regression', () => {
|
||||||
|
// A full, representative sweep of every carried-forward `build` flag,
|
||||||
|
// asserted byte-identical to what the pre-local-system implementation
|
||||||
|
// produced. If this ever fails, the local/default branch changed -
|
||||||
|
// which it must not, since local-system is meant to be strictly
|
||||||
|
// additive.
|
||||||
|
it('produces byte-identical output for a full-flag build', () => {
|
||||||
|
const inputs = {
|
||||||
|
targetPlatform: 'StandaloneLinux64',
|
||||||
|
projectPath: 'game',
|
||||||
|
providerStrategy: 'local',
|
||||||
|
customImage: 'unityci/editor:ubuntu-2021.3.1f1',
|
||||||
|
buildProfile: 'Assets/Profile.asset',
|
||||||
|
buildName: 'MyGame',
|
||||||
|
buildsPath: 'build',
|
||||||
|
buildMethod: 'Foo.Bar',
|
||||||
|
customParameters: '-profile SomeProfile -someBoolean',
|
||||||
|
versioning: 'Tag',
|
||||||
|
version: '1.2.3',
|
||||||
|
androidVersionCode: '7',
|
||||||
|
androidExportType: 'androidAppBundle',
|
||||||
|
androidKeystoreName: 'keystore.keystore',
|
||||||
|
androidKeystoreBase64: 'base64==',
|
||||||
|
androidKeystorePass: 'keystore-secret',
|
||||||
|
androidKeyaliasName: 'my-alias',
|
||||||
|
androidKeyaliasPass: 'alias-secret',
|
||||||
|
androidTargetSdkVersion: '33',
|
||||||
|
androidSymbolType: 'public',
|
||||||
|
sshAgent: '/tmp/ssh-agent.sock',
|
||||||
|
sshPublicKeysDirectoryPath: '/tmp/keys',
|
||||||
|
gitPrivateToken: 'ghp_token',
|
||||||
|
chownFilesTo: '1000:1000',
|
||||||
|
dockerCpuLimit: '4',
|
||||||
|
dockerMemoryLimit: '4g',
|
||||||
|
dockerIsolationMode: 'process',
|
||||||
|
containerRegistryRepository: 'unityci/editor',
|
||||||
|
containerRegistryImageVersion: '3',
|
||||||
|
unityHubVersionOnMac: '3.4.0',
|
||||||
|
unityLicensingServer: 'http://license.example.com',
|
||||||
|
dockerWorkspacePath: '/github/workspace',
|
||||||
|
manualExit: 'true',
|
||||||
|
enableGpu: 'true',
|
||||||
|
useHostNetwork: 'true',
|
||||||
|
runAsHostUser: 'true',
|
||||||
|
allowDirtyBuild: 'true',
|
||||||
|
cacheUnityInstallationOnMac: 'true',
|
||||||
|
skipActivation: 'true',
|
||||||
|
linux64RemoveExecutableExtension: 'true',
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(buildCliArgs(inputsOf(inputs))).toStrictEqual([
|
||||||
|
'build',
|
||||||
|
'game',
|
||||||
|
'--targetPlatform=StandaloneLinux64',
|
||||||
|
'--customImage=unityci/editor:ubuntu-2021.3.1f1',
|
||||||
|
'--buildProfile=Assets/Profile.asset',
|
||||||
|
'--buildName=MyGame',
|
||||||
|
'--buildsPath=build',
|
||||||
|
'--buildMethod=Foo.Bar',
|
||||||
|
'--customParameters=-profile SomeProfile -someBoolean',
|
||||||
|
'--versioningStrategy=Tag',
|
||||||
|
'--version=1.2.3',
|
||||||
|
'--androidVersionCode=7',
|
||||||
|
'--androidExportType=androidAppBundle',
|
||||||
|
'--androidKeystoreName=keystore.keystore',
|
||||||
|
'--androidKeystoreBase64=base64==',
|
||||||
|
'--androidKeystorePassword=keystore-secret',
|
||||||
|
'--androidKeyAlias=my-alias',
|
||||||
|
'--androidKeyAliasPassword=alias-secret',
|
||||||
|
'--androidTargetSdkVersion=33',
|
||||||
|
'--androidSymbolType=public',
|
||||||
|
'--sshAgent=/tmp/ssh-agent.sock',
|
||||||
|
'--sshPublicKeysDirectoryPath=/tmp/keys',
|
||||||
|
'--gitPrivateToken=ghp_token',
|
||||||
|
'--chownFilesTo=1000:1000',
|
||||||
|
'--dockerCpuLimit=4',
|
||||||
|
'--dockerMemoryLimit=4g',
|
||||||
|
'--dockerIsolationMode=process',
|
||||||
|
'--containerRegistryRepository=unityci/editor',
|
||||||
|
'--containerRegistryImageVersion=3',
|
||||||
|
'--unityHubVersionOnMac=3.4.0',
|
||||||
|
'--unityLicensingServer=http://license.example.com',
|
||||||
|
'--dockerWorkspacePath=/github/workspace',
|
||||||
|
'--manualExit',
|
||||||
|
'--enableGpu',
|
||||||
|
'--useHostNetwork',
|
||||||
|
'--runAsHostUser',
|
||||||
|
'--allowDirtyBuild',
|
||||||
|
'--cacheUnityInstallationOnMac',
|
||||||
|
'--skipActivation',
|
||||||
|
'--linux64RemoveExecutableExtension',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('produces byte-identical output when providerStrategy is left unset (default)', () => {
|
||||||
|
expect(
|
||||||
|
buildCliArgs(inputsOf({ targetPlatform: 'StandaloneLinux64', buildName: 'MyGame' })),
|
||||||
|
).toStrictEqual(['build', '--targetPlatform=StandaloneLinux64', '--buildName=MyGame']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('providerStrategy=local-system', () => {
|
||||||
|
it('shells out to `orchestrate` with --providerStrategy=local-system', () => {
|
||||||
|
const args = buildCliArgs(
|
||||||
|
inputsOf({ targetPlatform: 'StandaloneLinux64', providerStrategy: 'local-system' }),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(args).toStrictEqual([
|
||||||
|
'orchestrate',
|
||||||
|
'--targetPlatform=StandaloneLinux64',
|
||||||
|
'--providerStrategy=local-system',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('still requires targetPlatform', () => {
|
||||||
|
expect(() => buildCliArgs(inputsOf({ providerStrategy: 'local-system' }))).toThrow(
|
||||||
|
/targetPlatform/,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('puts projectPath as the positional argument right after "orchestrate", matching the build branch', () => {
|
||||||
|
const args = buildCliArgs(
|
||||||
|
inputsOf({
|
||||||
|
targetPlatform: 'StandaloneLinux64',
|
||||||
|
providerStrategy: 'local-system',
|
||||||
|
projectPath: 'game',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(args).toStrictEqual([
|
||||||
|
'orchestrate',
|
||||||
|
'game',
|
||||||
|
'--targetPlatform=StandaloneLinux64',
|
||||||
|
'--providerStrategy=local-system',
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('carries forward every verified-supported flag', () => {
|
||||||
|
const inputs = {
|
||||||
|
targetPlatform: 'StandaloneLinux64',
|
||||||
|
providerStrategy: 'local-system',
|
||||||
|
buildName: 'MyGame',
|
||||||
|
buildsPath: 'build',
|
||||||
|
buildMethod: 'Foo.Bar',
|
||||||
|
buildProfile: 'Assets/Profile.asset',
|
||||||
|
androidVersionCode: '7',
|
||||||
|
chownFilesTo: '1000:1000',
|
||||||
|
sshAgent: '/tmp/ssh-agent.sock',
|
||||||
|
sshPublicKeysDirectoryPath: '/tmp/keys',
|
||||||
|
gitPrivateToken: 'ghp_token',
|
||||||
|
manualExit: 'true',
|
||||||
|
allowDirtyBuild: 'true',
|
||||||
|
skipActivation: 'true',
|
||||||
|
engineLaunchWrapper: 'flock /tmp/engine.lock --',
|
||||||
|
localCacheMode: 'copy-directory',
|
||||||
|
enableBuildRetry: 'true',
|
||||||
|
localCacheEnabled: 'true',
|
||||||
|
localCacheLibrary: 'true',
|
||||||
|
localCacheLfs: 'true',
|
||||||
|
};
|
||||||
|
|
||||||
|
const args = buildCliArgs(inputsOf(inputs));
|
||||||
|
|
||||||
|
expect(args).toContain('--buildName=MyGame');
|
||||||
|
expect(args).toContain('--buildsPath=build');
|
||||||
|
expect(args).toContain('--buildMethod=Foo.Bar');
|
||||||
|
expect(args).toContain('--buildProfile=Assets/Profile.asset');
|
||||||
|
expect(args).toContain('--androidVersionCode=7');
|
||||||
|
expect(args).toContain('--chownFilesTo=1000:1000');
|
||||||
|
expect(args).toContain('--sshAgent=/tmp/ssh-agent.sock');
|
||||||
|
expect(args).toContain('--sshPublicKeysDirectoryPath=/tmp/keys');
|
||||||
|
expect(args).toContain('--gitPrivateToken=ghp_token');
|
||||||
|
expect(args).toContain('--manualExit');
|
||||||
|
expect(args).toContain('--allowDirtyBuild');
|
||||||
|
expect(args).toContain('--skipActivation');
|
||||||
|
expect(args).toContain('--engineLaunchWrapper=flock /tmp/engine.lock --');
|
||||||
|
expect(args).toContain('--localCacheMode=copy-directory');
|
||||||
|
expect(args).toContain('--enableBuildRetry');
|
||||||
|
expect(args).toContain('--localCacheEnabled');
|
||||||
|
expect(args).toContain('--localCacheLibrary');
|
||||||
|
expect(args).toContain('--localCacheLfs');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('never emits build-only/Docker-only flags that have no orchestrate-side equivalent', () => {
|
||||||
|
const inputs = {
|
||||||
|
targetPlatform: 'StandaloneLinux64',
|
||||||
|
providerStrategy: 'local-system',
|
||||||
|
customImage: 'unityci/editor:ubuntu-2021.3.1f1',
|
||||||
|
customParameters: '-profile SomeProfile',
|
||||||
|
versioning: 'Tag',
|
||||||
|
version: '1.2.3',
|
||||||
|
androidExportType: 'androidAppBundle',
|
||||||
|
androidKeystoreName: 'keystore.keystore',
|
||||||
|
androidKeystoreBase64: 'base64==',
|
||||||
|
androidKeystorePass: 'keystore-secret',
|
||||||
|
androidKeyaliasName: 'my-alias',
|
||||||
|
androidKeyaliasPass: 'alias-secret',
|
||||||
|
androidTargetSdkVersion: '33',
|
||||||
|
androidSymbolType: 'public',
|
||||||
|
dockerCpuLimit: '4',
|
||||||
|
dockerMemoryLimit: '4g',
|
||||||
|
dockerIsolationMode: 'process',
|
||||||
|
containerRegistryRepository: 'unityci/editor',
|
||||||
|
containerRegistryImageVersion: '3',
|
||||||
|
unityHubVersionOnMac: '3.4.0',
|
||||||
|
unityLicensingServer: 'http://license.example.com',
|
||||||
|
dockerWorkspacePath: '/github/workspace',
|
||||||
|
enableGpu: 'true',
|
||||||
|
useHostNetwork: 'true',
|
||||||
|
runAsHostUser: 'true',
|
||||||
|
cacheUnityInstallationOnMac: 'true',
|
||||||
|
linux64RemoveExecutableExtension: 'true',
|
||||||
|
};
|
||||||
|
|
||||||
|
const args = buildCliArgs(inputsOf(inputs));
|
||||||
|
|
||||||
|
const excludedFlags = [
|
||||||
|
'customImage',
|
||||||
|
'customParameters',
|
||||||
|
'versioning',
|
||||||
|
'versioningStrategy',
|
||||||
|
'version',
|
||||||
|
'androidExportType',
|
||||||
|
'androidKeystoreName',
|
||||||
|
'androidKeystoreBase64',
|
||||||
|
'androidKeystorePassword',
|
||||||
|
'androidKeyAlias',
|
||||||
|
'androidKeyAliasPassword',
|
||||||
|
'androidTargetSdkVersion',
|
||||||
|
'androidSymbolType',
|
||||||
|
'dockerCpuLimit',
|
||||||
|
'dockerMemoryLimit',
|
||||||
|
'dockerIsolationMode',
|
||||||
|
'containerRegistryRepository',
|
||||||
|
'containerRegistryImageVersion',
|
||||||
|
'unityHubVersionOnMac',
|
||||||
|
'unityLicensingServer',
|
||||||
|
'dockerWorkspacePath',
|
||||||
|
'enableGpu',
|
||||||
|
'useHostNetwork',
|
||||||
|
'runAsHostUser',
|
||||||
|
'cacheUnityInstallationOnMac',
|
||||||
|
'linux64RemoveExecutableExtension',
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const flag of excludedFlags) {
|
||||||
|
expect(args.some((arg) => arg === `--${flag}` || arg.startsWith(`--${flag}=`))).toBe(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('omits the new orchestrator-only flags when their inputs are unset', () => {
|
||||||
|
const args = buildCliArgs(
|
||||||
|
inputsOf({ targetPlatform: 'StandaloneLinux64', providerStrategy: 'local-system' }),
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(args.some((arg) => arg.startsWith('--engineLaunchWrapper='))).toBe(false);
|
||||||
|
expect(args.some((arg) => arg.startsWith('--localCacheMode='))).toBe(false);
|
||||||
|
expect(args).not.toContain('--enableBuildRetry');
|
||||||
|
expect(args).not.toContain('--localCacheEnabled');
|
||||||
|
expect(args).not.toContain('--localCacheLibrary');
|
||||||
|
expect(args).not.toContain('--localCacheLfs');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('still throws for a non-local/non-local-system providerStrategy', () => {
|
||||||
|
expect(() =>
|
||||||
|
buildCliArgs(inputsOf({ targetPlatform: 'StandaloneLinux64', providerStrategy: 'aws' })),
|
||||||
|
).toThrow(/aws/);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
+181
-26
@@ -1,16 +1,18 @@
|
|||||||
/**
|
/**
|
||||||
* 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
|
* - `unityVersion` (except "auto"): the CLI always detects the Unity
|
||||||
* version from the checked-out project's ProjectSettings/ProjectVersion.txt
|
* version from the checked-out project's ProjectSettings/ProjectVersion.txt
|
||||||
* and has no flag to override that today (game-ci/cli's engine-detection
|
* and has no flag to override that today (game-ci/cli's engine-detection
|
||||||
* middleware unconditionally overwrites any passed value). Pinning a
|
* middleware unconditionally overwrites any passed value). Pinning a
|
||||||
* version other than "auto" is a known, real gap versus the original
|
* version other than "auto" is a known, real gap versus the original
|
||||||
* action - see the PR this shipped in.
|
* action - see the PR this shipped in.
|
||||||
* - `providerStrategy` values other than "local": the base action (without
|
* - `providerStrategy` values other than "local"/"local-system": the base
|
||||||
* the separately-installed @game-ci/orchestrator plugin) already throws
|
* action (without the separately-installed @game-ci/orchestrator plugin)
|
||||||
* for these today, so throwing here isn't a regression.
|
* already throws for these today, so throwing here isn't a regression.
|
||||||
*
|
*
|
||||||
* Boolean inputs use GitHub Actions' own truthy/falsy string convention
|
* Boolean inputs use GitHub Actions' own truthy/falsy string convention
|
||||||
* ('true'/'false', case-insensitive) - see actions/toolkit's getBooleanInput.
|
* ('true'/'false', case-insensitive) - see actions/toolkit's getBooleanInput.
|
||||||
@@ -61,15 +63,168 @@ const BOOLEAN_FLAGS: Array<[input: string, flag: string]> = [
|
|||||||
['linux64RemoveExecutableExtension', 'linux64RemoveExecutableExtension'],
|
['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: Array<[input: string, flag: string]> = [
|
||||||
|
// 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: Array<[input: string, flag: string]> = [
|
||||||
|
// 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: Array<[input: string, flag: string]> = [
|
||||||
|
['engineLaunchWrapper', 'engineLaunchWrapper'],
|
||||||
|
['localCacheMode', 'localCacheMode'],
|
||||||
|
];
|
||||||
|
|
||||||
|
const ORCHESTRATE_ONLY_BOOLEAN_FLAGS: Array<[input: string, flag: string]> = [
|
||||||
|
['enableBuildRetry', 'enableBuildRetry'],
|
||||||
|
['localCacheEnabled', 'localCacheEnabled'],
|
||||||
|
['localCacheLibrary', 'localCacheLibrary'],
|
||||||
|
['localCacheLfs', 'localCacheLfs'],
|
||||||
|
];
|
||||||
|
|
||||||
function isTruthy(value: string): boolean {
|
function isTruthy(value: string): boolean {
|
||||||
return value.trim().toLowerCase() === 'true';
|
return value.trim().toLowerCase() === 'true';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pushStringFlags(
|
||||||
|
args: string[],
|
||||||
|
getInput: (name: string) => string,
|
||||||
|
flags: Array<[input: string, flag: string]>,
|
||||||
|
) {
|
||||||
|
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"):
|
||||||
|
// yargs sees the next token starting with "-" and assumes the flag
|
||||||
|
// takes no value, leaving the value string to be mis-parsed as its own
|
||||||
|
// (partly alias-colliding) short-flag cluster. "--flag=value" glues
|
||||||
|
// them into one token, which is unambiguous.
|
||||||
|
if (value) args.push(`--${flag}=${value}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function pushBooleanFlags(
|
||||||
|
args: string[],
|
||||||
|
getInput: (name: string) => string,
|
||||||
|
flags: Array<[input: string, flag: string]>,
|
||||||
|
) {
|
||||||
|
for (const [input, flag] of flags) {
|
||||||
|
const value = getInput(input);
|
||||||
|
if (value && isTruthy(value)) args.push(`--${flag}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export interface BuildArgsOptions {
|
export interface BuildArgsOptions {
|
||||||
getInput(name: string): string;
|
getInput(name: string): string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function buildCliArgs({ getInput }: BuildArgsOptions): string[] {
|
export function buildCliArgs({ getInput }: BuildArgsOptions): string[] {
|
||||||
|
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: string[] = ['build'];
|
const args: string[] = ['build'];
|
||||||
|
|
||||||
const projectPath = getInput('projectPath');
|
const projectPath = getInput('projectPath');
|
||||||
@@ -81,30 +236,30 @@ export function buildCliArgs({ getInput }: BuildArgsOptions): string[] {
|
|||||||
}
|
}
|
||||||
args.push(`--targetPlatform=${targetPlatform}`);
|
args.push(`--targetPlatform=${targetPlatform}`);
|
||||||
|
|
||||||
const providerStrategy = getInput('providerStrategy') || 'local';
|
pushStringFlags(args, getInput, STRING_FLAGS);
|
||||||
if (providerStrategy !== 'local') {
|
pushBooleanFlags(args, getInput, BOOLEAN_FLAGS);
|
||||||
throw new Error(
|
|
||||||
`Provider strategy "${providerStrategy}" is not supported by this thin wrapper. ` +
|
return args;
|
||||||
"Use providerStrategy=local, or invoke game-ci/cli's `orchestrate` command directly " +
|
}
|
||||||
'for remote builds.',
|
|
||||||
);
|
function buildOrchestrateArgs(getInput: (name: string) => string): string[] {
|
||||||
|
const args: string[] = ['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}`);
|
||||||
|
|
||||||
for (const [input, flag] of STRING_FLAGS) {
|
args.push('--providerStrategy=local-system');
|
||||||
const value = getInput(input);
|
|
||||||
// "--flag value" as two argv tokens is ambiguous when value itself
|
|
||||||
// starts with "-" (e.g. customParameters="-profile Foo -someBoolean"):
|
|
||||||
// yargs sees the next token starting with "-" and assumes the flag
|
|
||||||
// takes no value, leaving the value string to be mis-parsed as its own
|
|
||||||
// (partly alias-colliding) short-flag cluster. "--flag=value" glues
|
|
||||||
// them into one token, which is unambiguous.
|
|
||||||
if (value) args.push(`--${flag}=${value}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const [input, flag] of BOOLEAN_FLAGS) {
|
pushStringFlags(args, getInput, ORCHESTRATE_STRING_FLAGS);
|
||||||
const value = getInput(input);
|
pushStringFlags(args, getInput, ORCHESTRATE_ONLY_STRING_FLAGS);
|
||||||
if (value && isTruthy(value)) args.push(`--${flag}`);
|
pushBooleanFlags(args, getInput, ORCHESTRATE_BOOLEAN_FLAGS);
|
||||||
}
|
pushBooleanFlags(args, getInput, ORCHESTRATE_ONLY_BOOLEAN_FLAGS);
|
||||||
|
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-2
@@ -43,8 +43,11 @@ export async function run() {
|
|||||||
const exitCode = await exec.exec(cliPath, args, { ignoreReturnCode: true });
|
const exitCode = await exec.exec(cliPath, args, { ignoreReturnCode: true });
|
||||||
|
|
||||||
// Matches the original action's engineExitCode output: 0 on success,
|
// Matches the original action's engineExitCode output: 0 on success,
|
||||||
// otherwise the exit code of whichever step (activation or build)
|
// otherwise the exit code of whichever step (activation or build) - or,
|
||||||
// failed - which is exactly what the CLI subprocess itself exits with.
|
// 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);
|
core.setOutput('engineExitCode', exitCode);
|
||||||
|
|
||||||
if (exitCode !== 0) {
|
if (exitCode !== 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user