Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions .github/workflows/build-tests-ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 }}
Expand All @@ -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 }}
Expand All @@ -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 }}
Expand All @@ -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
6 changes: 5 additions & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: ''
Expand Down
109 changes: 70 additions & 39 deletions dist/default-build-script/Assets/Editor/UnityBuilderAction/Builder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
using UnityBuilderAction.Versioning;
using UnityEditor;
using UnityEditor.Build.Reporting;
#if UNITY_6000_0_OR_NEWER
using UnityEditor.Build.Profile;
#endif
Comment on lines +9 to +11
Copy link
Member

@GabLeRoux GabLeRoux Jan 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we upgrade the dist/default-build-script project to Unity 6 too? I don't think it would affect anything, but it would make opening Unity in that folder easier to confirm that feature and Unity would complain less. 🤔

The Unity complaints 😛

CleanShot 2025-01-30 at 12 07 11@2x

CleanShot 2025-01-30 at 12 07 50@2x

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, that'd probably be a great idea!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unless I am mistaken, the dist/default-build-script is never opened in Unity. Rather, that folder is bind mounted onto the Docker container, and the contents of dist/default-build-script/Assets/Editor are copied into the users's Unity project, then compiled by Unity when their project launches.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

^ For this reason, I think the code in dist/default-build-script must be backwards compatible using scripting defines: so that the default-build-script can be built by older versions of Unity.

Such as most of these jobs: https://github.com/game-ci/unity-builder/actions/runs/13057500403

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dist/default-build-script is never opened in Unity.

That’s correct for script usage, but opening the file in JetBrains Rider with Unity integration makes sense for development.

Rather, that folder is bind mounted onto the Docker container, and the contents of dist/default-build-script/Assets/Editor are copied into the users's Unity project, then compiled by Unity when their project launches.

✅ That is correct

I think the code in dist/default-build-script must be backwards compatible using scripting defines

I definitely agree that the code in dist/default-build-script should remain backwards compatible using scripting defines.

I can confirm the folder itself is a Unity project.

The directory structure of `dist/default-build-script`

.
├── Assets
│   ├── Editor
│   │   ├── UnityBuilderAction
│   │   │   ├── Builder.cs
│   │   │   ├── Builder.cs.meta
│   │   │   ├── Input
│   │   │   │   ├── AndroidSettings.cs
│   │   │   │   ├── AndroidSettings.cs.meta
│   │   │   │   ├── ArgumentsParser.cs
│   │   │   │   └── ArgumentsParser.cs.meta
│   │   │   ├── Input.meta
│   │   │   ├── Reporting
│   │   │   │   ├── CompileListener.cs
│   │   │   │   ├── CompileListener.cs.meta
│   │   │   │   ├── StdOutReporter.cs
│   │   │   │   └── StdOutReporter.cs.meta
│   │   │   ├── Reporting.meta
│   │   │   ├── System
│   │   │   │   ├── ProcessExtensions.cs
│   │   │   │   └── ProcessExtensions.cs.meta
│   │   │   ├── System.meta
│   │   │   ├── UnityBuilderAction.asmdef
│   │   │   ├── UnityBuilderAction.asmdef.meta
│   │   │   ├── Versioning
│   │   │   │   ├── Git.cs
│   │   │   │   ├── Git.cs.meta
│   │   │   │   ├── GitException.cs
│   │   │   │   ├── GitException.cs.meta
│   │   │   │   ├── VersionApplicator.cs
│   │   │   │   ├── VersionApplicator.cs.meta
│   │   │   │   ├── VersionGenerator.cs
│   │   │   │   └── VersionGenerator.cs.meta
│   │   │   └── Versioning.meta
│   │   └── UnityBuilderAction.meta
│   └── Editor.meta
├── Packages
│   └── manifest.json
└── ProjectSettings
    ├── AudioManager.asset
    ├── ClusterInputManager.asset
    ├── DynamicsManager.asset
    ├── EditorBuildSettings.asset
    ├── EditorSettings.asset
    ├── GraphicsSettings.asset
    ├── InputManager.asset
    ├── NavMeshAreas.asset
    ├── Physics2DSettings.asset
    ├── PresetManager.asset
    ├── ProjectSettings.asset
    ├── ProjectVersion.txt
    ├── QualitySettings.asset
    ├── TagManager.asset
    ├── TimeManager.asset
    ├── UnityConnectSettings.asset
    └── VFXManager.asset

10 directories, 46 files

My suggestion is more about files like ProjectSettings/ProjectVersion.txt, updating it wouldn’t change much, but it could improve development workflow. However, since the alternative is creating a blank Unity project and manually copying files (which mirrors the action’s behavior), that approach remains valid too.

The main goal was to open the project in Unity/Rider to check for issues. I'm just lacking an older version on my system. I’m still unsure if we should upgrade the project in that folder, it likely wouldn’t make much difference, but might make sense for development. :) 🤷

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@GabLeRoux Ah, I get what you are saying. That's actually what I did: I copied dist/default-build-script/Assets/Editor into my Unity 6 project, drafted/tested it, and copied it back into this repo.

using UnityEngine;

namespace UnityBuilderAction
Expand All @@ -17,47 +20,9 @@ public static void BuildProject()
// 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.
Expand All @@ -78,6 +43,72 @@ public static void BuildProject()
}
}

// 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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do build profiles set StandaloneBuildSubtarget?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's appears to me that the Build Profile feature eliminates the need for defining the build subtarget.

The old build method works by setting BuildPlayerOptions.target in conjunction with BuildPlayerOptions.subtarget. However these fields do not exist on the new BuildPlayerWithProfileOptions.

Rather, Windows Server, macOS Server, and Linux Server are entirely different types of build profiles:

Screenshot 2025-01-31 at 1 58 12 PM

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, then we should be fine to ignore subtarget when using a build profile. We may just want to add a comment about it to the docs.

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"]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we still set the androidVersionCode when using a build profile for Android?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a complex question, which applies not only to the version code, but also to all other Android settings supported by this unity-builder GitHub action.

You can set the Android Bundle Version Code within the Build Profile itself. In this case I suppose that you would run the GitHub action with buildProfile: ... and versioning: None.

However, if you want to rely on the GitHub action to determine the version code... I think I have not implemented that--actually, it might even be impossible:

After we load the BuildProfile from the /Assets folder, the object has no public members or methods by which we can modify/edit the Build Profile at runtime.

From decompiled code, I can see that the BuildProfile class has internal classes and getter/setters, as well as private members, that hold the platform build settings and player overrides deserialized from the build profile asset. There also seems to be no public constructor, so you cannot instantiate/build the build profile at runtime.

I would guess that Unity will add these features/abilities in a later version, but they do not seem to be in Unity 6.1 Beta, yet.

So, I suppose the only possible solution today would be to modify (edit in place) the build profile's .asset YAML before launching the build. For instance, I just made a new Android Build Profile and set Bundle Version Code: 990099:

Screenshot 2025-01-31 at 1 49 27 PM

This resulted in this on line 206 of the .asset file:

    - line: '|   AndroidBundleVersionCode: 990099'

So we would have to Regex replace that 6-digit value before launching Unity.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, we may want to create a follow-up issue to check on setting Android version code when using a build profile, but I don't think that should be a blocker for this PR.

AndroidSettings.Apply(options);
}

}

// Perform build
BuildReport buildReport = BuildPipeline.BuildPlayer(buildPlayerOptions);

Expand Down
6 changes: 6 additions & 0 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

18 changes: 18 additions & 0 deletions dist/platforms/mac/steps/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already said on line 20.
Probably better to only add the echo from when $BUILD_PROFILE is set.

#
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
#
Expand Down Expand Up @@ -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" \
Expand Down
17 changes: 17 additions & 0 deletions dist/platforms/ubuntu/steps/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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\"."
Comment on lines +29 to +34
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

#
fi

#
# Display build path and file
#
Expand Down Expand Up @@ -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" \
Expand Down
20 changes: 20 additions & 0 deletions dist/platforms/windows/build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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.')"
Comment on lines +27 to +34
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

#
}

#
# Display build path and file
#
Expand Down Expand Up @@ -143,6 +162,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`"",
Expand Down
6 changes: 6 additions & 0 deletions src/model/build-parameters.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions src/model/build-parameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/model/image-environment-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
Expand Down
13 changes: 13 additions & 0 deletions src/model/input.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});
Comment on lines +62 to +73
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test gods thank you for your contribution


describe('buildName', () => {
it('returns the default value', () => {
expect(Input.buildName).toStrictEqual(Input.targetPlatform);
Expand Down
4 changes: 4 additions & 0 deletions src/model/input.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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') ?? '';
}
Expand Down
Loading
Loading