unity-builder icon indicating copy to clipboard operation
unity-builder copied to clipboard

Support activeBuildProfile parameter

Open Sammmte opened this issue 1 year ago โ€ข 1 comments

Context

When using "activeBuildProfile" parameter (in "customParameters", example: customParameters: -activeBuildProfile Assets/Settings/BuildProfiles/WindowsDevelopment.asset) (for Unity 6) builder action throws an error message:

Aborting batchmode due to failure:
Can't have -activeBuildProfile argument defined when using -buildTarget argument

Problem is "targetPlatform" is a required parameter.

Suggested solution

Maybe require to have either "targetPlatform" parameter OR another parameter like "buildProfile". "buildProfile" parameter would hold the same value "activeBuildProfile" requires, a relative path to the profile asset.

Sammmte avatar Oct 03 '24 15:10 Sammmte

Build Profiles is a relatively new feature and would be a great addition. ๐Ÿ‘

More details about the build profiles

image

GabLeRoux avatar Oct 03 '24 15:10 GabLeRoux

This would be incredibly useful

OwenSignaturize avatar Dec 05 '24 14:12 OwenSignaturize

@coderabbitai read the following files and suggest an approach to add support for activeBuildProfile parameter for unity version >= 6 (6000)

dist/default-build-script/Assets/Editor/UnityBuilderAction/Builder.cs dist/default-build-script/Assets/Editor/UnityBuilderAction/Versioning/GitException.cs src/index.ts src/model/platform-setup/setup-windows.ts src/model/unity.test.ts src/model/input.ts dist/default-build-script/Assets/Editor/UnityBuilderAction/Versioning/VersionApplicator.cs src/model/action.ts dist/default-build-script/Assets/Editor/UnityBuilderAction/Versioning/Git.cs dist/default-build-script/Assets/Editor/UnityBuilderAction/Versioning/VersionGenerator.cs src/model/action.test.ts src/model/unity.ts dist/default-build-script/Assets/Editor/UnityBuilderAction/Input/ArgumentsParser.cs dist/default-build-script/Assets/Editor/UnityBuilderAction/Reporting/StdOutReporter.cs dist/default-build-script/Assets/Editor/UnityBuilderAction/Reporting/CompileListener.cs src/model/input.test.ts src/model/platform-setup/setup-mac.ts src/model/platform-setup/setup-android.ts src/model/docker.ts src/model/docker.test.ts action.yml

GabLeRoux avatar Dec 05 '24 17:12 GabLeRoux

[!TIP] For best results, initiate chat on the files or code changes.

Based on the files analyzed, I'll suggest an approach to add support for the activeBuildProfile parameter for Unity version >= 6. Here's what needs to be changed:

@GabLeRoux Here's the proposed approach:

  1. First, add a new optional input parameter in action.yml:
  buildProfile:
    required: false
    default: ''
    description: 'Path to the Unity Build Profile asset file. When specified, targetPlatform will be ignored as the build target is defined in the profile.'
  1. Update src/model/input.ts to include the new parameter:
static get buildProfile(): string {
  return Input.getInput('buildProfile') ?? '';
}
  1. Modify the validation logic in dist/default-build-script/Assets/Editor/UnityBuilderAction/Input/ArgumentsParser.cs to make buildTarget optional when buildProfile is specified:
public static Dictionary<string, string> GetValidatedOptions()
{
    Dictionary<string, string> validatedOptions;
    ParseCommandLineArguments(out validatedOptions);

    string projectPath;
    if (!validatedOptions.TryGetValue("projectPath", out projectPath)) {
        Console.WriteLine("Missing argument -projectPath");
        EditorApplication.Exit(110);
    }

    // Check if buildProfile is specified
    string buildProfile;
    bool hasBuildProfile = validatedOptions.TryGetValue("buildProfile", out buildProfile) && !string.IsNullOrEmpty(buildProfile);

    // Only require buildTarget if buildProfile is not specified
    if (!hasBuildProfile)
    {
        string buildTarget;
        if (!validatedOptions.TryGetValue("buildTarget", out buildTarget)) {
            Console.WriteLine("Missing argument -buildTarget (required when buildProfile is not specified)");
            EditorApplication.Exit(120);
        }

        if (!Enum.IsDefined(typeof(BuildTarget), buildTarget)) {
            Console.WriteLine(buildTarget + " is not a defined " + typeof(BuildTarget).Name);
            EditorApplication.Exit(121);
        }
    }

    // Rest of the validation...
}
  1. Update dist/default-build-script/Assets/Editor/UnityBuilderAction/Builder.cs to handle the build profile:
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;
        }
    }

    // Define BuildPlayer Options
    var buildPlayerOptions = new BuildPlayerOptions {
        scenes = scenes,
        locationPathName = options["customBuildPath"],
        options = buildOptions,
    };

    // If buildProfile is specified, add it to customParameters
    if (options.TryGetValue("buildProfile", out var buildProfile) && !string.IsNullOrEmpty(buildProfile))
    {
        if (options.ContainsKey("customParameters"))
        {
            options["customParameters"] += $" -activeBuildProfile {buildProfile}";
        }
        else
        {
            options["customParameters"] = $"-activeBuildProfile {buildProfile}";
        }
    }
    else
    {
        // Use traditional buildTarget approach
        buildPlayerOptions.target = (BuildTarget)Enum.Parse(typeof(BuildTarget), options["buildTarget"]);
    }

    // Rest of the build process...
}
  1. Update the documentation to explain the new parameter and its usage:
## Using Build Profiles (Unity 6+)

For Unity 6 and above, you can use Build Profiles instead of specifying a target platform. Build Profiles provide more control over your build settings and can be managed through Unity Editor.

Example workflow:
```yaml
- uses: game-ci/unity-builder@v4
  with:
    buildProfile: 'Assets/Settings/BuildProfiles/WindowsDevelopment.asset'
    buildName: 'My Game'
    buildsPath: build

Note: When using buildProfile, the targetPlatform parameter will be ignored as the build target is defined in the profile.


This approach:
1. Makes `targetPlatform` optional when `buildProfile` is specified
2. Passes the build profile path through the `-activeBuildProfile` parameter to Unity
3. Maintains backward compatibility for projects not using build profiles
4. Provides clear documentation for users

Would you like me to proceed with creating a GitHub issue to track this enhancement?



<details>
<summary>๐Ÿ”— Analysis chain</summary>

Let me gather the necessary information from the files to suggest an approach for adding support for the activeBuildProfile parameter.



</details>

<details>
<summary>๐Ÿ Scripts executed</summary>

The following scripts were executed for the analysis:

Script:

#!/bin/bash

First, let's check the action.yml to see the current input parameters

echo "=== action.yml ===" cat action.yml

Let's look at the input handling in TypeScript

echo -e "\n=== src/model/input.ts ===" cat src/model/input.ts

Check how build arguments are constructed

echo -e "\n=== src/model/unity.ts ===" cat src/model/unity.ts

Check the Builder.cs which handles the actual build

echo -e "\n=== dist/default-build-script/Assets/Editor/UnityBuilderAction/Builder.cs ===" cat "dist/default-build-script/Assets/Editor/UnityBuilderAction/Builder.cs"

Check how arguments are parsed

echo -e "\n=== dist/default-build-script/Assets/Editor/UnityBuilderAction/Input/ArgumentsParser.cs ===" cat "dist/default-build-script/Assets/Editor/UnityBuilderAction/Input/ArgumentsParser.cs"


Length of output: 25578

</details>

---

<details>
<summary>โœ๏ธ Learnings added</summary>

Learnt from: GabLeRoux URL: https://github.com/game-ci/unity-builder/issues/674

Timestamp: 2024-12-05T17:12:45.657Z Learning: For Unity version 6 and above, the -activeBuildProfile parameter cannot be used together with -buildTarget parameter as they are mutually exclusive. Build profiles are a new feature that encapsulate build settings including the target platform.


> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

coderabbitai[bot] avatar Dec 05 '24 17:12 coderabbitai[bot]

Is there a CLI flag for it? If yes, then that's probably preferable over modifying the build script itself.

If no, then this looks exactly how we should solve it.

Happy to take a PR for this!

webbertakken avatar Dec 05 '24 17:12 webbertakken

@coderabbitai There is an existing command line argument for this on the unity cli, it is -activeBuildProfile. Here is an example of how I used it in a different project (this is a confirmed to be working solution used with GitlabCI, not the GameCI Unity Builder GitHub Action):

#!/usr/bin/env bash

set -euo pipefail

echo "Building for '$BUILD_TARGET'"

# Ensure the build path is defined and exists
export BUILD_PATH="$UNITY_DIR/Builds/$BUILD_TARGET/"
mkdir -p "$BUILD_PATH"

# Base Unity command split into an array
mapfile -t UNITY_CMD < <(
  printf "%s\n" \
    "${UNITY_EXECUTABLE:-xvfb-run}" \
    "--auto-servernum" \
    "--server-args=-screen 0 640x480x24" \
    "unity-editor"
)

# Base Unity arguments
mapfile -t UNITY_ARGS < <(
  printf "%s\n" \
    "-projectPath" "$UNITY_DIR" \
    "-quit" \
    "-batchmode" \
    "-nographics" \
    "-customBuildTarget" "$BUILD_TARGET" \
    "-customBuildName" "$BUILD_NAME" \
    "-customBuildPath" "$BUILD_PATH" \
    "-executeMethod" "BuildCommand.PerformBuild" \
    "-logFile" "/dev/stdout"
)

# Adjust arguments based on whether a build profile is provided
if [[ -n "${BUILD_PROFILE_PATH:-}" && -f "$BUILD_PROFILE_PATH" ]]; then
  echo "Using build profile: '$BUILD_PROFILE_PATH'"
  UNITY_ARGS+=("-activeBuildProfile" "$BUILD_PROFILE_PATH")
else
  echo "No build profile specified or the file does not exist; proceeding with default build target."
  UNITY_ARGS+=("-buildTarget" "$BUILD_TARGET")
fi

# Print arguments for debugging
echo "Unity Command: ${UNITY_CMD[*]}"
echo "Unity Arguments: ${UNITY_ARGS[*]}"

# Run the Unity command with arguments
"${UNITY_CMD[@]}" "${UNITY_ARGS[@]}"

UNITY_EXIT_CODE=$?

# Check the Unity exit code and display appropriate messages
if [[ $UNITY_EXIT_CODE -eq 0 ]]; then
  echo "Build succeeded with no failures."
elif [[ $UNITY_EXIT_CODE -eq 2 ]]; then
  echo "Build succeeded, but some tests failed."
elif [[ $UNITY_EXIT_CODE -eq 3 ]]; then
  echo "Build failed due to other reasons."
else
  echo "Unexpected Unity exit code: $UNITY_EXIT_CODE."
fi

# List the build output directory contents
ls -la "$BUILD_PATH"

# Fail the job if the build folder is empty
if [[ -z "$(ls -A "$BUILD_PATH")" ]]; then
  echo "Build folder is empty! Failing the job."
  exit 1
fi

Here are some additional details I am aware of:

  1. It is important to not pass a full path or even a relative path starting with ./ to the -activeBuildProfile parameter. It must use a local path to the asset file. For exmaple:
BUILD_PROFILE_PATH: "Assets/Settings/Build Profiles/Android Mobile.asset"
BUILD_PROFILE_PATH: "Assets/Settings/Build Profiles/Android VR Oculus.asset"
  1. The documentation for the -activeBuildProfile is available here: https://docs.unity3d.com/Manual/EditorCommandLineArguments.html
Command Description
-activeBuildProfile <pathname> Set the build profile saved at the given path as an active build profile (e.g., -activeBuildProfile "Assets/Settings/Build Profiles/WindowsDemo.asset"). Applies custom scripting defines found in the Build data section of the active build profile before compilation. These scripting defines are additive and donโ€™t override other scripting defines in your project.

GabLeRoux avatar Dec 05 '24 17:12 GabLeRoux

Impressive @GabLeRoux :)

webbertakken avatar Dec 05 '24 17:12 webbertakken

[!TIP] For best results, initiate chat on the files or code changes.

@GabLeRoux Based on the code analysis and your example, I'll propose a simpler solution that leverages the existing CLI argument. Here's what we need to do:

  1. First, add the new input parameter in action.yml:
  buildProfile:
    required: false
    default: ''
    description: 'Path to the Unity Build Profile asset file (Unity 6+). When specified, targetPlatform will be ignored as the build target is defined in the profile. Must be a local path relative to project root (e.g., "Assets/Settings/Build Profiles/WindowsDemo.asset").'
  1. Add the new input getter in src/model/input.ts:
static get buildProfile(): string {
  return Input.getInput('buildProfile') ?? '';
}
  1. Update src/model/unity.ts to handle the build profile parameter:
class Unity {
  static get libraryFolder() {
    return 'Library';
  }

  static getBuildArguments(): string[] {
    const args: string[] = [];
    const buildProfile = Input.buildProfile;

    if (buildProfile) {
      // When using build profile, don't pass buildTarget as they are mutually exclusive
      args.push('-activeBuildProfile', buildProfile);
    } else {
      // Traditional build target approach
      args.push('-buildTarget', Input.targetPlatform);
    }

    return args;
  }
}
  1. Update the documentation to explain the new parameter and its usage:
## Using Build Profiles (Unity 6+)

For Unity 6 and above, you can use Build Profiles instead of specifying a target platform:

```yaml
- uses: game-ci/unity-builder@v4
  with:
    buildProfile: 'Assets/Settings/Build Profiles/WindowsDemo.asset'
    buildName: 'MyGame'
    buildsPath: build

Important notes:

  • When using buildProfile, the targetPlatform parameter will be ignored as the build target is defined in the profile
  • The build profile path must be relative to the project root and start with "Assets/" (e.g., "Assets/Settings/Build Profiles/WindowsDemo.asset")
  • Do not use ./ prefix in the path

This approach:
1. Makes it simple to add build profile support by using the existing `-activeBuildProfile` CLI argument
2. Maintains backward compatibility for projects not using build profiles
3. Follows the path requirements you mentioned (no `./` prefix, must be relative to project root)
4. Provides clear documentation for users

Would you like me to create a GitHub issue to track this enhancement?



<details>
<summary>๐Ÿ”— Analysis chain</summary>

@GabLeRoux Thank you for providing the detailed example and clarification! Based on your input, I'll revise my previous approach to leverage the existing CLI argument. Let me analyze the current implementation first.



</details>

<details>
<summary>๐Ÿ Scripts executed</summary>

The following scripts were executed for the analysis:

Script:

#!/bin/bash

Let's check how build arguments are currently constructed in unity.ts

echo "=== src/model/unity.ts ===" cat src/model/unity.ts

Check the current input parameters

echo -e "\n=== src/model/input.ts ===" cat src/model/input.ts

Check if there are any tests that need to be updated

echo -e "\n=== src/model/unity.test.ts ===" cat src/model/unity.test.ts


Length of output: 8412

</details>

---

<details>
<summary>โœ๏ธ Learnings added</summary>

Learnt from: GabLeRoux URL: https://github.com/game-ci/unity-builder/issues/674

Timestamp: 2024-12-05T17:39:42.136Z Learning: When using Unity Build Profiles, the path provided to -activeBuildProfile must be relative to the project root and start with "Assets/". Do not use "./" prefix in the path. Example: "Assets/Settings/Build Profiles/WindowsDemo.asset"


> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

---

<details>
<summary>๐Ÿง  Learnings used</summary>

Learnt from: GabLeRoux PR: game-ci/unity-builder#0 File: :0-0 Timestamp: 2024-12-05T17:12:46.110Z Learning: For Unity version 6 and above, the -activeBuildProfile parameter cannot be used together with -buildTarget parameter as they are mutually exclusive. Build profiles are a new feature that encapsulate build settings including the target platform.


</details>

coderabbitai[bot] avatar Dec 05 '24 17:12 coderabbitai[bot]

@GabLeRoux Looks like the formatting of that last message is a bit off towards the end. The solution also doesn't look viable.

@coderabbitai So we're not going to talk about how to get those new kinds of arguments inside of javascript will make it across the boundary of docker and be usable inside the bash script that's run inside the container?

webbertakken avatar Dec 05 '24 18:12 webbertakken

[!TIP] For best results, initiate chat on the files or code changes.

@webbertakken You're absolutely right - I apologize for missing the crucial Docker boundary aspect. Let me propose a complete solution that handles the argument flow through all layers:

  1. First, add the new input parameter in action.yml:
  buildProfile:
    required: false
    default: ''
    description: 'Path to the Unity Build Profile asset file (Unity 6+). When specified, targetPlatform will be ignored as the build target is defined in the profile. Must be a local path relative to project root (e.g., "Assets/Settings/Build Profiles/WindowsDemo.asset").'
  1. Add the input getter in src/model/input.ts:
static get buildProfile(): string {
  return Input.getInput('buildProfile') ?? '';
}
  1. Update src/model/docker.ts to pass the build profile through environment variables:
class ImageEnvironmentFactory {
  static getEnvVarString(parameters: DockerParameters, additionalVariables: StringKeyValuePair[] = []): string {
    const environmentVariables = [...additionalVariables];
    
    // Add build profile if specified
    if (Input.buildProfile) {
      environmentVariables.push({
        key: 'BUILD_PROFILE',
        value: Input.buildProfile,
      });
    }

    return environmentVariables
      .map(({ key, value }) => `--env ${key}="${value}"`)
      .join(' ');
  }
}
  1. Update the entrypoint script (platforms/ubuntu/entrypoint.sh) to pass the build profile to Unity:
# Construct Unity command line arguments
UNITY_ARGS=()

# Add common arguments
UNITY_ARGS+=("-projectPath" "$GITHUB_WORKSPACE")
UNITY_ARGS+=("-quit")
UNITY_ARGS+=("-batchmode")
UNITY_ARGS+=("-executeMethod" "UnityBuilderAction.Builder.BuildProject")

# Add build profile or target platform
if [ -n "$BUILD_PROFILE" ]; then
  echo "Using build profile: $BUILD_PROFILE"
  UNITY_ARGS+=("-activeBuildProfile" "$BUILD_PROFILE")
else
  echo "Using build target: $BUILD_TARGET"
  UNITY_ARGS+=("-buildTarget" "$BUILD_TARGET")
fi

# Run Unity with all arguments
unity-editor "${UNITY_ARGS[@]}"
  1. Update dist/default-build-script/Assets/Editor/UnityBuilderAction/Input/ArgumentsParser.cs to make buildTarget optional when using build profile:
public static Dictionary<string, string> GetValidatedOptions()
{
    Dictionary<string, string> validatedOptions;
    ParseCommandLineArguments(out validatedOptions);

    // Validate project path
    string projectPath;
    if (!validatedOptions.TryGetValue("projectPath", out projectPath)) {
        Console.WriteLine("Missing argument -projectPath");
        EditorApplication.Exit(110);
    }

    // Check if using build profile
    bool usingBuildProfile = validatedOptions.ContainsKey("activeBuildProfile");

    // Validate build target only if not using build profile
    if (!usingBuildProfile)
    {
        string buildTarget;
        if (!validatedOptions.TryGetValue("buildTarget", out buildTarget)) {
            Console.WriteLine("Missing argument -buildTarget");
            EditorApplication.Exit(120);
        }

        if (!Enum.IsDefined(typeof(BuildTarget), buildTarget)) {
            Console.WriteLine(buildTarget + " is not a defined " + typeof(BuildTarget).Name);
            EditorApplication.Exit(121);
        }
    }

    // Rest of the validation...
    return validatedOptions;
}
  1. Update dist/default-build-script/Assets/Editor/UnityBuilderAction/Builder.cs to handle the build profile:
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;
        }
    }

    // Define BuildPlayer Options
    var buildPlayerOptions = new BuildPlayerOptions {
        scenes = scenes,
        locationPathName = options["customBuildPath"],
        options = buildOptions
    };

    // Set build target only if not using build profile
    if (!options.ContainsKey("activeBuildProfile"))
    {
        buildPlayerOptions.target = (BuildTarget)Enum.Parse(typeof(BuildTarget), options["buildTarget"]);
    }

    // Rest of the build process...
}

This solution:

  1. Passes the build profile through Docker's environment variables
  2. Modifies the entrypoint script to handle both build profile and target platform
  3. Updates the C# code to handle the build profile parameter
  4. Maintains backward compatibility for projects not using build profiles

Would you like me to create a GitHub issue to track this enhancement?

๐Ÿ”— Analysis chain

Let me analyze how the arguments flow through the system to provide a complete solution.

๐Ÿ Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check how arguments are passed from action to docker
echo "=== src/model/docker.ts ==="
cat src/model/docker.ts

# Check how arguments are passed to Unity
echo -e "\n=== src/model/unity.ts ==="
cat src/model/unity.ts

# Check the build script that's used inside docker
echo -e "\n=== dist/default-build-script/Assets/Editor/UnityBuilderAction/Builder.cs ==="
cat "dist/default-build-script/Assets/Editor/UnityBuilderAction/Builder.cs"

# Check how arguments are parsed inside docker
echo -e "\n=== dist/default-build-script/Assets/Editor/UnityBuilderAction/Input/ArgumentsParser.cs ==="
cat "dist/default-build-script/Assets/Editor/UnityBuilderAction/Input/ArgumentsParser.cs"

Length of output: 12517


๐Ÿง  Learnings used
Learnt from: GabLeRoux
PR: game-ci/unity-builder#0
File: :0-0
Timestamp: 2024-12-05T17:12:46.110Z
Learning: For Unity version 6 and above, the -activeBuildProfile parameter cannot be used together with -buildTarget parameter as they are mutually exclusive. Build profiles are a new feature that encapsulate build settings including the target platform.

coderabbitai[bot] avatar Dec 05 '24 18:12 coderabbitai[bot]

Ended up hiding those comments as they add a stream of information that isn't really helpful.

For whoever feels like taking a stab at this, please have a look at the combination of the first comment from Coderabbat and the followup from @GabLeRoux.

webbertakken avatar Dec 05 '24 18:12 webbertakken

I've looked a bit at this already and so far the biggest issue (I think) has not been mentioned. (Which is why I think that AI assistants are mostly useless, surface-level noise; but whatever)

The action currently does a bit of build platform specific setup depending on the build target. If the build profile were to be specified instead of the build target, the necessary information would not be available at the required time.

We could try to extract the build target information from the BuildProfile asset file directly and use that to fill in the action parameter. That way we wouldn't need to change most of the existing code. I'm just not sure how robust and reliable that would be.

JanikHelbig-NB avatar Dec 13 '24 15:12 JanikHelbig-NB

We're just casually trialing this bot. It's been doing some very helpful comments as well.

Agreed that auto-detect would be preferable where possible. We already do that for the unityVersion or editorVersion param and could do the same for this.

That said, I think the whole point of Build Profiles is to have multiple. So beyond auto-detecting we do need to be able to specify it as a param as well.

webbertakken avatar Dec 13 '24 20:12 webbertakken

My team has run into this problem as well, and I'd like to take a crack at resolving this issue.


@JanikHelbig-NB is right when he says "The action currently does a bit of build platform specific setup depending on the build target".

However, I don't know that auto-detection is preferable. The targetPlatform GitHub action parameter is not necessarily a one-to-one mapping to the Unity -buildTarget CLI option. It is also an instruction to this unity-builder GitHub action of which image to pull and run from Docker Hub.

Therefore, we should be able to simply add an optional buildProfile GitHub action parameter to this GitHub action, keeping the required targetPlatform parameter. Then, immediately before the command to run the Unity build (i.e. in build.sh or build.ps1), the script decides whether it will pass -buildTarget ... or -activeBuildProfile ... in as a CLI param, based on the presence of the (new) BUILD_PROFILE env var, which will be set from the buildProfile GitHub action param.

In this case, if the end user (GitHub workflow author) instructs the provides incompatible values for the targetPlatform and buildProfile GitHub action params (for instance, a Windows target platform with a WebGL build profile), then Unity will throw an error when it runs, because the required build platform packages are not installed via Unity Hub.

I can quickly pursue this path.


Alternately, I can try the auto detection. A core difference here is that the current targetPlatform GitHub action parameter would become optional, as well as adding the new, and also optional, buildProfile parameter. Logic will need to be added to disallow providing both parameters, or first check the build profile to first see if it is compatible with target platform before continuing. Alternately, we can document that if both parameters are provided, the targetPlatform is ignored.

In this case of auto-detection, the GitHub action will need to open the provided build profile .asset file, parse the yaml, and inspect MonoBehavior.m_PlatformBuildProfile.rid. It will use that rid find the appropriate entry within MonoBehavior.references.RefIds. Within that reference entry, the type (type.class or type.ns) should have what we need to run the correct Docker image.

The build script (build.sh or build.ps1) will still need to choose whether to pass -buildTarget ... or -activeBuildProfile ... into Unity, as these are mutually exclusive options.


@webbertakken Let me know your thoughts.

MichaelBuhler avatar Jan 28 '25 13:01 MichaelBuhler

@MichaelBuhler thanks for sharing your ideas and insights. I think what you're saying makes much sense.

Generally speaking the rationale of GameCI is to use as many 'great defaults' as possible, so that the threshold for newer developers is as low as possible, and the complexity for experienced devs is as low as possible. In that line it would make sense to keep auto-detection for things like 'unity version'.

If there's not a one-to-one mapping between targetPlatform and buildProfile, then we should probably not link them together, as it does the opposite of reducing complexity.

I would like to make sure that we're using the concept as intended. According to BuildProfile API The buildProfile provides a set of configuration settings you can use to build your application on a particular platform.

When I'm thinking of a case where you'd build 2 different profiles, lets say dev and prod for Windows 64 Standalone, it is slightly ambiguous to me whether the buildProfile dictates targetPlatform (i.e. is on the same level) or whether buildProfiles would sit under targetPlatform hierarchically speaking. Can targetPlatform reliably be inferred from buildProfile perhaps?

For historical reasons I believe buildProfile (or activeBuildProfile, but all parameters are essentially "active") parameter of unity-builder should be optional; that way legacy builds will not break - and from a technical perspective we don't have to differentiate implementations between different Unity Editor versions.

With regards to your main and alternative proposal, lets maybe write out the different scenarios of the combinations of targetPlatform and buildProfile. Personally I lack sufficient understanding to make the call here. My first intuition is that we should keep targetPlatform as is and simply add buildProfile as a new optional parameter.

Either way I would be in favour for what makes the most sense on the larger scale and keeps in mind new devs (i.e. unity-builder abstracts away complexity where possible and effective).

webbertakken avatar Jan 28 '25 15:01 webbertakken

I agree that it would be better to go with the first option and simply add an optional buildProfile parameter to unity-builder.

We could read the build profile's .asset file to get the platform and therefore allow targetPlatform to be optional, but I think that's unnecessary for the first iteration of support for build profiles.

In general, I think it's better to be iterative with smaller updates, and we can follow up with making target platform optional after we've got the initial support for buildProfile.

For context, I'm also planning on using build profiles for new builds of my personal project, so that's also part of the reason I'm interested in quicker iterations for this.

davidmfinol avatar Jan 28 '25 19:01 davidmfinol

Yea, sounds good to me.

As long as we can stay backwards compatible and people don't have to change their pipelines just to upgrade the action (i.e. only need changes if they opt-in to buildProfile).

webbertakken avatar Jan 28 '25 22:01 webbertakken

@GabLeRoux @webbertakken @davidmfinol I have opened draft PRs here:

  • https://github.com/game-ci/unity-builder/pull/685
  • https://github.com/game-ci/documentation/pull/498

which do as requested: add a new, optional buildProfile parameter to unity-builder. As I said, the build script decides whether to pass -buildTarget OR -activeBuildProfile into Unity based on the presence of the new buildProfile parameter. This should be backwards compatible with existing consumers.

I have left the PRs in draft right now, as I am not really sure if or how I will be able to test Mac and Windows builds. Furthermore, my workflows on GitHub are failing (on docker pull) due to the default GitHub Actions runner running out of disk space, which I have not worked around yet. Let me know if you are able to assist me with testing.

docker: failed to register layer: write /opt/unity/Editor/Data/Tools/netcorerun/System.Private.Xml.dll: no space left on device.

MichaelBuhler avatar Jan 29 '25 04:01 MichaelBuhler

~Perhaps something has changed since @Sammmte's original post four months ago. When I run Unity with a -activeBuildProfile ... I get an error Missing argument -buildTarget and the process exits. Maybe these are no longer mutually exclusive?~

^Never mind about this. The error is not coming from Unity, but rather the custom build code of this repo:

https://github.com/game-ci/unity-builder/blob/83c85328dd491a4268b41f1aae9de279e1fe7be5/dist/default-build-script/Assets/Editor/UnityBuilderAction/Input/ArgumentsParser.cs#L26

MichaelBuhler avatar Jan 29 '25 08:01 MichaelBuhler

Things got more complicated than I expected. This unity-builder repo uses a custom build method.

I can pass -projectPath, -activeBuildProfile, and -build into Unity and get a successful, headless build locally:

/Applications/Unity/Hub/Editor/6000.0.33f1/Unity.app/Contents/MacOS/Unity \
  -logFile - \
  -quit \
  -batchmode \
  -nographics \
  -projectPath "/absolute/path/to/unity/project" \
  -activeBuildProfile "relative/path/to/build-profile.asset" \
  -build "/absolute/path/to/build/output/folder"

The problem is that unity-builder always passes -executeMethod into Unity; it never uses the default batch mode method.

So, if we don't want to require the user to define a custom static build class method to use build profiles with this GitHub action, I'll have to also modify the dist/default-build-script/* within this repo.


It was a very long time ago (5years 1month) when the decision was made to always execute the unity-builder custom build method: https://github.com/game-ci/unity-builder/commit/5dcd509017c77a4ac38dcbb331b45d2b43950edb#diff-6f9d41d046756f0ddc2fcee0626bdb50100d12b88f293734eff742818e03efa2R68

@webbertakken What was the reasoning there? I wonder if it was a way to provide a mechanism to programmatically parameterize/customize more build options--the exact functionality that has now been added to Unity as "Build Profiles".

Some thought could be given to adding (yet) another GitHub action param, such as noBuildMethod: true, which would skip unity-builder's custom build method.

Another option is that if the user provides the buildProfile GitHub action param, and does not provide the executeMethod param, then Unity is not passed -executeMethod UnityBuilderAction.Builder.BuildProject on the CLI, though I suspect this will have other negative consequences.

MichaelBuhler avatar Jan 29 '25 09:01 MichaelBuhler

The UnityBuilderAction.Builder.BuildProject() method is intricately connected to the BuildPlayerOptions object, but building with Build Profiles requires instantiating a BuildPlayerWithProfileOptions object instead.

Therefore, we will need some branching strategy within the custom build script implementation. At least two options:

  • Within UnityBuilderAction.Builder.BuildProject(), we add an if short-circuit at the top if the user has requested to build with a build profile. Optionally, do not short-circuit, but rather indent all the existing code in the else block as well.
  • Add a new static build method, such as UnityBuilderAction.Builder.BuildProjectFromProfile() which is logically separate from the existing build method, but which will have a few lines of code duplication. Optionally, the actual build, report summary, and exit can be abstracted to a common helper method.
    • With this choice, it will be up to the docker container's build script to decide which value for -executeMethod UnityBuilderAction.Builder.BuildProject[FromProfile] to pass into Unity.

The BuildPlayerWithProfileOptions class was not added until Unity 6 (6000), so there will need to be compiler directives in any case.

MichaelBuhler avatar Jan 29 '25 10:01 MichaelBuhler

I have successfully built locally with a build profile and a modified version of UnityBuilderAction.Builder.BuildProject(), visible here https://github.com/MichaelBuhler/unity-builder/commit/222b8068bdfe2da016b71418908862891db99cdd, summarized here:

      if (options.ContainsKey("buildProfile")) {

#if UNITY_6000_0_OR_NEWER
        // Load build profile from Assets folder
        BuildProfile buildProfile = AssetDatabase.LoadAssetAtPath<BuildProfile>(options["buildProfile"]);

        // Set it as active
        BuildProfile.SetActiveBuildProfile(buildProfile);

        // Define BuildPlayerWithProfileOptions
        buildPlayerOptions = new BuildPlayerWithProfileOptions {
            buildProfile = buildProfile,
            locationPathName = options["customBuildPath"],
            options = buildOptions,
        };
#endif

      } else {
        // old code here
      }

MichaelBuhler avatar Jan 29 '25 12:01 MichaelBuhler

@MichaelBuhler Excellent! Great work on making the steps and documenting each iteration.

I feel this is the right way to move forward.

One question: Will there be cases where buildProfiles will completely remove the need for customBuildMethod?

webbertakken avatar Jan 29 '25 12:01 webbertakken

One question: Will there be cases where buildProfiles will completely remove the need for customBuildMethod?

@webbertakken, yes that it currently the case.

The name of the "customBuildMethod" is passed to Unity via the -executeMethod ... CLI option. I can build my project without passing -executeMethod at all; I only need to supply the -projectPath, -activeBuildProfile, and -build options to successfully run a build.

MichaelBuhler avatar Jan 30 '25 01:01 MichaelBuhler

@MichaelBuhler ok, then would the features from the build script still work? I.e. do we need the BuildProject method at all in case ห‹buildProfileห‹ is passed?

webbertakken avatar Jan 30 '25 01:01 webbertakken

@webbertakken It is not necessary to do a Unity build with a custom method, such as BuildProject(). The current code changes that I have drafted still use the BuildProject() custom build method.

It would be possible for me to rework it (continue iterating) to provide a way bypass the use BuildProject() entirely.

From what I can tell, the BuildProject() custom build method mainly provides features to determine/set the application version and, in the case of an Android build, to set Android parameters.

MichaelBuhler avatar Jan 30 '25 02:01 MichaelBuhler

@webbertakken @davidmfinol @GabLeRoux

I made some simplifications, tested with a self-hosted (macOS) GitHub Actions Runner (n.b. this uses the MacBuilder to run Unity directly--not from a unityci/editor Docker Hub image), and submitted my PR for review: #685

I also added two more matrix tests to the Ubuntu tests, which will test Unity 6 builds both with and without a Build Profile.

MichaelBuhler avatar Jan 30 '25 07:01 MichaelBuhler

Thanks for explaining.

It would be possible for me to rework it (continue iterating) to provide a way bypass the use BuildProject() entirely.

I think it's not needed to do immediately. There may be advantages to sticking to the custom build method for now, as we learn more.

webbertakken avatar Jan 30 '25 13:01 webbertakken

@webbertakken Hi, excuse me to necro this ticket.

Our team has a specific use case that your current workaround doesn't cover and we do want to have a proper interface to -activeBuildProfile. In short, our build relies on preprocessors that are listed in each build profile in order to link platform-specific assemblies.

BuildProfile.SetActiveBuildProfile() (as in @MichaelBuhler 's suggestion) won't immediately take effect for preprocessors: it will "queue" the script recompile for the next editor update, which doesn't take place in batch mode.

AFAIK before 6000+, this kind of ci/cd scenarios needed to run two executeMethod passes which is partly why a lot of teams opted out to self-hosted runners with Unity Editor pre-installed.

Maybe I can tackle on implementation if you guys don't mind. Also I'm all ears for other workarounds. cheers

ryo0ka avatar Sep 24 '25 16:09 ryo0ka

@ryo0ka thank you for your comment.

Sure, I think many would be happy if you would like to take a stab at this.

There is a fair amount of information already in this thread. And multiple approaches. Would you mind opening a new ticket, slightly better outlining your use case (as I didn't understand it word for word) as well as the proposed solution?

webbertakken avatar Sep 24 '25 18:09 webbertakken