diff --git a/src/cli-plugin/build-parameters-adapter.ts b/src/cli-plugin/build-parameters-adapter.ts index 8593eb1..caabdc5 100644 --- a/src/cli-plugin/build-parameters-adapter.ts +++ b/src/cli-plugin/build-parameters-adapter.ts @@ -21,9 +21,11 @@ export function createBuildParametersFromCliOptions(options: Record // ── identity / build settings ───────────────────────────────────── bp.editorVersion = options.engineVersion || options.editorVersion || options.unityVersion || ''; bp.customImage = options.customImage || ''; - bp.unitySerial = options.unitySerial || process.env.UNITY_SERIAL || ''; - bp.unityLicensingServer = options.unityLicensingServer || ''; - bp.skipActivation = options.skipActivation || 'false'; + // Engine-specific licensing fields (unitySerial, unityLicensingServer, + // unityLicensingToolset, skipActivation, ...) are not assigned here. + // They flow opaquely through BuildParameters' index signature when the host + // populates them; orchestrator does not read them. + // See https://github.com/game-ci/orchestrator/issues/25 bp.runnerTempPath = options.runnerTempPath || process.env.RUNNER_TEMP || ''; bp.targetPlatform = options.targetPlatform || 'StandaloneLinux64'; bp.projectPath = options.projectPath || '.'; diff --git a/src/cli/__tests__/commands.test.ts b/src/cli/__tests__/commands.test.ts index c6f6c26..478ac71 100644 --- a/src/cli/__tests__/commands.test.ts +++ b/src/cli/__tests__/commands.test.ts @@ -87,8 +87,15 @@ describe('CLI commands', () => { expect(options['chown-files-to']).toBeDefined(); expect(options['provider-strategy']).toBeDefined(); - expect(options['skip-activation']).toBeDefined(); - expect(options['unity-licensing-server']).toBeDefined(); + + // Engine-specific licensing flags are intentionally NOT defined on + // orchestrator's build command — orchestrator is engine-agnostic. + // Hosts (unity-builder action, @game-ci/cli) own those flags and pass + // them through to the build container as environment variables. + // See https://github.com/game-ci/orchestrator/issues/25 + expect(options['skip-activation']).toBeUndefined(); + expect(options['unity-licensing-server']).toBeUndefined(); + expect(options['unity-licensing-toolset']).toBeUndefined(); }); it('sets correct default values', () => { diff --git a/src/cli/commands/build.ts b/src/cli/commands/build.ts index 16ba1a5..44f8717 100644 --- a/src/cli/commands/build.ts +++ b/src/cli/commands/build.ts @@ -11,258 +11,256 @@ const buildCommand: CommandModule = { command: 'build', describe: 'Build a Unity project', builder: (yargs) => { - return yargs - .option('target-platform', { - alias: 'targetPlatform', - type: 'string', - description: 'Platform that the build should target', - demandOption: true, - }) - .option('unity-version', { - alias: 'unityVersion', - type: 'string', - description: 'Version of Unity to use for building the project. Use "auto" to detect.', - default: 'auto', - }) - .option('project-path', { - alias: 'projectPath', - type: 'string', - description: 'Path to the Unity project to be built', - default: '.', - }) - .option('build-profile', { - alias: 'buildProfile', - type: 'string', - description: 'Path to the build profile to activate, relative to the project root', - default: '', - }) - .option('build-name', { - alias: 'buildName', - type: 'string', - description: 'Name of the build (no file extension)', - default: '', - }) - .option('builds-path', { - alias: 'buildsPath', - type: 'string', - description: 'Path where the builds should be stored', - default: 'build', - }) - .option('build-method', { - alias: 'buildMethod', - type: 'string', - description: 'Path to a Namespace.Class.StaticMethod to run to perform the build', - default: '', - }) - .option('custom-parameters', { - alias: 'customParameters', - type: 'string', - description: 'Custom parameters to configure the build', - default: '', - }) - .option('versioning', { - type: 'string', - description: 'The versioning scheme to use when building the project', - default: 'Semantic', - }) - .option('version', { - type: 'string', - description: 'The version, when used with the "Custom" versioning scheme', - default: '', - }) - .option('custom-image', { - alias: 'customImage', - type: 'string', - description: 'Specific docker image that should be used for building the project', - default: '', - }) - .option('manual-exit', { - alias: 'manualExit', - type: 'boolean', - description: - 'Suppresses -quit. Exit your build method using EditorApplication.Exit(0) instead.', - default: false, - }) - .option('enable-gpu', { - alias: 'enableGpu', - type: 'boolean', - description: 'Launches unity without specifying -nographics', - default: false, - }) - .option('android-version-code', { - alias: 'androidVersionCode', - type: 'string', - description: 'The android versionCode', - default: '', - }) - .option('android-export-type', { - alias: 'androidExportType', - type: 'string', - description: - 'The android export type (androidPackage, androidAppBundle, androidStudioProject)', - default: 'androidPackage', - }) - .option('android-keystore-name', { - alias: 'androidKeystoreName', - type: 'string', - description: 'The android keystoreName', - default: '', - }) - .option('android-keystore-base64', { - alias: 'androidKeystoreBase64', - type: 'string', - description: 'The base64 contents of the android keystore file', - default: '', - }) - .option('android-keystore-pass', { - alias: 'androidKeystorePass', - type: 'string', - description: 'The android keystorePass', - default: '', - }) - .option('android-keyalias-name', { - alias: 'androidKeyaliasName', - type: 'string', - description: 'The android keyaliasName', - default: '', - }) - .option('android-keyalias-pass', { - alias: 'androidKeyaliasPass', - type: 'string', - description: 'The android keyaliasPass', - default: '', - }) - .option('android-target-sdk-version', { - alias: 'androidTargetSdkVersion', - type: 'string', - description: 'The android target API level', - default: '', - }) - .option('android-symbol-type', { - alias: 'androidSymbolType', - type: 'string', - description: 'The android symbol type to export (none, public, debugging)', - default: 'none', - }) - .option('docker-cpu-limit', { - alias: 'dockerCpuLimit', - type: 'string', - description: 'Number of CPU cores to assign the docker container', - default: '', - }) - .option('docker-memory-limit', { - alias: 'dockerMemoryLimit', - type: 'string', - description: 'Amount of memory to assign the docker container (e.g. 512m, 4g)', - default: '', - }) - .option('docker-workspace-path', { - alias: 'dockerWorkspacePath', - type: 'string', - description: 'The path to mount the workspace inside the docker container', - default: '/github/workspace', - }) - .option('run-as-host-user', { - alias: 'runAsHostUser', - type: 'string', - description: 'Whether to run as a user that matches the host system', - default: 'false', - }) - .option('chown-files-to', { - alias: 'chownFilesTo', - type: 'string', - description: 'User and optionally group to give ownership of build artifacts', - default: '', - }) - .option('ssh-agent', { - alias: 'sshAgent', - type: 'string', - description: 'SSH Agent path to forward to the container', - default: '', - }) - .option('git-private-token', { - alias: 'gitPrivateToken', - type: 'string', - description: 'GitHub private token to pull from GitHub', - default: '', - }) - .option('provider-strategy', { - alias: 'providerStrategy', - type: 'string', - description: 'Execution strategy: local, k8s, or aws', - default: 'local', - }) - .option('skip-activation', { - alias: 'skipActivation', - type: 'string', - description: 'Skip the activation/deactivation of Unity', - default: 'false', - }) - .option('unity-licensing-server', { - alias: 'unityLicensingServer', - type: 'string', - description: 'The Unity licensing server address', - default: '', - }) - .option('container-registry-repository', { - alias: 'containerRegistryRepository', - type: 'string', - description: - 'Container registry and repository to pull image from. Only applicable if customImage is not set.', - default: 'unityci/editor', - }) - .option('container-registry-image-version', { - alias: 'containerRegistryImageVersion', - type: 'string', - description: 'Container registry image version. Only applicable if customImage is not set.', - default: '3', - }) - .option('docker-isolation-mode', { - alias: 'dockerIsolationMode', - type: 'string', - description: - 'Isolation mode to use for the docker container (process, hyperv, or default). Only applicable on Windows.', - default: 'default', - }) - .option('ssh-public-keys-directory-path', { - alias: 'sshPublicKeysDirectoryPath', - type: 'string', - description: 'Path to a directory containing SSH public keys to forward to the container', - default: '', - }) - .option('engine', { - type: 'string', - description: 'Game engine name (unity, godot, unreal, etc.)', - default: 'unity', - }) - .option('engine-plugin', { - alias: 'enginePlugin', - type: 'string', - description: - 'Engine plugin source: module:, cli:, docker:, or an npm package name', - default: '', - }) - .option('cache-unity-installation-on-mac', { - alias: 'cacheUnityInstallationOnMac', - type: 'boolean', - description: 'Whether to cache the Unity hub and editor installation on MacOS', - default: false, - }) - .option('unity-hub-version-on-mac', { - alias: 'unityHubVersionOnMac', - type: 'string', - description: - 'The version of Unity Hub to install on MacOS (e.g. 3.4.0). Defaults to latest available on brew.', - default: '', - }) - .example( - 'game-ci build --target-platform StandaloneLinux64 --provider-strategy aws', - 'Build on AWS via orchestrator', - ) - .example( - 'game-ci build --target-platform Android --provider-strategy k8s --kube-config ', - 'Build on Kubernetes via orchestrator', - ) as any; + return ( + yargs + .option('target-platform', { + alias: 'targetPlatform', + type: 'string', + description: 'Platform that the build should target', + demandOption: true, + }) + .option('unity-version', { + alias: 'unityVersion', + type: 'string', + description: 'Version of Unity to use for building the project. Use "auto" to detect.', + default: 'auto', + }) + .option('project-path', { + alias: 'projectPath', + type: 'string', + description: 'Path to the Unity project to be built', + default: '.', + }) + .option('build-profile', { + alias: 'buildProfile', + type: 'string', + description: 'Path to the build profile to activate, relative to the project root', + default: '', + }) + .option('build-name', { + alias: 'buildName', + type: 'string', + description: 'Name of the build (no file extension)', + default: '', + }) + .option('builds-path', { + alias: 'buildsPath', + type: 'string', + description: 'Path where the builds should be stored', + default: 'build', + }) + .option('build-method', { + alias: 'buildMethod', + type: 'string', + description: 'Path to a Namespace.Class.StaticMethod to run to perform the build', + default: '', + }) + .option('custom-parameters', { + alias: 'customParameters', + type: 'string', + description: 'Custom parameters to configure the build', + default: '', + }) + .option('versioning', { + type: 'string', + description: 'The versioning scheme to use when building the project', + default: 'Semantic', + }) + .option('version', { + type: 'string', + description: 'The version, when used with the "Custom" versioning scheme', + default: '', + }) + .option('custom-image', { + alias: 'customImage', + type: 'string', + description: 'Specific docker image that should be used for building the project', + default: '', + }) + .option('manual-exit', { + alias: 'manualExit', + type: 'boolean', + description: + 'Suppresses -quit. Exit your build method using EditorApplication.Exit(0) instead.', + default: false, + }) + .option('enable-gpu', { + alias: 'enableGpu', + type: 'boolean', + description: 'Launches unity without specifying -nographics', + default: false, + }) + .option('android-version-code', { + alias: 'androidVersionCode', + type: 'string', + description: 'The android versionCode', + default: '', + }) + .option('android-export-type', { + alias: 'androidExportType', + type: 'string', + description: + 'The android export type (androidPackage, androidAppBundle, androidStudioProject)', + default: 'androidPackage', + }) + .option('android-keystore-name', { + alias: 'androidKeystoreName', + type: 'string', + description: 'The android keystoreName', + default: '', + }) + .option('android-keystore-base64', { + alias: 'androidKeystoreBase64', + type: 'string', + description: 'The base64 contents of the android keystore file', + default: '', + }) + .option('android-keystore-pass', { + alias: 'androidKeystorePass', + type: 'string', + description: 'The android keystorePass', + default: '', + }) + .option('android-keyalias-name', { + alias: 'androidKeyaliasName', + type: 'string', + description: 'The android keyaliasName', + default: '', + }) + .option('android-keyalias-pass', { + alias: 'androidKeyaliasPass', + type: 'string', + description: 'The android keyaliasPass', + default: '', + }) + .option('android-target-sdk-version', { + alias: 'androidTargetSdkVersion', + type: 'string', + description: 'The android target API level', + default: '', + }) + .option('android-symbol-type', { + alias: 'androidSymbolType', + type: 'string', + description: 'The android symbol type to export (none, public, debugging)', + default: 'none', + }) + .option('docker-cpu-limit', { + alias: 'dockerCpuLimit', + type: 'string', + description: 'Number of CPU cores to assign the docker container', + default: '', + }) + .option('docker-memory-limit', { + alias: 'dockerMemoryLimit', + type: 'string', + description: 'Amount of memory to assign the docker container (e.g. 512m, 4g)', + default: '', + }) + .option('docker-workspace-path', { + alias: 'dockerWorkspacePath', + type: 'string', + description: 'The path to mount the workspace inside the docker container', + default: '/github/workspace', + }) + .option('run-as-host-user', { + alias: 'runAsHostUser', + type: 'string', + description: 'Whether to run as a user that matches the host system', + default: 'false', + }) + .option('chown-files-to', { + alias: 'chownFilesTo', + type: 'string', + description: 'User and optionally group to give ownership of build artifacts', + default: '', + }) + .option('ssh-agent', { + alias: 'sshAgent', + type: 'string', + description: 'SSH Agent path to forward to the container', + default: '', + }) + .option('git-private-token', { + alias: 'gitPrivateToken', + type: 'string', + description: 'GitHub private token to pull from GitHub', + default: '', + }) + .option('provider-strategy', { + alias: 'providerStrategy', + type: 'string', + description: 'Execution strategy: local, k8s, or aws', + default: 'local', + }) + // Note: engine-specific licensing flags (--skip-activation, + // --unity-licensing-server, --unity-licensing-toolset, --unity-serial) + // are not exposed here. Pass them via environment variables (e.g. + // UNITY_SERIAL, UNITY_LICENSING_SERVER) which are forwarded to the + // build container automatically, or via the engine-specific host + // entry point (unity-builder action / @game-ci/cli). See + // https://github.com/game-ci/orchestrator/issues/25 + .option('container-registry-repository', { + alias: 'containerRegistryRepository', + type: 'string', + description: + 'Container registry and repository to pull image from. Only applicable if customImage is not set.', + default: 'unityci/editor', + }) + .option('container-registry-image-version', { + alias: 'containerRegistryImageVersion', + type: 'string', + description: + 'Container registry image version. Only applicable if customImage is not set.', + default: '3', + }) + .option('docker-isolation-mode', { + alias: 'dockerIsolationMode', + type: 'string', + description: + 'Isolation mode to use for the docker container (process, hyperv, or default). Only applicable on Windows.', + default: 'default', + }) + .option('ssh-public-keys-directory-path', { + alias: 'sshPublicKeysDirectoryPath', + type: 'string', + description: 'Path to a directory containing SSH public keys to forward to the container', + default: '', + }) + .option('engine', { + type: 'string', + description: 'Game engine name (unity, godot, unreal, etc.)', + default: 'unity', + }) + .option('engine-plugin', { + alias: 'enginePlugin', + type: 'string', + description: + 'Engine plugin source: module:, cli:, docker:, or an npm package name', + default: '', + }) + .option('cache-unity-installation-on-mac', { + alias: 'cacheUnityInstallationOnMac', + type: 'boolean', + description: 'Whether to cache the Unity hub and editor installation on MacOS', + default: false, + }) + .option('unity-hub-version-on-mac', { + alias: 'unityHubVersionOnMac', + type: 'string', + description: + 'The version of Unity Hub to install on MacOS (e.g. 3.4.0). Defaults to latest available on brew.', + default: '', + }) + .example( + 'game-ci build --target-platform StandaloneLinux64 --provider-strategy aws', + 'Build on AWS via orchestrator', + ) + .example( + 'game-ci build --target-platform Android --provider-strategy k8s --kube-config ', + 'Build on Kubernetes via orchestrator', + ) as any + ); }, handler: async (cliArguments) => { try { diff --git a/src/cli/commands/orchestrate.ts b/src/cli/commands/orchestrate.ts index 6359e69..27e5d51 100644 --- a/src/cli/commands/orchestrate.ts +++ b/src/cli/commands/orchestrate.ts @@ -13,182 +13,180 @@ const orchestrateCommand: CommandModule = { command: 'orchestrate', describe: 'Orchestrator — remote builds, cache management, and provider tools', builder: (yargs) => { - return yargs - .command(cacheCommand) - .option('target-platform', { - alias: 'targetPlatform', - type: 'string', - description: 'Platform that the build should target', - }) - .option('provider-strategy', { - alias: 'providerStrategy', - type: 'string', - description: 'Orchestrator provider: aws, k8s, local-docker, local-system', - default: 'aws', - }) - .option('unity-version', { - alias: 'unityVersion', - type: 'string', - description: 'Version of Unity to use for building', - default: 'auto', - }) - .option('project-path', { - alias: 'projectPath', - type: 'string', - description: 'Path to the Unity project to be built', - default: '.', - }) - .option('build-name', { - alias: 'buildName', - type: 'string', - description: 'Name of the build', - default: '', - }) - .option('builds-path', { - alias: 'buildsPath', - type: 'string', - description: 'Path where the builds should be stored', - default: 'build', - }) - .option('build-method', { - alias: 'buildMethod', - type: 'string', - description: 'Path to a Namespace.Class.StaticMethod to run to perform the build', - default: '', - }) - .option('custom-parameters', { - alias: 'customParameters', - type: 'string', - description: 'Custom parameters to configure the build', - default: '', - }) - .option('versioning', { - type: 'string', - description: 'The versioning scheme to use', - default: 'None', - }) - .option('aws-stack-name', { - alias: 'awsStackName', - type: 'string', - description: 'The Cloud Formation stack name (AWS provider)', - default: 'game-ci', - }) - .option('kube-config', { - alias: 'kubeConfig', - type: 'string', - description: 'Base64 encoded Kubernetes config (K8s provider)', - default: '', - }) - .option('kube-volume', { - alias: 'kubeVolume', - type: 'string', - description: 'Persistent Volume Claim name for Unity build (K8s provider)', - default: '', - }) - .option('kube-volume-size', { - alias: 'kubeVolumeSize', - type: 'string', - description: 'Disc space for Kubernetes Persistent Volume', - default: '5Gi', - }) - .option('container-cpu', { - alias: 'containerCpu', - type: 'string', - description: 'CPU allocation for remote build container', - default: '1024', - }) - .option('container-memory', { - alias: 'containerMemory', - type: 'string', - description: 'Memory allocation for remote build container', - default: '3072', - }) - .option('cache-key', { - alias: 'cacheKey', - type: 'string', - description: 'Cache key to indicate bucket for cache', - default: '', - }) - .option('git-private-token', { - alias: 'gitPrivateToken', - type: 'string', - description: 'GitHub private token for repository access', - default: '', - }) - .option('allow-dirty-build', { - alias: 'allowDirtyBuild', - type: 'boolean', - description: 'Allow builds from dirty branches', - default: false, - }) - .option('watch-to-end', { - alias: 'watchToEnd', - type: 'string', - description: 'Whether to watch the build to completion', - default: 'true', - }) - .option('clone-depth', { - alias: 'cloneDepth', - type: 'string', - description: 'Git clone depth (0 for full clone)', - default: '50', - }) - .option('skip-activation', { - alias: 'skipActivation', - type: 'string', - description: 'Skip Unity activation/deactivation', - default: 'false', - }) - .option('kube-storage-class', { - alias: 'kubeStorageClass', - type: 'string', - description: - 'Kubernetes storage class to use for orchestrator jobs. Leave empty to install rook cluster.', - default: '', - }) - .option('read-input-from-override-list', { - alias: 'readInputFromOverrideList', - type: 'string', - description: - 'Comma separated list of input value names to read from the input override command', - default: '', - }) - .option('read-input-override-command', { - alias: 'readInputOverrideCommand', - type: 'string', - description: - 'Command to execute to pull input from an external source (e.g. cloud provider secret managers)', - default: '', - }) - .option('post-build-steps', { - alias: 'postBuildSteps', - type: 'string', - description: - 'Post build job in yaml format with the keys image, secrets (name, value object array), command string', - default: '', - }) - .option('pre-build-steps', { - alias: 'preBuildSteps', - type: 'string', - description: - 'Pre build job after repository setup but before the build job (yaml format with keys image, secrets, command)', - default: '', - }) - .option('custom-job', { - alias: 'customJob', - type: 'string', - description: - 'Custom job instead of the standard build automation (yaml format with keys image, secrets, command)', - default: '', - }) - .example( - 'game-ci orchestrate --target-platform StandaloneLinux64 --provider-strategy aws', - 'Build on AWS using the orchestrator', - ) - .example( - 'game-ci orchestrate --target-platform StandaloneLinux64 --provider-strategy k8s --kube-config ', - 'Build on Kubernetes', - ) as any; + return ( + yargs + .command(cacheCommand) + .option('target-platform', { + alias: 'targetPlatform', + type: 'string', + description: 'Platform that the build should target', + }) + .option('provider-strategy', { + alias: 'providerStrategy', + type: 'string', + description: 'Orchestrator provider: aws, k8s, local-docker, local-system', + default: 'aws', + }) + .option('unity-version', { + alias: 'unityVersion', + type: 'string', + description: 'Version of Unity to use for building', + default: 'auto', + }) + .option('project-path', { + alias: 'projectPath', + type: 'string', + description: 'Path to the Unity project to be built', + default: '.', + }) + .option('build-name', { + alias: 'buildName', + type: 'string', + description: 'Name of the build', + default: '', + }) + .option('builds-path', { + alias: 'buildsPath', + type: 'string', + description: 'Path where the builds should be stored', + default: 'build', + }) + .option('build-method', { + alias: 'buildMethod', + type: 'string', + description: 'Path to a Namespace.Class.StaticMethod to run to perform the build', + default: '', + }) + .option('custom-parameters', { + alias: 'customParameters', + type: 'string', + description: 'Custom parameters to configure the build', + default: '', + }) + .option('versioning', { + type: 'string', + description: 'The versioning scheme to use', + default: 'None', + }) + .option('aws-stack-name', { + alias: 'awsStackName', + type: 'string', + description: 'The Cloud Formation stack name (AWS provider)', + default: 'game-ci', + }) + .option('kube-config', { + alias: 'kubeConfig', + type: 'string', + description: 'Base64 encoded Kubernetes config (K8s provider)', + default: '', + }) + .option('kube-volume', { + alias: 'kubeVolume', + type: 'string', + description: 'Persistent Volume Claim name for Unity build (K8s provider)', + default: '', + }) + .option('kube-volume-size', { + alias: 'kubeVolumeSize', + type: 'string', + description: 'Disc space for Kubernetes Persistent Volume', + default: '5Gi', + }) + .option('container-cpu', { + alias: 'containerCpu', + type: 'string', + description: 'CPU allocation for remote build container', + default: '1024', + }) + .option('container-memory', { + alias: 'containerMemory', + type: 'string', + description: 'Memory allocation for remote build container', + default: '3072', + }) + .option('cache-key', { + alias: 'cacheKey', + type: 'string', + description: 'Cache key to indicate bucket for cache', + default: '', + }) + .option('git-private-token', { + alias: 'gitPrivateToken', + type: 'string', + description: 'GitHub private token for repository access', + default: '', + }) + .option('allow-dirty-build', { + alias: 'allowDirtyBuild', + type: 'boolean', + description: 'Allow builds from dirty branches', + default: false, + }) + .option('watch-to-end', { + alias: 'watchToEnd', + type: 'string', + description: 'Whether to watch the build to completion', + default: 'true', + }) + .option('clone-depth', { + alias: 'cloneDepth', + type: 'string', + description: 'Git clone depth (0 for full clone)', + default: '50', + }) + // Note: engine-specific licensing flags are not exposed by orchestrator's + // CLI surface. See https://github.com/game-ci/orchestrator/issues/25 + .option('kube-storage-class', { + alias: 'kubeStorageClass', + type: 'string', + description: + 'Kubernetes storage class to use for orchestrator jobs. Leave empty to install rook cluster.', + default: '', + }) + .option('read-input-from-override-list', { + alias: 'readInputFromOverrideList', + type: 'string', + description: + 'Comma separated list of input value names to read from the input override command', + default: '', + }) + .option('read-input-override-command', { + alias: 'readInputOverrideCommand', + type: 'string', + description: + 'Command to execute to pull input from an external source (e.g. cloud provider secret managers)', + default: '', + }) + .option('post-build-steps', { + alias: 'postBuildSteps', + type: 'string', + description: + 'Post build job in yaml format with the keys image, secrets (name, value object array), command string', + default: '', + }) + .option('pre-build-steps', { + alias: 'preBuildSteps', + type: 'string', + description: + 'Pre build job after repository setup but before the build job (yaml format with keys image, secrets, command)', + default: '', + }) + .option('custom-job', { + alias: 'customJob', + type: 'string', + description: + 'Custom job instead of the standard build automation (yaml format with keys image, secrets, command)', + default: '', + }) + .example( + 'game-ci orchestrate --target-platform StandaloneLinux64 --provider-strategy aws', + 'Build on AWS using the orchestrator', + ) + .example( + 'game-ci orchestrate --target-platform StandaloneLinux64 --provider-strategy k8s --kube-config ', + 'Build on Kubernetes', + ) as any + ); }, handler: async (cliArguments) => { try { diff --git a/src/cli/input-mapper.ts b/src/cli/input-mapper.ts index 73e2979..a2a9a2d 100644 --- a/src/cli/input-mapper.ts +++ b/src/cli/input-mapper.ts @@ -55,7 +55,6 @@ export interface CliArguments { cacheKey?: string; watchToEnd?: string; allowDirtyBuild?: boolean; - skipActivation?: string; cloneDepth?: string; readInputFromOverrideList?: string; @@ -64,7 +63,10 @@ export interface CliArguments { preBuildSteps?: string; customJob?: string; - unityLicensingServer?: string; + // Note: engine-specific fields (skipActivation, unityLicensingServer, ...) + // are NOT declared here. They flow through the [key: string] index + // signature below to the plugin's opaque config dict. Orchestrator does not + // read them. See https://github.com/game-ci/orchestrator/issues/25 cacheUnityInstallationOnMac?: boolean; unityHubVersionOnMac?: string; diff --git a/src/model/build-parameters.ts b/src/model/build-parameters.ts index cf34464..3863cce 100644 --- a/src/model/build-parameters.ts +++ b/src/model/build-parameters.ts @@ -7,6 +7,16 @@ * * During Phase 3 of the extraction plan this will be replaced by a proper * lean interface supplied by the host (unity-builder or CLI). + * + * ── Engine-agnostic boundary ───────────────────────────────────────── + * + * Orchestrator's typed surfaces must NOT carry engine-specific vocabulary + * (Unity-licensing fields, etc.). The plugin contract is opaque + * (Record) — hosts pass their full config through, plugins + * pull what they need. Engine-specific concerns ride in the dict + * untouched by orchestrator. + * + * Tracking issue: https://github.com/game-ci/orchestrator/issues/25 */ import * as core from '@actions/core'; @@ -22,9 +32,6 @@ class BuildParameters { // ── identity ──────────────────────────────────────────────────────── editorVersion!: string; customImage!: string; - unitySerial!: string; - unityLicensingServer!: string; - skipActivation!: string; runnerTempPath!: string; targetPlatform!: string; projectPath!: string; @@ -201,9 +208,6 @@ class BuildParameters { p.buildVersion = '1.0.0'; p.androidVersionCode = ''; p.customImage = Input.customImage || Input.getInput('image') || ''; - p.unitySerial = ''; - p.unityLicensingServer = ''; - p.skipActivation = ''; p.runnerTempPath = process.env.RUNNER_TEMP || ''; p.manualExit = Input.manualExit; p.enableGpu = Input.enableGpu; diff --git a/src/model/orchestrator/interfaces.ts b/src/model/orchestrator/interfaces.ts index 9363a2b..1af75dd 100644 --- a/src/model/orchestrator/interfaces.ts +++ b/src/model/orchestrator/interfaces.ts @@ -111,9 +111,11 @@ export interface OrchestratorConfig { garbageMaxAge: number; // Authentication - unitySerial?: string; - unityLicensingServer?: string; - skipActivation?: string; + // ── Note ─────────────────────────────────────────────────────────── + // Engine-specific licensing fields (e.g. unitySerial, unityLicensingServer, + // unityLicensingToolset, skipActivation) are NOT declared here. They ride + // opaquely inside the host's plugin-config dict; orchestrator does not read + // them and must not name them. See https://github.com/game-ci/orchestrator/issues/25 runnerTempPath?: string; manualExit?: boolean; enableGpu?: boolean; diff --git a/src/model/orchestrator/orchestrator.ts b/src/model/orchestrator/orchestrator.ts index 8dce2f4..82f3d72 100644 --- a/src/model/orchestrator/orchestrator.ts +++ b/src/model/orchestrator/orchestrator.ts @@ -470,6 +470,12 @@ class Orchestrator { private static async updateStatusWithBuildParameters() { const content = { ...Orchestrator.buildParameters }; + // Defensive scrub of well-known secret keys before posting to GitHub Checks. + // unity* keys are no longer typed on BuildParameters but may still be + // present via the host's opaque pluginConfig (BuildParameters has a + // [key: string]: any index signature). Scrubbing by key name is safe. + // Future: generalize to a configurable secret-key allowlist. + // See https://github.com/game-ci/orchestrator/issues/25 content.gitPrivateToken = ``; content.unitySerial = ``; content.unityEmail = ``; diff --git a/src/plugin-lifecycle.ts b/src/plugin-lifecycle.ts index 66399fa..0a59ad3 100644 --- a/src/plugin-lifecycle.ts +++ b/src/plugin-lifecycle.ts @@ -9,6 +9,30 @@ * const { createPlugin } = await import('@game-ci/orchestrator'); * const plugin = createPlugin(); * await plugin.initialize(coreParams, workspace); + * + * ── Engine-agnostic contract ───────────────────────────────────────── + * + * `coreParams: Record` is intentionally opaque. The host + * (unity-builder today, @game-ci/cli in the future) passes its full + * BuildParameters object through. This plugin only reads: + * + * - Generic build context: targetPlatform, projectPath, buildPath, + * buildGuid, branch, gitSha, customParameters, gitPrivateToken, + * runnerTempPath, providerStrategy, cacheRetentionDays, testResultPath + * - Plugin-owned config (read directly from env/inputs via getInput, NOT + * from coreParams): everything in the `config` object below + * + * The plugin MUST NOT depend on engine-specific keys like `unitySerial` / + * `unityLicensingServer` / `unityLicensingToolset` / `skipActivation`. + * Those keys may be present in coreParams (the host put them there for + * downstream consumers like the build container's env vars) but the + * orchestrator plugin treats them as opaque pass-through. + * + * Future state — see https://github.com/game-ci/orchestrator/issues/25: + * eventually @game-ci/cli becomes the top-level entry. cli composes + * unity-builder (Unity runtime) and orchestrator (dispatch) and supplies + * the same coreParams shape. Because the contract is opaque, no plugin + * change is needed to support the future caller. */ import * as core from '@actions/core';