From b10438d53c4efbe4052dbef67908a5ca1988291d Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sat, 20 Dec 2025 16:37:46 -0500 Subject: [PATCH 01/36] 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 b1671291..7da9e5d3 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 1e66910b..527eb23d 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 00000000..ce75c3fe --- /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 00000000..f365a770 --- /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 00000000..056f16ee --- /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 00000000..c1fc0778 --- /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 00000000..4c4d89c9 --- /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 00000000..729b1664 --- /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/36] 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 55a86a3b..8796015b 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 759c2d44..581f58cc 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/36] 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 527eb23d..85234f45 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/36] 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 85234f45..b60e366b 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/36] 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 b60e366b..c8b3aeb4 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/36] 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 c8b3aeb4..075fe5ea 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/36] 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 7da9e5d3..86304101 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 06d1bd8e..02c80c42 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 075fe5ea..b8323bdc 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 9a5acedf..b34eee66 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 16a1f15a..f8439b08 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 62d1fd77..bc3feafa 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 52cbb9a2..d149bb5a 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/36] 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 02c80c42..f72d8f10 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/36] 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 564f9015..62ed36d8 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/36] 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 f8439b08..739760a5 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/36] 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 a549304a..446f2c8c 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/36] 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 00000000..eb280d20 --- /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 86304101..b1671291 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 b8323bdc..ec7ccbae 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/36] 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 eb280d20..6b27391a 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 ec7ccbae..2defcb05 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/36] 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 f72d8f10..9b02d643 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 2defcb05..c40eb45c 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/36] 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 6b27391a..e14f98a0 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/36] 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 e14f98a0..806e97c2 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 c40eb45c..97d2698a 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/36] 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 806e97c2..f4863355 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/36] 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 f4863355..61f7f842 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/36] 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 61f7f842..0d32d79c 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 00000000..98d8ad1f --- /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 From a9f7246dd327c09605ee14171f1cfc844773dcb7 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sat, 10 Jan 2026 19:58:07 -0500 Subject: [PATCH 20/36] update run utp tests --- .github/actions/scripts/run-utp-tests.sh | 30 +++++++++++++++++------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/.github/actions/scripts/run-utp-tests.sh b/.github/actions/scripts/run-utp-tests.sh index 98d8ad1f..a37dd431 100755 --- a/.github/actions/scripts/run-utp-tests.sh +++ b/.github/actions/scripts/run-utp-tests.sh @@ -92,25 +92,37 @@ for raw_test in "${tests[@]}"; do exp_msg=${expected_message[$test_name]:-} test_failed=0 + message_found=0 + + if [ -n "$exp_msg" ]; then + while IFS= read -r log_file; do + if [ -z "$log_file" ]; then + continue + fi + if grep -qi -- "$exp_msg" "$log_file" 2>/dev/null; then + message_found=1 + break + fi + done < <(find "$UNITY_PROJECT_PATH/Builds/Logs" -maxdepth 1 -type f -name "*${test_name}*.log") + fi 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 + if [ -n "$exp_msg" ] && [ "$message_found" -eq 0 ]; then + echo "::error::Test $test_name did not emit expected message '$exp_msg'" + test_failed=1 + fi else - if [ "$validate_rc" -eq 0 ] && [ "$build_rc" -eq 0 ]; then + if [ "$validate_rc" -ne 0 ] || [ "$build_rc" -ne 0 ] || [ "$message_found" -eq 1 ]; then + : # Expected failure observed + else 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 + if [ -n "$exp_msg" ] && [ "$message_found" -eq 0 ]; then echo "::error::Test $test_name did not emit expected message '$exp_msg'" test_failed=1 fi From c48ad26963391d514d285f0243a5d4749bee2c29 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 11 Jan 2026 11:28:19 -0500 Subject: [PATCH 21/36] relax expected message success check --- .github/actions/scripts/run-utp-tests.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/actions/scripts/run-utp-tests.sh b/.github/actions/scripts/run-utp-tests.sh index a37dd431..5feb61bc 100755 --- a/.github/actions/scripts/run-utp-tests.sh +++ b/.github/actions/scripts/run-utp-tests.sh @@ -122,7 +122,9 @@ for raw_test in "${tests[@]}"; do echo "::error::Test $test_name was expected to fail but succeeded" test_failed=1 fi - if [ -n "$exp_msg" ] && [ "$message_found" -eq 0 ]; then + + # Only insist on the expected message if both invocations claimed success. + if [ -n "$exp_msg" ] && [ "$message_found" -eq 0 ] && [ "$validate_rc" -eq 0 ] && [ "$build_rc" -eq 0 ]; then echo "::error::Test $test_name did not emit expected message '$exp_msg'" test_failed=1 fi From cf870e68d943a96d7965a366ee238321438e7a21 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 11 Jan 2026 14:08:00 -0500 Subject: [PATCH 22/36] update utp tests --- .github/actions/run-unity-test-batch/action.yml | 3 ++- .github/actions/scripts/run-utp-tests.sh | 13 ++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/.github/actions/run-unity-test-batch/action.yml b/.github/actions/run-unity-test-batch/action.yml index 0d32d79c..746653b7 100644 --- a/.github/actions/run-unity-test-batch/action.yml +++ b/.github/actions/run-unity-test-batch/action.yml @@ -9,7 +9,8 @@ inputs: required: true build-args: description: Additional build args. - required: true + required: false + default: "" artifact-name: description: Artifact name for uploaded UTP logs (must be unique per matrix job). required: false diff --git a/.github/actions/scripts/run-utp-tests.sh b/.github/actions/scripts/run-utp-tests.sh index 5feb61bc..c79a50ed 100755 --- a/.github/actions/scripts/run-utp-tests.sh +++ b/.github/actions/scripts/run-utp-tests.sh @@ -6,6 +6,17 @@ BUILD_TARGET=${BUILD_TARGET:?BUILD_TARGET is required} BUILD_ARGS=${BUILD_ARGS:-} TESTS_INPUT=${TESTS_INPUT:-} +if printf '%s' "$BUILD_ARGS" | grep -qE '[;&`|]'; then + echo "::error::BUILD_ARGS contains disallowed shell metacharacters" + exit 1 +fi + +build_args=() +if [ -n "$BUILD_ARGS" ]; then + # Split on whitespace into an array without invoking the shell + read -r -a build_args <<< "$BUILD_ARGS" +fi + IFS=',' read -ra tests <<< "$TESTS_INPUT" failures=0 @@ -86,7 +97,7 @@ for raw_test in "${tests[@]}"; do 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=$? + 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]:-} From 5e644f27542dd0284c43ab3f8ebe58bd6c8eed79 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 11 Jan 2026 14:09:42 -0500 Subject: [PATCH 23/36] update utp tests --- .github/actions/scripts/run-utp-tests.sh | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/actions/scripts/run-utp-tests.sh b/.github/actions/scripts/run-utp-tests.sh index c79a50ed..60784ec2 100755 --- a/.github/actions/scripts/run-utp-tests.sh +++ b/.github/actions/scripts/run-utp-tests.sh @@ -104,6 +104,7 @@ for raw_test in "${tests[@]}"; do test_failed=0 message_found=0 + utp_error_found=0 if [ -n "$exp_msg" ]; then while IFS= read -r log_file; do @@ -117,17 +118,32 @@ for raw_test in "${tests[@]}"; do done < <(find "$UNITY_PROJECT_PATH/Builds/Logs" -maxdepth 1 -type f -name "*${test_name}*.log") fi + # Look for error-level UTP entries for this test to treat as expected failure evidence. + while IFS= read -r utp_file; do + if [ -z "$utp_file" ]; then + continue + fi + if node -e "const fs=require('fs');const p=process.argv[1];try{const data=JSON.parse(fs.readFileSync(p,'utf8'));if(Array.isArray(data)&&data.some(e=>['Error','Exception','Assert'].includes(e?.severity))){process.exit(0);} }catch{}process.exit(1);" "$utp_file"; then + utp_error_found=1 + break + fi + done < <(find "$UNITY_PROJECT_PATH/Builds/Logs" -maxdepth 1 -type f -name "*${test_name}*-utp-json.log") + 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 + if [ "$utp_error_found" -eq 1 ]; then + echo "::error::Test $test_name produced UTP errors but was expected to succeed" + test_failed=1 + fi if [ -n "$exp_msg" ] && [ "$message_found" -eq 0 ]; then echo "::error::Test $test_name did not emit expected message '$exp_msg'" test_failed=1 fi else - if [ "$validate_rc" -ne 0 ] || [ "$build_rc" -ne 0 ] || [ "$message_found" -eq 1 ]; then + if [ "$validate_rc" -ne 0 ] || [ "$build_rc" -ne 0 ] || [ "$message_found" -eq 1 ] || [ "$utp_error_found" -eq 1 ]; then : # Expected failure observed else echo "::error::Test $test_name was expected to fail but succeeded" From 78cd83d32ecbbaef3996275f6d8238c578aa994a Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 11 Jan 2026 16:26:04 -0500 Subject: [PATCH 24/36] update edit mode test error --- .github/actions/scripts/run-utp-tests.sh | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/actions/scripts/run-utp-tests.sh b/.github/actions/scripts/run-utp-tests.sh index 60784ec2..2074956f 100755 --- a/.github/actions/scripts/run-utp-tests.sh +++ b/.github/actions/scripts/run-utp-tests.sh @@ -96,8 +96,18 @@ for raw_test in "${tests[@]}"; do 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=$? + ran_custom_flow=0 + + if [ "$test_name" = "EditmodeTestsErrors" ]; then + unity-cli run --log-name "${test_name}-EditMode" -runTests -testPlatform editmode -testResults "$UNITY_PROJECT_PATH/Builds/Logs/${test_name}-results.xml" -quit || validate_rc=$? + build_rc=$validate_rc + ran_custom_flow=1 + fi + + if [ "$ran_custom_flow" -eq 0 ]; then + 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=$? + fi expected=${expected_status[$test_name]:-0} exp_msg=${expected_message[$test_name]:-} From e4a60c65a6d4e91ecdfb3b07ce7ca125eaee2c55 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 11 Jan 2026 18:35:56 -0500 Subject: [PATCH 25/36] update tests --- .github/actions/scripts/run-utp-tests.sh | 3 ++- .github/workflows/build-options.json | 3 ++- unity-tests/BuildErrors.cs | 2 +- unity-tests/BuildWarnings.cs | 2 +- unity-tests/CompilerWarnings.cs | 2 +- unity-tests/EditmodeTestsErrors.cs | 2 +- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/actions/scripts/run-utp-tests.sh b/.github/actions/scripts/run-utp-tests.sh index 2074956f..a2e37e84 100755 --- a/.github/actions/scripts/run-utp-tests.sh +++ b/.github/actions/scripts/run-utp-tests.sh @@ -25,6 +25,7 @@ clean_tests() { 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 + rm -f "$UNITY_PROJECT_PATH/Assets/Tests/EditMode/Editor/UnityCliTests"/*.cs 2>/dev/null || true } clean_build_outputs() { @@ -80,7 +81,7 @@ for raw_test in "${tests[@]}"; do dest="$UNITY_PROJECT_PATH/Assets/Tests/PlayMode/UnityCliTests" ;; EditmodeTestsErrors) - dest="$UNITY_PROJECT_PATH/Assets/Tests/EditMode/UnityCliTests" + dest="$UNITY_PROJECT_PATH/Assets/Tests/EditMode/Editor/UnityCliTests" ;; *) echo "::error::Unknown test selection '$test_name'" diff --git a/.github/workflows/build-options.json b/.github/workflows/build-options.json index b1671291..0d9a0e85 100644 --- a/.github/workflows/build-options.json +++ b/.github/workflows/build-options.json @@ -15,7 +15,8 @@ "2022.3.*", "6000.0.x", "6000.1.*", - "6000.2" + "6000.2", + "6000" ], "include": [ { diff --git a/unity-tests/BuildErrors.cs b/unity-tests/BuildErrors.cs index ce75c3fe..dc344195 100644 --- a/unity-tests/BuildErrors.cs +++ b/unity-tests/BuildErrors.cs @@ -14,7 +14,7 @@ public class BuildErrors : IPreprocessBuildWithReport public void OnPreprocessBuild(BuildReport report) { - throw new System.Exception("Intentional build failure for test matrix coverage."); + throw new System.Exception("Intentional build failure."); } } } diff --git a/unity-tests/BuildWarnings.cs b/unity-tests/BuildWarnings.cs index f365a770..e4c2a3d7 100644 --- a/unity-tests/BuildWarnings.cs +++ b/unity-tests/BuildWarnings.cs @@ -14,7 +14,7 @@ public class BuildWarnings : IPreprocessBuildWithReport public void OnPreprocessBuild(BuildReport report) { - UnityEngine.Debug.LogWarning("Intentional build warning for test matrix coverage."); + UnityEngine.Debug.LogWarning("Intentional build warning."); } } } diff --git a/unity-tests/CompilerWarnings.cs b/unity-tests/CompilerWarnings.cs index c1fc0778..ae35bd22 100644 --- a/unity-tests/CompilerWarnings.cs +++ b/unity-tests/CompilerWarnings.cs @@ -12,7 +12,7 @@ private void Awake() ObsoleteApi(); // CS0618: call to obsolete member } - [System.Obsolete("Intentional warning for test matrix coverage", false)] + [System.Obsolete("Intentional warning", false)] private static void ObsoleteApi() { } diff --git a/unity-tests/EditmodeTestsErrors.cs b/unity-tests/EditmodeTestsErrors.cs index 4c4d89c9..70ea08a5 100644 --- a/unity-tests/EditmodeTestsErrors.cs +++ b/unity-tests/EditmodeTestsErrors.cs @@ -10,7 +10,7 @@ public class EditmodeTestsErrors [Test] public void FailsEditmodeSuite() { - Assert.Fail("Intentional editmode failure for test matrix coverage."); + Assert.Fail("Intentional editmode failure."); } } } From 556902ea3b2a6e1b13fe4d0a576ff52a1f784497 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 11 Jan 2026 21:03:50 -0500 Subject: [PATCH 26/36] add editor assembly for editor tests --- .github/actions/scripts/run-utp-tests.sh | 21 ++++++++++++++++++- .../UnityCliTests.EditMode.Editor.asmdef | 14 +++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 unity-tests/UnityCliTests.EditMode.Editor.asmdef diff --git a/.github/actions/scripts/run-utp-tests.sh b/.github/actions/scripts/run-utp-tests.sh index a2e37e84..8dc46b4e 100755 --- a/.github/actions/scripts/run-utp-tests.sh +++ b/.github/actions/scripts/run-utp-tests.sh @@ -25,6 +25,7 @@ clean_tests() { 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 + rm -f "$UNITY_PROJECT_PATH/Assets/Tests/EditMode/UnityCliTests"/*.asmdef 2>/dev/null || true rm -f "$UNITY_PROJECT_PATH/Assets/Tests/EditMode/Editor/UnityCliTests"/*.cs 2>/dev/null || true } @@ -70,6 +71,8 @@ for raw_test in "${tests[@]}"; do clean_tests clean_build_outputs + asmdef_src="" + case "$test_name" in CompilerWarnings|CompilerErrors) dest="$UNITY_PROJECT_PATH/Assets/UnityCliTests" @@ -81,7 +84,8 @@ for raw_test in "${tests[@]}"; do dest="$UNITY_PROJECT_PATH/Assets/Tests/PlayMode/UnityCliTests" ;; EditmodeTestsErrors) - dest="$UNITY_PROJECT_PATH/Assets/Tests/EditMode/Editor/UnityCliTests" + dest="$UNITY_PROJECT_PATH/Assets/Tests/EditMode/UnityCliTests" + asmdef_src="$GITHUB_WORKSPACE/unity-tests/UnityCliTests.EditMode.Editor.asmdef" ;; *) echo "::error::Unknown test selection '$test_name'" @@ -91,6 +95,14 @@ for raw_test in "${tests[@]}"; do esac mkdir -p "$dest" + if [ -n "$asmdef_src" ]; then + if [ ! -f "$asmdef_src" ]; then + echo "::error::Assembly definition for editmode tests not found at $asmdef_src" + failures=$((failures+1)) + continue + fi + cp "$asmdef_src" "$dest/" + fi cp "$src" "$dest/" echo "Running test: $test_name (copied to $dest)" @@ -101,6 +113,13 @@ for raw_test in "${tests[@]}"; do if [ "$test_name" = "EditmodeTestsErrors" ]; then unity-cli run --log-name "${test_name}-EditMode" -runTests -testPlatform editmode -testResults "$UNITY_PROJECT_PATH/Builds/Logs/${test_name}-results.xml" -quit || validate_rc=$? + + # Guard against zero-discovery runs that exit 0 by treating no test cases as a failure. + results_xml="$UNITY_PROJECT_PATH/Builds/Logs/${test_name}-results.xml" + if [ -f "$results_xml" ] && ! node -e "const fs=require('fs');const p=process.argv[1];try{const t=fs.readFileSync(p,'utf8');const m=t.match(/0){process.exit(0);} }catch(e){}process.exit(1);" "$results_xml"; then + echo "::error::No editmode tests were discovered for $test_name" + validate_rc=1 + fi build_rc=$validate_rc ran_custom_flow=1 fi diff --git a/unity-tests/UnityCliTests.EditMode.Editor.asmdef b/unity-tests/UnityCliTests.EditMode.Editor.asmdef new file mode 100644 index 00000000..ce9be1f3 --- /dev/null +++ b/unity-tests/UnityCliTests.EditMode.Editor.asmdef @@ -0,0 +1,14 @@ +{ + "name": "UnityCli.EditMode.EditorTests", + "references": [], + "optionalUnityReferences": ["TestAssemblies"], + "includePlatforms": ["Editor"], + "excludePlatforms": [], + "allowUnsafeCode": false, + "overrideReferences": false, + "precompiledReferences": [], + "autoReferenced": true, + "defineConstraints": [], + "versionDefines": [], + "noEngineReferences": false +} From f7bfda986d5724e47c9afb9ee518819f24af6cb5 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 11 Jan 2026 21:12:53 -0500 Subject: [PATCH 27/36] remove cp warning for gnu clobber --- .github/actions/scripts/run-utp-tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/scripts/run-utp-tests.sh b/.github/actions/scripts/run-utp-tests.sh index 8dc46b4e..9f4ae028 100755 --- a/.github/actions/scripts/run-utp-tests.sh +++ b/.github/actions/scripts/run-utp-tests.sh @@ -195,7 +195,7 @@ for raw_test in "${tests[@]}"; do 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 + find "$GITHUB_WORKSPACE" -path "$test_artifacts" -prune -o -type f -name "*${test_name}*-utp-json.log" -print -exec cp --update=none {} "$test_artifacts" \; || true done From 9410cc8815ba682694395f8ab4f9e82f8f3f51a8 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 11 Jan 2026 22:21:22 -0500 Subject: [PATCH 28/36] fix test --- unity-tests/EditmodeTestsErrors.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unity-tests/EditmodeTestsErrors.cs b/unity-tests/EditmodeTestsErrors.cs index 70ea08a5..a0304d8d 100644 --- a/unity-tests/EditmodeTestsErrors.cs +++ b/unity-tests/EditmodeTestsErrors.cs @@ -10,7 +10,7 @@ public class EditmodeTestsErrors [Test] public void FailsEditmodeSuite() { - Assert.Fail("Intentional editmode failure."); + Assert.Fail("Intentional editmode failure"); } } } From ede76eee5b64162b7dbced79fdc4bfe907688f55 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 11 Jan 2026 23:49:26 -0500 Subject: [PATCH 29/36] fix macos tests --- .github/actions/scripts/run-utp-tests.sh | 50 +++++++++++++++--------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/.github/actions/scripts/run-utp-tests.sh b/.github/actions/scripts/run-utp-tests.sh index 9f4ae028..4ad247bd 100755 --- a/.github/actions/scripts/run-utp-tests.sh +++ b/.github/actions/scripts/run-utp-tests.sh @@ -36,21 +36,29 @@ clean_build_outputs() { # 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" +expected_status_for() { + case "$1" in + CompilerWarnings) echo 0 ;; + BuildWarnings) echo 0 ;; + CompilerErrors) echo 1 ;; + BuildErrors) echo 1 ;; + PlaymodeTestsErrors) echo 1 ;; + EditmodeTestsErrors) echo 1 ;; + *) echo 0 ;; + esac +} + +expected_message_for() { + case "$1" in + CompilerErrors) echo "Intentional compiler error" ;; + BuildErrors) echo "Intentional build failure" ;; + PlaymodeTestsErrors) echo "Intentional playmode failure" ;; + EditmodeTestsErrors) echo "Intentional editmode failure" ;; + CompilerWarnings) echo "Intentional warning" ;; + BuildWarnings) echo "Intentional build warning" ;; + *) echo "" ;; + esac +} mkdir -p "$GITHUB_WORKSPACE/utp-artifacts" @@ -129,8 +137,8 @@ for raw_test in "${tests[@]}"; do 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=$? fi - expected=${expected_status[$test_name]:-0} - exp_msg=${expected_message[$test_name]:-} + expected=$(expected_status_for "$test_name") + exp_msg=$(expected_message_for "$test_name") test_failed=0 message_found=0 @@ -195,7 +203,13 @@ for raw_test in "${tests[@]}"; do 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 --update=none {} "$test_artifacts" \; || true + find "$GITHUB_WORKSPACE" -path "$test_artifacts" -prune -o -type f -name "*${test_name}*-utp-json.log" -print | while IFS= read -r utp_src; do + [ -z "$utp_src" ] && continue + dest_file="$test_artifacts/$(basename "$utp_src")" + if [ ! -f "$dest_file" ]; then + cp "$utp_src" "$dest_file" || true + fi + done || true done From 0f3a2c5b61c2e0aaee9fea13fc6785228806332f Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 11 Jan 2026 23:52:17 -0500 Subject: [PATCH 30/36] refactor tets --- .gitattributes | 1 + .github/actions/scripts/run-utp-tests.sh | 8 +++----- 2 files changed, 4 insertions(+), 5 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..526c8a38 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +*.sh text eol=lf \ No newline at end of file diff --git a/.github/actions/scripts/run-utp-tests.sh b/.github/actions/scripts/run-utp-tests.sh index 4ad247bd..e179e646 100755 --- a/.github/actions/scripts/run-utp-tests.sh +++ b/.github/actions/scripts/run-utp-tests.sh @@ -120,12 +120,10 @@ for raw_test in "${tests[@]}"; do ran_custom_flow=0 if [ "$test_name" = "EditmodeTestsErrors" ]; then - unity-cli run --log-name "${test_name}-EditMode" -runTests -testPlatform editmode -testResults "$UNITY_PROJECT_PATH/Builds/Logs/${test_name}-results.xml" -quit || validate_rc=$? + unity-cli run --log-name "${test_name}-EditMode" -runTests -testPlatform editmode -assemblyNames "UnityCli.EditMode.EditorTests" -testResults "$UNITY_PROJECT_PATH/Builds/Logs/${test_name}-results.xml" -quit || validate_rc=$? - # Guard against zero-discovery runs that exit 0 by treating no test cases as a failure. results_xml="$UNITY_PROJECT_PATH/Builds/Logs/${test_name}-results.xml" - if [ -f "$results_xml" ] && ! node -e "const fs=require('fs');const p=process.argv[1];try{const t=fs.readFileSync(p,'utf8');const m=t.match(/0){process.exit(0);} }catch(e){}process.exit(1);" "$results_xml"; then - echo "::error::No editmode tests were discovered for $test_name" + if ! grep -q "/dev/null; then validate_rc=1 fi build_rc=$validate_rc @@ -161,7 +159,7 @@ for raw_test in "${tests[@]}"; do if [ -z "$utp_file" ]; then continue fi - if node -e "const fs=require('fs');const p=process.argv[1];try{const data=JSON.parse(fs.readFileSync(p,'utf8'));if(Array.isArray(data)&&data.some(e=>['Error','Exception','Assert'].includes(e?.severity))){process.exit(0);} }catch{}process.exit(1);" "$utp_file"; then + if grep -qi '"severity"[[:space:]]*:[[:space:]]*"\(Error\|Exception\|Assert\)"' "$utp_file" 2>/dev/null; then utp_error_found=1 break fi From 6a335384985ff7870cf815507080e0abfbd95a44 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 25 Jan 2026 10:54:43 -0500 Subject: [PATCH 31/36] update utp tests --- .github/actions/scripts/run-utp-tests.sh | 18 +- package-lock.json | 250 +++++++++++------------ package.json | 4 +- 3 files changed, 143 insertions(+), 129 deletions(-) diff --git a/.github/actions/scripts/run-utp-tests.sh b/.github/actions/scripts/run-utp-tests.sh index e179e646..360f97d0 100755 --- a/.github/actions/scripts/run-utp-tests.sh +++ b/.github/actions/scripts/run-utp-tests.sh @@ -11,7 +11,7 @@ if printf '%s' "$BUILD_ARGS" | grep -qE '[;&`|]'; then exit 1 fi -build_args=() +declare -a build_args=() if [ -n "$BUILD_ARGS" ]; then # Split on whitespace into an array without invoking the shell read -r -a build_args <<< "$BUILD_ARGS" @@ -132,7 +132,21 @@ for raw_test in "${tests[@]}"; do if [ "$ran_custom_flow" -eq 0 ]; then 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=$? + + build_cmd=( + unity-cli run + --log-name "${test_name}-Build" + -buildTarget "$BUILD_TARGET" + -quit + -executeMethod Utilities.Editor.BuildPipeline.UnityPlayerBuildTools.StartCommandLineBuild + -sceneList Assets/Scenes/SampleScene.unity + ) + + if [ ${#build_args[@]} -gt 0 ]; then + build_cmd+=("${build_args[@]}") + fi + + "${build_cmd[@]}" || build_rc=$? fi expected=$(expected_status_for "$test_name") diff --git a/package-lock.json b/package-lock.json index 8796015b..10f98985 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,7 +15,7 @@ "glob": "^11.1.0", "semver": "^7.7.3", "source-map-support": "^0.5.21", - "tar": "^7.5.2", + "tar": "^7.5.6", "update-notifier": "^7.3.1", "yaml": "^2.8.2" }, @@ -24,7 +24,7 @@ }, "devDependencies": { "@types/jest": "^30.0.0", - "@types/node": "^24.10.4", + "@types/node": "^24.10.9", "@types/semver": "^7.7.1", "@types/update-notifier": "^6.0.8", "jest": "^30.2.0", @@ -34,13 +34,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", + "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -49,9 +49,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", - "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", + "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", "dev": true, "license": "MIT", "engines": { @@ -59,21 +59,21 @@ } }, "node_modules/@babel/core": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", - "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", + "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -100,14 +100,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", - "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", + "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.5", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -117,13 +117,13 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.27.2", + "@babel/compat-data": "^7.28.6", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", @@ -154,29 +154,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -186,9 +186,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "dev": true, "license": "MIT", "engines": { @@ -226,27 +226,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", - "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.5" + "@babel/types": "^7.28.6" }, "bin": { "parser": "bin/babel-parser.js" @@ -311,13 +311,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -353,13 +353,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -479,13 +479,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -495,33 +495,33 @@ } }, "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", - "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", + "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.5", + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.5", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.5", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6", "debug": "^4.3.1" }, "engines": { @@ -529,9 +529,9 @@ } }, "node_modules/@babel/types": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", - "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", "dev": true, "license": "MIT", "dependencies": { @@ -600,9 +600,9 @@ } }, "node_modules/@emnapi/core": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", - "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.8.1.tgz", + "integrity": "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg==", "dev": true, "license": "MIT", "optional": true, @@ -612,9 +612,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", - "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", "dev": true, "license": "MIT", "optional": true, @@ -1452,9 +1452,9 @@ "license": "ISC" }, "node_modules/@pnpm/npm-conf": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-2.3.1.tgz", - "integrity": "sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@pnpm/npm-conf/-/npm-conf-3.0.2.tgz", + "integrity": "sha512-h104Kh26rR8tm+a3Qkc5S4VLYint3FE48as7+/5oCEcKR2idC/pF1G6AhIXKI+eHPJa/3J9i5z0Al47IeGHPkA==", "license": "MIT", "dependencies": { "@pnpm/config.env-replace": "^1.1.0", @@ -1475,9 +1475,9 @@ } }, "node_modules/@sinclair/typebox": { - "version": "0.34.41", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.41.tgz", - "integrity": "sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==", + "version": "0.34.48", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", + "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "dev": true, "license": "MIT" }, @@ -1631,9 +1631,9 @@ } }, "node_modules/@types/node": { - "version": "24.10.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.4.tgz", - "integrity": "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg==", + "version": "24.10.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.9.tgz", + "integrity": "sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==", "dev": true, "license": "MIT", "dependencies": { @@ -2269,9 +2269,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.9.11", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.11.tgz", - "integrity": "sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==", + "version": "2.9.18", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.18.tgz", + "integrity": "sha512-e23vBV1ZLfjb9apvfPk4rHVu2ry6RIr2Wfs+O324okSidrX7pTAnEJPCh/O5BtRlr7QtZI7ktOP3vsqr7Z5XoA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -2411,9 +2411,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001761", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001761.tgz", - "integrity": "sha512-JF9ptu1vP2coz98+5051jZ4PwQgd2ni8A+gYSN7EA7dPKIMf0pDlSUxhdmVOaV3/fYK5uWBkgSXJaRLr4+3A6g==", + "version": "1.0.30001766", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz", + "integrity": "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==", "dev": true, "funding": [ { @@ -2479,9 +2479,9 @@ } }, "node_modules/cjs-module-lexer": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.1.tgz", - "integrity": "sha512-+CmxIZ/L2vNcEfvNtLdU0ZQ6mbq3FZnwAP2PPTiKP+1QOoKwlKlPgb8UKV0Dds7QVaMnHm+FwSft2VB0s/SLjQ==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.2.0.tgz", + "integrity": "sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==", "dev": true, "license": "MIT" }, @@ -2768,9 +2768,9 @@ } }, "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -2811,9 +2811,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.267", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", - "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "version": "1.5.278", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.278.tgz", + "integrity": "sha512-dQ0tM1svDRQOwxnXxm+twlGTjr9Upvt8UFWAgmLsxEzFQxhbti4VwxmMjsDxVC51Zo84swW7FVCXEV+VAkhuPw==", "dev": true, "license": "ISC" }, @@ -4719,9 +4719,9 @@ } }, "node_modules/ky": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/ky/-/ky-1.14.1.tgz", - "integrity": "sha512-hYje4L9JCmpEQBtudo+v52X5X8tgWXUYyPcxKSuxQNboqufecl9VMWjGiucAFH060AwPXHZuH+WB2rrqfkmafw==", + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/ky/-/ky-1.14.2.tgz", + "integrity": "sha512-q3RBbsO5A5zrPhB6CaCS8ZUv+NWCXv6JJT4Em0i264G9W0fdPB8YRfnnEi7Dm7X7omAkBIPojzYJ2D1oHTHqug==", "license": "MIT", "engines": { "node": ">=18" @@ -5287,12 +5287,12 @@ "license": "MIT" }, "node_modules/registry-auth-token": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.0.tgz", - "integrity": "sha512-GdekYuwLXLxMuFTwAPg5UKGLW/UXzQrZvH/Zj791BQif5T05T0RsaLfHc9q3ZOKi7n+BoprPD9mJ0O0k4xzUlw==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-5.1.1.tgz", + "integrity": "sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q==", "license": "MIT", "dependencies": { - "@pnpm/npm-conf": "^2.1.0" + "@pnpm/npm-conf": "^3.0.2" }, "engines": { "node": ">=14" @@ -5635,9 +5635,9 @@ } }, "node_modules/synckit": { - "version": "0.11.11", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz", - "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==", + "version": "0.11.12", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.12.tgz", + "integrity": "sha512-Bh7QjT8/SuKUIfObSXNHNSK6WHo6J1tHCqJsuaFDP7gP0fkzSfTxI8y85JrppZ0h8l0maIgc2tfuZQ6/t3GtnQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5651,9 +5651,9 @@ } }, "node_modules/tar": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", - "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.6.tgz", + "integrity": "sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA==", "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", diff --git a/package.json b/package.json index 581f58cc..29138c96 100644 --- a/package.json +++ b/package.json @@ -54,13 +54,13 @@ "glob": "^11.1.0", "semver": "^7.7.3", "source-map-support": "^0.5.21", - "tar": "^7.5.2", + "tar": "^7.5.6", "update-notifier": "^7.3.1", "yaml": "^2.8.2" }, "devDependencies": { "@types/jest": "^30.0.0", - "@types/node": "^24.10.4", + "@types/node": "^24.10.9", "@types/semver": "^7.7.1", "@types/update-notifier": "^6.0.8", "jest": "^30.2.0", From f638dded2a96dd983f5f6130d8dc90301d692072 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 25 Jan 2026 11:38:20 -0500 Subject: [PATCH 32/36] remove dup step --- .github/workflows/unity-build.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/unity-build.yml b/.github/workflows/unity-build.yml index 97d2698a..dd46ebcd 100644 --- a/.github/workflows/unity-build.yml +++ b/.github/workflows/unity-build.yml @@ -175,13 +175,6 @@ jobs: 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: ${{ steps.utp-artifact-name.outputs.name }} - path: '**/*-utp-json.log' - if-no-files-found: ignore - name: Return License if: always() run: unity-cli return-license --license personal From f8d637c7234c51ce8da6429f0e65862fbe25b15b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 25 Jan 2026 11:41:12 -0500 Subject: [PATCH 33/36] Bump tar from 7.5.2 to 7.5.3 in the npm_and_yarn group across 1 directory (#64) Bumps the npm_and_yarn group with 1 update in the / directory: [tar](https://github.com/isaacs/node-tar). Updates `tar` from 7.5.2 to 7.5.3
Commits

[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=tar&package-manager=npm_and_yarn&previous-version=7.5.2&new-version=7.5.3)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores) Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) ---
Dependabot commands and options
You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot merge` will merge this PR after your CI passes on it - `@dependabot squash and merge` will squash and merge this PR after your CI passes on it - `@dependabot cancel merge` will cancel a previously requested merge and block automerging - `@dependabot reopen` will reopen this PR if it is closed - `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually - `@dependabot show ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore ` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore ` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore ` will remove the ignore condition of the specified dependency and ignore conditions You can disable automated security fix PRs for this repo from the [Security Alerts page](https://github.com/RageAgainstThePixel/unity-cli/network/alerts).
Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Stephen Hodgson From 18a04cf6662d9f639e026dcf9bc11531698e54a1 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 25 Jan 2026 11:45:32 -0500 Subject: [PATCH 34/36] remove some build versions to speed up testing --- .github/workflows/build-options.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/build-options.json b/.github/workflows/build-options.json index 0d9a0e85..5eec9059 100644 --- a/.github/workflows/build-options.json +++ b/.github/workflows/build-options.json @@ -5,10 +5,6 @@ "macos-latest" ], "unity-version": [ - "4.7.2", - "5.6.7f1 (e80cc3114ac1)", - "2017.4.40f1", - "2018", "2019.x", "2020.*", "2021.3.x", From ce4aa2e6fe8f8c8427acf1c1205380db4b80b2d7 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 25 Jan 2026 14:27:11 -0500 Subject: [PATCH 35/36] fix artifact names --- .github/workflows/unity-build.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/unity-build.yml b/.github/workflows/unity-build.yml index dd46ebcd..21c44ed7 100644 --- a/.github/workflows/unity-build.yml +++ b/.github/workflows/unity-build.yml @@ -100,6 +100,13 @@ jobs: else echo "Skipping build: Unity version $version does not support the build pipeline package (requires 2019.4+)" fi + - name: Compute safe artifact name + id: artifact-name + run: | + unity_version="${{ matrix.unity-version }}" + unity_version="${unity_version//'*'/x}" + echo "name=${{ matrix.os }}-${unity_version}-${{ matrix.build-target }}-tests-batch-utp-logs" >> $GITHUB_OUTPUT + shell: bash - name: Run Unity UTP test batches if: ${{ steps.verify-project-path.outputs.RUN_BUILD == 'true' }} uses: ./.github/actions/run-unity-test-batch @@ -107,7 +114,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 + artifact-name: ${{ steps.artifact-name.outputs.name }} - name: Update Android Target Sdk Version if: ${{ matrix.build-target == 'Android' }} run: | From c56991b00fc5d63375f8c40dae9fab82bb9edb57 Mon Sep 17 00:00:00 2001 From: Stephen Hodgson Date: Sun, 25 Jan 2026 16:48:59 -0500 Subject: [PATCH 36/36] fix log uploads --- .github/actions/run-unity-test-batch/action.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/actions/run-unity-test-batch/action.yml b/.github/actions/run-unity-test-batch/action.yml index 746653b7..237c73ff 100644 --- a/.github/actions/run-unity-test-batch/action.yml +++ b/.github/actions/run-unity-test-batch/action.yml @@ -43,10 +43,12 @@ runs: UNITY_PROJECT_PATH: ${{ inputs.unity-project-path }} BUILD_TARGET: ${{ inputs.build-target }} BUILD_ARGS: ${{ inputs.build-args }} + continue-on-error: true run: | bash "${GITHUB_WORKSPACE}/.github/actions/scripts/run-utp-tests.sh" - name: Upload UTP logs + if: always() uses: actions/upload-artifact@v6 with: name: ${{ inputs.artifact-name }}