diff --git a/.github/workflows/build-tests-mac.yml b/.github/workflows/build-tests-mac.yml index 1c383a21..4775aef9 100644 --- a/.github/workflows/build-tests-mac.yml +++ b/.github/workflows/build-tests-mac.yml @@ -18,7 +18,7 @@ jobs: projectPath: - test-project unityVersion: - - 2021.3.32f1 + - 2021.3.45f1 - 2022.3.13f1 - 2023.2.2f1 targetPlatform: diff --git a/.github/workflows/build-tests-windows.yml b/.github/workflows/build-tests-windows.yml index db2fab20..c0c5de19 100644 --- a/.github/workflows/build-tests-windows.yml +++ b/.github/workflows/build-tests-windows.yml @@ -26,6 +26,14 @@ jobs: - StandaloneWindows64 # Build a Windows 64-bit standalone. - WSAPlayer # Build a UWP App - tvOS # Build an Apple TV XCode project + enableGpu: + - false + include: + # Additionally test enableGpu build for a standalone windows target + - projectPath: test-project + unityVersion: 2023.2.2f1 + targetPlatform: StandaloneWindows64 + enableGpu: true steps: ########################### @@ -71,6 +79,7 @@ jobs: projectPath: ${{ matrix.projectPath }} unityVersion: ${{ matrix.unityVersion }} targetPlatform: ${{ matrix.targetPlatform }} + enableGpu: ${{ matrix.enableGpu }} customParameters: -profile SomeProfile -someBoolean -someValue exampleValue allowDirtyBuild: true # We use dirty build because we are replacing the default project settings file above @@ -96,6 +105,7 @@ jobs: projectPath: ${{ matrix.projectPath }} unityVersion: ${{ matrix.unityVersion }} targetPlatform: ${{ matrix.targetPlatform }} + enableGpu: ${{ matrix.enableGpu }} customParameters: -profile SomeProfile -someBoolean -someValue exampleValue allowDirtyBuild: true # We use dirty build because we are replacing the default project settings file above @@ -120,6 +130,7 @@ jobs: projectPath: ${{ matrix.projectPath }} unityVersion: ${{ matrix.unityVersion }} targetPlatform: ${{ matrix.targetPlatform }} + enableGpu: ${{ matrix.enableGpu }} customParameters: -profile SomeProfile -someBoolean -someValue exampleValue allowDirtyBuild: true # We use dirty build because we are replacing the default project settings file above @@ -129,6 +140,6 @@ jobs: ########################### - uses: actions/upload-artifact@v4 with: - name: Build ${{ matrix.targetPlatform }} on Windows (${{ matrix.unityVersion }}) + name: Build ${{ matrix.targetPlatform }} on Windows (${{ matrix.unityVersion }})${{ matrix.enableGpu && ' With GPU' || '' }} path: build retention-days: 14 diff --git a/.github/workflows/cloud-runner-ci-pipeline.yml b/.github/workflows/cloud-runner-ci-pipeline.yml index c8886b8e..c0d16a6d 100644 --- a/.github/workflows/cloud-runner-ci-pipeline.yml +++ b/.github/workflows/cloud-runner-ci-pipeline.yml @@ -3,6 +3,11 @@ name: Cloud Runner CI Pipeline on: push: { branches: [cloud-runner-develop, cloud-runner-preview, main] } workflow_dispatch: + inputs: + runGithubIntegrationTests: + description: 'Run GitHub Checks integration tests' + required: false + default: 'false' permissions: checks: write @@ -211,3 +216,20 @@ jobs: name: ${{ matrix.providerStrategy }} Build (${{ matrix.targetPlatform }}) path: ${{ steps.unity-build.outputs.BUILD_ARTIFACT }} retention-days: 14 + + githubChecksIntegration: + name: GitHub Checks Integration + runs-on: ubuntu-latest + if: github.event_name == 'workflow_dispatch' && github.event.inputs.runGithubIntegrationTests == 'true' + env: + RUN_GITHUB_INTEGRATION_TESTS: true + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'yarn' + - run: yarn install --frozen-lockfile + - run: yarn test cloud-runner-github-checks-integration-test --detectOpenHandles --forceExit --runInBand + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/dist/default-build-script/Assets/Editor/UnityBuilderAction/Input/AndroidSettings.cs b/dist/default-build-script/Assets/Editor/UnityBuilderAction/Input/AndroidSettings.cs index 06aad4f2..1a593167 100644 --- a/dist/default-build-script/Assets/Editor/UnityBuilderAction/Input/AndroidSettings.cs +++ b/dist/default-build-script/Assets/Editor/UnityBuilderAction/Input/AndroidSettings.cs @@ -74,7 +74,20 @@ namespace UnityBuilderAction.Input string symbolType; if (options.TryGetValue("androidSymbolType", out symbolType) && !string.IsNullOrEmpty(symbolType)) { -#if UNITY_2021_1_OR_NEWER +#if UNITY_6000_0_OR_NEWER + switch (symbolType) + { + case "public": + SetDebugSymbols("SymbolTable"); + break; + case "debugging": + SetDebugSymbols("Full"); + break; + case "none": + SetDebugSymbols("None"); + break; + } +#elif UNITY_2021_1_OR_NEWER switch (symbolType) { case "public": @@ -101,5 +114,35 @@ namespace UnityBuilderAction.Input #endif } } + + private static void SetDebugSymbols(string enumValueName) + { + // UnityEditor.Android.UserBuildSettings and Unity.Android.Types.DebugSymbolLevel are part of the Unity Android module. + // Reflection is used here to ensure the code works even if the module is not installed. + + var debugSymbolsType = Type.GetType("UnityEditor.Android.UserBuildSettings+DebugSymbols, UnityEditor.Android.Extensions"); + if (debugSymbolsType == null) + { + return; + } + + var levelProp = debugSymbolsType.GetProperty("level", BindingFlags.Static | BindingFlags.Public); + if (levelProp == null) + { + return; + } + + var enumType = Type.GetType("Unity.Android.Types.DebugSymbolLevel, Unity.Android.Types"); + if (enumType == null) + { + return; + } + + if (!Enum.TryParse(enumType, enumValueName, false , out var enumValue)) + { + return; + } + levelProp.SetValue(null, enumValue); + } } } diff --git a/dist/licenses.txt b/dist/licenses.txt index 497a23d4..138f7f39 100644 --- a/dist/licenses.txt +++ b/dist/licenses.txt @@ -13820,6 +13820,210 @@ Apache License See the License for the specific language governing permissions and limitations under the License. +@smithy/util-body-length-browser +Apache-2.0 +Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + @smithy/util-body-length-node Apache-2.0 Apache License diff --git a/dist/platforms/windows/build.ps1 b/dist/platforms/windows/build.ps1 index 6b126058..ac78d62b 100644 --- a/dist/platforms/windows/build.ps1 +++ b/dist/platforms/windows/build.ps1 @@ -148,13 +148,20 @@ Write-Output "# Building project #" Write-Output "###########################" Write-Output "" +$unityGraphics = "-nographics" + +if ($LLVMPIPE_INSTALLED -eq "true") +{ + $unityGraphics = "-force-opengl" +} + # If $Env:CUSTOM_PARAMETERS contains spaces and is passed directly on the command line to Unity, powershell will wrap it # in double quotes. To avoid this, parse $Env:CUSTOM_PARAMETERS into an array, while respecting any quotations within the string. $_, $customParametersArray = Invoke-Expression('Write-Output -- "" ' + $Env:CUSTOM_PARAMETERS) $unityArgs = @( "-quit", "-batchmode", - "-nographics", + $unityGraphics, "-silent-crashes", "-customBuildName", "`"$Env:BUILD_NAME`"", "-projectPath", "`"$Env:UNITY_PROJECT_PATH`"", diff --git a/dist/platforms/windows/entrypoint.ps1 b/dist/platforms/windows/entrypoint.ps1 index 4d7057e6..0868015d 100644 --- a/dist/platforms/windows/entrypoint.ps1 +++ b/dist/platforms/windows/entrypoint.ps1 @@ -1,5 +1,13 @@ Get-Process +# Copy .upmconfig.toml if it exists +if (Test-Path "C:\githubhome\.upmconfig.toml") { + Write-Host "Copying .upmconfig.toml to $Env:USERPROFILE\.upmconfig.toml" + Copy-Item -Path "C:\githubhome\.upmconfig.toml" -Destination "$Env:USERPROFILE\.upmconfig.toml" -Force +} else { + Write-Host "No .upmconfig.toml found at C:\githubhome" +} + # Import any necessary registry keys, ie: location of windows 10 sdk # No guarantee that there will be any necessary registry keys, ie: tvOS Get-ChildItem -Path c:\regkeys -File | ForEach-Object { reg import $_.fullname } @@ -13,6 +21,11 @@ Get-Process -Name regsvr32 | ForEach-Object { Stop-Process -Id $_.Id -Force } # Setup Git Credentials . "c:\steps\set_gitcredential.ps1" +if ($env:ENABLE_GPU -eq "true") { + # Install LLVMpipe software graphics driver + . "c:\steps\install_llvmpipe.ps1" +} + # Activate Unity if ($env:SKIP_ACTIVATION -ne "true") { . "c:\steps\activate.ps1" diff --git a/dist/platforms/windows/install_llvmpipe.ps1 b/dist/platforms/windows/install_llvmpipe.ps1 new file mode 100644 index 00000000..fa88d2ea --- /dev/null +++ b/dist/platforms/windows/install_llvmpipe.ps1 @@ -0,0 +1,56 @@ +$Private:repo = "mmozeiko/build-mesa" +$Private:downloadPath = "$Env:TEMP\mesa.zip" +$Private:extractPath = "$Env:TEMP\mesa" +$Private:destinationPath = "$Env:UNITY_PATH\Editor\" +$Private:version = "25.1.0" + +$LLVMPIPE_INSTALLED = "false" + +try { + # Get the release info from GitHub API (version fixed to decrease probability of breakage) + $releaseUrl = "https://api.github.com/repos/$repo/releases/tags/$version" + $release = Invoke-RestMethod -Uri $releaseUrl -Headers @{ "User-Agent" = "PowerShell" } + + # Get the download URL for the zip asset + $zipUrl = $release.assets | Where-Object { $_.name -like "mesa-llvmpipe-x64*.zip" } | Select-Object -First 1 -ExpandProperty browser_download_url + + if (-not $zipUrl) { + throw "No zip file found in the latest release." + } + + # Download the zip file + Write-Host "Downloading $zipUrl..." + Invoke-WebRequest -Uri $zipUrl -OutFile $downloadPath + + # Create extraction directory if it doesn't exist + if (-not (Test-Path $extractPath)) { + New-Item -ItemType Directory -Path $extractPath | Out-Null + } + + # Extract the zip file + Write-Host "Extracting $downloadPath to $extractPath..." + Expand-Archive -Path $downloadPath -DestinationPath $extractPath -Force + + # Create destination directory if it doesn't exist + if (-not (Test-Path $destinationPath)) { + New-Item -ItemType Directory -Path $destinationPath | Out-Null + } + + # Copy extracted files to destination + Write-Host "Copying files to $destinationPath..." + Copy-Item -Path "$extractPath\*" -Destination $destinationPath -Recurse -Force + + Write-Host "Successfully downloaded, extracted, and copied Mesa files to $destinationPath" + + $LLVMPIPE_INSTALLED = "true" +} catch { + Write-Error "An error occurred: $_" +} finally { + # Clean up temporary files + if (Test-Path $downloadPath) { + Remove-Item $downloadPath -Force + } + if (Test-Path $extractPath) { + Remove-Item $extractPath -Recurse -Force + } +} diff --git a/src/integration/cloud-runner-github-checks-integration-test.ts b/src/integration/cloud-runner-github-checks-integration-test.ts new file mode 100644 index 00000000..be5a70b5 --- /dev/null +++ b/src/integration/cloud-runner-github-checks-integration-test.ts @@ -0,0 +1,29 @@ +// Integration test for exercising real GitHub check creation and updates. +import CloudRunner from '../model/cloud-runner/cloud-runner'; +import UnityVersioning from '../model/unity-versioning'; +import GitHub from '../model/github'; +import { TIMEOUT_INFINITE, createParameters } from '../test-utils/cloud-runner-test-helpers'; + +const runIntegration = process.env.RUN_GITHUB_INTEGRATION_TESTS === 'true'; +const describeOrSkip = runIntegration ? describe : describe.skip; + +describeOrSkip('Cloud Runner Github Checks Integration', () => { + it( + 'creates and updates a real GitHub check', + async () => { + const buildParameter = await createParameters({ + versioning: 'None', + projectPath: 'test-project', + unityVersion: UnityVersioning.read('test-project'), + asyncCloudRunner: `true`, + githubChecks: `true`, + }); + await CloudRunner.setup(buildParameter); + const checkId = await GitHub.createGitHubCheck(`integration create`); + expect(checkId).not.toEqual(''); + await GitHub.updateGitHubCheck(`1 ${new Date().toISOString()}`, `integration`); + await GitHub.updateGitHubCheck(`2 ${new Date().toISOString()}`, `integration`, `success`, `completed`); + }, + TIMEOUT_INFINITE, + ); +}); diff --git a/src/jest.globals.ts b/src/jest.globals.ts new file mode 100644 index 00000000..b3905158 --- /dev/null +++ b/src/jest.globals.ts @@ -0,0 +1,3 @@ +import { fetch as undiciFetch, Headers, Request, Response } from 'undici'; + +Object.assign(globalThis, { fetch: undiciFetch, Headers, Request, Response }); diff --git a/src/model/cloud-runner/tests/cloud-runner-github-checks.test.ts b/src/model/cloud-runner/tests/cloud-runner-github-checks.test.ts index adb5b4b6..dbae58cf 100644 --- a/src/model/cloud-runner/tests/cloud-runner-github-checks.test.ts +++ b/src/model/cloud-runner/tests/cloud-runner-github-checks.test.ts @@ -1,59 +1,65 @@ -import { BuildParameters } from '../..'; import CloudRunner from '../cloud-runner'; import UnityVersioning from '../../unity-versioning'; -import { Cli } from '../../cli/cli'; -import CloudRunnerOptions from '../options/cloud-runner-options'; import setups from './cloud-runner-suite.test'; -import { OptionValues } from 'commander'; import GitHub from '../../github'; -export const TIMEOUT_INFINITE = 1e9; -async function CreateParameters(overrides: OptionValues | undefined) { - if (overrides) Cli.options = overrides; - - return BuildParameters.create(); -} +import { TIMEOUT_INFINITE, createParameters } from '../../../test-utils/cloud-runner-test-helpers'; describe('Cloud Runner Github Checks', () => { setups(); it('Responds', () => {}); - if (CloudRunnerOptions.cloudRunnerDebug) { - it( - 'Check Handling Direct', - async () => { - // Setup parameters - const buildParameter = await CreateParameters({ - versioning: 'None', - projectPath: 'test-project', - unityVersion: UnityVersioning.read('test-project'), - asyncCloudRunner: `true`, - githubChecks: `true`, - }); - await CloudRunner.setup(buildParameter); - CloudRunner.buildParameters.githubCheckId = await GitHub.createGitHubCheck(`direct create`); - await GitHub.updateGitHubCheck(`1 ${new Date().toISOString()}`, `direct`); - await GitHub.updateGitHubCheck(`2 ${new Date().toISOString()}`, `direct`, `success`, `completed`); - }, - TIMEOUT_INFINITE, - ); - it( - 'Check Handling Via Async Workflow', - async () => { - // Setup parameters - const buildParameter = await CreateParameters({ - versioning: 'None', - projectPath: 'test-project', - unityVersion: UnityVersioning.read('test-project'), - asyncCloudRunner: `true`, - githubChecks: `true`, - }); - GitHub.forceAsyncTest = true; - await CloudRunner.setup(buildParameter); - CloudRunner.buildParameters.githubCheckId = await GitHub.createGitHubCheck(`async create`); - await GitHub.updateGitHubCheck(`1 ${new Date().toISOString()}`, `async`); - await GitHub.updateGitHubCheck(`2 ${new Date().toISOString()}`, `async`, `success`, `completed`); - GitHub.forceAsyncTest = false; - }, - TIMEOUT_INFINITE, - ); - } + beforeEach(() => { + // Mock GitHub API requests to avoid real network calls + jest.spyOn(GitHub as any, 'createGitHubCheckRequest').mockResolvedValue({ + status: 201, + data: { id: '1' }, + }); + jest.spyOn(GitHub as any, 'updateGitHubCheckRequest').mockResolvedValue({ + status: 200, + data: {}, + }); + jest.spyOn(GitHub as any, 'runUpdateAsyncChecksWorkflow').mockResolvedValue(undefined); + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + it( + 'Check Handling Direct', + async () => { + // Setup parameters + const buildParameter = await createParameters({ + versioning: 'None', + projectPath: 'test-project', + unityVersion: UnityVersioning.read('test-project'), + asyncCloudRunner: `true`, + githubChecks: `true`, + }); + await CloudRunner.setup(buildParameter); + CloudRunner.buildParameters.githubCheckId = await GitHub.createGitHubCheck(`direct create`); + await GitHub.updateGitHubCheck(`1 ${new Date().toISOString()}`, `direct`); + await GitHub.updateGitHubCheck(`2 ${new Date().toISOString()}`, `direct`, `success`, `completed`); + }, + TIMEOUT_INFINITE, + ); + it( + 'Check Handling Via Async Workflow', + async () => { + // Setup parameters + const buildParameter = await createParameters({ + versioning: 'None', + projectPath: 'test-project', + unityVersion: UnityVersioning.read('test-project'), + asyncCloudRunner: `true`, + githubChecks: `true`, + }); + GitHub.forceAsyncTest = true; + await CloudRunner.setup(buildParameter); + CloudRunner.buildParameters.githubCheckId = await GitHub.createGitHubCheck(`async create`); + await GitHub.updateGitHubCheck(`1 ${new Date().toISOString()}`, `async`); + await GitHub.updateGitHubCheck(`2 ${new Date().toISOString()}`, `async`, `success`, `completed`); + GitHub.forceAsyncTest = false; + }, + TIMEOUT_INFINITE, + ); }); diff --git a/src/model/docker.ts b/src/model/docker.ts index 544540f7..8820fbbe 100644 --- a/src/model/docker.ts +++ b/src/model/docker.ts @@ -92,6 +92,7 @@ class Docker { const { workspace, actionFolder, + runnerTempPath, gitPrivateToken, dockerWorkspacePath, dockerCpuLimit, @@ -99,6 +100,9 @@ class Docker { dockerIsolationMode, } = parameters; + const githubHome = path.join(runnerTempPath, '_github_home'); + if (!existsSync(githubHome)) mkdirSync(githubHome); + return `docker run \ --workdir c:${dockerWorkspacePath} \ --rm \ @@ -106,6 +110,7 @@ class Docker { --env GITHUB_WORKSPACE=c:${dockerWorkspacePath} \ ${gitPrivateToken ? `--env GIT_PRIVATE_TOKEN="${gitPrivateToken}"` : ''} \ --volume "${workspace}":"c:${dockerWorkspacePath}" \ + --volume "${githubHome}":"C:/githubhome" \ --volume "c:/regkeys":"c:/regkeys" \ --volume "C:/Program Files/Microsoft Visual Studio":"C:/Program Files/Microsoft Visual Studio" \ --volume "C:/Program Files (x86)/Microsoft Visual Studio":"C:/Program Files (x86)/Microsoft Visual Studio" \ diff --git a/src/model/image-tag.ts b/src/model/image-tag.ts index 2b9fd910..485abb6c 100644 --- a/src/model/image-tag.ts +++ b/src/model/image-tag.ts @@ -58,6 +58,7 @@ class ImageTag { android: 'android', ios: 'ios', tvos: 'appletv', + visionos: 'visionos', facebook: 'facebook', }; } @@ -82,8 +83,21 @@ class ImageTag { version: string, providerStrategy: string, ): string { - const { generic, webgl, mac, windows, windowsIl2cpp, wsaPlayer, linux, linuxIl2cpp, android, ios, tvos, facebook } = - ImageTag.targetPlatformSuffixes; + const { + generic, + webgl, + mac, + windows, + windowsIl2cpp, + wsaPlayer, + linux, + linuxIl2cpp, + android, + ios, + tvos, + visionos, + facebook, + } = ImageTag.targetPlatformSuffixes; const [major, minor] = version.split('.').map((digit) => Number(digit)); @@ -136,11 +150,17 @@ class ImageTag { case Platform.types.XboxOne: return windows; case Platform.types.tvOS: - if (process.platform !== 'win32') { - throw new Error(`tvOS can only be built on a windows base OS`); + if (process.platform !== 'win32' && process.platform !== 'darwin') { + throw new Error(`tvOS can only be built on Windows or macOS base OS`); } return tvos; + case Platform.types.VisionOS: + if (process.platform !== 'darwin') { + throw new Error(`visionOS can only be built on a macOS base OS`); + } + + return visionos; case Platform.types.Switch: return windows; diff --git a/src/model/platform-setup/setup-mac.ts b/src/model/platform-setup/setup-mac.ts index 17c0d413..962fba0f 100644 --- a/src/model/platform-setup/setup-mac.ts +++ b/src/model/platform-setup/setup-mac.ts @@ -101,7 +101,10 @@ class SetupMac { moduleArgument.push('--module', 'ios'); break; case 'tvOS': - moduleArgument.push('--module', 'tvos'); + moduleArgument.push('--module', 'appletv'); + break; + case 'VisionOS': + moduleArgument.push('--module', 'visionos'); break; case 'StandaloneOSX': moduleArgument.push('--module', 'mac-il2cpp'); diff --git a/src/model/platform.ts b/src/model/platform.ts index 768e51b4..ed1631aa 100644 --- a/src/model/platform.ts +++ b/src/model/platform.ts @@ -16,6 +16,7 @@ class Platform { PS4: 'PS4', XboxOne: 'XboxOne', tvOS: 'tvOS', + VisionOS: 'VisionOS', Switch: 'Switch', // Unsupported diff --git a/src/model/unity-versioning.test.ts b/src/model/unity-versioning.test.ts index e5ffd6e9..39d90312 100644 --- a/src/model/unity-versioning.test.ts +++ b/src/model/unity-versioning.test.ts @@ -7,9 +7,9 @@ describe('Unity Versioning', () => { }); it('parses from ProjectVersion.txt', () => { - const projectVersionContents = `m_EditorVersion: 2021.3.4f1 - m_EditorVersionWithRevision: 2021.3.4f1 (cb45f9cae8b7)`; - expect(UnityVersioning.parse(projectVersionContents)).toBe('2021.3.4f1'); + const projectVersionContents = `m_EditorVersion: 2021.3.45f1 + m_EditorVersionWithRevision: 2021.3.45f1 (cb45f9cae8b7)`; + expect(UnityVersioning.parse(projectVersionContents)).toBe('2021.3.45f1'); }); it('parses Unity 6000 and newer from ProjectVersion.txt', () => { @@ -25,13 +25,13 @@ describe('Unity Versioning', () => { }); it('reads from test-project', () => { - expect(UnityVersioning.read('./test-project')).toBe('2021.3.4f1'); + expect(UnityVersioning.read('./test-project')).toBe('2021.3.45f1'); }); }); describe('determineUnityVersion', () => { it('defaults to parsed version', () => { - expect(UnityVersioning.determineUnityVersion('./test-project', 'auto')).toBe('2021.3.4f1'); + expect(UnityVersioning.determineUnityVersion('./test-project', 'auto')).toBe('2021.3.45f1'); }); it('use specified unityVersion', () => { diff --git a/src/test-utils/cloud-runner-test-helpers.ts b/src/test-utils/cloud-runner-test-helpers.ts new file mode 100644 index 00000000..4fd6bba6 --- /dev/null +++ b/src/test-utils/cloud-runner-test-helpers.ts @@ -0,0 +1,11 @@ +import { BuildParameters } from '../model'; +import { Cli } from '../model/cli/cli'; +import { OptionValues } from 'commander'; + +export const TIMEOUT_INFINITE = 1e9; + +export async function createParameters(overrides?: OptionValues) { + if (overrides) Cli.options = overrides; + + return BuildParameters.create(); +} diff --git a/test-project/Packages/manifest.json b/test-project/Packages/manifest.json index b9f3e7ac..e9004e6a 100644 --- a/test-project/Packages/manifest.json +++ b/test-project/Packages/manifest.json @@ -1,7 +1,7 @@ { "dependencies": { - "com.unity.burst": "1.6.6", - "com.unity.ide.visualstudio": "2.0.22", + "com.unity.burst": "1.8.22", + "com.unity.ide.visualstudio": "2.0.23", "com.unity.modules.ai": "1.0.0", "com.unity.modules.androidjni": "1.0.0", "com.unity.modules.animation": "1.0.0", diff --git a/test-project/Packages/packages-lock.json b/test-project/Packages/packages-lock.json index 032b909c..acb049a1 100644 --- a/test-project/Packages/packages-lock.json +++ b/test-project/Packages/packages-lock.json @@ -1,11 +1,12 @@ { "dependencies": { "com.unity.burst": { - "version": "1.6.6", + "version": "1.8.22", "depth": 0, "source": "registry", "dependencies": { - "com.unity.mathematics": "1.2.1" + "com.unity.mathematics": "1.2.1", + "com.unity.modules.jsonserialize": "1.0.0" }, "url": "https://packages.unity.com" }, @@ -17,7 +18,7 @@ "url": "https://packages.unity.com" }, "com.unity.ide.visualstudio": { - "version": "2.0.22", + "version": "2.0.23", "depth": 0, "source": "registry", "dependencies": { @@ -33,7 +34,7 @@ "url": "https://packages.unity.com" }, "com.unity.test-framework": { - "version": "1.1.31", + "version": "1.1.33", "depth": 1, "source": "registry", "dependencies": { diff --git a/test-project/ProjectSettings/ProjectSettings.asset b/test-project/ProjectSettings/ProjectSettings.asset index 547b11f2..a5f6d021 100644 --- a/test-project/ProjectSettings/ProjectSettings.asset +++ b/test-project/ProjectSettings/ProjectSettings.asset @@ -3,7 +3,7 @@ --- !u!129 &1 PlayerSettings: m_ObjectHideFlags: 0 - serializedVersion: 23 + serializedVersion: 24 productGUID: f3f6a917a3bba0046bb55998f8678f8c AndroidProfiler: 0 AndroidFilterTouchesWhenObscured: 0 @@ -48,6 +48,7 @@ PlayerSettings: defaultScreenHeightWeb: 600 m_StereoRenderingPath: 0 m_ActiveColorSpace: 0 + unsupportedMSAAFallback: 0 m_MTRendering: 1 mipStripping: 0 numberOfMipsStripped: 0 @@ -74,6 +75,7 @@ PlayerSettings: androidMinimumWindowWidth: 400 androidMinimumWindowHeight: 300 androidFullscreenMode: 1 + androidAutoRotationBehavior: 1 defaultIsNativeResolution: 1 macRetinaSupport: 1 runInBackground: 1 @@ -121,6 +123,7 @@ PlayerSettings: switchNVNOtherPoolsGranularity: 16777216 switchNVNMaxPublicTextureIDCount: 0 switchNVNMaxPublicSamplerIDCount: 0 + switchMaxWorkerMultiple: 8 stadiaPresentMode: 0 stadiaTargetFramerate: 0 vulkanNumSwapchainBuffers: 3 @@ -180,10 +183,10 @@ PlayerSettings: StripUnusedMeshComponents: 1 VertexChannelCompressionMask: 4054 iPhoneSdkVersion: 988 - iOSTargetOSVersionString: 11.0 + iOSTargetOSVersionString: 12.0 tvOSSdkVersion: 0 tvOSRequireExtendedGameController: 0 - tvOSTargetOSVersionString: 11.0 + tvOSTargetOSVersionString: 12.0 uIPrerenderedIcon: 0 uIRequiresPersistentWiFi: 0 uIRequiresFullScreen: 1 @@ -247,6 +250,7 @@ PlayerSettings: useCustomLauncherGradleManifest: 0 useCustomBaseGradleTemplate: 0 useCustomGradlePropertiesTemplate: 0 + useCustomGradleSettingsTemplate: 0 useCustomProguardFile: 0 AndroidTargetArchitectures: 3 AndroidTargetDevices: 0 @@ -267,7 +271,6 @@ PlayerSettings: banner: {fileID: 0} androidGamepadSupportLevel: 0 chromeosInputEmulation: 1 - AndroidMinifyWithR8: 0 AndroidMinifyRelease: 0 AndroidMinifyDebug: 0 AndroidValidateAppBundleSize: 1 @@ -516,6 +519,7 @@ PlayerSettings: - m_BuildTarget: WebGL m_StaticBatching: 0 m_DynamicBatching: 0 + m_BuildTargetShaderSettings: [] m_BuildTargetGraphicsJobs: - m_BuildTarget: MacStandaloneSupport m_GraphicsJobs: 0 @@ -567,6 +571,8 @@ PlayerSettings: m_Devices: - Oculus - OpenVR + m_DefaultShaderChunkSizeInMB: 16 + m_DefaultShaderChunkCount: 0 openGLRequireES31: 0 openGLRequireES31AEP: 0 openGLRequireES32: 0 @@ -610,7 +616,7 @@ PlayerSettings: switchSocketConcurrencyLimit: 14 switchScreenResolutionBehavior: 2 switchUseCPUProfiler: 0 - switchUseGOLDLinker: 0 + switchEnableFileSystemTrace: 0 switchLTOSetting: 0 switchApplicationID: 0x01004b9000490000 switchNSODependencies: @@ -687,7 +693,6 @@ PlayerSettings: switchReleaseVersion: 0 switchDisplayVersion: 1.0.0 switchStartupUserAccount: 0 - switchTouchScreenUsage: 0 switchSupportedLanguagesMask: 0 switchLogoType: 0 switchApplicationErrorCodeCategory: @@ -729,6 +734,7 @@ PlayerSettings: switchNativeFsCacheSize: 32 switchIsHoldTypeHorizontal: 0 switchSupportedNpadCount: 8 + switchEnableTouchScreen: 1 switchSocketConfigEnabled: 0 switchTcpInitialSendBufferSize: 32 switchTcpInitialReceiveBufferSize: 64 @@ -739,8 +745,8 @@ PlayerSettings: switchSocketBufferEfficiency: 4 switchSocketInitializeEnabled: 1 switchNetworkInterfaceManagerInitializeEnabled: 1 - switchPlayerConnectionEnabled: 1 switchUseNewStyleFilepaths: 0 + switchUseLegacyFmodPriorities: 1 switchUseMicroSleepForYield: 1 switchEnableRamDiskSupport: 0 switchMicroSleepForYieldTime: 25 @@ -815,6 +821,7 @@ PlayerSettings: ps4videoRecordingFeaturesUsed: 0 ps4contentSearchFeaturesUsed: 0 ps4CompatibilityPS5: 0 + ps4AllowPS5Detection: 0 ps4GPU800MHz: 1 ps4attribEyeToEyeDistanceSettingVR: 0 ps4IncludedModules: [] @@ -839,6 +846,7 @@ PlayerSettings: webGLLinkerTarget: 1 webGLThreadsSupport: 0 webGLDecompressionFallback: 0 + webGLPowerPreference: 2 scriptingDefineSymbols: {} additionalCompilerArguments: {} platformArchitecture: {} @@ -847,7 +855,21 @@ PlayerSettings: Server: 0 Standalone: 0 il2cppCompilerConfiguration: {} - managedStrippingLevel: {} + managedStrippingLevel: + Android: 1 + EmbeddedLinux: 1 + GameCoreScarlett: 1 + GameCoreXboxOne: 1 + Lumin: 1 + Nintendo Switch: 1 + PS4: 1 + PS5: 1 + Stadia: 1 + WebGL: 1 + Windows Store Apps: 1 + XboxOne: 1 + iPhone: 1 + tvOS: 1 incrementalIl2cppBuild: {} suppressCommonWarnings: 1 allowUnsafeCode: 0 @@ -863,11 +885,11 @@ PlayerSettings: m_MobileRenderingPath: 1 metroPackageName: Template3D metroPackageVersion: 1.0.0.0 - metroCertificatePath: + metroCertificatePath: C:\Users\david\Documents\GitHub\unity-builder\test-project\Assets\WSATestCertificate.pfx metroCertificatePassword: - metroCertificateSubject: - metroCertificateIssuer: - metroCertificateNotAfter: 0000000000000000 + metroCertificateSubject: GameCI + metroCertificateIssuer: GameCI + metroCertificateNotAfter: 00b8ac9241f7dc01 metroApplicationDescription: Template_3D wsaImages: {} metroTileShortName: TestProject @@ -882,6 +904,7 @@ PlayerSettings: metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, a: 1} metroSplashScreenUseBackgroundColor: 0 + syncCapabilities: 0 platformCapabilities: {} metroTargetDeviceFamilies: {} metroFTAName: @@ -931,6 +954,7 @@ PlayerSettings: m_VersionName: apiCompatibilityLevel: 6 activeInputHandler: 0 + windowsGamepadBackendHint: 0 cloudProjectId: framebufferDepthMemorylessMode: 0 qualitySettingsNames: [] diff --git a/test-project/ProjectSettings/ProjectVersion.txt b/test-project/ProjectSettings/ProjectVersion.txt index 501a25e5..8386a052 100644 --- a/test-project/ProjectSettings/ProjectVersion.txt +++ b/test-project/ProjectSettings/ProjectVersion.txt @@ -1,2 +1,2 @@ -m_EditorVersion: 2021.3.4f1 -m_EditorVersionWithRevision: 2021.3.4f1 (cb45f9cae8b7) +m_EditorVersion: 2021.3.45f1 +m_EditorVersionWithRevision: 2021.3.45f1 (0da89fac8e79)