Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 68 additions & 2 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14309,7 +14309,8 @@ class System {
return new Promise((resolve5, reject) => {
const proc = spawn(shell, shellArgs, {
cwd: options.cwd,
stdio: ["inherit", "pipe", "pipe"]
stdio: ["inherit", "pipe", "pipe"],
env: options.env ? { ...process.env, ...options.env } : process.env
});
const runResult = { output: "", error: "" };
proc.stdout.on("data", (chunk) => {
Expand Down Expand Up @@ -16815,12 +16816,33 @@ class PlatformSetup {
// src/model/mac-builder.ts
init_system();
init_unity_build_validation();
init_image_environment_factory();
init_environment();

class MacBuilder {
static buildEnv(options) {
const { currentWorkDir, cliDistPath } = options;
const extraVariables = options.engine === "unity" ? UnityEnvironment.getVariables(options) : [];
const variables = ImageEnvironmentFactory.getEnvironmentVariables(options, extraVariables);
const env3 = {};
for (const { name, value } of variables) {
if (value === "" || value === undefined)
continue;
env3[name] = value.toString();
}
if (currentWorkDir)
env3.GITHUB_WORKSPACE = currentWorkDir;
if (cliDistPath)
env3.ACTION_FOLDER = cliDistPath;
return env3;
}
static async run(options, silent = false) {
const { cliDistPath, engine } = options;
log.warning("running the process");
const macRun = await System.run(`bash ${cliDistPath}/platforms/mac/entrypoint.sh`, undefined, { silent });
const macRun = await System.run(`bash ${cliDistPath}/platforms/mac/entrypoint.sh`, undefined, {
silent,
env: MacBuilder.buildEnv(options)
});
switch (engine) {
case "unity":
UnityBuildValidation.validateBuild(macRun.output);
Expand Down Expand Up @@ -17683,6 +17705,11 @@ class UnityCliAdapter {
const result = await System.run(cliCommand, undefined, { silent: true });
return { success: result.status?.success ?? false, output: result.output };
}
static async test(extraArgs = []) {
const cliCommand = ["unity", "test", ...extraArgs].join(" ");
const result = await System.run(cliCommand, undefined, { silent: true });
return { success: result.status?.success ?? false, output: result.output };
}
}

// src/command-options/unity-run-options.ts
Expand Down Expand Up @@ -17727,6 +17754,43 @@ class UnityRunCommand extends CommandBase {
}
}

// src/command-options/unity-test-options.ts
class UnityTestOptions {
static configure(yargs) {
yargs.option("unityCliArgs", {
description: String.dedent`
Raw arguments passed through to \`unity test\` verbatim, space-separated.
Unity's own CLI reference doesn't publish a flag table for this command;
run \`unity test --help\` on the installed binary for the authoritative
list (see docs.unity.com/en-us/unity-cli).`,
type: "string",
demandOption: false,
default: ""
});
}
}

// src/command/test/unity-test-command.ts
class UnityTestCommand extends CommandBase {
async execute(options) {
const extraArgs = String(options.unityCliArgs || "").split(" ").map((arg) => arg.trim()).filter(Boolean);
const available = await UnityCliAdapter.isAvailable();
if (!available) {
throw new Error("test: requires Unity's official `unity` CLI binary on PATH " + "(https://docs.unity.com/en-us/unity-cli). Not found in this environment.");
}
try {
const result = await UnityCliAdapter.test(extraArgs);
log.info(result.output);
return result.success;
} catch (error) {
throw new Error(`test: 'unity test' failed: ${error.message}`);
}
}
async configureOptions(yargs) {
await UnityTestOptions.configure(yargs);
}
}

// src/plugin/builtin/unity-plugin.ts
var unityPlugin = {
name: "unity",
Expand Down Expand Up @@ -17754,6 +17818,8 @@ var unityPlugin = {
return new UnityOrchestrateCommand(command2);
case "run":
return new UnityRunCommand(command2);
case "test":
return new UnityTestCommand(command2);
case "remote":
switch (subCommands[0]) {
case "run":
Expand Down
68 changes: 53 additions & 15 deletions dist/platforms/mac/steps/activate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,59 @@
echo "Changing to \"$ACTIVATE_LICENSE_PATH\" directory."
pushd "$ACTIVATE_LICENSE_PATH"

echo "Requesting activation"

# Activate license
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
-logFile - \
-batchmode \
-nographics \
-quit \
-serial "$UNITY_SERIAL" \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD" \
-projectPath "$ACTIVATE_LICENSE_PATH"

# Store the exit code from the verify command
UNITY_EXIT_CODE=$?
if [[ -n "$UNITY_SERIAL" && -n "$UNITY_EMAIL" && -n "$UNITY_PASSWORD" ]]; then
#
# SERIAL LICENSE MODE
#
echo "Requesting activation"

# Activate license
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
-logFile - \
-batchmode \
-nographics \
-quit \
-serial "$UNITY_SERIAL" \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD" \
-projectPath "$ACTIVATE_LICENSE_PATH"

# Store the exit code from the verify command
UNITY_EXIT_CODE=$?
elif [[ -n "$UNITY_LICENSING_SERVER" ]]; then
#
# Custom Unity License Server
#
# This platform previously had no floating-license support at all -
# UNITY_LICENSING_SERVER was silently ignored and activation always
# attempted (empty) serial mode instead (game-ci/cli, found while
# auditing for divergence from unity-builder's real source).
echo "Requesting floating license"

/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/Frameworks/UnityLicensingClient.app/Contents/MacOS/Unity.Licensing.Client \
--acquire-floating > license.txt
UNITY_EXIT_CODE=$?

if [ $UNITY_EXIT_CODE -eq 0 ]; then
PARSEDFILE=$(grep -oE '\"[^"]*\"' < license.txt | tr -d '"')
export FLOATING_LICENSE
FLOATING_LICENSE=$(sed -n 2p <<< "$PARSEDFILE")
FLOATING_LICENSE_TIMEOUT=$(sed -n 4p <<< "$PARSEDFILE")

echo "Acquired floating license: \"$FLOATING_LICENSE\" with timeout $FLOATING_LICENSE_TIMEOUT"
fi
else
#
# NO LICENSE ACTIVATION STRATEGY MATCHED
#
echo "License activation strategy could not be determined."
echo ""
echo "Visit https://game.ci/docs/github/activation for more"
echo "details on how to set up one of the possible activation strategies."

# Immediately exit as no UNITY_EXIT_CODE can be derived.
exit 1;
fi

#
# Display information about the result
Expand Down
46 changes: 46 additions & 0 deletions src/model/mac-builder.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { describe, it, expect } from 'bun:test';
import { MacBuilder } from './mac-builder.ts';

describe('MacBuilder', () => {
describe('buildEnv (private, accessed via any-cast)', () => {
const buildEnv = (options: any): Record<string, string> => (MacBuilder as any).buildEnv(options);

it('includes generic build options as env vars', () => {
const env = buildEnv({
currentWorkDir: '/Users/runner/work/repo/repo',
cliDistPath: '/Users/runner/work/repo/repo/dist',
projectPath: 'test-project',
targetPlatform: 'StandaloneOSX',
buildName: 'StandaloneOSX',
});

expect(env.PROJECT_PATH).toBe('test-project');
expect(env.BUILD_TARGET).toBe('StandaloneOSX');
expect(env.BUILD_NAME).toBe('StandaloneOSX');
});

it('sets GITHUB_WORKSPACE and ACTION_FOLDER from currentWorkDir/cliDistPath', () => {
const env = buildEnv({
currentWorkDir: '/Users/runner/work/repo/repo',
cliDistPath: '/Users/runner/work/repo/repo/dist',
});

expect(env.GITHUB_WORKSPACE).toBe('/Users/runner/work/repo/repo');
expect(env.ACTION_FOLDER).toBe('/Users/runner/work/repo/repo/dist');
});

it('includes Unity-specific env vars only when engine is unity', () => {
const unityEnv = buildEnv({ engine: 'unity', unitySerial: 'F4-1234-1234-1234' });
expect(unityEnv.UNITY_SERIAL).toBe('F4-1234-1234-1234');

const godotEnv = buildEnv({ engine: 'godot', unitySerial: 'F4-1234-1234-1234' });
expect(godotEnv.UNITY_SERIAL).toBeUndefined();
});

it('omits empty/undefined values entirely', () => {
const env = buildEnv({ projectPath: '', buildName: undefined });
expect('PROJECT_PATH' in env).toBe(false);
expect('BUILD_NAME' in env).toBe(false);
});
});
});
31 changes: 30 additions & 1 deletion src/model/mac-builder.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,41 @@
import type { Options } from '../dependencies.ts';
import { System } from './system/system.ts';
import { UnityBuildValidation } from './unity/build-validation/unity-build-validation.ts';
import { ImageEnvironmentFactory } from './image-environment-factory.ts';
import { UnityEnvironment } from '../logic/unity/environment.ts';

class MacBuilder {
/**
* Native mac builds run entrypoint.sh as a plain child process (no Docker
* --env flags), so parsed CLI options were never actually reaching the
* build - only vars a user separately exported in their shell mattered.
* Reuses the same env var set Docker builds already send, this time
* passed directly via System.run's `env` option.
*/
private static buildEnv(options: Options): Record<string, string> {
const { currentWorkDir, cliDistPath } = options;
const extraVariables = options.engine === 'unity' ? UnityEnvironment.getVariables(options) : [];
const variables = ImageEnvironmentFactory.getEnvironmentVariables(options, extraVariables);
const env: Record<string, string> = {};
for (const { name, value } of variables) {
if (value === '' || value === undefined) continue;
env[name] = value.toString();
}
// Not part of ImageEnvironmentFactory's set - Docker builds get these via
// explicit --env flags / volume-mount paths instead; the native mac path
// needs the real host values since there's no container remapping.
if (currentWorkDir) env.GITHUB_WORKSPACE = currentWorkDir;
if (cliDistPath) env.ACTION_FOLDER = cliDistPath;
return env;
}

public static async run(options: Options, silent = false) {
const { cliDistPath, engine } = options;
log.warning('running the process');
const macRun = await System.run(`bash ${cliDistPath}/platforms/mac/entrypoint.sh`, undefined, { silent });
const macRun = await System.run(`bash ${cliDistPath}/platforms/mac/entrypoint.sh`, undefined, {
silent,
env: MacBuilder.buildEnv(options),
});

switch (engine) {
case 'unity':
Expand Down
26 changes: 26 additions & 0 deletions src/model/system/system.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,35 @@
// Skipping until rewritten with bun:test mock APIs.

import { describe, test, expect } from 'bun:test';
import { System } from './system.ts';

describe.skip('System (unit)', () => {
test('placeholder', () => {
expect(true).toBe(true);
});
});

describe('System.run env option', () => {
// Uses `node -e` instead of shell-native env var syntax (`$VAR` vs `$env:VAR`)
// so this works identically under sh (Linux/Mac) and powershell (Windows).
const printEnvVarCommand = (name: string) => `node -e "process.stdout.write(process.env.${name} || '')"`;

test('merges options.env on top of the current process env for the spawned command', async () => {
const result = await System.run(printEnvVarCommand('SOME_TEST_VAR'), undefined, {
silent: true,
env: { SOME_TEST_VAR: 'from-options-env' },
});

expect(result.output.trim()).toBe('from-options-env');
});

test('inherits the current process env when options.env is not given', async () => {
process.env.SOME_INHERITED_VAR = 'inherited-value';
try {
const result = await System.run(printEnvVarCommand('SOME_INHERITED_VAR'), undefined, { silent: true });
expect(result.output.trim()).toBe('inherited-value');
} finally {
delete process.env.SOME_INHERITED_VAR;
}
});
});
3 changes: 3 additions & 0 deletions src/model/system/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { spawn } from 'node:child_process';
export interface RunOptions {
cwd?: string;
silent?: boolean;
/** Extra env vars merged on top of the current process's env for the spawned command. */
env?: Record<string, string | undefined>;
}

export interface RunResult {
Expand Down Expand Up @@ -52,6 +54,7 @@ class System {
const proc = spawn(shell, shellArgs, {
cwd: options.cwd,
stdio: ['inherit', 'pipe', 'pipe'],
env: options.env ? { ...process.env, ...options.env } : process.env,
});

const runResult: RunResult = { output: '', error: '' };
Expand Down
Loading