Compare commits

...

11 Commits
v4.3.0 ... main

Author SHA1 Message Date
David Finol
9e91ca9749
Update unity version for macOS (#712)
* Update Unity version

* Test updating unity version for mac
2025-06-10 09:03:26 -04:00
Eric_Lian
9cd9f7e0e7
fix: androidCreateSymbols has been deprecated (#701) 2025-06-08 21:21:32 -05:00
David Finol
0b822c28fb
Update Unity version (#711) 2025-06-08 11:00:16 -04:00
Daniel Lupiañez Casares
65607f9ebb
Adds support for .upmconfig.toml in Windows Docker images (#705)
* Supports github_home in windows-latest

* Attempt at copying from specific volume

* Adding some more logging

* Fix and compiles index.js

* Debugging and some other approach

* Another attempt at debugging

* Testing two more approaches

* Try only copying the file

* Cleanup

* Updates index.js, index.js.map and licenses.txt

After `yarn` + `npm run build`

* Update index.js.map
2025-06-07 16:11:18 -05:00
Daniel Lupiañez Casares
a1ebdb7abd
Adds support for VisionOS in UnityHub in macos (#710)
* Adds support for VisionOS in UnityHub in macos

* Adds support for VisionOS in UnityHub in macos

* Syncs index.js.map
2025-06-07 20:20:18 +02:00
Daniel Lupiañez Casares
3b26780ddf
Adds build support for tvOS in macos-latest (#709)
* Removes limit for tvOS only in Windows

* Fix UnityHub argument for tvOS

* Allows macos as a build platform for tvOS
2025-06-07 18:08:47 +02:00
Kirill Artemov
819c2511e0
Added install_llvmpipe script to replace -nographics in Windows builds (#706)
* Added install_llvmpipe script

* Replace ternary with a regular condition

* Revert files I haven't changed

* Pin llvmpipe version, expand test matrix with a single enableGPU target

* Fixed parameter name

* EnableGPU false by default

* Fixed nitpick

* Fixed scripts

* Pass enableGpu into tests properly

* Fixed script

* Append With GPU to build name

* Fix expression
2025-05-17 19:17:08 +02:00
Matheus Costa
81ed299e10
Feat/migrate aws sdk v3 (#698)
* chore(cloud-runner): migrate/replace deps aws-sdk v2 to v3

* chore(aws): refactor aws services to support SDK v3

* chore(aws): refactor aws runner to support SDK v3

* chore(aws): update dist

* fix(aws): error handling wrap try/catch to avoid unhandled promise rejections.

* fix(aws): keeping the syntax simpler for arrays
2025-04-10 22:48:14 +02:00
Michael Buhler
9d6bdcbdc5
feat: add buildProfile parameter (#685)
* feat: add `buildProfile` parameter

add new `buildProfile` action param, which will be passed into
Unity as the `-activeBuildProfile ...` CLI param.

closes https://github.com/game-ci/unity-builder/issues/674

* ci: add tests for Unity 6 and build profiles
2025-02-17 11:41:38 -06:00
Egorrko
3ae9ec8536
Update @actions/cache and @actions/core to support actions/upload-artifact: v4 dependency (#688)
* Bump versions of @actions/cache, @actions/core to support actions/upload-artifact: v4 dependency. Bump version actions/upload-artifact in repo actions.

* Add UNITY_LICENSE secret to CI workflows.
2025-02-08 17:14:07 +01:00
zdickinsonfugro
83c85328dd
Removed all instances of interpolated strings from editor scripts so we don't get compiler errors on old versions of .NET in Unity 2018 (#633)
Co-authored-by: Zac <zac@dickinson.xyz>
Co-authored-by: Andrew Kahr <22359829+AndrewKahr@users.noreply.github.com>
Co-authored-by: David Finol <davidmfinol@gmail.com>
2024-10-10 09:02:39 -05:00
48 changed files with 2154 additions and 259 deletions

View File

@ -13,7 +13,7 @@ jobs:
id: requestActivationFile
uses: game-ci/unity-request-activation-file@v2.0-alpha-1
- name: Upload activation file
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: ${{ steps.requestActivationFile.outputs.filePath }}
path: ${{ steps.requestActivationFile.outputs.filePath }}

View File

@ -18,7 +18,7 @@ jobs:
projectPath:
- test-project
unityVersion:
- 2021.3.32f1
- 2021.3.45f1
- 2022.3.13f1
- 2023.2.2f1
targetPlatform:
@ -59,6 +59,7 @@ jobs:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with:
buildName: 'GameCI Test Build'
projectPath: ${{ matrix.projectPath }}

View File

@ -36,7 +36,8 @@ env:
jobs:
buildForAllPlatformsUbuntu:
name: ${{ matrix.targetPlatform }} on ${{ matrix.unityVersion }}
name:
"${{ matrix.targetPlatform }} on ${{ matrix.unityVersion}}${{startsWith(matrix.buildProfile, 'Assets') && ' (via Build Profile)' || '' }}"
runs-on: ubuntu-latest
strategy:
fail-fast: false
@ -91,6 +92,12 @@ jobs:
- targetPlatform: StandaloneWindows64
additionalParameters: -standaloneBuildSubtarget Server
buildWithIl2cpp: true
include:
- unityVersion: 6000.0.36f1
targetPlatform: WebGL
- unityVersion: 6000.0.36f1
targetPlatform: WebGL
buildProfile: 'Assets/Settings/Build Profiles/Sample WebGL Build Profile.asset'
steps:
- name: Clear Space for Android Build
@ -136,6 +143,7 @@ jobs:
with:
buildName: 'GameCI Test Build'
projectPath: ${{ matrix.projectPath }}
buildProfile: ${{ matrix.buildProfile }}
unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }}
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue ${{ matrix.additionalParameters }}
@ -158,6 +166,7 @@ jobs:
with:
buildName: 'GameCI Test Build'
projectPath: ${{ matrix.projectPath }}
buildProfile: ${{ matrix.buildProfile }}
unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }}
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue ${{ matrix.additionalParameters }}
@ -179,6 +188,7 @@ jobs:
with:
buildName: 'GameCI Test Build'
projectPath: ${{ matrix.projectPath }}
buildProfile: ${{ matrix.buildProfile }}
unityVersion: ${{ matrix.unityVersion }}
targetPlatform: ${{ matrix.targetPlatform }}
customParameters: -profile SomeProfile -someBoolean -someValue exampleValue ${{ matrix.additionalParameters }}
@ -191,7 +201,6 @@ jobs:
- uses: actions/upload-artifact@v4
with:
name:
'Build ${{ matrix.targetPlatform }} on Ubuntu (${{ matrix.unityVersion }}_il2cpp_${{ matrix.buildWithIl2cpp
}}_params_${{ matrix.additionalParameters }})'
"Build ${{ matrix.targetPlatform }}${{ startsWith(matrix.buildProfile, 'Assets') && ' (via Build Profile)' || '' }} on Ubuntu (${{ matrix.unityVersion }}_il2cpp_${{ matrix.buildWithIl2cpp }}_params_${{ matrix.additionalParameters }})"
path: build
retention-days: 14

View File

@ -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:
###########################
@ -65,11 +73,13 @@ jobs:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with:
buildName: 'GameCI Test Build'
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
@ -89,11 +99,13 @@ jobs:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with:
buildName: 'GameCI Test Build'
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
@ -112,11 +124,13 @@ jobs:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
with:
buildName: 'GameCI Test Build'
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
@ -126,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

View File

@ -190,6 +190,7 @@ jobs:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
GIT_PRIVATE_TOKEN: ${{ secrets.GIT_PRIVATE_TOKEN }}
@ -201,7 +202,7 @@ jobs:
providerStrategy: ${{ matrix.providerStrategy }}
- run: |
cp ./cloud-runner-cache/cache/${{ steps.unity-build.outputs.CACHE_KEY }}/build/${{ steps.unity-build.outputs.BUILD_ARTIFACT }} ${{ steps.unity-build.outputs.BUILD_ARTIFACT }}
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.providerStrategy }} Build (${{ matrix.targetPlatform }})
path: ${{ steps.unity-build.outputs.BUILD_ARTIFACT }}

View File

@ -18,7 +18,11 @@ inputs:
projectPath:
required: false
default: ''
description: 'Relative path to the project to be built.'
description: 'Path to the project to be built, relative to the repository root.'
buildProfile:
required: false
default: ''
description: 'Path to the build profile to activate, relative to the project root.'
buildName:
required: false
default: ''

View File

@ -6,6 +6,9 @@ using UnityBuilderAction.Reporting;
using UnityBuilderAction.Versioning;
using UnityEditor;
using UnityEditor.Build.Reporting;
#if UNITY_6000_0_OR_NEWER
using UnityEditor.Build.Profile;
#endif
using UnityEngine;
namespace UnityBuilderAction
@ -17,47 +20,9 @@ namespace UnityBuilderAction
// Gather values from args
var options = ArgumentsParser.GetValidatedOptions();
// Gather values from project
var scenes = EditorBuildSettings.scenes.Where(scene => scene.enabled).Select(s => s.path).ToArray();
// Get all buildOptions from options
BuildOptions buildOptions = BuildOptions.None;
foreach (string buildOptionString in Enum.GetNames(typeof(BuildOptions))) {
if (options.ContainsKey(buildOptionString)) {
BuildOptions buildOptionEnum = (BuildOptions) Enum.Parse(typeof(BuildOptions), buildOptionString);
buildOptions |= buildOptionEnum;
}
}
#if UNITY_2021_2_OR_NEWER
// Determine subtarget
StandaloneBuildSubtarget buildSubtarget;
if (!options.TryGetValue("standaloneBuildSubtarget", out var subtargetValue) || !Enum.TryParse(subtargetValue, out buildSubtarget)) {
buildSubtarget = default;
}
#endif
// Define BuildPlayer Options
var buildPlayerOptions = new BuildPlayerOptions {
scenes = scenes,
locationPathName = options["customBuildPath"],
target = (BuildTarget) Enum.Parse(typeof(BuildTarget), options["buildTarget"]),
options = buildOptions,
#if UNITY_2021_2_OR_NEWER
subtarget = (int) buildSubtarget
#endif
};
// Set version for this build
VersionApplicator.SetVersion(options["buildVersion"]);
// Apply Android settings
if (buildPlayerOptions.target == BuildTarget.Android)
{
VersionApplicator.SetAndroidVersionCode(options["androidVersionCode"]);
AndroidSettings.Apply(options);
}
// Execute default AddressableAsset content build, if the package is installed.
// Version defines would be the best solution here, but Unity 2018 doesn't support that,
// so we fall back to using reflection instead.
@ -74,10 +39,76 @@ namespace UnityBuilderAction
}
catch (Exception e)
{
Debug.LogError($"Failed to run default addressables build:\n{e}");
Debug.LogError("Failed to run default addressables build:\n" + e);
}
}
// Get all buildOptions from options
BuildOptions buildOptions = BuildOptions.None;
foreach (string buildOptionString in Enum.GetNames(typeof(BuildOptions))) {
if (options.ContainsKey(buildOptionString)) {
BuildOptions buildOptionEnum = (BuildOptions) Enum.Parse(typeof(BuildOptions), buildOptionString);
buildOptions |= buildOptionEnum;
}
}
// Depending on whether the build is using a build profile, `buildPlayerOptions` will an instance
// of either `UnityEditor.BuildPlayerOptions` or `UnityEditor.BuildPlayerWithProfileOptions`
dynamic buildPlayerOptions;
if (options["customBuildProfile"] != "") {
#if UNITY_6000_0_OR_NEWER
// Load build profile from Assets folder
BuildProfile buildProfile = AssetDatabase.LoadAssetAtPath<BuildProfile>(options["customBuildProfile"]);
// Set it as active
BuildProfile.SetActiveBuildProfile(buildProfile);
// Define BuildPlayerWithProfileOptions
buildPlayerOptions = new BuildPlayerWithProfileOptions {
buildProfile = buildProfile,
locationPathName = options["customBuildPath"],
options = buildOptions,
};
#else
throw new Exception("Build profiles are not supported by this version of Unity (" + Application.unityVersion +")");
#endif
} else {
// Gather values from project
var scenes = EditorBuildSettings.scenes.Where(scene => scene.enabled).Select(s => s.path).ToArray();
#if UNITY_2021_2_OR_NEWER
// Determine subtarget
StandaloneBuildSubtarget buildSubtarget;
if (!options.TryGetValue("standaloneBuildSubtarget", out var subtargetValue) || !Enum.TryParse(subtargetValue, out buildSubtarget)) {
buildSubtarget = default;
}
#endif
BuildTarget buildTarget = (BuildTarget) Enum.Parse(typeof(BuildTarget), options["buildTarget"]);
// Define BuildPlayerOptions
buildPlayerOptions = new BuildPlayerOptions {
scenes = scenes,
locationPathName = options["customBuildPath"],
target = buildTarget,
options = buildOptions,
#if UNITY_2021_2_OR_NEWER
subtarget = (int) buildSubtarget
#endif
};
// Apply Android settings
if (buildTarget == BuildTarget.Android) {
VersionApplicator.SetAndroidVersionCode(options["androidVersionCode"]);
AndroidSettings.Apply(options);
}
}
// Perform build
BuildReport buildReport = BuildPipeline.BuildPlayer(buildPlayerOptions);

View File

@ -56,17 +56,17 @@ namespace UnityBuilderAction.Input
case "androidStudioProject":
EditorUserBuildSettings.exportAsGoogleAndroidProject = true;
if (buildAppBundle != null)
buildAppBundle.SetValue(null, false);
buildAppBundle.SetValue(null, false, null);
break;
case "androidAppBundle":
EditorUserBuildSettings.exportAsGoogleAndroidProject = false;
if (buildAppBundle != null)
buildAppBundle.SetValue(null, true);
buildAppBundle.SetValue(null, true, null);
break;
case "androidPackage":
EditorUserBuildSettings.exportAsGoogleAndroidProject = false;
if (buildAppBundle != null)
buildAppBundle.SetValue(null, false);
buildAppBundle.SetValue(null, false, null);
break;
}
}
@ -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);
}
}
}

View File

@ -28,7 +28,7 @@ namespace UnityBuilderAction.Input
}
if (!Enum.IsDefined(typeof(BuildTarget), buildTarget)) {
Console.WriteLine($"{buildTarget} is not a defined {nameof(BuildTarget)}");
Console.WriteLine(buildTarget + " is not a defined " + typeof(BuildTarget).Name);
EditorApplication.Exit(121);
}
@ -41,10 +41,10 @@ namespace UnityBuilderAction.Input
const string defaultCustomBuildName = "TestBuild";
string customBuildName;
if (!validatedOptions.TryGetValue("customBuildName", out customBuildName)) {
Console.WriteLine($"Missing argument -customBuildName, defaulting to {defaultCustomBuildName}.");
Console.WriteLine("Missing argument -customBuildName, defaulting to" + defaultCustomBuildName);
validatedOptions.Add("customBuildName", defaultCustomBuildName);
} else if (customBuildName == "") {
Console.WriteLine($"Invalid argument -customBuildName, defaulting to {defaultCustomBuildName}.");
Console.WriteLine("Invalid argument -customBuildName, defaulting to" + defaultCustomBuildName);
validatedOptions.Add("customBuildName", defaultCustomBuildName);
}
@ -57,11 +57,11 @@ namespace UnityBuilderAction.Input
string[] args = Environment.GetCommandLineArgs();
Console.WriteLine(
$"{EOL}" +
$"###########################{EOL}" +
$"# Parsing settings #{EOL}" +
$"###########################{EOL}" +
$"{EOL}"
EOL +
"###########################" + EOL +
"# Parsing settings #" + EOL +
"###########################" + EOL +
EOL
);
// Extract flags with optional values
@ -78,7 +78,7 @@ namespace UnityBuilderAction.Input
string displayValue = secret ? "*HIDDEN*" : "\"" + value + "\"";
// Assign
Console.WriteLine($"Found flag \"{flag}\" with value {displayValue}.");
Console.WriteLine("Found flag \"" + flag + "\" with value " + displayValue);
providedArguments.Add(flag, value);
}
}

View File

@ -30,7 +30,7 @@ namespace UnityBuilderAction.Reporting
prefix = "error";
break;
}
Console.WriteLine($"{Environment.NewLine}::{prefix} ::{condition}{Environment.NewLine}{stackTrace}");
Console.WriteLine(Environment.NewLine + "::" + prefix + "::" + condition + Environment.NewLine + stackTrace);
}
}
}

View File

@ -11,16 +11,16 @@ namespace UnityBuilderAction.Reporting
public static void ReportSummary(BuildSummary summary)
{
Console.WriteLine(
$"{EOL}" +
$"###########################{EOL}" +
$"# Build results #{EOL}" +
$"###########################{EOL}" +
$"{EOL}" +
$"Duration: {summary.totalTime.ToString()}{EOL}" +
$"Warnings: {summary.totalWarnings.ToString()}{EOL}" +
$"Errors: {summary.totalErrors.ToString()}{EOL}" +
$"Size: {summary.totalSize.ToString()} bytes{EOL}" +
$"{EOL}"
EOL +
"###########################" + EOL +
"# Build results #" + EOL +
"###########################" + EOL +
EOL +
"Duration: " + summary.totalTime.ToString() + EOL +
"Warnings: " + summary.totalWarnings.ToString() + EOL +
"Errors: " + summary.totalErrors.ToString() + EOL +
"Size: " + summary.totalSize.ToString() + " bytes" + EOL +
EOL
);
}

View File

@ -21,11 +21,11 @@ namespace UnityBuilderAction.Versioning
version = GetSemanticCommitVersion();
Console.WriteLine("Repository has a valid version tag.");
} else {
version = $"0.0.{GetTotalNumberOfCommits()}";
version = "0.0." + GetTotalNumberOfCommits();
Console.WriteLine("Repository does not have tags to base the version on.");
}
Console.WriteLine($"Version is {version}");
Console.WriteLine("Version is " + version);
return version;
}

BIN
dist/index.js generated vendored

Binary file not shown.

BIN
dist/index.js.map generated vendored

Binary file not shown.

BIN
dist/licenses.txt generated vendored

Binary file not shown.

View File

@ -19,6 +19,23 @@ echo "Using build name \"$BUILD_NAME\"."
echo "Using build target \"$BUILD_TARGET\"."
#
# Display the build profile
#
if [ -z "$BUILD_PROFILE" ]; then
# User has not provided a build profile
#
echo "Doing a default \"$BUILD_TARGET\" platform build."
#
else
# User has provided a path to a build profile `.asset` file
#
echo "Using build profile \"$BUILD_PROFILE\" relative to \"$UNITY_PROJECT_PATH\"."
#
fi
#
# Display build path and file
#
@ -139,6 +156,7 @@ echo ""
-buildTarget "$BUILD_TARGET" \
-customBuildTarget "$BUILD_TARGET" \
-customBuildPath "$CUSTOM_BUILD_PATH" \
-customBuildProfile "$BUILD_PROFILE" \
-executeMethod "$BUILD_METHOD" \
-buildVersion "$VERSION" \
-androidVersionCode "$ANDROID_VERSION_CODE" \

View File

@ -19,6 +19,22 @@ echo "Using build name \"$BUILD_NAME\"."
echo "Using build target \"$BUILD_TARGET\"."
#
# Display the build profile
#
if [ -z "$BUILD_PROFILE" ]; then
# User has not provided a build profile
#
echo "Doing a default \"$BUILD_TARGET\" platform build."
#
else
# User has provided a path to a build profile `.asset` file
#
echo "Using build profile \"$BUILD_PROFILE\" relative to \"$UNITY_PROJECT_PATH\"."
#
fi
#
# Display build path and file
#
@ -112,6 +128,7 @@ unity-editor \
-buildTarget "$BUILD_TARGET" \
-customBuildTarget "$BUILD_TARGET" \
-customBuildPath "$CUSTOM_BUILD_PATH" \
-customBuildProfile "$BUILD_PROFILE" \
-executeMethod "$BUILD_METHOD" \
-buildVersion "$VERSION" \
-androidVersionCode "$ANDROID_VERSION_CODE" \

View File

@ -16,6 +16,25 @@ Write-Output "$('Using build name "')$($Env:BUILD_NAME)$('".')"
Write-Output "$('Using build target "')$($Env:BUILD_TARGET)$('".')"
#
# Display the build profile
#
if ($Env:BUILD_PROFILE)
{
# User has provided a path to a build profile `.asset` file
#
Write-Output "$('Using build profile "')$($Env:BUILD_PROFILE)$('" relative to "')$($Env:UNITY_PROJECT_PATH)$('".')"
#
}
else
{
# User has not provided a build profile
#
Write-Output "$('Doing a default "')$($Env:BUILD_TARGET)$('" platform build.')"
#
}
#
# Display build path and file
#
@ -129,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`"",
@ -143,6 +169,7 @@ $unityArgs = @(
"-buildTarget", "`"$Env:BUILD_TARGET`"",
"-customBuildTarget", "`"$Env:BUILD_TARGET`"",
"-customBuildPath", "`"$Env:CUSTOM_BUILD_PATH`"",
"-customBuildProfile", "`"$Env:BUILD_PROFILE`"",
"-buildVersion", "`"$Env:VERSION`"",
"-androidVersionCode", "`"$Env:ANDROID_VERSION_CODE`"",
"-androidKeystorePass", "`"$Env:ANDROID_KEYSTORE_PASS`"",

View File

@ -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"

View File

@ -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
}
}

View File

@ -28,10 +28,15 @@
"node": ">=18.x"
},
"dependencies": {
"@actions/cache": "^3.2.4",
"@actions/core": "^1.10.1",
"@actions/cache": "^4.0.0",
"@actions/core": "^1.11.1",
"@actions/exec": "^1.1.1",
"@actions/github": "^6.0.0",
"@aws-sdk/client-cloudformation": "^3.777.0",
"@aws-sdk/client-cloudwatch-logs": "^3.777.0",
"@aws-sdk/client-ecs": "^3.778.0",
"@aws-sdk/client-kinesis": "^3.777.0",
"@aws-sdk/client-s3": "^3.779.0",
"@kubernetes/client-node": "^0.16.3",
"@octokit/core": "^5.1.0",
"async-wait-until": "^2.0.12",
@ -79,4 +84,4 @@
"node": "20.5.1",
"yarn": "1.22.19"
}
}
}

View File

@ -71,6 +71,12 @@ describe('BuildParameters', () => {
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ projectPath: mockValue }));
});
it('returns the build profile', async () => {
const mockValue = 'path/to/build_profile.asset';
jest.spyOn(Input, 'buildProfile', 'get').mockReturnValue(mockValue);
await expect(BuildParameters.create()).resolves.toEqual(expect.objectContaining({ buildProfile: mockValue }));
});
it('returns the build name', async () => {
const mockValue = 'someBuildName';
jest.spyOn(Input, 'buildName', 'get').mockReturnValue(mockValue);

View File

@ -26,6 +26,7 @@ class BuildParameters {
public runnerTempPath!: string;
public targetPlatform!: string;
public projectPath!: string;
public buildProfile!: string;
public buildName!: string;
public buildPath!: string;
public buildFile!: string;
@ -152,6 +153,7 @@ class BuildParameters {
runnerTempPath: Input.runnerTempPath,
targetPlatform: Input.targetPlatform,
projectPath: Input.projectPath,
buildProfile: Input.buildProfile,
buildName: Input.buildName,
buildPath: `${Input.buildsPath}/${Input.targetPlatform}`,
buildFile,

View File

@ -1,6 +1,18 @@
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
import * as core from '@actions/core';
import * as SDK from 'aws-sdk';
import {
CloudFormation,
CreateStackCommand,
CreateStackCommandInput,
DescribeStacksCommand,
DescribeStacksCommandInput,
ListStacksCommand,
Parameter,
UpdateStackCommand,
UpdateStackCommandInput,
waitUntilStackCreateComplete,
waitUntilStackUpdateComplete,
} from '@aws-sdk/client-cloudformation';
import { BaseStackFormation } from './cloud-formations/base-stack-formation';
import crypto from 'node:crypto';
@ -10,51 +22,49 @@ export class AWSBaseStack {
}
private baseStackName: string;
async setupBaseStack(CF: SDK.CloudFormation) {
async setupBaseStack(CF: CloudFormation) {
const baseStackName = this.baseStackName;
const baseStack = BaseStackFormation.formation;
// Cloud Formation Input
const describeStackInput: SDK.CloudFormation.DescribeStacksInput = {
const describeStackInput: DescribeStacksCommandInput = {
StackName: baseStackName,
};
const parametersWithoutHash: SDK.CloudFormation.Parameter[] = [
{ ParameterKey: 'EnvironmentName', ParameterValue: baseStackName },
];
const parametersWithoutHash: Parameter[] = [{ ParameterKey: 'EnvironmentName', ParameterValue: baseStackName }];
const parametersHash = crypto
.createHash('md5')
.update(baseStack + JSON.stringify(parametersWithoutHash))
.digest('hex');
const parameters: SDK.CloudFormation.Parameter[] = [
const parameters: Parameter[] = [
...parametersWithoutHash,
...[{ ParameterKey: 'Version', ParameterValue: parametersHash }],
];
const updateInput: SDK.CloudFormation.UpdateStackInput = {
const updateInput: UpdateStackCommandInput = {
StackName: baseStackName,
TemplateBody: baseStack,
Parameters: parameters,
Capabilities: ['CAPABILITY_IAM'],
};
const createStackInput: SDK.CloudFormation.CreateStackInput = {
const createStackInput: CreateStackCommandInput = {
StackName: baseStackName,
TemplateBody: baseStack,
Parameters: parameters,
Capabilities: ['CAPABILITY_IAM'],
};
const stacks = await CF.listStacks({
StackStatusFilter: ['UPDATE_COMPLETE', 'CREATE_COMPLETE', 'ROLLBACK_COMPLETE'],
}).promise();
const stacks = await CF.send(
new ListStacksCommand({ StackStatusFilter: ['UPDATE_COMPLETE', 'CREATE_COMPLETE', 'ROLLBACK_COMPLETE'] }),
);
const stackNames = stacks.StackSummaries?.map((x) => x.StackName) || [];
const stackExists: Boolean = stackNames.includes(baseStackName) || false;
const describeStack = async () => {
return await CF.describeStacks(describeStackInput).promise();
return await CF.send(new DescribeStacksCommand(describeStackInput));
};
try {
if (!stackExists) {
CloudRunnerLogger.log(`${baseStackName} stack does not exist (${JSON.stringify(stackNames)})`);
await CF.createStack(createStackInput).promise();
await CF.send(new CreateStackCommand(createStackInput));
CloudRunnerLogger.log(`created stack (version: ${parametersHash})`);
}
const CFState = await describeStack();
@ -65,7 +75,13 @@ export class AWSBaseStack {
const stackVersion = stack.Parameters?.find((x) => x.ParameterKey === 'Version')?.ParameterValue;
if (stack.StackStatus === 'CREATE_IN_PROGRESS') {
await CF.waitFor('stackCreateComplete', describeStackInput).promise();
await waitUntilStackCreateComplete(
{
client: CF,
maxWaitTime: 200,
},
describeStackInput,
);
}
if (stackExists) {
@ -73,7 +89,7 @@ export class AWSBaseStack {
if (parametersHash !== stackVersion) {
CloudRunnerLogger.log(`Attempting update of base stack`);
try {
await CF.updateStack(updateInput).promise();
await CF.send(new UpdateStackCommand(updateInput));
} catch (error: any) {
if (error['message'].includes('No updates are to be performed')) {
CloudRunnerLogger.log(`No updates are to be performed`);
@ -93,7 +109,13 @@ export class AWSBaseStack {
);
}
if (stack.StackStatus === 'UPDATE_IN_PROGRESS') {
await CF.waitFor('stackUpdateComplete', describeStackInput).promise();
await waitUntilStackUpdateComplete(
{
client: CF,
maxWaitTime: 200,
},
describeStackInput,
);
}
}
CloudRunnerLogger.log('base stack is now ready');

View File

@ -1,15 +1,15 @@
import CloudRunnerLogger from '../../services/core/cloud-runner-logger';
import * as SDK from 'aws-sdk';
import { CloudFormation, DescribeStackEventsCommand } from '@aws-sdk/client-cloudformation';
import * as core from '@actions/core';
import CloudRunner from '../../cloud-runner';
export class AWSError {
static async handleStackCreationFailure(error: any, CF: SDK.CloudFormation, taskDefStackName: string) {
static async handleStackCreationFailure(error: any, CF: CloudFormation, taskDefStackName: string) {
CloudRunnerLogger.log('aws error: ');
core.error(JSON.stringify(error, undefined, 4));
if (CloudRunner.buildParameters.cloudRunnerDebug) {
CloudRunnerLogger.log('Getting events and resources for task stack');
const events = (await CF.describeStackEvents({ StackName: taskDefStackName }).promise()).StackEvents;
const events = (await CF.send(new DescribeStackEventsCommand({ StackName: taskDefStackName }))).StackEvents;
CloudRunnerLogger.log(JSON.stringify(events, undefined, 4));
}
}

View File

@ -1,4 +1,12 @@
import * as SDK from 'aws-sdk';
import {
CloudFormation,
CreateStackCommand,
CreateStackCommandInput,
DescribeStackResourcesCommand,
DescribeStacksCommand,
ListStacksCommand,
waitUntilStackCreateComplete,
} from '@aws-sdk/client-cloudformation';
import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def';
import CloudRunnerSecret from '../../options/cloud-runner-secret';
import { AWSCloudFormationTemplates } from './aws-cloud-formation-templates';
@ -16,7 +24,7 @@ export class AWSJobStack {
}
public async setupCloudFormations(
CF: SDK.CloudFormation,
CF: CloudFormation,
buildGuid: string,
image: string,
entrypoint: string[],
@ -119,7 +127,7 @@ export class AWSJobStack {
let previousStackExists = true;
while (previousStackExists) {
previousStackExists = false;
const stacks = await CF.listStacks().promise();
const stacks = await CF.send(new ListStacksCommand({}));
if (!stacks.StackSummaries) {
throw new Error('Faild to get stacks');
}
@ -132,7 +140,7 @@ export class AWSJobStack {
}
}
}
const createStackInput: SDK.CloudFormation.CreateStackInput = {
const createStackInput: CreateStackCommandInput = {
StackName: taskDefStackName,
TemplateBody: taskDefCloudFormation,
Capabilities: ['CAPABILITY_IAM'],
@ -140,9 +148,15 @@ export class AWSJobStack {
};
try {
CloudRunnerLogger.log(`Creating job aws formation ${taskDefStackName}`);
await CF.createStack(createStackInput).promise();
await CF.waitFor('stackCreateComplete', { StackName: taskDefStackName }).promise();
const describeStack = await CF.describeStacks({ StackName: taskDefStackName }).promise();
await CF.send(new CreateStackCommand(createStackInput));
await waitUntilStackCreateComplete(
{
client: CF,
maxWaitTime: 200,
},
{ StackName: taskDefStackName },
);
const describeStack = await CF.send(new DescribeStacksCommand({ StackName: taskDefStackName }));
for (const parameter of parameters) {
if (!describeStack.Stacks?.[0].Parameters?.some((x) => x.ParameterKey === parameter.ParameterKey)) {
throw new Error(`Parameter ${parameter.ParameterKey} not found in stack`);
@ -153,7 +167,7 @@ export class AWSJobStack {
throw error;
}
const createCleanupStackInput: SDK.CloudFormation.CreateStackInput = {
const createCleanupStackInput: CreateStackCommandInput = {
StackName: `${taskDefStackName}-cleanup`,
TemplateBody: CleanupCronFormation.formation,
Capabilities: ['CAPABILITY_IAM'],
@ -183,7 +197,7 @@ export class AWSJobStack {
if (CloudRunnerOptions.useCleanupCron) {
try {
CloudRunnerLogger.log(`Creating job cleanup formation`);
await CF.createStack(createCleanupStackInput).promise();
await CF.send(new CreateStackCommand(createCleanupStackInput));
// await CF.waitFor('stackCreateComplete', { StackName: createCleanupStackInput.StackName }).promise();
} catch (error) {
@ -193,12 +207,15 @@ export class AWSJobStack {
}
const taskDefResources = (
await CF.describeStackResources({
StackName: taskDefStackName,
}).promise()
await CF.send(
new DescribeStackResourcesCommand({
StackName: taskDefStackName,
}),
)
).StackResources;
const baseResources = (await CF.describeStackResources({ StackName: this.baseStackName }).promise()).StackResources;
const baseResources = (await CF.send(new DescribeStackResourcesCommand({ StackName: this.baseStackName })))
.StackResources;
return {
taskDefStackName,

View File

@ -1,4 +1,19 @@
import * as AWS from 'aws-sdk';
import {
DescribeTasksCommand,
ECS,
RunTaskCommand,
RunTaskCommandInput,
Task,
waitUntilTasksRunning,
} from '@aws-sdk/client-ecs';
import {
DescribeStreamCommand,
DescribeStreamCommandOutput,
GetRecordsCommand,
GetRecordsCommandOutput,
GetShardIteratorCommand,
Kinesis,
} from '@aws-sdk/client-kinesis';
import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
import * as core from '@actions/core';
import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def';
@ -12,8 +27,8 @@ import CloudRunnerOptions from '../../options/cloud-runner-options';
import GitHub from '../../../github';
class AWSTaskRunner {
public static ECS: AWS.ECS;
public static Kinesis: AWS.Kinesis;
public static ECS: ECS;
public static Kinesis: Kinesis;
private static readonly encodedUnderscore = `$252F`;
static async runTask(
taskDef: CloudRunnerAWSTaskDef,
@ -60,7 +75,7 @@ class AWSTaskRunner {
throw new Error(`Container Overrides length must be at most 8192`);
}
const task = await AWSTaskRunner.ECS.runTask(runParameters).promise();
const task = await AWSTaskRunner.ECS.send(new RunTaskCommand(runParameters as RunTaskCommandInput));
const taskArn = task.tasks?.[0].taskArn || '';
CloudRunnerLogger.log('Cloud runner job is starting');
await AWSTaskRunner.waitUntilTaskRunning(taskArn, cluster);
@ -108,7 +123,13 @@ class AWSTaskRunner {
private static async waitUntilTaskRunning(taskArn: string, cluster: string) {
try {
await AWSTaskRunner.ECS.waitFor('tasksRunning', { tasks: [taskArn], cluster }).promise();
await waitUntilTasksRunning(
{
client: AWSTaskRunner.ECS,
maxWaitTime: 120,
},
{ tasks: [taskArn], cluster },
);
} catch (error_) {
const error = error_ as Error;
await new Promise((resolve) => setTimeout(resolve, 3000));
@ -124,10 +145,7 @@ class AWSTaskRunner {
}
static async describeTasks(clusterName: string, taskArn: string) {
const tasks = await AWSTaskRunner.ECS.describeTasks({
cluster: clusterName,
tasks: [taskArn],
}).promise();
const tasks = await AWSTaskRunner.ECS.send(new DescribeTasksCommand({ cluster: clusterName, tasks: [taskArn] }));
if (tasks.tasks?.[0]) {
return tasks.tasks?.[0];
} else {
@ -169,9 +187,7 @@ class AWSTaskRunner {
output: string,
shouldCleanup: boolean,
) {
const records = await AWSTaskRunner.Kinesis.getRecords({
ShardIterator: iterator,
}).promise();
const records = await AWSTaskRunner.Kinesis.send(new GetRecordsCommand({ ShardIterator: iterator }));
iterator = records.NextShardIterator || '';
({ shouldReadLogs, output, shouldCleanup } = AWSTaskRunner.logRecords(
records,
@ -184,7 +200,7 @@ class AWSTaskRunner {
return { iterator, shouldReadLogs, output, shouldCleanup };
}
private static checkStreamingShouldContinue(taskData: AWS.ECS.Task, timestamp: number, shouldReadLogs: boolean) {
private static checkStreamingShouldContinue(taskData: Task, timestamp: number, shouldReadLogs: boolean) {
if (taskData?.lastStatus === 'UNKNOWN') {
CloudRunnerLogger.log('## Cloud runner job unknwon');
}
@ -204,15 +220,17 @@ class AWSTaskRunner {
}
private static logRecords(
records: AWS.Kinesis.GetRecordsOutput,
records: GetRecordsCommandOutput,
iterator: string,
shouldReadLogs: boolean,
output: string,
shouldCleanup: boolean,
) {
if (records.Records.length > 0 && iterator) {
for (const record of records.Records) {
const json = JSON.parse(zlib.gunzipSync(Buffer.from(record.Data as string, 'base64')).toString('utf8'));
if ((records.Records ?? []).length > 0 && iterator) {
for (const record of records.Records ?? []) {
const json = JSON.parse(
zlib.gunzipSync(Buffer.from(record.Data as unknown as string, 'base64')).toString('utf8'),
);
if (json.messageType === 'DATA_MESSAGE') {
for (const logEvent of json.logEvents) {
({ shouldReadLogs, shouldCleanup, output } = FollowLogStreamService.handleIteration(
@ -230,19 +248,19 @@ class AWSTaskRunner {
}
private static async getLogStream(kinesisStreamName: string) {
return await AWSTaskRunner.Kinesis.describeStream({
StreamName: kinesisStreamName,
}).promise();
return await AWSTaskRunner.Kinesis.send(new DescribeStreamCommand({ StreamName: kinesisStreamName }));
}
private static async getLogIterator(stream: AWS.Kinesis.DescribeStreamOutput) {
private static async getLogIterator(stream: DescribeStreamCommandOutput) {
return (
(
await AWSTaskRunner.Kinesis.getShardIterator({
ShardIteratorType: 'TRIM_HORIZON',
StreamName: stream.StreamDescription.StreamName,
ShardId: stream.StreamDescription.Shards[0].ShardId,
}).promise()
await AWSTaskRunner.Kinesis.send(
new GetShardIteratorCommand({
ShardIteratorType: 'TRIM_HORIZON',
StreamName: stream.StreamDescription?.StreamName ?? '',
ShardId: stream.StreamDescription?.Shards?.[0]?.ShardId || '',
}),
)
).ShardIterator || ''
);
}

View File

@ -1,9 +1,9 @@
import * as AWS from 'aws-sdk';
import { StackResource } from '@aws-sdk/client-cloudformation';
class CloudRunnerAWSTaskDef {
public taskDefStackName!: string;
public taskDefCloudFormation!: string;
public taskDefResources: AWS.CloudFormation.StackResources | undefined;
public baseResources: AWS.CloudFormation.StackResources | undefined;
public taskDefResources: StackResource[] | undefined;
public baseResources: StackResource[] | undefined;
}
export default CloudRunnerAWSTaskDef;

View File

@ -1,4 +1,6 @@
import * as SDK from 'aws-sdk';
import { CloudFormation, DeleteStackCommand, waitUntilStackDeleteComplete } from '@aws-sdk/client-cloudformation';
import { ECS as ECSClient } from '@aws-sdk/client-ecs';
import { Kinesis } from '@aws-sdk/client-kinesis';
import CloudRunnerSecret from '../../options/cloud-runner-secret';
import CloudRunnerEnvironmentVariable from '../../options/cloud-runner-environment-variable';
import CloudRunnerAWSTaskDef from './cloud-runner-aws-task-def';
@ -75,7 +77,7 @@ class AWSBuildEnvironment implements ProviderInterface {
defaultSecretsArray: { ParameterKey: string; EnvironmentVariable: string; ParameterValue: string }[],
) {
process.env.AWS_REGION = Input.region;
const CF = new SDK.CloudFormation();
const CF = new CloudFormation({ region: Input.region });
await new AwsBaseStack(this.baseStackName).setupBaseStack(CF);
}
@ -89,10 +91,10 @@ class AWSBuildEnvironment implements ProviderInterface {
secrets: CloudRunnerSecret[],
): Promise<string> {
process.env.AWS_REGION = Input.region;
const ECS = new SDK.ECS();
const CF = new SDK.CloudFormation();
const ECS = new ECSClient({ region: Input.region });
const CF = new CloudFormation({ region: Input.region });
AwsTaskRunner.ECS = ECS;
AwsTaskRunner.Kinesis = new SDK.Kinesis();
AwsTaskRunner.Kinesis = new Kinesis({ region: Input.region });
CloudRunnerLogger.log(`AWS Region: ${CF.config.region}`);
const entrypoint = ['/bin/sh'];
const startTimeMs = Date.now();
@ -129,23 +131,31 @@ class AWSBuildEnvironment implements ProviderInterface {
}
}
async cleanupResources(CF: SDK.CloudFormation, taskDef: CloudRunnerAWSTaskDef) {
async cleanupResources(CF: CloudFormation, taskDef: CloudRunnerAWSTaskDef) {
CloudRunnerLogger.log('Cleanup starting');
await CF.deleteStack({
StackName: taskDef.taskDefStackName,
}).promise();
await CF.send(new DeleteStackCommand({ StackName: taskDef.taskDefStackName }));
if (CloudRunnerOptions.useCleanupCron) {
await CF.deleteStack({
StackName: `${taskDef.taskDefStackName}-cleanup`,
}).promise();
await CF.send(new DeleteStackCommand({ StackName: `${taskDef.taskDefStackName}-cleanup` }));
}
await CF.waitFor('stackDeleteComplete', {
StackName: taskDef.taskDefStackName,
}).promise();
await CF.waitFor('stackDeleteComplete', {
StackName: `${taskDef.taskDefStackName}-cleanup`,
}).promise();
await waitUntilStackDeleteComplete(
{
client: CF,
maxWaitTime: 200,
},
{
StackName: taskDef.taskDefStackName,
},
);
await waitUntilStackDeleteComplete(
{
client: CF,
maxWaitTime: 200,
},
{
StackName: `${taskDef.taskDefStackName}-cleanup`,
},
);
CloudRunnerLogger.log(`Deleted Stack: ${taskDef.taskDefStackName}`);
CloudRunnerLogger.log('Cleanup complete');
}

View File

@ -1,4 +1,11 @@
import AWS from 'aws-sdk';
import {
CloudFormation,
DeleteStackCommand,
DeleteStackCommandInput,
DescribeStackResourcesCommand,
} from '@aws-sdk/client-cloudformation';
import { CloudWatchLogs, DeleteLogGroupCommand } from '@aws-sdk/client-cloudwatch-logs';
import { ECS, StopTaskCommand } from '@aws-sdk/client-ecs';
import Input from '../../../../input';
import CloudRunnerLogger from '../../../services/core/cloud-runner-logger';
import { TaskService } from './task-service';
@ -12,9 +19,9 @@ export class GarbageCollectionService {
public static async cleanup(deleteResources = false, OneDayOlderOnly: boolean = false) {
process.env.AWS_REGION = Input.region;
const CF = new AWS.CloudFormation();
const ecs = new AWS.ECS();
const cwl = new AWS.CloudWatchLogs();
const CF = new CloudFormation({ region: Input.region });
const ecs = new ECS({ region: Input.region });
const cwl = new CloudWatchLogs({ region: Input.region });
const taskDefinitionsInUse = new Array();
const tasks = await TaskService.getTasks();
@ -23,14 +30,14 @@ export class GarbageCollectionService {
taskDefinitionsInUse.push(taskElement.taskDefinitionArn);
if (deleteResources && (!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(taskElement.createdAt!))) {
CloudRunnerLogger.log(`Stopping task ${taskElement.containers?.[0].name}`);
await ecs.stopTask({ task: taskElement.taskArn || '', cluster: element }).promise();
await ecs.send(new StopTaskCommand({ task: taskElement.taskArn || '', cluster: element }));
}
}
const jobStacks = await TaskService.getCloudFormationJobStacks();
for (const element of jobStacks) {
if (
(await CF.describeStackResources({ StackName: element.StackName }).promise()).StackResources?.some(
(await CF.send(new DescribeStackResourcesCommand({ StackName: element.StackName }))).StackResources?.some(
(x) => x.ResourceType === 'AWS::ECS::TaskDefinition' && taskDefinitionsInUse.includes(x.PhysicalResourceId),
)
) {
@ -39,7 +46,10 @@ export class GarbageCollectionService {
return;
}
if (deleteResources && (!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(element.CreationTime))) {
if (
deleteResources &&
(!OneDayOlderOnly || (element.CreationTime && GarbageCollectionService.isOlderThan1day(element.CreationTime)))
) {
if (element.StackName === 'game-ci' || element.TemplateDescription === 'Game-CI base stack') {
CloudRunnerLogger.log(`Skipping ${element.StackName} ignore list`);
@ -47,8 +57,8 @@ export class GarbageCollectionService {
}
CloudRunnerLogger.log(`Deleting ${element.StackName}`);
const deleteStackInput: AWS.CloudFormation.DeleteStackInput = { StackName: element.StackName };
await CF.deleteStack(deleteStackInput).promise();
const deleteStackInput: DeleteStackCommandInput = { StackName: element.StackName };
await CF.send(new DeleteStackCommand(deleteStackInput));
}
}
const logGroups = await TaskService.getLogGroups();
@ -58,7 +68,7 @@ export class GarbageCollectionService {
(!OneDayOlderOnly || GarbageCollectionService.isOlderThan1day(new Date(element.creationTime!)))
) {
CloudRunnerLogger.log(`Deleting ${element.logGroupName}`);
await cwl.deleteLogGroup({ logGroupName: element.logGroupName || '' }).promise();
await cwl.send(new DeleteLogGroupCommand({ logGroupName: element.logGroupName || '' }));
}
}

View File

@ -1,12 +1,31 @@
import AWS from 'aws-sdk';
import {
CloudFormation,
DescribeStackResourcesCommand,
DescribeStacksCommand,
ListStacksCommand,
StackSummary,
} from '@aws-sdk/client-cloudformation';
import {
CloudWatchLogs,
DescribeLogGroupsCommand,
DescribeLogGroupsCommandInput,
LogGroup,
} from '@aws-sdk/client-cloudwatch-logs';
import {
DescribeTasksCommand,
DescribeTasksCommandInput,
ECS,
ListClustersCommand,
ListTasksCommand,
ListTasksCommandInput,
Task,
} from '@aws-sdk/client-ecs';
import { ListObjectsCommand, ListObjectsCommandInput, S3 } from '@aws-sdk/client-s3';
import Input from '../../../../input';
import CloudRunnerLogger from '../../../services/core/cloud-runner-logger';
import { BaseStackFormation } from '../cloud-formations/base-stack-formation';
import AwsTaskRunner from '../aws-task-runner';
import { ListObjectsRequest } from 'aws-sdk/clients/s3';
import CloudRunner from '../../../cloud-runner';
import { StackSummaries } from 'aws-sdk/clients/cloudformation';
import { LogGroups } from 'aws-sdk/clients/cloudwatchlogs';
export class TaskService {
static async watch() {
@ -20,20 +39,24 @@ export class TaskService {
return output;
}
public static async getCloudFormationJobStacks() {
const result: StackSummaries = [];
const result: StackSummary[] = [];
CloudRunnerLogger.log(``);
CloudRunnerLogger.log(`List Cloud Formation Stacks`);
process.env.AWS_REGION = Input.region;
const CF = new AWS.CloudFormation();
const CF = new CloudFormation({ region: Input.region });
const stacks =
(await CF.listStacks().promise()).StackSummaries?.filter(
(await CF.send(new ListStacksCommand({}))).StackSummaries?.filter(
(_x) =>
_x.StackStatus !== 'DELETE_COMPLETE' && _x.TemplateDescription !== BaseStackFormation.baseStackDecription,
) || [];
CloudRunnerLogger.log(``);
CloudRunnerLogger.log(`Cloud Formation Stacks ${stacks.length}`);
for (const element of stacks) {
const ageDate: Date = new Date(Date.now() - element.CreationTime.getTime());
if (!element.CreationTime) {
CloudRunnerLogger.log(`${element.StackName} due to undefined CreationTime`);
}
const ageDate: Date = new Date(Date.now() - (element.CreationTime?.getTime() ?? 0));
CloudRunnerLogger.log(
`Task Stack ${element.StackName} - Age D${Math.floor(
@ -43,14 +66,18 @@ export class TaskService {
result.push(element);
}
const baseStacks =
(await CF.listStacks().promise()).StackSummaries?.filter(
(await CF.send(new ListStacksCommand({}))).StackSummaries?.filter(
(_x) =>
_x.StackStatus !== 'DELETE_COMPLETE' && _x.TemplateDescription === BaseStackFormation.baseStackDecription,
) || [];
CloudRunnerLogger.log(``);
CloudRunnerLogger.log(`Base Stacks ${baseStacks.length}`);
for (const element of baseStacks) {
const ageDate: Date = new Date(Date.now() - element.CreationTime.getTime());
if (!element.CreationTime) {
CloudRunnerLogger.log(`${element.StackName} due to undefined CreationTime`);
}
const ageDate: Date = new Date(Date.now() - (element.CreationTime?.getTime() ?? 0));
CloudRunnerLogger.log(
`Task Stack ${element.StackName} - Age D${Math.floor(
@ -64,22 +91,22 @@ export class TaskService {
return result;
}
public static async getTasks() {
const result: { taskElement: AWS.ECS.Task; element: string }[] = [];
const result: { taskElement: Task; element: string }[] = [];
CloudRunnerLogger.log(``);
CloudRunnerLogger.log(`List Tasks`);
process.env.AWS_REGION = Input.region;
const ecs = new AWS.ECS();
const clusters = (await ecs.listClusters().promise()).clusterArns || [];
const ecs = new ECS({ region: Input.region });
const clusters = (await ecs.send(new ListClustersCommand({}))).clusterArns || [];
CloudRunnerLogger.log(`Task Clusters ${clusters.length}`);
for (const element of clusters) {
const input: AWS.ECS.ListTasksRequest = {
const input: ListTasksCommandInput = {
cluster: element,
};
const list = (await ecs.listTasks(input).promise()).taskArns || [];
const list = (await ecs.send(new ListTasksCommand(input))).taskArns || [];
if (list.length > 0) {
const describeInput: AWS.ECS.DescribeTasksRequest = { tasks: list, cluster: element };
const describeList = (await ecs.describeTasks(describeInput).promise()).tasks || [];
const describeInput: DescribeTasksCommandInput = { tasks: list, cluster: element };
const describeList = (await ecs.send(new DescribeTasksCommand(describeInput))).tasks || [];
if (describeList.length === 0) {
CloudRunnerLogger.log(`No Tasks`);
continue;
@ -105,37 +132,48 @@ export class TaskService {
}
public static async awsDescribeJob(job: string) {
process.env.AWS_REGION = Input.region;
const CF = new AWS.CloudFormation();
const stack = (await CF.listStacks().promise()).StackSummaries?.find((_x) => _x.StackName === job) || undefined;
const stackInfo = (await CF.describeStackResources({ StackName: job }).promise()) || undefined;
const stackInfo2 = (await CF.describeStacks({ StackName: job }).promise()) || undefined;
if (stack === undefined) {
throw new Error('stack not defined');
}
const ageDate: Date = new Date(Date.now() - stack.CreationTime.getTime());
const message = `
const CF = new CloudFormation({ region: Input.region });
try {
const stack =
(await CF.send(new ListStacksCommand({}))).StackSummaries?.find((_x) => _x.StackName === job) || undefined;
const stackInfo = (await CF.send(new DescribeStackResourcesCommand({ StackName: job }))) || undefined;
const stackInfo2 = (await CF.send(new DescribeStacksCommand({ StackName: job }))) || undefined;
if (stack === undefined) {
throw new Error('stack not defined');
}
if (!stack.CreationTime) {
CloudRunnerLogger.log(`${stack.StackName} due to undefined CreationTime`);
}
const ageDate: Date = new Date(Date.now() - (stack.CreationTime?.getTime() ?? 0));
const message = `
Task Stack ${stack.StackName}
Age D${Math.floor(ageDate.getHours() / 24)} H${ageDate.getHours()} M${ageDate.getMinutes()}
${JSON.stringify(stack, undefined, 4)}
${JSON.stringify(stackInfo, undefined, 4)}
${JSON.stringify(stackInfo2, undefined, 4)}
`;
CloudRunnerLogger.log(message);
CloudRunnerLogger.log(message);
return message;
return message;
} catch (error) {
CloudRunnerLogger.error(
`Failed to describe job ${job}: ${error instanceof Error ? error.message : String(error)}`,
);
throw error;
}
}
public static async getLogGroups() {
const result: LogGroups = [];
const result: Array<LogGroup> = [];
process.env.AWS_REGION = Input.region;
const ecs = new AWS.CloudWatchLogs();
let logStreamInput: AWS.CloudWatchLogs.DescribeLogGroupsRequest = {
const ecs = new CloudWatchLogs();
let logStreamInput: DescribeLogGroupsCommandInput = {
/* logGroupNamePrefix: 'game-ci' */
};
let logGroupsDescribe = await ecs.describeLogGroups(logStreamInput).promise();
let logGroupsDescribe = await ecs.send(new DescribeLogGroupsCommand(logStreamInput));
const logGroups = logGroupsDescribe.logGroups || [];
while (logGroupsDescribe.nextToken) {
logStreamInput = { /* logGroupNamePrefix: 'game-ci',*/ nextToken: logGroupsDescribe.nextToken };
logGroupsDescribe = await ecs.describeLogGroups(logStreamInput).promise();
logGroupsDescribe = await ecs.send(new DescribeLogGroupsCommand(logStreamInput));
logGroups.push(...(logGroupsDescribe?.logGroups || []));
}
@ -159,11 +197,12 @@ export class TaskService {
}
public static async getLocks() {
process.env.AWS_REGION = Input.region;
const s3 = new AWS.S3();
const listRequest: ListObjectsRequest = {
const s3 = new S3({ region: Input.region });
const listRequest: ListObjectsCommandInput = {
Bucket: CloudRunner.buildParameters.awsStackName,
};
const results = await s3.listObjects(listRequest).promise();
const results = await s3.send(new ListObjectsCommand(listRequest));
return results.Contents || [];
}

View File

@ -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" \

View File

@ -36,6 +36,7 @@ class ImageEnvironmentFactory {
value: process.env.USYM_UPLOAD_AUTH_TOKEN,
},
{ name: 'PROJECT_PATH', value: parameters.projectPath },
{ name: 'BUILD_PROFILE', value: parameters.buildProfile },
{ name: 'BUILD_TARGET', value: parameters.targetPlatform },
{ name: 'BUILD_NAME', value: parameters.buildName },
{ name: 'BUILD_PATH', value: parameters.buildPath },

View File

@ -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;

View File

@ -59,6 +59,19 @@ describe('Input', () => {
});
});
describe('buildProfile', () => {
it('returns the default value', () => {
expect(Input.buildProfile).toStrictEqual('');
});
it('takes input from the users workflow', () => {
const mockValue = 'path/to/build_profile.asset';
const spy = jest.spyOn(core, 'getInput').mockReturnValue(mockValue);
expect(Input.buildProfile).toStrictEqual(mockValue);
expect(spy).toHaveBeenCalledTimes(1);
});
});
describe('buildName', () => {
it('returns the default value', () => {
expect(Input.buildName).toStrictEqual(Input.targetPlatform);

View File

@ -107,6 +107,10 @@ class Input {
return rawProjectPath.replace(/\/$/, '');
}
static get buildProfile(): string {
return Input.getInput('buildProfile') ?? '';
}
static get runnerTempPath(): string {
return Input.getInput('RUNNER_TEMP') ?? '';
}

View File

@ -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');
@ -170,6 +173,7 @@ class SetupMac {
process.env.UNITY_LICENSING_SERVER = buildParameters.unityLicensingServer;
process.env.SKIP_ACTIVATION = buildParameters.skipActivation;
process.env.PROJECT_PATH = buildParameters.projectPath;
process.env.BUILD_PROFILE = buildParameters.buildProfile;
process.env.BUILD_TARGET = buildParameters.targetPlatform;
process.env.BUILD_NAME = buildParameters.buildName;
process.env.BUILD_PATH = buildParameters.buildPath;

View File

@ -16,6 +16,7 @@ class Platform {
PS4: 'PS4',
XboxOne: 'XboxOne',
tvOS: 'tvOS',
VisionOS: 'VisionOS',
Switch: 'Switch',
// Unsupported

View File

@ -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', () => {

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 28bfc999a135648538355bfcb6a23aee
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: cd91492ed9aca40c49d42156a4a8f387
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,46 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 15003, guid: 0000000000000000e000000000000000, type: 0}
m_Name: Sample WebGL Build Profile
m_EditorClassIdentifier:
m_AssetVersion: 1
m_BuildTarget: 20
m_Subtarget: 0
m_PlatformId: 84a3bb9e7420477f885e98145999eb20
m_PlatformBuildProfile:
rid: 200022742090383361
m_OverrideGlobalSceneList: 0
m_Scenes: []
m_ScriptingDefines: []
m_PlayerSettingsYaml:
m_Settings: []
references:
version: 2
RefIds:
- rid: 200022742090383361
type: {class: WebGLPlatformSettings, ns: UnityEditor.WebGL, asm: UnityEditor.WebGL.Extensions}
data:
m_Development: 0
m_ConnectProfiler: 0
m_BuildWithDeepProfilingSupport: 0
m_AllowDebugging: 0
m_WaitForManagedDebugger: 0
m_ManagedDebuggerFixedPort: 0
m_ExplicitNullChecks: 0
m_ExplicitDivideByZeroChecks: 0
m_ExplicitArrayBoundsChecks: 0
m_CompressionType: -1
m_InstallInBuildFolder: 0
m_CodeOptimization: 0
m_WebGLClientBrowserPath:
m_WebGLClientBrowserType: 0
m_WebGLTextureSubtarget: 0

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b9aac23ad2add4b439decb0cf65b0d68
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -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",

View File

@ -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": {

View File

@ -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: []

View File

@ -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)

1439
yarn.lock

File diff suppressed because it is too large Load Diff