From 654d320f890e375b6741a4534eda27282c3ec4fc Mon Sep 17 00:00:00 2001 From: Alexis Gervacio Date: Fri, 3 Jul 2026 15:24:19 -0700 Subject: [PATCH 1/3] Fix wrong mac path for License Server Activation when using Unity 6.3+ --- dist/platforms/mac/steps/activate.sh | 11 ++++++++++- dist/platforms/mac/steps/return_license.sh | 11 ++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/dist/platforms/mac/steps/activate.sh b/dist/platforms/mac/steps/activate.sh index 92f4e9e8d..e3eb54b4e 100755 --- a/dist/platforms/mac/steps/activate.sh +++ b/dist/platforms/mac/steps/activate.sh @@ -35,7 +35,16 @@ elif [[ -n "$UNITY_LICENSING_SERVER" ]]; then mkdir -p "$UNITY_LICENSE_PATH/config/" cp "$ACTION_FOLDER/unity-config/services-config.json" "$UNITY_LICENSE_PATH/config/services-config.json" - /Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/Frameworks/UnityLicensingClient.app/Contents/MacOS/Unity.Licensing.Client \ + UNITY_EDITOR_DIR="/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app" + UNITY_LICENSING_CLIENT_SUBDIR="Frameworks" + + # Unity 6000.3+ moved UnityLicensingClient from Contents/Frameworks to Contents/Helpers. + # See: https://docs.unity.com/en-us/licensing-server/client-config + if [[ "$UNITY_VERSION" =~ ^6000\.([3-9]|[1-9][0-9]) ]]; then + UNITY_LICENSING_CLIENT_SUBDIR="Helpers" + fi + + "$UNITY_EDITOR_DIR/Contents/$UNITY_LICENSING_CLIENT_SUBDIR/UnityLicensingClient.app/Contents/MacOS/Unity.Licensing.Client" \ --acquire-floating > license.txt # Store the exit code from the verify command diff --git a/dist/platforms/mac/steps/return_license.sh b/dist/platforms/mac/steps/return_license.sh index c7748fcc2..f2d0376e2 100755 --- a/dist/platforms/mac/steps/return_license.sh +++ b/dist/platforms/mac/steps/return_license.sh @@ -9,7 +9,16 @@ if [[ -n "$UNITY_LICENSING_SERVER" ]]; then # Return any floating license used. # echo "Returning floating license: \"$FLOATING_LICENSE\"" - /Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/Frameworks/UnityLicensingClient.app/Contents/MacOS/Unity.Licensing.Client \ + UNITY_EDITOR_DIR="/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app" + UNITY_LICENSING_CLIENT_SUBDIR="Frameworks" + + # Unity 6000.3+ moved UnityLicensingClient from Contents/Frameworks to Contents/Helpers. + # See: https://docs.unity.com/en-us/licensing-server/client-config + if [[ "$UNITY_VERSION" =~ ^6000\.([3-9]|[1-9][0-9]) ]]; then + UNITY_LICENSING_CLIENT_SUBDIR="Helpers" + fi + + "$UNITY_EDITOR_DIR/Contents/$UNITY_LICENSING_CLIENT_SUBDIR/UnityLicensingClient.app/Contents/MacOS/Unity.Licensing.Client" \ --return-floating "$FLOATING_LICENSE" elif [[ -n "$UNITY_SERIAL" ]]; then # From ab9dcf5f9f13d40eecd82fc4dc63c9c8182160e2 Mon Sep 17 00:00:00 2001 From: Alexis Gervacio Date: Fri, 3 Jul 2026 17:00:57 -0700 Subject: [PATCH 2/3] Add unit tests for licensing path branches --- src/integrity.test.ts | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/integrity.test.ts b/src/integrity.test.ts index d3c59ce05..2d5df2ad4 100644 --- a/src/integrity.test.ts +++ b/src/integrity.test.ts @@ -1,5 +1,6 @@ import { describe, it, expect, vi, beforeEach, afterEach, beforeAll, afterAll, test } from 'vitest'; import { stat } from 'node:fs/promises'; +import { readFile } from 'node:fs/promises'; describe('Integrity tests', () => { describe('package-lock.json', () => { @@ -7,4 +8,32 @@ describe('Integrity tests', () => { await expect(stat(`${process.cwd()}/package-lock.json`)).rejects.toThrowError(); }); }); + + describe('mac licensing scripts', () => { + it('activate script switches Unity Licensing Client path for Unity 6000.3+', async () => { + const activateScriptPath = `${process.cwd()}/dist/platforms/mac/steps/activate.sh`; + const activateScript = await readFile(activateScriptPath, 'utf8'); + + expect(activateScript).toContain('UNITY_LICENSING_CLIENT_SUBDIR="Frameworks"'); + expect(activateScript).toContain('UNITY_LICENSING_CLIENT_SUBDIR="Helpers"'); + expect(activateScript).toContain('^6000\\.([3-9]|[1-9][0-9])'); + expect(activateScript).toContain('https://docs.unity.com/en-us/licensing-server/client-config'); + expect(activateScript).toContain( + '"$UNITY_EDITOR_DIR/Contents/$UNITY_LICENSING_CLIENT_SUBDIR/UnityLicensingClient.app/Contents/MacOS/Unity.Licensing.Client"', + ); + }); + + it('return license script uses the same Unity version-gated path logic', async () => { + const returnLicenseScriptPath = `${process.cwd()}/dist/platforms/mac/steps/return_license.sh`; + const returnLicenseScript = await readFile(returnLicenseScriptPath, 'utf8'); + + expect(returnLicenseScript).toContain('UNITY_LICENSING_CLIENT_SUBDIR="Frameworks"'); + expect(returnLicenseScript).toContain('UNITY_LICENSING_CLIENT_SUBDIR="Helpers"'); + expect(returnLicenseScript).toContain('^6000\\.([3-9]|[1-9][0-9])'); + expect(returnLicenseScript).toContain('https://docs.unity.com/en-us/licensing-server/client-config'); + expect(returnLicenseScript).toContain( + '"$UNITY_EDITOR_DIR/Contents/$UNITY_LICENSING_CLIENT_SUBDIR/UnityLicensingClient.app/Contents/MacOS/Unity.Licensing.Client"', + ); + }); + }); }); From 1d177e4fda7fd0b475237ed3d9d425b8069732c9 Mon Sep 17 00:00:00 2001 From: Alexis Gervacio Date: Fri, 3 Jul 2026 18:15:34 -0700 Subject: [PATCH 3/3] Update tests based on bot feedback --- src/integrity.test.ts | 120 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 106 insertions(+), 14 deletions(-) diff --git a/src/integrity.test.ts b/src/integrity.test.ts index 2d5df2ad4..16c906cfb 100644 --- a/src/integrity.test.ts +++ b/src/integrity.test.ts @@ -1,6 +1,108 @@ import { describe, it, expect, vi, beforeEach, afterEach, beforeAll, afterAll, test } from 'vitest'; -import { stat } from 'node:fs/promises'; -import { readFile } from 'node:fs/promises'; +import { stat, mkdtemp, mkdir, writeFile, chmod, readFile } from 'node:fs/promises'; +import { tmpdir } from 'node:os'; +import { join } from 'node:path'; +import { execFile } from 'node:child_process'; +import { promisify } from 'node:util'; + +const execFileAsync = promisify(execFile); + +type ScriptKind = 'activate' | 'return'; + +async function makeExecutableFile(path: string, content: string): Promise { + await writeFile(path, content, 'utf8'); + await chmod(path, 0o755); +} + +async function prepareMockUnityClient(editorDir: string): Promise { + const frameworkClientDir = join( + editorDir, + 'Contents', + 'Frameworks', + 'UnityLicensingClient.app', + 'Contents', + 'MacOS', + ); + const helperClientDir = join( + editorDir, + 'Contents', + 'Helpers', + 'UnityLicensingClient.app', + 'Contents', + 'MacOS', + ); + + await mkdir(frameworkClientDir, { recursive: true }); + await mkdir(helperClientDir, { recursive: true }); + + const mockClient = `#!/usr/bin/env bash +echo "$0" > "$CAPTURE_PATH_FILE" +if [[ "$1" == "--acquire-floating" ]]; then + echo '"ok"' + echo '"floating-license"' + echo '"timeout"' + echo '"3600"' +fi +`; + + await makeExecutableFile(join(frameworkClientDir, 'Unity.Licensing.Client'), mockClient); + await makeExecutableFile(join(helperClientDir, 'Unity.Licensing.Client'), mockClient); +} + +async function executeAndCaptureResolvedClientPath( + sourceScriptPath: string, + unityVersion: string, + kind: ScriptKind, +): Promise { + const root = await mkdtemp(join(tmpdir(), 'unity-builder-integrity-')); + const editorDir = join(root, 'mock-editor', 'Unity.app'); + const activatePath = join(root, 'activate-path'); + const unityLicensePath = join(root, 'unity-license-path'); + const actionFolder = join(root, 'action-folder'); + const unityConfigDir = join(actionFolder, 'unity-config'); + const capturePathFile = join(root, 'captured-client-path.txt'); + const localScriptPath = join(root, kind === 'activate' ? 'activate.sh' : 'return_license.sh'); + + await mkdir(activatePath, { recursive: true }); + await mkdir(unityLicensePath, { recursive: true }); + await mkdir(unityConfigDir, { recursive: true }); + await writeFile(join(unityConfigDir, 'services-config.json'), '{}', 'utf8'); + await prepareMockUnityClient(editorDir); + + const scriptSource = await readFile(sourceScriptPath, 'utf8'); + const patchedScript = scriptSource.replace( + 'UNITY_EDITOR_DIR="/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app"', + 'UNITY_EDITOR_DIR="$TEST_UNITY_EDITOR_DIR"', + ); + await makeExecutableFile(localScriptPath, patchedScript); + + const environment: NodeJS.ProcessEnv = { + ...process.env, + TEST_UNITY_EDITOR_DIR: editorDir, + ACTIVATE_LICENSE_PATH: activatePath, + UNITY_LICENSE_PATH: unityLicensePath, + ACTION_FOLDER: actionFolder, + UNITY_LICENSING_SERVER: 'http://mock-server', + UNITY_VERSION: unityVersion, + CAPTURE_PATH_FILE: capturePathFile, + UNITY_SERIAL: '', + UNITY_EMAIL: '', + UNITY_PASSWORD: '', + FLOATING_LICENSE: 'floating-license', + }; + + await execFileAsync('bash', [localScriptPath], { env: environment }); + + return (await readFile(capturePathFile, 'utf8')).trim(); +} + +async function expectVersionGateBehavior(scriptPath: string, kind: ScriptKind): Promise { + const belowGatePath = await executeAndCaptureResolvedClientPath(scriptPath, '6000.2.9f1', kind); + const aboveGatePath = await executeAndCaptureResolvedClientPath(scriptPath, '6000.3.0f1', kind); + + expect(belowGatePath).toContain('/Contents/Frameworks/UnityLicensingClient.app/Contents/MacOS/Unity.Licensing.Client'); + expect(aboveGatePath).toContain('/Contents/Helpers/UnityLicensingClient.app/Contents/MacOS/Unity.Licensing.Client'); +} describe('Integrity tests', () => { describe('package-lock.json', () => { @@ -14,26 +116,16 @@ describe('Integrity tests', () => { const activateScriptPath = `${process.cwd()}/dist/platforms/mac/steps/activate.sh`; const activateScript = await readFile(activateScriptPath, 'utf8'); - expect(activateScript).toContain('UNITY_LICENSING_CLIENT_SUBDIR="Frameworks"'); - expect(activateScript).toContain('UNITY_LICENSING_CLIENT_SUBDIR="Helpers"'); - expect(activateScript).toContain('^6000\\.([3-9]|[1-9][0-9])'); expect(activateScript).toContain('https://docs.unity.com/en-us/licensing-server/client-config'); - expect(activateScript).toContain( - '"$UNITY_EDITOR_DIR/Contents/$UNITY_LICENSING_CLIENT_SUBDIR/UnityLicensingClient.app/Contents/MacOS/Unity.Licensing.Client"', - ); + await expectVersionGateBehavior(activateScriptPath, 'activate'); }); it('return license script uses the same Unity version-gated path logic', async () => { const returnLicenseScriptPath = `${process.cwd()}/dist/platforms/mac/steps/return_license.sh`; const returnLicenseScript = await readFile(returnLicenseScriptPath, 'utf8'); - expect(returnLicenseScript).toContain('UNITY_LICENSING_CLIENT_SUBDIR="Frameworks"'); - expect(returnLicenseScript).toContain('UNITY_LICENSING_CLIENT_SUBDIR="Helpers"'); - expect(returnLicenseScript).toContain('^6000\\.([3-9]|[1-9][0-9])'); expect(returnLicenseScript).toContain('https://docs.unity.com/en-us/licensing-server/client-config'); - expect(returnLicenseScript).toContain( - '"$UNITY_EDITOR_DIR/Contents/$UNITY_LICENSING_CLIENT_SUBDIR/UnityLicensingClient.app/Contents/MacOS/Unity.Licensing.Client"', - ); + await expectVersionGateBehavior(returnLicenseScriptPath, 'return'); }); }); });