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
33 changes: 33 additions & 0 deletions src/command/activate/activate-command.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Docker } from '../../model/index.ts';
import { MacBuilder } from '../../model/mac-builder.ts';
import { PlatformSetup } from '../../logic/unity/platform-setup/index.ts';
import { PlatformValidation } from '../../logic/unity/platform-validation/platform-validation.ts';
import { yargs } from '../../dependencies.ts';

const originalDockerRun = Docker.run;
const originalMacBuilderRun = MacBuilder.run;
Expand Down Expand Up @@ -62,4 +63,36 @@ describe('ActivateCommand', () => {

expect(baseOptions.activateOnly).toBeUndefined();
});

it('defaults targetPlatform to NoTarget, not the build-oriented StandaloneWindows64 default', async () => {
// Real bug (game-ci/unity-activate#111 thin-wrapper CI): `game-ci
// activate <path>` with no --target-platform inherited UnityOptions'
// build default of StandaloneWindows64, which threw "Windows-based
// builds are only supported on 2019.3.X+" for older Unity versions even
// though activation doesn't build anything. NoTarget maps to the
// generic image and skips that check entirely.
const parser = yargs([]).exitProcess(false).fail((message, error) => {
throw error || new Error(message);
});

const command = new ActivateCommand('activate');
await command.configureOptions(parser as any);

const argv = await parser.parseAsync();

expect(argv.targetPlatform).toBe('NoTarget');
});

it('still honors an explicitly passed targetPlatform', async () => {
const parser = yargs(['--targetPlatform', 'StandaloneLinux64']).exitProcess(false).fail((message, error) => {
throw error || new Error(message);
});

const command = new ActivateCommand('activate');
await command.configureOptions(parser as any);

const argv = await parser.parseAsync();

expect(argv.targetPlatform).toBe('StandaloneLinux64');
});
});
10 changes: 10 additions & 0 deletions src/command/activate/activate-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { UnityOptions } from '../../command-options/unity-options.ts';
import type { YargsInstance, Options } from '../../dependencies.ts';
import { PlatformValidation } from '../../logic/unity/platform-validation/platform-validation.ts';
import { ProjectOptions } from '../../command-options/project-options.ts';
import { UnityTargetPlatform } from '../../model/unity/target-platform/unity-target-platform.ts';

/**
* Activates (and only activates) a Unity license, leaving it active for a
Expand Down Expand Up @@ -44,6 +45,15 @@ export class ActivateCommand extends CommandBase implements CommandInterface {
public async configureOptions(yargs: YargsInstance): Promise<void> {
await ProjectOptions.configure(yargs);
await UnityOptions.configure(yargs);
// UnityOptions.configure() defaults targetPlatform to StandaloneWindows64
// (a build-oriented default). Activation doesn't build anything - it
// just needs *an* editor image to run license activation inside - so
// override the default to NoTarget, which RunnerImageTag maps to the
// generic image and skips build-target validation entirely (e.g. the
// "Windows builds need 2019.3+" check, which has nothing to do with
// activating a license). Callers can still pass --target-platform
// explicitly if they have a reason to.
yargs.option('targetPlatform', { default: UnityTargetPlatform.NoTarget });
// Needed by Docker.run() for the container mount path - normally comes
// from BuildOptions, but `activate` intentionally doesn't pull in
// BuildOptions' build-specific flags (buildName, buildMethod, etc.),
Expand Down
28 changes: 28 additions & 0 deletions src/core/logger/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,31 @@ describe('logger groups', () => {
expect(result).toBe(42);
});
});

describe('logger error formatting', () => {
const originalConsoleError = console.error;
let errorLines: string[] = [];

beforeEach(async () => {
errorLines = [];
console.error = mock((...args: any[]) => {
errorLines.push(args.join(' '));
});
await configureLogger(Verbosity.normal);
});

afterEach(() => {
console.error = originalConsoleError;
});

it('prints an Error object\'s message and stack, not "{}"', () => {
// Real bug: Error's message/stack/name are non-enumerable own
// properties, so JSON.stringify(error) - the previous fallback for any
// non-string value - silently produced '{}', masking every uncaught
// failure's actual message (see game-ci/unity-activate#111).
(globalThis as any).log.error(new Error('something specific broke'));

expect(errorLines.some((line) => line.includes('something specific broke'))).toBe(true);
expect(errorLines.some((line) => line.trim() === '[ERROR] {}')).toBe(false);
});
});
7 changes: 7 additions & 0 deletions src/core/logger/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ export const configureLogger = async (verbosity: Verbosity) => {
if (value === undefined) return 'undefined';
if (value === null) return 'null';
if (typeof value === 'string') return value;
// Error's message/stack/name are non-enumerable own properties, so
// JSON.stringify(error) below silently produces '{}' for any bare Error
// - masking every uncaught failure's actual message. Must be handled
// before the JSON.stringify fallback, not caught by it.
if (value instanceof Error) {
return value.stack || `${value.name}: ${value.message}`;
}
try {
return JSON.stringify(value, null, 2);
} catch {
Expand Down
Loading