From b10438d53c4efbe4052dbef67908a5ca1988291d Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sat, 20 Dec 2025 16:37:46 -0500 Subject: [PATCH 01/19] unity-cli@v1.8.2 - add additional unity utp log handling --- .github/workflows/build-options.json | 8 ++++++++ .github/workflows/unity-build.yml | 17 ++++++++++++----- unity-tests/BuildErrors.cs | 20 ++++++++++++++++++++ unity-tests/BuildWarnings.cs | 20 ++++++++++++++++++++ unity-tests/CompilerErrors.cs | 4 ++++ unity-tests/CompilerWarnings.cs | 20 ++++++++++++++++++++ unity-tests/EditmodeTestsErrors.cs | 16 ++++++++++++++++ unity-tests/PlaymodeTestsErrors.cs | 19 +++++++++++++++++++ 8 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 unity-tests/BuildErrors.cs create mode 100644 unity-tests/BuildWarnings.cs create mode 100644 unity-tests/CompilerErrors.cs create mode 100644 unity-tests/CompilerWarnings.cs create mode 100644 unity-tests/EditmodeTestsErrors.cs create mode 100644 unity-tests/PlaymodeTestsErrors.cs diff --git a/.github/workflows/build-options.json b/.github/workflows/build-options.json index b167129..7da9e5d 100644 --- a/.github/workflows/build-options.json +++ b/.github/workflows/build-options.json @@ -17,6 +17,14 @@ "6000.1.*", "6000.2" ], + "tests": [ + "CompilerWarnings", + "CompilerErrors", + "BuildWarnings", + "BuildErrors", + "PlaymodeTestsErrors", + "EditmodeTestsErrors" + ], "include": [ { "os": "ubuntu-latest", diff --git a/.github/workflows/unity-build.yml b/.github/workflows/unity-build.yml index 1e66910..527eb23 100644 --- a/.github/workflows/unity-build.yml +++ b/.github/workflows/unity-build.yml @@ -132,12 +132,12 @@ jobs: PACKAGE_MANAGER_LOG_PATH=$(unity-cli package-manager-logs) LICENSING_CLIENT_LOG_PATH=$(unity-cli licensing-client-logs) LICENSING_AUDIT_LOG_PATH=$(unity-cli licensing-audit-logs) - + echo "Hub Log Path: ${HUB_LOG_PATH}" echo "Package Manager Log Path: ${PACKAGE_MANAGER_LOG_PATH}" echo "Licensing Client Log Path: ${LICENSING_CLIENT_LOG_PATH}" echo "Licensing Audit Log Path: ${LICENSING_AUDIT_LOG_PATH}" - + if [ ! -f "${HUB_LOG_PATH}" ]; then echo "::warning:: Hub log file does not exist at ${HUB_LOG_PATH}" # find all info-log.json files in ~/.config/unity3d/ - print their paths @@ -151,18 +151,25 @@ jobs: find ~/.config/ -type f -exec echo "{}" \; echo "::warning:: Hub log file does not exist at any known location" fi - + if [ ! -f "${PACKAGE_MANAGER_LOG_PATH}" ]; then echo "::warning::Package Manager log file does not exist at ${PACKAGE_MANAGER_LOG_PATH}" fi - + if [ ! -f "${LICENSING_CLIENT_LOG_PATH}" ]; then echo "::error::Licensing Client log file does not exist at ${LICENSING_CLIENT_LOG_PATH}" fi - + if [ ! -f "${LICENSING_AUDIT_LOG_PATH}" ]; then echo "::error::Licensing Audit log file does not exist at ${LICENSING_AUDIT_LOG_PATH}" fi + - name: Upload UTP logs + if: always() + uses: actions/upload-artifact@v6 + with: + name: utp-logs-${{ matrix.name }} + path: '**/*-utp-json.log' + if-no-files-found: ignore - name: Return License if: always() run: unity-cli return-license --license personal diff --git a/unity-tests/BuildErrors.cs b/unity-tests/BuildErrors.cs new file mode 100644 index 0000000..ce75c3f --- /dev/null +++ b/unity-tests/BuildErrors.cs @@ -0,0 +1,20 @@ +using UnityEditor; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; + +namespace UnityCli.UtpSamples +{ + /// + /// Forces the build pipeline to fail by throwing a BuildFailedException. + /// Place under an Editor folder when copying into a project. + /// + public class BuildErrors : IPreprocessBuildWithReport + { + public int callbackOrder => 0; + + public void OnPreprocessBuild(BuildReport report) + { + throw new System.Exception("Intentional build failure for test matrix coverage."); + } + } +} diff --git a/unity-tests/BuildWarnings.cs b/unity-tests/BuildWarnings.cs new file mode 100644 index 0000000..f365a77 --- /dev/null +++ b/unity-tests/BuildWarnings.cs @@ -0,0 +1,20 @@ +using UnityEditor; +using UnityEditor.Build; +using UnityEditor.Build.Reporting; + +namespace UnityCli.UtpSamples +{ + /// + /// Emits a build-time warning via the build pipeline (no custom UTP JSON logging). + /// Place under an Editor folder when copying into a project. + /// + public class BuildWarnings : IPreprocessBuildWithReport + { + public int callbackOrder => 0; + + public void OnPreprocessBuild(BuildReport report) + { + UnityEngine.Debug.LogWarning("Intentional build warning for test matrix coverage."); + } + } +} diff --git a/unity-tests/CompilerErrors.cs b/unity-tests/CompilerErrors.cs new file mode 100644 index 0000000..056f16e --- /dev/null +++ b/unity-tests/CompilerErrors.cs @@ -0,0 +1,4 @@ +// Intentional compiler error for matrix scenario coverage. +#error Intentional compiler error: CS1029 + +// Note: file is kept minimal so it can be copied into a project to force a build failure. diff --git a/unity-tests/CompilerWarnings.cs b/unity-tests/CompilerWarnings.cs new file mode 100644 index 0000000..c1fc077 --- /dev/null +++ b/unity-tests/CompilerWarnings.cs @@ -0,0 +1,20 @@ +using UnityEngine; + +namespace UnityCli.UtpSamples +{ + /// + /// Introduces a benign compiler warning (unused variable) without emitting custom logs. + /// + public class CompilerWarnings : MonoBehaviour + { + private void Awake() + { + ObsoleteApi(); // CS0618: call to obsolete member + } + + [System.Obsolete("Intentional warning for test matrix coverage", false)] + private static void ObsoleteApi() + { + } + } +} diff --git a/unity-tests/EditmodeTestsErrors.cs b/unity-tests/EditmodeTestsErrors.cs new file mode 100644 index 0000000..4c4d89c --- /dev/null +++ b/unity-tests/EditmodeTestsErrors.cs @@ -0,0 +1,16 @@ +using NUnit.Framework; + +namespace UnityCli.UtpSamples +{ + /// + /// Editmode test that intentionally fails to produce real test failure output. + /// + public class EditmodeTestsErrors + { + [Test] + public void FailsEditmodeSuite() + { + Assert.Fail("Intentional editmode failure for test matrix coverage."); + } + } +} diff --git a/unity-tests/PlaymodeTestsErrors.cs b/unity-tests/PlaymodeTestsErrors.cs new file mode 100644 index 0000000..729b166 --- /dev/null +++ b/unity-tests/PlaymodeTestsErrors.cs @@ -0,0 +1,19 @@ +using System.Collections; +using NUnit.Framework; +using UnityEngine.TestTools; + +namespace UnityCli.UtpSamples +{ + /// + /// Playmode test that intentionally fails to generate real test failure output. + /// + public class PlaymodeTestsErrors + { + [UnityTest] + public IEnumerator FailsPlaymodeSuite() + { + yield return null; + Assert.Fail("Intentional playmode failure for test matrix coverage."); + } + } +} From ed170475fcdeba6af410bde2baa3d83f71873afb Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sat, 20 Dec 2025 16:38:47 -0500 Subject: [PATCH 02/19] bump --- package-lock.json | 28 ++++++++++++++-------------- package.json | 2 +- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 55a86a3..8796015 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@rage-against-the-pixel/unity-cli", - "version": "1.8.1", + "version": "1.8.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@rage-against-the-pixel/unity-cli", - "version": "1.8.1", + "version": "1.8.2", "license": "MIT", "dependencies": { "@electron/asar": "^4.0.1", @@ -2269,9 +2269,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.9.7", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.7.tgz", - "integrity": "sha512-k9xFKplee6KIio3IDbwj+uaCLpqzOwakOgmqzPezM0sFJlFKcg30vk2wOiAJtkTSfx0SSQDSe8q+mWA/fSH5Zg==", + "version": "2.9.11", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", + "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -2411,9 +2411,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001760", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001760.tgz", - "integrity": "sha512-7AAMPcueWELt1p3mi13HR/LHH0TJLT11cnwDJEs3xA4+CK/PLKeO9Kl1oru24htkyUKtkGCvAx4ohB0Ttry8Dw==", + "version": "1.0.30001761", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz", + "integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==", "dev": true, "funding": [ { @@ -2724,9 +2724,9 @@ } }, "node_modules/dedent": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", - "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", + "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", "dev": true, "license": "MIT", "peerDependencies": { @@ -5968,9 +5968,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz", - "integrity": "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { diff --git a/package.json b/package.json index 759c2d4..581f58c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@rage-against-the-pixel/unity-cli", - "version": "1.8.1", + "version": "1.8.2", "description": "A command line utility for the Unity Game Engine.", "author": "RageAgainstThePixel", "license": "MIT", From 80515c3119c5abcbb6ea162e4e4d82a0b2427001 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sat, 20 Dec 2025 16:40:16 -0500 Subject: [PATCH 03/19] integrate tests --- .github/workflows/unity-build.yml | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/.github/workflows/unity-build.yml b/.github/workflows/unity-build.yml index 527eb23..85234f4 100644 --- a/.github/workflows/unity-build.yml +++ b/.github/workflows/unity-build.yml @@ -97,6 +97,37 @@ jobs: else echo "Skipping build: Unity version $version does not support the build pipeline package (requires 2019.4+)" fi + - name: Copy selected Unity test + if: ${{ matrix.unity-version != 'none' && matrix.tests != '' }} + run: | + set -euo pipefail + test_name="${{ matrix.tests }}" + src="${GITHUB_WORKSPACE}/unity-tests/${test_name}.cs" + if [ ! -f "$src" ]; then + echo "::error::Requested test '$test_name' not found at $src" && exit 1 + fi + + case "$test_name" in + CompilerWarnings|CompilerErrors) + dest="$UNITY_PROJECT_PATH/Assets/UnityCliTests" + ;; + BuildWarnings|BuildErrors) + dest="$UNITY_PROJECT_PATH/Assets/Editor/UnityCliTests" + ;; + PlaymodeTestsErrors) + dest="$UNITY_PROJECT_PATH/Assets/Tests/PlayMode/UnityCliTests" + ;; + EditmodeTestsErrors) + dest="$UNITY_PROJECT_PATH/Assets/Tests/EditMode/UnityCliTests" + ;; + *) + echo "::error::Unknown test selection '$test_name'" && exit 1 + ;; + esac + + mkdir -p "$dest" + cp "$src" "$dest/" + echo "Copied $test_name to $dest" - name: Install OpenUPM and build pipeline package if: ${{ steps.verify-project-path.outputs.RUN_BUILD == 'true' }} working-directory: ${{ env.UNITY_PROJECT_PATH }} From f38b09944f9c778497378a00ae7be90215e80983 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sat, 20 Dec 2025 16:40:48 -0500 Subject: [PATCH 04/19] default permissions --- .github/workflows/unity-build.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/unity-build.yml b/.github/workflows/unity-build.yml index 85234f4..b60e366 100644 --- a/.github/workflows/unity-build.yml +++ b/.github/workflows/unity-build.yml @@ -17,6 +17,8 @@ jobs: strategy: matrix: ${{ fromJSON(inputs.matrix) }} fail-fast: false + permissions: + contents: read defaults: run: shell: bash From ca76f67d4684fbde99fdb1647dcaa8c9ecde7934 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sat, 20 Dec 2025 16:42:33 -0500 Subject: [PATCH 05/19] update test logic --- .github/workflows/unity-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unity-build.yml b/.github/workflows/unity-build.yml index b60e366..c8b3aeb 100644 --- a/.github/workflows/unity-build.yml +++ b/.github/workflows/unity-build.yml @@ -100,7 +100,7 @@ jobs: echo "Skipping build: Unity version $version does not support the build pipeline package (requires 2019.4+)" fi - name: Copy selected Unity test - if: ${{ matrix.unity-version != 'none' && matrix.tests != '' }} + if: ${{ steps.verify-project-path.outputs.RUN_BUILD == 'true' && matrix.tests != '' }} run: | set -euo pipefail test_name="${{ matrix.tests }}" From b531116b9701f543d239ab286ea70c542e927c2f Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sat, 20 Dec 2025 18:00:06 -0500 Subject: [PATCH 06/19] update artifact names --- .github/workflows/unity-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/unity-build.yml b/.github/workflows/unity-build.yml index c8b3aeb..075fe5e 100644 --- a/.github/workflows/unity-build.yml +++ b/.github/workflows/unity-build.yml @@ -200,7 +200,7 @@ jobs: if: always() uses: actions/upload-artifact@v6 with: - name: utp-logs-${{ matrix.name }} + name: ${{ matrix.os }}-${{ matrix.unity-version }}-${{ matrix.build-target }}-utp-logs path: '**/*-utp-json.log' if-no-files-found: ignore - name: Return License From 0d99a0c53bdeed0246396bbc8be8f81de0cb2d4d Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 21 Dec 2025 19:18:50 -0500 Subject: [PATCH 07/19] test dev job builder --- .github/workflows/build-options.json | 1 + .github/workflows/integration-tests.yml | 3 +- .github/workflows/unity-build.yml | 12 +++++-- .gitignore | 2 ++ src/logging.ts | 46 ++++++++++++++++++++++--- src/unity-editor.ts | 1 + src/unity-logging.ts | 2 ++ 7 files changed, 59 insertions(+), 8 deletions(-) diff --git a/.github/workflows/build-options.json b/.github/workflows/build-options.json index 7da9e5d..8630410 100644 --- a/.github/workflows/build-options.json +++ b/.github/workflows/build-options.json @@ -18,6 +18,7 @@ "6000.2" ], "tests": [ + "None", "CompilerWarnings", "CompilerErrors", "BuildWarnings", diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 06d1bd8..02c80c4 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -15,11 +15,12 @@ jobs: runs-on: ubuntu-latest permissions: contents: read + checks: write # to publish unit test results via checks github api steps: - uses: actions/checkout@v6 with: sparse-checkout: .github/ - - uses: RageAgainstThePixel/job-builder@v1 + - uses: RageAgainstThePixel/job-builder@development id: setup-jobs with: build-options: ./.github/workflows/build-options.json diff --git a/.github/workflows/unity-build.yml b/.github/workflows/unity-build.yml index 075fe5e..b8323bd 100644 --- a/.github/workflows/unity-build.yml +++ b/.github/workflows/unity-build.yml @@ -19,6 +19,7 @@ jobs: fail-fast: false permissions: contents: read + checks: write # to publish unit test results via checks github api defaults: run: shell: bash @@ -100,7 +101,7 @@ jobs: echo "Skipping build: Unity version $version does not support the build pipeline package (requires 2019.4+)" fi - name: Copy selected Unity test - if: ${{ steps.verify-project-path.outputs.RUN_BUILD == 'true' && matrix.tests != '' }} + if: ${{ steps.verify-project-path.outputs.RUN_BUILD == 'true' && matrix.tests != '' && matrix.tests != 'None' }} run: | set -euo pipefail test_name="${{ matrix.tests }}" @@ -136,6 +137,11 @@ jobs: run: | npm install -g openupm-cli openupm add com.utilities.buildpipeline + case "${{ matrix.tests }}" in + PlaymodeTestsErrors|EditmodeTestsErrors) + openupm add com.unity.test-framework + ;; + esac - name: Update Android Target Sdk Version if: ${{ matrix.build-target == 'Android' }} run: | @@ -147,9 +153,11 @@ jobs: if: ${{ steps.verify-project-path.outputs.RUN_BUILD == 'true' }} timeout-minutes: 60 run: | + set -euo pipefail # we don't have to specify the project path or unity editor path as unity-cli will use the environment variables unity-cli run --log-name Validate -quit -executeMethod Utilities.Editor.BuildPipeline.UnityPlayerBuildTools.ValidateProject -importTMProEssentialsAsset unity-cli run --log-name Build -buildTarget ${{ matrix.build-target }} -quit -executeMethod Utilities.Editor.BuildPipeline.UnityPlayerBuildTools.StartCommandLineBuild -sceneList Assets/Scenes/SampleScene.unity ${{ matrix.build-args }} + continue-on-error: ${{ matrix.tests != 'None' }} - name: Uninstall Editor if: ${{ matrix.unity-version != 'none' }} run: | @@ -200,7 +208,7 @@ jobs: if: always() uses: actions/upload-artifact@v6 with: - name: ${{ matrix.os }}-${{ matrix.unity-version }}-${{ matrix.build-target }}-utp-logs + name: ${{ matrix.os }}-${{ matrix.unity-version }}-${{ matrix.build-target }}-${{ matrix.tests }}-utp-logs path: '**/*-utp-json.log' if-no-files-found: ignore - name: Return License diff --git a/.gitignore b/.gitignore index 9a5aced..b34eee6 100644 --- a/.gitignore +++ b/.gitignore @@ -137,3 +137,5 @@ dist # Vite logs files vite.config.js.timestamp-* vite.config.ts.timestamp-* + +.artifacts/ diff --git a/src/logging.ts b/src/logging.ts index 16a1f15..f8439b0 100644 --- a/src/logging.ts +++ b/src/logging.ts @@ -1,4 +1,5 @@ import * as fs from 'fs'; +import { UTP } from './utp/utp'; export enum LogLevel { DEBUG = 'debug', @@ -258,19 +259,54 @@ export class Logger { } } - public CI_appendWorkflowSummary(telemetry: any[]) { + public CI_appendWorkflowSummary(name: string, telemetry: UTP[]) { switch (this._ci) { case 'GITHUB_ACTIONS': { const githubSummary = process.env.GITHUB_STEP_SUMMARY; if (githubSummary) { - let table = `| Key | Value |\n| --- | ----- |\n`; - telemetry.forEach(item => { - table += `| ${item.key} | ${item.value} |\n`; - }); + // for now lets just log the number of items we get per type + const typeCounts: Record = {}; + for (const entry of telemetry) { + const type = entry.type || 'unknown'; + + if (!typeCounts[type]) { + typeCounts[type] = 0; + } + + typeCounts[type]++; + } + + let table = `## ${name} Summary\n\n| Type | Count |\n| --- | ---: |\n`; + for (const [type, count] of Object.entries(typeCounts)) { + table += `| ${type} | ${count} |\n`; + } + + // guard against very large summaries over 1MB. Trim at a row boundary to avoid mangled tables. + const byteLimit = 1024 * 1024; + if (Buffer.byteLength(table, 'utf8') > byteLimit) { + const footer = `\n| ... | ... |\n\n***Summary truncated due to size limits.***\n`; + const footerSize = Buffer.byteLength(footer, 'utf8'); + + const lines = table.split('\n'); + let rebuilt = ''; + + for (const line of lines) { + const nextSize = Buffer.byteLength(rebuilt + line + '\n', 'utf8') + footerSize; + + if (nextSize > byteLimit) { + break; + } + + rebuilt += `${line}\n`; + } + + table = rebuilt + footer; + } fs.appendFileSync(githubSummary, table, { encoding: 'utf8' }); } + break; } } } diff --git a/src/unity-editor.ts b/src/unity-editor.ts index 62d1fd7..bc3feaf 100644 --- a/src/unity-editor.ts +++ b/src/unity-editor.ts @@ -277,6 +277,7 @@ export class UnityEditor { const baseEditorEnv: NodeJS.ProcessEnv = { ...process.env, UNITY_THISISABUILDMACHINE: '1', + DISABLE_EMBEDDED_BUILD_PIPELINE_PLUGIN_LOGGING: '1', ...(linuxEnvOverrides ?? {}) }; diff --git a/src/unity-logging.ts b/src/unity-logging.ts index 52cbb9a..d149bb5 100644 --- a/src/unity-logging.ts +++ b/src/unity-logging.ts @@ -973,6 +973,8 @@ export function TailLogFile(logPath: string, projectPath: string | undefined): L if (telemetryFlushed) { return; } telemetryFlushed = true; await writeUtpTelemetryLog(utpLogPath, telemetry, logger); + const parsed = path.parse(logPath); + Logger.instance.CI_appendWorkflowSummary(parsed.name, telemetry); }; const writeStdoutThenTableContent = (content: string, restoreTable: boolean = true): void => { From 7d0df3d5f790c463119752bb7b192784bc6f053a Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 21 Dec 2025 19:20:43 -0500 Subject: [PATCH 08/19] fix permissions --- .github/workflows/integration-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 02c80c4..f72d8f1 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -33,6 +33,7 @@ jobs: name: build ${{ matrix.jobs.name }} permissions: contents: read + checks: write # required by nested unity-build workflow strategy: matrix: ${{ fromJSON(needs.setup.outputs.jobs) }} fail-fast: false From 2ce7a94abb69757216cdfdcc10bc5d9cb3fe433a Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 21 Dec 2025 19:31:52 -0500 Subject: [PATCH 09/19] misc --- .github/workflows/publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 564f901..62ed36d 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,4 +1,4 @@ -name: Publish +name: publish on: push: branches: [main] From 28d2f8e132321dbdfacea693e89e311d3751f7cf Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 21 Dec 2025 19:41:25 -0500 Subject: [PATCH 10/19] don't write summary if no telemetry output --- src/logging.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/logging.ts b/src/logging.ts index f8439b0..739760a 100644 --- a/src/logging.ts +++ b/src/logging.ts @@ -260,6 +260,7 @@ export class Logger { } public CI_appendWorkflowSummary(name: string, telemetry: UTP[]) { + if (telemetry.length === 0) { return; } switch (this._ci) { case 'GITHUB_ACTIONS': { const githubSummary = process.env.GITHUB_STEP_SUMMARY; From fe5423a38984b967fbdd8f887a91265d692fa219 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 28 Dec 2025 11:39:46 -0500 Subject: [PATCH 11/19] add additional utp types for logging [skip ci] --- src/utp/utp.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/utp/utp.ts b/src/utp/utp.ts index a549304..446f2c8 100644 --- a/src/utp/utp.ts +++ b/src/utp/utp.ts @@ -20,13 +20,19 @@ export class UTPBase { errors?: unknown[]; } +export class UTPAction extends UTPBase { } + export class UTPMemoryLeak extends UTPBase { allocatedMemory?: number; memoryLabels?: Record | Array>; } +export class UTPMemoryLeaks extends UTPMemoryLeak { } + export class UTPLogEntry extends UTPBase { } +export class UTPCompiler extends UTPBase { } + export class UTPTestPlan extends UTPBase { tests?: string[]; } @@ -117,6 +123,8 @@ export class UTPPlayerBuildInfo extends UTPBase { } export type UTP = + | UTPAction + | UTPCompiler | UTPBase | UTPLogEntry | UTPTestPlan @@ -127,6 +135,7 @@ export type UTP = | UTPQualitySettings | UTPTestStatus | UTPMemoryLeak + | UTPMemoryLeaks | UTPPlayerBuildInfo; export enum Phase { From 545be7a98fd1acb8572230ddcb3a06991a1dbb5e Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 28 Dec 2025 11:49:27 -0500 Subject: [PATCH 12/19] rework tests --- .../actions/run-unity-test-batch/action.yml | 134 ++++++++++++++++++ .github/workflows/build-options.json | 9 -- .github/workflows/unity-build.yml | 58 +------- 3 files changed, 141 insertions(+), 60 deletions(-) create mode 100644 .github/actions/run-unity-test-batch/action.yml diff --git a/.github/actions/run-unity-test-batch/action.yml b/.github/actions/run-unity-test-batch/action.yml new file mode 100644 index 0000000..eb280d2 --- /dev/null +++ b/.github/actions/run-unity-test-batch/action.yml @@ -0,0 +1,134 @@ +name: Run Unity Test Batch +description: Run a list of Unity tests in a single job/install and upload UTP logs per test. +inputs: + unity_project_path: + description: Absolute path to the Unity project. + required: true + build_target: + description: Build target to use. + required: true + build_args: + description: Additional build args. + required: false + default: "" + workspace: + description: Root workspace path (defaults to github.workspace). + required: false + default: ${{ github.workspace }} +runs: + using: composite + steps: + - name: Prepare test list and install packages + shell: bash + run: | + set -euo pipefail + tests_input="CompilerWarnings,CompilerErrors,BuildWarnings,BuildErrors,PlaymodeTestsErrors,EditmodeTestsErrors" + echo "TESTS_INPUT=$tests_input" >> $GITHUB_ENV + + needs_test_framework=false + if [[ "$tests_input" == *"PlaymodeTestsErrors"* || "$tests_input" == *"EditmodeTestsErrors"* ]]; then + needs_test_framework=true + fi + + npm install -g openupm-cli + openupm add com.utilities.buildpipeline + if [ "$needs_test_framework" = true ]; then + openupm add com.unity.test-framework + fi + + - name: Run tests sequentially + shell: bash + env: + UNITY_PROJECT_PATH: ${{ inputs.unity_project_path }} + BUILD_TARGET: ${{ inputs.build_target }} + BUILD_ARGS: ${{ inputs.build_args }} + WORKSPACE_ROOT: ${{ inputs.workspace }} + run: | + set -euo pipefail + + tests_input="$TESTS_INPUT" + IFS=',' read -ra tests <<< "$tests_input" + failures=0 + + clean_tests() { + rm -f "$UNITY_PROJECT_PATH/Assets/UnityCliTests"/*.cs 2>/dev/null || true + rm -f "$UNITY_PROJECT_PATH/Assets/Editor/UnityCliTests"/*.cs 2>/dev/null || true + rm -f "$UNITY_PROJECT_PATH/Assets/Tests/PlayMode/UnityCliTests"/*.cs 2>/dev/null || true + rm -f "$UNITY_PROJECT_PATH/Assets/Tests/EditMode/UnityCliTests"/*.cs 2>/dev/null || true + } + + mkdir -p "$WORKSPACE_ROOT/utp-artifacts" + + for raw_test in "${tests[@]}"; do + test_name="$(echo "$raw_test" | xargs)" + if [ -z "$test_name" ] || [ "$test_name" = "None" ]; then + echo "Skipping empty/None test entry" + continue + fi + + src="$WORKSPACE_ROOT/unity-tests/${test_name}.cs" + if [ ! -f "$src" ]; then + echo "::error::Requested test '$test_name' not found at $src" + failures=$((failures+1)) + continue + fi + + clean_tests + + case "$test_name" in + CompilerWarnings|CompilerErrors) + dest="$UNITY_PROJECT_PATH/Assets/UnityCliTests" + ;; + BuildWarnings|BuildErrors) + dest="$UNITY_PROJECT_PATH/Assets/Editor/UnityCliTests" + ;; + PlaymodeTestsErrors) + dest="$UNITY_PROJECT_PATH/Assets/Tests/PlayMode/UnityCliTests" + ;; + EditmodeTestsErrors) + dest="$UNITY_PROJECT_PATH/Assets/Tests/EditMode/UnityCliTests" + ;; + *) + echo "::error::Unknown test selection '$test_name'" + failures=$((failures+1)) + continue + ;; + esac + + mkdir -p "$dest" + cp "$src" "$dest/" + echo "Running test: $test_name (copied to $dest)" + + if unity-cli run --log-name "${test_name}-Validate" -quit -executeMethod Utilities.Editor.BuildPipeline.UnityPlayerBuildTools.ValidateProject -importTMProEssentialsAsset && \ + unity-cli run --log-name "${test_name}-Build" -buildTarget "$BUILD_TARGET" -quit -executeMethod Utilities.Editor.BuildPipeline.UnityPlayerBuildTools.StartCommandLineBuild -sceneList Assets/Scenes/SampleScene.unity $BUILD_ARGS; then + echo "::notice::Test $test_name succeeded" + else + echo "::error::Test $test_name failed" + failures=$((failures+1)) + fi + + # Collect logs for this test + test_artifacts="$WORKSPACE_ROOT/utp-artifacts/$test_name" + mkdir -p "$test_artifacts" + find "$WORKSPACE_ROOT" -type f -name "*${test_name}*-utp-json.log" -print -exec cp {} "$test_artifacts" \; || true + + # Reset the Unity project to a clean state before the next test + if git -C "$UNITY_PROJECT_PATH" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + git -C "$UNITY_PROJECT_PATH" clean -fdx + git -C "$UNITY_PROJECT_PATH" reset --hard + else + echo "::warning::UNITY_PROJECT_PATH is not a git repository; skipping git clean/reset" + fi + done + + if [ "$failures" -gt 0 ]; then + echo "::error::One or more tests failed in batch ($failures)" + exit 1 + fi + + - name: Upload UTP logs (per test) + uses: actions/upload-artifact@v6 + with: + name: unity-tests-batch-utp-logs + path: utp-artifacts/**/*-utp-json.log + if-no-files-found: ignore diff --git a/.github/workflows/build-options.json b/.github/workflows/build-options.json index 8630410..b167129 100644 --- a/.github/workflows/build-options.json +++ b/.github/workflows/build-options.json @@ -17,15 +17,6 @@ "6000.1.*", "6000.2" ], - "tests": [ - "None", - "CompilerWarnings", - "CompilerErrors", - "BuildWarnings", - "BuildErrors", - "PlaymodeTestsErrors", - "EditmodeTestsErrors" - ], "include": [ { "os": "ubuntu-latest", diff --git a/.github/workflows/unity-build.yml b/.github/workflows/unity-build.yml index b8323bd..ec7ccba 100644 --- a/.github/workflows/unity-build.yml +++ b/.github/workflows/unity-build.yml @@ -100,48 +100,13 @@ jobs: else echo "Skipping build: Unity version $version does not support the build pipeline package (requires 2019.4+)" fi - - name: Copy selected Unity test - if: ${{ steps.verify-project-path.outputs.RUN_BUILD == 'true' && matrix.tests != '' && matrix.tests != 'None' }} - run: | - set -euo pipefail - test_name="${{ matrix.tests }}" - src="${GITHUB_WORKSPACE}/unity-tests/${test_name}.cs" - if [ ! -f "$src" ]; then - echo "::error::Requested test '$test_name' not found at $src" && exit 1 - fi - - case "$test_name" in - CompilerWarnings|CompilerErrors) - dest="$UNITY_PROJECT_PATH/Assets/UnityCliTests" - ;; - BuildWarnings|BuildErrors) - dest="$UNITY_PROJECT_PATH/Assets/Editor/UnityCliTests" - ;; - PlaymodeTestsErrors) - dest="$UNITY_PROJECT_PATH/Assets/Tests/PlayMode/UnityCliTests" - ;; - EditmodeTestsErrors) - dest="$UNITY_PROJECT_PATH/Assets/Tests/EditMode/UnityCliTests" - ;; - *) - echo "::error::Unknown test selection '$test_name'" && exit 1 - ;; - esac - - mkdir -p "$dest" - cp "$src" "$dest/" - echo "Copied $test_name to $dest" - - name: Install OpenUPM and build pipeline package + - name: Run Unity test batch (single install) if: ${{ steps.verify-project-path.outputs.RUN_BUILD == 'true' }} - working-directory: ${{ env.UNITY_PROJECT_PATH }} - run: | - npm install -g openupm-cli - openupm add com.utilities.buildpipeline - case "${{ matrix.tests }}" in - PlaymodeTestsErrors|EditmodeTestsErrors) - openupm add com.unity.test-framework - ;; - esac + uses: ./.github/actions/run-unity-test-batch + with: + unity_project_path: ${{ env.UNITY_PROJECT_PATH }} + build_target: ${{ matrix.build-target }} + build_args: ${{ matrix.build-args }} - name: Update Android Target Sdk Version if: ${{ matrix.build-target == 'Android' }} run: | @@ -149,15 +114,6 @@ jobs: sed -i 's/AndroidTargetSdkVersion: [0-9]*/AndroidTargetSdkVersion: 32/' "${UNITY_PROJECT_PATH}/ProjectSettings/ProjectSettings.asset" # ensure android dependencies are installed unity-cli setup-unity -p "${UNITY_PROJECT_PATH}" -m android - - name: Build Project - if: ${{ steps.verify-project-path.outputs.RUN_BUILD == 'true' }} - timeout-minutes: 60 - run: | - set -euo pipefail - # we don't have to specify the project path or unity editor path as unity-cli will use the environment variables - unity-cli run --log-name Validate -quit -executeMethod Utilities.Editor.BuildPipeline.UnityPlayerBuildTools.ValidateProject -importTMProEssentialsAsset - unity-cli run --log-name Build -buildTarget ${{ matrix.build-target }} -quit -executeMethod Utilities.Editor.BuildPipeline.UnityPlayerBuildTools.StartCommandLineBuild -sceneList Assets/Scenes/SampleScene.unity ${{ matrix.build-args }} - continue-on-error: ${{ matrix.tests != 'None' }} - name: Uninstall Editor if: ${{ matrix.unity-version != 'none' }} run: | @@ -208,7 +164,7 @@ jobs: if: always() uses: actions/upload-artifact@v6 with: - name: ${{ matrix.os }}-${{ matrix.unity-version }}-${{ matrix.build-target }}-${{ matrix.tests }}-utp-logs + name: ${{ matrix.os }}-${{ matrix.unity-version }}-${{ matrix.build-target }}-tests-batch-utp-logs path: '**/*-utp-json.log' if-no-files-found: ignore - name: Return License From 5b6735812b4a740e4a3c4432beb86f1cf826c0d5 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 28 Dec 2025 12:09:53 -0500 Subject: [PATCH 13/19] tweaks --- .../actions/run-unity-test-batch/action.yml | 42 ++++++++----------- .github/workflows/unity-build.yml | 8 ++-- 2 files changed, 22 insertions(+), 28 deletions(-) diff --git a/.github/actions/run-unity-test-batch/action.yml b/.github/actions/run-unity-test-batch/action.yml index eb280d2..6b27391 100644 --- a/.github/actions/run-unity-test-batch/action.yml +++ b/.github/actions/run-unity-test-batch/action.yml @@ -1,20 +1,15 @@ -name: Run Unity Test Batch -description: Run a list of Unity tests in a single job/install and upload UTP logs per test. +name: Run Unity UTP Test Batch +description: Runs a batch of Unity UTP tests in a given Unity project. inputs: - unity_project_path: + unity-project-path: description: Absolute path to the Unity project. required: true - build_target: + build-target: description: Build target to use. required: true - build_args: + build-args: description: Additional build args. - required: false - default: "" - workspace: - description: Root workspace path (defaults to github.workspace). - required: false - default: ${{ github.workspace }} + required: true runs: using: composite steps: @@ -39,10 +34,9 @@ runs: - name: Run tests sequentially shell: bash env: - UNITY_PROJECT_PATH: ${{ inputs.unity_project_path }} - BUILD_TARGET: ${{ inputs.build_target }} - BUILD_ARGS: ${{ inputs.build_args }} - WORKSPACE_ROOT: ${{ inputs.workspace }} + UNITY_PROJECT_PATH: ${{ inputs.unity-project-path }} + BUILD_TARGET: ${{ inputs.build-target }} + BUILD_ARGS: ${{ inputs.build-args }} run: | set -euo pipefail @@ -57,7 +51,7 @@ runs: rm -f "$UNITY_PROJECT_PATH/Assets/Tests/EditMode/UnityCliTests"/*.cs 2>/dev/null || true } - mkdir -p "$WORKSPACE_ROOT/utp-artifacts" + mkdir -p "$GITHUB_WORKSPACE/utp-artifacts" for raw_test in "${tests[@]}"; do test_name="$(echo "$raw_test" | xargs)" @@ -66,7 +60,7 @@ runs: continue fi - src="$WORKSPACE_ROOT/unity-tests/${test_name}.cs" + src="$GITHUB_WORKSPACE/unity-tests/${test_name}.cs" if [ ! -f "$src" ]; then echo "::error::Requested test '$test_name' not found at $src" failures=$((failures+1)) @@ -108,16 +102,16 @@ runs: fi # Collect logs for this test - test_artifacts="$WORKSPACE_ROOT/utp-artifacts/$test_name" + test_artifacts="$GITHUB_WORKSPACE/utp-artifacts/$test_name" mkdir -p "$test_artifacts" - find "$WORKSPACE_ROOT" -type f -name "*${test_name}*-utp-json.log" -print -exec cp {} "$test_artifacts" \; || true + find "$GITHUB_WORKSPACE" -type f -name "*${test_name}*-utp-json.log" -print -exec cp {} "$test_artifacts" \; || true # Reset the Unity project to a clean state before the next test - if git -C "$UNITY_PROJECT_PATH" rev-parse --is-inside-work-tree >/dev/null 2>&1; then - git -C "$UNITY_PROJECT_PATH" clean -fdx - git -C "$UNITY_PROJECT_PATH" reset --hard + if git -C "$GITHUB_WORKSPACE" rev-parse --is-inside-work-tree >/dev/null 2>&1; then + git -C "$GITHUB_WORKSPACE" clean -fdx + git -C "$GITHUB_WORKSPACE" reset --hard else - echo "::warning::UNITY_PROJECT_PATH is not a git repository; skipping git clean/reset" + echo "::warning::GITHUB_WORKSPACE is not a git repository; skipping git clean/reset" fi done @@ -126,7 +120,7 @@ runs: exit 1 fi - - name: Upload UTP logs (per test) + - name: Upload UTP logs uses: actions/upload-artifact@v6 with: name: unity-tests-batch-utp-logs diff --git a/.github/workflows/unity-build.yml b/.github/workflows/unity-build.yml index ec7ccba..2defcb0 100644 --- a/.github/workflows/unity-build.yml +++ b/.github/workflows/unity-build.yml @@ -100,13 +100,13 @@ jobs: else echo "Skipping build: Unity version $version does not support the build pipeline package (requires 2019.4+)" fi - - name: Run Unity test batch (single install) + - name: Run Unity UTP test batches if: ${{ steps.verify-project-path.outputs.RUN_BUILD == 'true' }} uses: ./.github/actions/run-unity-test-batch with: - unity_project_path: ${{ env.UNITY_PROJECT_PATH }} - build_target: ${{ matrix.build-target }} - build_args: ${{ matrix.build-args }} + unity-project-path: ${{ steps.verify-project-path.outputs.UNITY_PROJECT_PATH }} + build-target: ${{ matrix.build-target }} + build-args: ${{ matrix.build-args }} - name: Update Android Target Sdk Version if: ${{ matrix.build-target == 'Android' }} run: | From c7bbdd172515f617e17c3a7fd199a0b9fee29ef4 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 28 Dec 2025 14:00:48 -0500 Subject: [PATCH 14/19] fixes to workflow --- .github/workflows/integration-tests.yml | 2 +- .github/workflows/unity-build.yml | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index f72d8f1..9b02d64 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v6 with: sparse-checkout: .github/ - - uses: RageAgainstThePixel/job-builder@development + - uses: RageAgainstThePixel/job-builder@v1 id: setup-jobs with: build-options: ./.github/workflows/build-options.json diff --git a/.github/workflows/unity-build.yml b/.github/workflows/unity-build.yml index 2defcb0..c40eb45 100644 --- a/.github/workflows/unity-build.yml +++ b/.github/workflows/unity-build.yml @@ -104,7 +104,7 @@ jobs: if: ${{ steps.verify-project-path.outputs.RUN_BUILD == 'true' }} uses: ./.github/actions/run-unity-test-batch with: - unity-project-path: ${{ steps.verify-project-path.outputs.UNITY_PROJECT_PATH }} + unity-project-path: ${{ env.UNITY_PROJECT_PATH }} build-target: ${{ matrix.build-target }} build-args: ${{ matrix.build-args }} - name: Update Android Target Sdk Version @@ -160,11 +160,25 @@ jobs: if [ ! -f "${LICENSING_AUDIT_LOG_PATH}" ]; then echo "::error::Licensing Audit log file does not exist at ${LICENSING_AUDIT_LOG_PATH}" fi - - name: Upload UTP logs + - name: Compute UTP artifact name + if: always() + id: utp-artifact-name + env: + MATRIX_OS: ${{ matrix.os }} + MATRIX_UNITY_VERSION: ${{ matrix.unity-version }} + MATRIX_BUILD_TARGET: ${{ matrix.build-target }} + run: | + set -euo pipefail + unity_version="$MATRIX_UNITY_VERSION" + unity_version="${unity_version//\*/x}" + artifact_name="${MATRIX_OS}-${unity_version}-${MATRIX_BUILD_TARGET}-tests-batch-utp-logs" + echo "name=$artifact_name" >> $GITHUB_OUTPUT + shell: bash + - name: Upload UTP logs artifact if: always() uses: actions/upload-artifact@v6 with: - name: ${{ matrix.os }}-${{ matrix.unity-version }}-${{ matrix.build-target }}-tests-batch-utp-logs + name: ${{ steps.utp-artifact-name.outputs.name }} path: '**/*-utp-json.log' if-no-files-found: ignore - name: Return License From 4a6cacc146d74fdd1b7d41d83d983b7ee85ef484 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 28 Dec 2025 15:46:23 -0500 Subject: [PATCH 15/19] fix openupm installs --- .github/actions/run-unity-test-batch/action.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/actions/run-unity-test-batch/action.yml b/.github/actions/run-unity-test-batch/action.yml index 6b27391..e14f98a 100644 --- a/.github/actions/run-unity-test-batch/action.yml +++ b/.github/actions/run-unity-test-batch/action.yml @@ -15,6 +15,7 @@ runs: steps: - name: Prepare test list and install packages shell: bash + working-directory: ${{ inputs.unity-project-path }} run: | set -euo pipefail tests_input="CompilerWarnings,CompilerErrors,BuildWarnings,BuildErrors,PlaymodeTestsErrors,EditmodeTestsErrors" From 32278ac06be2c994161e3f5d4dd0ba6c9d20ead7 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 4 Jan 2026 11:11:18 -0500 Subject: [PATCH 16/19] update matrix build artifact names --- .github/actions/run-unity-test-batch/action.yml | 6 +++++- .github/workflows/unity-build.yml | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/actions/run-unity-test-batch/action.yml b/.github/actions/run-unity-test-batch/action.yml index e14f98a..806e97c 100644 --- a/.github/actions/run-unity-test-batch/action.yml +++ b/.github/actions/run-unity-test-batch/action.yml @@ -10,6 +10,10 @@ inputs: build-args: description: Additional build args. required: true + artifact-name: + description: Artifact name for uploaded UTP logs (must be unique per matrix job). + required: false + default: unity-tests-batch-utp-logs runs: using: composite steps: @@ -124,6 +128,6 @@ runs: - name: Upload UTP logs uses: actions/upload-artifact@v6 with: - name: unity-tests-batch-utp-logs + name: ${{ inputs.artifact-name }} path: utp-artifacts/**/*-utp-json.log if-no-files-found: ignore diff --git a/.github/workflows/unity-build.yml b/.github/workflows/unity-build.yml index c40eb45..97d2698 100644 --- a/.github/workflows/unity-build.yml +++ b/.github/workflows/unity-build.yml @@ -107,6 +107,7 @@ jobs: unity-project-path: ${{ env.UNITY_PROJECT_PATH }} build-target: ${{ matrix.build-target }} build-args: ${{ matrix.build-args }} + artifact-name: ${{ matrix.os }}-${{ matrix.unity-version }}-${{ matrix.build-target }}-tests-batch-utp-logs - name: Update Android Target Sdk Version if: ${{ matrix.build-target == 'Android' }} run: | From 15bce316530014ea63427371fc7d73db0d698d31 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 4 Jan 2026 21:38:06 -0500 Subject: [PATCH 17/19] don't clean between runs --- .github/actions/run-unity-test-batch/action.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.github/actions/run-unity-test-batch/action.yml b/.github/actions/run-unity-test-batch/action.yml index 806e97c..f486335 100644 --- a/.github/actions/run-unity-test-batch/action.yml +++ b/.github/actions/run-unity-test-batch/action.yml @@ -110,14 +110,6 @@ runs: test_artifacts="$GITHUB_WORKSPACE/utp-artifacts/$test_name" mkdir -p "$test_artifacts" find "$GITHUB_WORKSPACE" -type f -name "*${test_name}*-utp-json.log" -print -exec cp {} "$test_artifacts" \; || true - - # Reset the Unity project to a clean state before the next test - if git -C "$GITHUB_WORKSPACE" rev-parse --is-inside-work-tree >/dev/null 2>&1; then - git -C "$GITHUB_WORKSPACE" clean -fdx - git -C "$GITHUB_WORKSPACE" reset --hard - else - echo "::warning::GITHUB_WORKSPACE is not a git repository; skipping git clean/reset" - fi done if [ "$failures" -gt 0 ]; then From 937832a36667f32cb5beae9cd38b910c52f4525e Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 4 Jan 2026 21:38:54 -0500 Subject: [PATCH 18/19] cleanup artifacts between tests --- .github/actions/run-unity-test-batch/action.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/actions/run-unity-test-batch/action.yml b/.github/actions/run-unity-test-batch/action.yml index f486335..61f7f84 100644 --- a/.github/actions/run-unity-test-batch/action.yml +++ b/.github/actions/run-unity-test-batch/action.yml @@ -56,6 +56,11 @@ runs: rm -f "$UNITY_PROJECT_PATH/Assets/Tests/EditMode/UnityCliTests"/*.cs 2>/dev/null || true } + clean_build_outputs() { + rm -rf "$UNITY_PROJECT_PATH/Builds" 2>/dev/null || true + mkdir -p "$UNITY_PROJECT_PATH/Builds/Logs" + } + mkdir -p "$GITHUB_WORKSPACE/utp-artifacts" for raw_test in "${tests[@]}"; do @@ -73,6 +78,7 @@ runs: fi clean_tests + clean_build_outputs case "$test_name" in CompilerWarnings|CompilerErrors) @@ -109,7 +115,7 @@ runs: # Collect logs for this test test_artifacts="$GITHUB_WORKSPACE/utp-artifacts/$test_name" mkdir -p "$test_artifacts" - find "$GITHUB_WORKSPACE" -type f -name "*${test_name}*-utp-json.log" -print -exec cp {} "$test_artifacts" \; || true + find "$GITHUB_WORKSPACE" -path "$test_artifacts" -prune -o -type f -name "*${test_name}*-utp-json.log" -print -exec cp -n {} "$test_artifacts" \; || true done if [ "$failures" -gt 0 ]; then From 1c532076b88f36a2ed9b79e681180744e0f2c197 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sat, 10 Jan 2026 12:29:36 -0500 Subject: [PATCH 19/19] upgate utp tests --- .../actions/run-unity-test-batch/action.yml | 82 +---------- .github/actions/scripts/run-utp-tests.sh | 136 ++++++++++++++++++ 2 files changed, 138 insertions(+), 80 deletions(-) create mode 100755 .github/actions/scripts/run-utp-tests.sh diff --git a/.github/actions/run-unity-test-batch/action.yml b/.github/actions/run-unity-test-batch/action.yml index 61f7f84..0d32d79 100644 --- a/.github/actions/run-unity-test-batch/action.yml +++ b/.github/actions/run-unity-test-batch/action.yml @@ -36,92 +36,14 @@ runs: openupm add com.unity.test-framework fi - - name: Run tests sequentially + - name: Run tests shell: bash env: UNITY_PROJECT_PATH: ${{ inputs.unity-project-path }} BUILD_TARGET: ${{ inputs.build-target }} BUILD_ARGS: ${{ inputs.build-args }} run: | - set -euo pipefail - - tests_input="$TESTS_INPUT" - IFS=',' read -ra tests <<< "$tests_input" - failures=0 - - clean_tests() { - rm -f "$UNITY_PROJECT_PATH/Assets/UnityCliTests"/*.cs 2>/dev/null || true - rm -f "$UNITY_PROJECT_PATH/Assets/Editor/UnityCliTests"/*.cs 2>/dev/null || true - rm -f "$UNITY_PROJECT_PATH/Assets/Tests/PlayMode/UnityCliTests"/*.cs 2>/dev/null || true - rm -f "$UNITY_PROJECT_PATH/Assets/Tests/EditMode/UnityCliTests"/*.cs 2>/dev/null || true - } - - clean_build_outputs() { - rm -rf "$UNITY_PROJECT_PATH/Builds" 2>/dev/null || true - mkdir -p "$UNITY_PROJECT_PATH/Builds/Logs" - } - - mkdir -p "$GITHUB_WORKSPACE/utp-artifacts" - - for raw_test in "${tests[@]}"; do - test_name="$(echo "$raw_test" | xargs)" - if [ -z "$test_name" ] || [ "$test_name" = "None" ]; then - echo "Skipping empty/None test entry" - continue - fi - - src="$GITHUB_WORKSPACE/unity-tests/${test_name}.cs" - if [ ! -f "$src" ]; then - echo "::error::Requested test '$test_name' not found at $src" - failures=$((failures+1)) - continue - fi - - clean_tests - clean_build_outputs - - case "$test_name" in - CompilerWarnings|CompilerErrors) - dest="$UNITY_PROJECT_PATH/Assets/UnityCliTests" - ;; - BuildWarnings|BuildErrors) - dest="$UNITY_PROJECT_PATH/Assets/Editor/UnityCliTests" - ;; - PlaymodeTestsErrors) - dest="$UNITY_PROJECT_PATH/Assets/Tests/PlayMode/UnityCliTests" - ;; - EditmodeTestsErrors) - dest="$UNITY_PROJECT_PATH/Assets/Tests/EditMode/UnityCliTests" - ;; - *) - echo "::error::Unknown test selection '$test_name'" - failures=$((failures+1)) - continue - ;; - esac - - mkdir -p "$dest" - cp "$src" "$dest/" - echo "Running test: $test_name (copied to $dest)" - - if unity-cli run --log-name "${test_name}-Validate" -quit -executeMethod Utilities.Editor.BuildPipeline.UnityPlayerBuildTools.ValidateProject -importTMProEssentialsAsset && \ - unity-cli run --log-name "${test_name}-Build" -buildTarget "$BUILD_TARGET" -quit -executeMethod Utilities.Editor.BuildPipeline.UnityPlayerBuildTools.StartCommandLineBuild -sceneList Assets/Scenes/SampleScene.unity $BUILD_ARGS; then - echo "::notice::Test $test_name succeeded" - else - echo "::error::Test $test_name failed" - failures=$((failures+1)) - fi - - # Collect logs for this test - test_artifacts="$GITHUB_WORKSPACE/utp-artifacts/$test_name" - mkdir -p "$test_artifacts" - find "$GITHUB_WORKSPACE" -path "$test_artifacts" -prune -o -type f -name "*${test_name}*-utp-json.log" -print -exec cp -n {} "$test_artifacts" \; || true - done - - if [ "$failures" -gt 0 ]; then - echo "::error::One or more tests failed in batch ($failures)" - exit 1 - fi + bash "${GITHUB_WORKSPACE}/.github/actions/scripts/run-utp-tests.sh" - name: Upload UTP logs uses: actions/upload-artifact@v6 diff --git a/.github/actions/scripts/run-utp-tests.sh b/.github/actions/scripts/run-utp-tests.sh new file mode 100755 index 0000000..98d8ad1 --- /dev/null +++ b/.github/actions/scripts/run-utp-tests.sh @@ -0,0 +1,136 @@ +#!/usr/bin/env bash +set -uo pipefail + +UNITY_PROJECT_PATH=${UNITY_PROJECT_PATH:?UNITY_PROJECT_PATH is required} +BUILD_TARGET=${BUILD_TARGET:?BUILD_TARGET is required} +BUILD_ARGS=${BUILD_ARGS:-} +TESTS_INPUT=${TESTS_INPUT:-} + +IFS=',' read -ra tests <<< "$TESTS_INPUT" +failures=0 + +clean_tests() { + rm -f "$UNITY_PROJECT_PATH/Assets/UnityCliTests"/*.cs 2>/dev/null || true + rm -f "$UNITY_PROJECT_PATH/Assets/Editor/UnityCliTests"/*.cs 2>/dev/null || true + rm -f "$UNITY_PROJECT_PATH/Assets/Tests/PlayMode/UnityCliTests"/*.cs 2>/dev/null || true + rm -f "$UNITY_PROJECT_PATH/Assets/Tests/EditMode/UnityCliTests"/*.cs 2>/dev/null || true +} + +clean_build_outputs() { + rm -rf "$UNITY_PROJECT_PATH/Builds" 2>/dev/null || true + mkdir -p "$UNITY_PROJECT_PATH/Builds/Logs" +} + +# Expectations for each synthetic test +# expected_status: 0 = should succeed, 1 = should fail +declare -A expected_status +expected_status[CompilerWarnings]=0 +expected_status[BuildWarnings]=0 +expected_status[CompilerErrors]=1 +expected_status[BuildErrors]=1 +expected_status[PlaymodeTestsErrors]=1 +expected_status[EditmodeTestsErrors]=1 + +declare -A expected_message +expected_message[CompilerErrors]="Intentional compiler error" +expected_message[BuildErrors]="Intentional build failure" +expected_message[PlaymodeTestsErrors]="Intentional playmode failure" +expected_message[EditmodeTestsErrors]="Intentional editmode failure" +expected_message[CompilerWarnings]="Intentional warning" +expected_message[BuildWarnings]="Intentional build warning" + +mkdir -p "$GITHUB_WORKSPACE/utp-artifacts" + +for raw_test in "${tests[@]}"; do + test_name="$(echo "$raw_test" | xargs)" + if [ -z "$test_name" ] || [ "$test_name" = "None" ]; then + echo "Skipping empty/None test entry" + continue + fi + + src="$GITHUB_WORKSPACE/unity-tests/${test_name}.cs" + if [ ! -f "$src" ]; then + echo "::error::Requested test '$test_name' not found at $src" + failures=$((failures+1)) + continue + fi + + clean_tests + clean_build_outputs + + case "$test_name" in + CompilerWarnings|CompilerErrors) + dest="$UNITY_PROJECT_PATH/Assets/UnityCliTests" + ;; + BuildWarnings|BuildErrors) + dest="$UNITY_PROJECT_PATH/Assets/Editor/UnityCliTests" + ;; + PlaymodeTestsErrors) + dest="$UNITY_PROJECT_PATH/Assets/Tests/PlayMode/UnityCliTests" + ;; + EditmodeTestsErrors) + dest="$UNITY_PROJECT_PATH/Assets/Tests/EditMode/UnityCliTests" + ;; + *) + echo "::error::Unknown test selection '$test_name'" + failures=$((failures+1)) + continue + ;; + esac + + mkdir -p "$dest" + cp "$src" "$dest/" + echo "Running test: $test_name (copied to $dest)" + + validate_rc=0 + build_rc=0 + + unity-cli run --log-name "${test_name}-Validate" -quit -executeMethod Utilities.Editor.BuildPipeline.UnityPlayerBuildTools.ValidateProject -importTMProEssentialsAsset || validate_rc=$? + unity-cli run --log-name "${test_name}-Build" -buildTarget "$BUILD_TARGET" -quit -executeMethod Utilities.Editor.BuildPipeline.UnityPlayerBuildTools.StartCommandLineBuild -sceneList Assets/Scenes/SampleScene.unity $BUILD_ARGS || build_rc=$? + + expected=${expected_status[$test_name]:-0} + exp_msg=${expected_message[$test_name]:-} + + test_failed=0 + + if [ "$expected" -eq 0 ]; then + if [ "$validate_rc" -ne 0 ] || [ "$build_rc" -ne 0 ]; then + echo "::error::Test $test_name was expected to succeed but failed (validate_rc=$validate_rc, build_rc=$build_rc)" + test_failed=1 + fi + else + if [ "$validate_rc" -eq 0 ] && [ "$build_rc" -eq 0 ]; then + echo "::error::Test $test_name was expected to fail but succeeded" + test_failed=1 + fi + fi + + # Check logs for expected message when provided + if [ "$test_failed" -eq 0 ] && [ -n "$exp_msg" ]; then + validate_log=$(find "$UNITY_PROJECT_PATH/Builds/Logs" -maxdepth 1 -type f -name "*${test_name}-Validate*.log" | head -n 1) + build_log=$(find "$UNITY_PROJECT_PATH/Builds/Logs" -maxdepth 1 -type f -name "*${test_name}-Build*.log" | head -n 1) + + if ! grep -qi "$exp_msg" "$validate_log" "$build_log" 2>/dev/null; then + echo "::error::Test $test_name did not emit expected message '$exp_msg'" + test_failed=1 + fi + fi + + if [ "$test_failed" -eq 0 ]; then + echo "::notice::Test $test_name behaved as expected (validate_rc=$validate_rc, build_rc=$build_rc)" + else + failures=$((failures+1)) + fi + + test_artifacts="$GITHUB_WORKSPACE/utp-artifacts/$test_name" + mkdir -p "$test_artifacts" + find "$GITHUB_WORKSPACE" -path "$test_artifacts" -prune -o -type f -name "*${test_name}*-utp-json.log" -print -exec cp -n {} "$test_artifacts" \; || true + +done + +if [ "$failures" -gt 0 ]; then + echo "::error::One or more tests did not meet expectations ($failures)" + exit 1 +fi + +exit 0