diff --git a/README.md b/README.md index 6abf2b9..e6ea61a 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,9 @@ A powerful command line utility for the Unity Game Engine. Automate Unity projec npm install -g @rage-against-the-pixel/unity-cli ``` +> [!IMPORTANT] +> Requires Node v22.12 or higher. + ## Usage In general, the command structure is: @@ -96,7 +99,7 @@ unity-cli license-version - `-e`, `--email`: Email associated with the Unity account. Required when activating a personal or professional license. - `-p`, `--password`: Password for the Unity account. Required when activating a personal or professional license. - `-s`, `--serial`: License serial number. Required when activating a professional license. -- `-c`, `--config`: Path to the configuration file, or base64 encoded JSON string. Required when activating a floating license. +- `-c`, `--config`: Path to the configuration file, raw JSON, or base64 encoded JSON string. Required when activating a floating license. - `--json`: Prints the last line of output as JSON string. - `--verbose`: Enable verbose output. diff --git a/package-lock.json b/package-lock.json index 5347667..e827a85 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@rage-against-the-pixel/unity-cli", - "version": "1.6.5", + "version": "1.6.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@rage-against-the-pixel/unity-cli", - "version": "1.6.5", + "version": "1.6.6", "license": "MIT", "dependencies": { "@electron/asar": "^4.0.1", diff --git a/package.json b/package.json index 34052d0..1178d55 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rage-against-the-pixel/unity-cli", - "version": "1.6.5", + "version": "1.6.6", "description": "A command line utility for the Unity Game Engine.", "author": "RageAgainstThePixel", "license": "MIT", diff --git a/src/license-client.ts b/src/license-client.ts index ea618e0..267b26f 100644 --- a/src/license-client.ts +++ b/src/license-client.ts @@ -92,7 +92,7 @@ export class LicensingClient { switch (process.platform) { case 'win32': // %PROGRAMDATA%\Unity\Config - servicesConfigDirectory = path.join(process.env.PROGRAMDATA || '', 'Unity', 'Config'); + servicesConfigDirectory = path.join(process.env.PROGRAMDATA || 'C:\\ProgramData', 'Unity', 'Config'); break; case 'darwin': // /Library/Application Support/Unity/config @@ -119,6 +119,45 @@ export class LicensingClient { return path.join(servicesConfigDirectory, 'services-config.json'); } + private tryParseJson(content: string | undefined): string | undefined { + if (!content) { + return undefined; + } + + try { + JSON.parse(content); + return content; + } catch { + return undefined; + } + } + + private resolveServicesConfigContent(input: string): string { + const trimmedInput = input.trim(); + + if (trimmedInput.length === 0) { + throw new Error('Services config value is empty. Provide a file path, JSON, or base64 encoded JSON string.'); + } + + const directJson = this.tryParseJson(trimmedInput); + + if (directJson) { + return directJson; + } + + const base64Regex = /^[A-Za-z0-9+/]*={0,2}$/; + if (base64Regex.test(trimmedInput)) { + const decoded = Buffer.from(trimmedInput, 'base64').toString('utf-8').trim(); + const decodedJson = this.tryParseJson(decoded); + + if (decodedJson) { + return decodedJson; + } + } + + throw new Error('Services config value is not a valid JSON string or base64 encoded JSON string.'); + } + /** * Gets the path to the Unity Licensing Client log file. * @see https://docs.unity.com/en-us/licensing-server/troubleshooting-client#logs @@ -441,7 +480,8 @@ export class LicensingClient { fs.copyFileSync(options.servicesConfig, servicesConfigPath); } else { - fs.writeFileSync(servicesConfigPath, Buffer.from(options.servicesConfig, 'base64')); + const configContent = this.resolveServicesConfigContent(options.servicesConfig); + fs.writeFileSync(servicesConfigPath, configContent, { encoding: 'utf-8' }); } if (process.platform !== 'win32') { diff --git a/tests/license-client.test.ts b/tests/license-client.test.ts new file mode 100644 index 0000000..b771564 --- /dev/null +++ b/tests/license-client.test.ts @@ -0,0 +1,27 @@ +import { LicensingClient } from '../src/license-client'; + +describe('LicensingClient services config handling', () => { + const invokeResolver = (input: string) => { + const client = new LicensingClient(); + return (client as any).resolveServicesConfigContent(input); + }; + + it('accepts raw JSON input', () => { + const json = '{"floatingServer":"https://example.com"}'; + expect(invokeResolver(json)).toBe(json); + }); + + it('accepts base64 encoded JSON input', () => { + const json = '{"floatingServer":"https://example.com"}'; + const encoded = Buffer.from(json, 'utf-8').toString('base64'); + expect(invokeResolver(encoded)).toBe(json); + }); + + it('rejects invalid inline config', () => { + expect(() => invokeResolver('not-a-valid-config')).toThrow('Services config value is not a valid JSON string or base64 encoded JSON string.'); + }); + + it('rejects empty inline config', () => { + expect(() => invokeResolver(' ')).toThrow('Services config value is empty. Provide a file path, JSON, or base64 encoded JSON string.'); + }); +});