diff --git a/.github/workflows/build-options.json b/.github/workflows/build-options.json index b9b9f77..5264ed0 100644 --- a/.github/workflows/build-options.json +++ b/.github/workflows/build-options.json @@ -5,6 +5,11 @@ "macos-latest" ], "unity-version": [ + "4.7.2", + "5.6.7f1 (e80cc3114ac1)", + "2017.4.40f1", + "2018", + "2018.4", "2019.x", "2020.*", "2021.3.x", diff --git a/.github/workflows/unity-build.yml b/.github/workflows/unity-build.yml index 881acaa..c2ea094 100644 --- a/.github/workflows/unity-build.yml +++ b/.github/workflows/unity-build.yml @@ -23,7 +23,6 @@ jobs: contents: read env: UNITY_PROJECT_PATH: '' # Set from create-project step - RUN_BUILD: '' # Set to true if the build pipeline package can be installed and used steps: - name: Free Disk Space if: ${{ matrix.os == 'ubuntu-latest' && (matrix.unity-version != '2018' && matrix.unity-version != '2017.4.40f1') }} @@ -83,6 +82,7 @@ jobs: unity-cli create-project --name "Unity Project" --unity-editor "${UNITY_EDITOR_PATH}" --json - name: Verify UNITY_PROJECT_PATH variable if: ${{ matrix.unity-version != 'none' }} + id: verify-project-path shell: bash run: | if [ -z "${UNITY_PROJECT_PATH}" ]; then @@ -97,16 +97,16 @@ jobs: if [ -z "$minor" ]; then minor=0 fi - # numeric comparison: enable build for major > 2019 or major == 2019 and minor >= 4 - if [ "$major" -gt 2019 ] || { [ "$major" -eq 2019 ] && [ "$minor" -ge 4 ]; }; then + # numeric comparison: enable build for major >= 2019 + if [ "$major" -ge 2019 ]; then echo "Proceeding with build for Unity version $version" - echo "RUN_BUILD=true" >> $GITHUB_ENV + echo "RUN_BUILD=true" >> $GITHUB_OUTPUT else echo "Skipping build: Unity version $version does not support the build pipeline package (requires 2019.4+)" fi - name: Install OpenUPM and build pipeline package shell: bash - if: ${{ env.RUN_BUILD == 'true' }} + if: ${{ steps.verify-project-path.outputs.RUN_BUILD == 'true' }} run: | npm install -g openupm-cli cd "${UNITY_PROJECT_PATH}" @@ -120,7 +120,7 @@ jobs: # ensure android dependencies are installed unity-cli setup-unity -p "${UNITY_PROJECT_PATH}" -m android - name: Build Project - if: ${{ env.RUN_BUILD == 'true' }} + if: ${{ steps.verify-project-path.outputs.RUN_BUILD == 'true' }} timeout-minutes: 60 shell: bash run: | diff --git a/package-lock.json b/package-lock.json index 61531a7..4b37b8a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@rage-against-the-pixel/unity-cli", - "version": "1.7.1", + "version": "1.7.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@rage-against-the-pixel/unity-cli", - "version": "1.7.1", + "version": "1.7.2", "license": "MIT", "dependencies": { "@electron/asar": "^4.0.1", diff --git a/package.json b/package.json index d8f8ee4..e3cd30f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rage-against-the-pixel/unity-cli", - "version": "1.7.1", + "version": "1.7.2", "description": "A command line utility for the Unity Game Engine.", "author": "RageAgainstThePixel", "license": "MIT", diff --git a/src/cli.ts b/src/cli.ts index aef0894..324b2a5 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -421,7 +421,7 @@ program.command('run') .option('--unity-editor ', 'The path to the Unity Editor executable. If unspecified, --unity-project or the UNITY_EDITOR_PATH environment variable must be set.') .option('--unity-project ', 'The path to a Unity project. If unspecified, the UNITY_PROJECT_PATH environment variable will be used, otherwise no project will be specified.') .option('--log-name ', 'The name of the log file.') - .option('--log-level ', 'Set the logging level (debug, info, minimal, warning, error).') + .option('--log-level ', 'Set the logging level (debug, info, minimal, warning, error). Default is info.') .option('--verbose', 'Enable verbose logging. Deprecated, use --log-level instead.') .allowUnknownOption(true) .argument('', 'Arguments to pass to the Unity Editor executable.') diff --git a/src/unity-logging.ts b/src/unity-logging.ts index 76b3559..d100ce5 100644 --- a/src/unity-logging.ts +++ b/src/unity-logging.ts @@ -95,7 +95,7 @@ class ActionTelemetryAccumulator { private totalErrorCount = 0; private playerBuildInfoSteps: PlayerBuildInfoStepSummary[] = []; - record(action: UTPBase): boolean { + public record(action: UTPBase): boolean { if (action.phase === Phase.Begin) { this.pendingActions.set(this.getActionKey(action), action); return true; @@ -133,7 +133,7 @@ class ActionTelemetryAccumulator { return false; } - recordPlayerBuildInfo(info: UTPPlayerBuildInfo): boolean { + public recordPlayerBuildInfo(info: UTPPlayerBuildInfo): boolean { if (!Array.isArray(info.steps) || info.steps.length === 0) { return false; } @@ -162,7 +162,7 @@ class ActionTelemetryAccumulator { return true; } - snapshot(): ActionTableSnapshot | undefined { + public snapshot(): ActionTableSnapshot | undefined { if (this.completedActions.length === 0 && this.pendingActions.size === 0 && this.playerBuildInfoSteps.length === 0) { return undefined; } @@ -902,14 +902,13 @@ function formatMemoryLeakTable(memLeaks: UTPMemoryLeak): string { function buildUtpLogPath(logPath: string): string { const parsed = path.parse(logPath); - const utpFileName = `utp-${parsed.name}.json`; + const utpFileName = `${parsed.name}-utp-json.log`; return parsed.dir ? path.join(parsed.dir, utpFileName) : utpFileName; } async function writeUtpTelemetryLog(filePath: string, entries: UTP[], logger: Logger): Promise { try { - const content = `${JSON.stringify(entries, null, 2)}\n`; - await fs.promises.writeFile(filePath, content, 'utf8'); + await fs.promises.writeFile(filePath, `${JSON.stringify(entries)}\n`, 'utf8'); } catch (error) { logger.warn(`Failed to write UTP telemetry log (${filePath}): ${error}`); } @@ -940,14 +939,12 @@ export function TailLogFile(logPath: string, projectPath: string | undefined): L }; const flushTelemetryLog = async (): Promise => { - if (telemetryFlushed) { - return; - } + if (telemetryFlushed) { return; } telemetryFlushed = true; await writeUtpTelemetryLog(utpLogPath, telemetry, logger); }; - const writeStdout = (content: string, restoreTable: boolean = true): void => { + const writeStdoutThenTableContent = (content: string, restoreTable: boolean = true): void => { actionTableRenderer.prepareForContent(); process.stdout.write(content); if (restoreTable) { @@ -955,6 +952,39 @@ export function TailLogFile(logPath: string, projectPath: string | undefined): L } }; + function printUTP(utp: UTP): void { + // switch utp types, fallback to json if we don't have a toString() implementation or a type implementation + switch (utp.type) { + case 'Action': { + const actionEntry = utp as UTPBase; + const tableChanged = actionAccumulator.record(actionEntry); + + if (tableChanged) { + renderActionTable(); + } + + break; + } + case 'MemoryLeaks': + logger.debug(formatMemoryLeakTable(utp as UTPMemoryLeak)); + break; + case 'PlayerBuildInfo': { + const infoEntry = utp as UTPPlayerBuildInfo; + const changed = actionAccumulator.recordPlayerBuildInfo(infoEntry); + + if (changed) { + renderActionTable(); + } + + break; + } + default: + // Print raw JSON for unhandled UTP types + writeStdoutThenTableContent(`${JSON.stringify(utp)}\n`); + break; + } + } + async function readNewLogContent(): Promise { try { if (!fs.existsSync(logPath)) { return; } @@ -990,10 +1020,7 @@ export function TailLogFile(logPath: string, projectPath: string | undefined): L const jsonPart = line.substring('##utp:'.length).trim(); try { const sanitizedJson = sanitizeTelemetryJson(jsonPart); - - if (!sanitizedJson) { - continue; - } + if (!sanitizedJson) { continue; } const utpJson = JSON.parse(sanitizedJson); const utp = utpJson as UTP; @@ -1030,44 +1057,15 @@ export function TailLogFile(logPath: string, projectPath: string | undefined): L } } } - } else { - // switch utp types, fallback to json if we don't have a toString() implementation or a type implementation - switch (utp.type) { - case 'Action': { - const actionEntry = utp as UTPBase; - const tableChanged = actionAccumulator.record(actionEntry); - - if (tableChanged) { - renderActionTable(); - } - - break; - } - case 'MemoryLeaks': - logger.debug(formatMemoryLeakTable(utp as UTPMemoryLeak)); - break; - case 'PlayerBuildInfo': { - const infoEntry = utp as UTPPlayerBuildInfo; - const changed = actionAccumulator.recordPlayerBuildInfo(infoEntry); - - if (changed) { - renderActionTable(); - } - - break; - } - default: - // Print raw JSON for unhandled UTP types - writeStdout(`${jsonPart}\n`); - break; - } + } else if (Logger.instance.logLevel === LogLevel.UTP) { + printUTP(utp); } } catch (error) { logger.warn(`Failed to parse telemetry JSON: ${error} -- raw: ${jsonPart}`); } } else { if (Logger.instance.logLevel !== LogLevel.UTP) { - writeStdout(`${line}\n`); + process.stdout.write(`${line}\n`); } } } @@ -1098,7 +1096,7 @@ export function TailLogFile(logPath: string, projectPath: string | undefined): L try { // write a final newline to separate log output - writeStdout('\n'); + process.stdout.write('\n'); } catch (error: any) { if (error.code !== 'EPIPE') { logger.warn(`Error while writing log tail: ${error}`);