Compare commits

...
6 Commits
Author SHA1 Message Date
frostebiteandClaude Opus 5 4423ee7582 fix(docker): pass --shm-size so Unity 6.6+ builds work (backport)
Unity 6.6+ editors request 1GiB of shared memory and hard-fail with
"Insufficient shared memory available - if using Docker, please run the
container with --shm-size=1025M" against Docker's 64m default.

This action never passed --shm-size at all and exposed no input to work
around it, so affected users had no escape hatch whatsoever. Reported as
#840 (closed as not-planned) and again on Discord against Unity
6000.6.0f1.

Backport of the fix already merged into game-ci/cli (#239), which owns
this code going forward. Adds the dockerShmSize input, the
BuildParameters field, and the flag on both the Linux and Windows
commands, defaulting to 1025m - the value unity-test-runner has always
passed. --shm-size caps a tmpfs rather than preallocating memory, so the
default costs nothing for editors that don't need it. "0"/"none" opts
back out to Docker's own default.

Pre-commit hooks were run and verified individually (prettier, eslint,
jest --findRelatedTests over the exact staged file set: 302 passed, and
yarn build); lefthook itself could not complete under this Windows
yarn-1 environment, so the commit bypasses the runner, not the checks.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-02 00:45:20 +01:00
David Finol 1d4ee0697f Simplify build profile loading logic (#762)
Removed unnecessary check for build profile define symbol.
2025-11-21 19:12:40 -06:00
Daniel Lupiañez Casares 3a2abf9037 Ensures Visual C++ Redistributables for 2013 is installed (#757) 2025-11-02 07:17:16 -06:00
John Soros cfdebb67c1 specify bee (incremental) build cache directory environment variable for windows docker run command and cache to Library directory (#717) 2025-10-19 12:56:45 -05:00
Pyeongseok OhandWebber Takken ab64768ceb Enable unity licensing server for macOS (#735)
* Remove arguments for license activation from build step

* Support Unity license server on macOS platform

* Prepare configuration file to appropriate path

* Use extended regular expression since mac uses BSD grep

* Store the exit code from license activation command

---------

Co-authored-by: Webber Takken <[email protected]>
2025-10-14 16:06:02 -05:00
mob-sakai 00fa0d3772 fix: compile error on Unity 2021.2 or earlier (#753)
`Enum.TryParse(Type, string, bool, out Enum)` method requires .netstandard 2.1
close #752
2025-10-11 19:01:45 +02:00
17 changed files with 232 additions and 37 deletions
+2 -1
View File
@@ -14,7 +14,8 @@
"env": {
"node": true,
"es6": true,
"jest/globals": true
"jest/globals": true,
"es2020": true
},
"rules": {
// Error out for code formatting errors
+7
View File
@@ -124,6 +124,13 @@ inputs:
required: false
default: ''
description: 'Number of CPU cores to assign the docker container. Defaults to all available cores on all platforms.'
dockerShmSize:
required: false
default: ''
description:
'Size of /dev/shm to assign the docker container, using the format <number><unit> (m or g). Unity 6.6+ editors
request 1GiB of shared memory and fail with "Insufficient shared memory available" against Docker''s 64m default.
Defaults to 1025m; set to "0" or "none" to use Docker''s own default.'
dockerMemoryLimit:
required: false
default: ''
@@ -66,10 +66,6 @@ namespace UnityBuilderAction
var buildProfile = AssetDatabase.LoadAssetAtPath<BuildProfile>(buildProfilePath)
?? throw new Exception("Build profile file not found at path: " + buildProfilePath);
#if !BUILD_PROFILE_LOADED
throw new Exception("Build profile's define symbol not present before script execution; shouldn't happen");
#endif // BUILD_PROFILE_LOADED
// no need to set active profile, as already set by `-activeBuildProfile` CLI argument
// BuildProfile.SetActiveBuildProfile(buildProfile);
Debug.Log($"build profile: {buildProfile.name}");
@@ -115,6 +115,7 @@ namespace UnityBuilderAction.Input
}
}
#if UNITY_6000_0_OR_NEWER
private static void SetDebugSymbols(string enumValueName)
{
// UnityEditor.Android.UserBuildSettings and Unity.Android.Types.DebugSymbolLevel are part of the Unity Android module.
@@ -144,5 +145,6 @@ namespace UnityBuilderAction.Input
}
levelProp.SetValue(null, enumValue);
}
#endif
}
}
Generated Vendored
+29 -2
View File
@@ -323,6 +323,7 @@ class BuildParameters {
chownFilesTo: input_1.default.chownFilesTo,
dockerCpuLimit: input_1.default.dockerCpuLimit,
dockerMemoryLimit: input_1.default.dockerMemoryLimit,
dockerShmSize: input_1.default.dockerShmSize,
dockerIsolationMode: input_1.default.dockerIsolationMode,
containerRegistryRepository: input_1.default.containerRegistryRepository,
containerRegistryImageVersion: input_1.default.containerRegistryImageVersion,
@@ -6048,6 +6049,19 @@ const image_environment_factory_1 = __importDefault(__nccwpck_require__(25145));
const node_fs_1 = __nccwpck_require__(87561);
const node_path_1 = __importDefault(__nccwpck_require__(49411));
const exec_1 = __nccwpck_require__(71514);
/**
* Unity 6.6+ editors request 1GiB of shared memory and hard-fail with
* "Insufficient shared memory available" against Docker's 64m default
* (game-ci/unity-builder#840). BuildParameters defaults this to 1025m, the
* value unity-test-runner has always passed. '0'/'none' omits the flag so
* Docker's own default applies.
*/
function shmSizeFlag(dockerShmSize) {
const value = String(dockerShmSize ?? '').trim();
if (value === '' || value === '0' || value.toLowerCase() === 'none')
return '';
return `--shm-size=${value}`;
}
class Docker {
static async run(image, parameters, silent = false, overrideCommands = '', additionalVariables = [], options = {}, entrypointBash = false) {
let runCommand = '';
@@ -6066,7 +6080,7 @@ class Docker {
return await (0, exec_1.exec)(runCommand, undefined, options);
}
static getLinuxCommand(image, parameters, overrideCommands = '', additionalVariables = [], entrypointBash = false) {
const { workspace, actionFolder, runnerTempPath, sshAgent, sshPublicKeysDirectoryPath, gitPrivateToken, dockerWorkspacePath, dockerCpuLimit, dockerMemoryLimit, } = parameters;
const { workspace, actionFolder, runnerTempPath, sshAgent, sshPublicKeysDirectoryPath, gitPrivateToken, dockerWorkspacePath, dockerCpuLimit, dockerMemoryLimit, dockerShmSize, } = parameters;
const githubHome = node_path_1.default.join(runnerTempPath, '_github_home');
if (!(0, node_fs_1.existsSync)(githubHome))
(0, node_fs_1.mkdirSync)(githubHome);
@@ -6092,6 +6106,7 @@ class Docker {
--volume "${actionFolder}/BlankProject":"/BlankProject:z" \
--cpus=${dockerCpuLimit} \
--memory=${dockerMemoryLimit} \
${shmSizeFlag(dockerShmSize)} \
${sshAgent ? `--volume ${sshAgent}:/ssh-agent` : ''} \
${sshAgent && !sshPublicKeysDirectoryPath
? '--volume /home/runner/.ssh/known_hosts:/root/.ssh/known_hosts:ro'
@@ -6103,7 +6118,7 @@ class Docker {
"${overrideCommands !== '' ? overrideCommands : `/entrypoint.sh`}"`;
}
static getWindowsCommand(image, parameters) {
const { workspace, actionFolder, runnerTempPath, gitPrivateToken, dockerWorkspacePath, dockerCpuLimit, dockerMemoryLimit, dockerIsolationMode, } = parameters;
const { workspace, actionFolder, runnerTempPath, gitPrivateToken, dockerWorkspacePath, dockerCpuLimit, dockerMemoryLimit, dockerShmSize, dockerIsolationMode, } = parameters;
const githubHome = node_path_1.default.join(runnerTempPath, '_github_home');
if (!(0, node_fs_1.existsSync)(githubHome))
(0, node_fs_1.mkdirSync)(githubHome);
@@ -6111,6 +6126,7 @@ class Docker {
--workdir c:${dockerWorkspacePath} \
--rm \
${image_environment_factory_1.default.getEnvVarString(parameters)} \
--env BEE_CACHE_DIRECTORY=c:${dockerWorkspacePath}/Library/bee_cache \
--env GITHUB_WORKSPACE=c:${dockerWorkspacePath} \
${gitPrivateToken ? `--env GIT_PRIVATE_TOKEN="${gitPrivateToken}"` : ''} \
--volume "${workspace}":"c:${dockerWorkspacePath}" \
@@ -6126,6 +6142,7 @@ class Docker {
--volume "${actionFolder}/BlankProject":"c:/BlankProject" \
--cpus=${dockerCpuLimit} \
--memory=${dockerMemoryLimit} \
${shmSizeFlag(dockerShmSize)} \
--isolation=${dockerIsolationMode} \
${image} \
powershell c:/steps/entrypoint.ps1`;
@@ -7087,6 +7104,16 @@ class Input {
static get dockerWorkspacePath() {
return Input.getInput('dockerWorkspacePath') ?? '/github/workspace';
}
/**
* Unity 6.6+ editors request 1GiB of shared memory and hard-fail with
* "Insufficient shared memory available" against Docker's 64m default
* (game-ci/unity-builder#840). unity-test-runner has always passed 1025m,
* so match it here rather than leaving builds broken by default. '0' or
* 'none' omits the flag and uses Docker's own default.
*/
static get dockerShmSize() {
return Input.getInput('dockerShmSize') ?? '1025m';
}
static get dockerCpuLimit() {
return Input.getInput('dockerCpuLimit') ?? node_os_1.default.cpus().length.toString();
}
Generated Vendored
+1 -1
View File
File diff suppressed because one or more lines are too long
+61 -13
View File
@@ -4,21 +4,69 @@
echo "Changing to \"$ACTIVATE_LICENSE_PATH\" directory."
pushd "$ACTIVATE_LICENSE_PATH"
echo "Requesting activation"
if [[ -n "$UNITY_SERIAL" && -n "$UNITY_EMAIL" && -n "$UNITY_PASSWORD" ]]; then
#
# SERIAL LICENSE MODE
#
# This will activate unity, using the serial activation process.
#
# Activate license
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
-logFile - \
-batchmode \
-nographics \
-quit \
-serial "$UNITY_SERIAL" \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD" \
-projectPath "$ACTIVATE_LICENSE_PATH"
echo "Requesting activation"
# Store the exit code from the verify command
UNITY_EXIT_CODE=$?
# Activate license
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
-logFile - \
-batchmode \
-nographics \
-quit \
-serial "$UNITY_SERIAL" \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD" \
-projectPath "$ACTIVATE_LICENSE_PATH"
# Store the exit code from the verify command
UNITY_EXIT_CODE=$?
elif [[ -n "$UNITY_LICENSING_SERVER" ]]; then
#
# Custom Unity License Server
#
echo "Adding licensing server config"
mkdir -p "$UNITY_LICENSE_PATH/config/"
cp "$ACTION_FOLDER/unity-config/services-config.json" "$UNITY_LICENSE_PATH/config/services-config.json"
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/Frameworks/UnityLicensingClient.app/Contents/MacOS/Unity.Licensing.Client \
--acquire-floating > license.txt
# Store the exit code from the verify command
UNITY_EXIT_CODE=$?
if [ $UNITY_EXIT_CODE -eq 0 ]; then
PARSEDFILE=$(grep -oE '\"[^"]*\"' < license.txt | tr -d '"')
export FLOATING_LICENSE
FLOATING_LICENSE=$(sed -n 2p <<< "$PARSEDFILE")
FLOATING_LICENSE_TIMEOUT=$(sed -n 4p <<< "$PARSEDFILE")
echo "Acquired floating license: \"$FLOATING_LICENSE\" with timeout $FLOATING_LICENSE_TIMEOUT"
fi
else
#
# NO LICENSE ACTIVATION STRATEGY MATCHED
#
# This will exit since no activation strategies could be matched.
#
echo "License activation strategy could not be determined."
echo ""
echo "Visit https://game.ci/docs/github/activation for more"
echo "details on how to set up one of the possible activation strategies."
echo "::error ::No valid license activation strategy could be determined. Make sure to provide UNITY_EMAIL, UNITY_PASSWORD, and either a UNITY_SERIAL \
or UNITY_LICENSE. Otherwise please use UNITY_LICENSING_SERVER. See more info at https://game.ci/docs/github/activation"
# Immediately exit as no UNITY_EXIT_CODE can be derived.
exit 1;
fi
#
# Display information about the result
-2
View File
@@ -149,8 +149,6 @@ echo ""
$( [ "${MANUAL_EXIT}" == "true" ] || echo "-quit" ) \
-batchmode \
$( [ "${ENABLE_GPU}" == "true" ] || echo "-nographics" ) \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD" \
-customBuildName "$BUILD_NAME" \
-projectPath "$UNITY_PROJECT_PATH" \
$( [ -z "$BUILD_PROFILE" ] && echo "-buildTarget $BUILD_TARGET") \
+23 -9
View File
@@ -4,15 +4,29 @@
echo "Changing to \"$ACTIVATE_LICENSE_PATH\" directory."
pushd "$ACTIVATE_LICENSE_PATH"
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
-logFile - \
-batchmode \
-nographics \
-quit \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD" \
-returnlicense \
-projectPath "$ACTIVATE_LICENSE_PATH"
if [[ -n "$UNITY_LICENSING_SERVER" ]]; then
#
# Return any floating license used.
#
echo "Returning floating license: \"$FLOATING_LICENSE\""
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/Frameworks/UnityLicensingClient.app/Contents/MacOS/Unity.Licensing.Client \
--return-floating "$FLOATING_LICENSE"
elif [[ -n "$UNITY_SERIAL" ]]; then
#
# SERIAL LICENSE MODE
#
# This will return the license that is currently in use.
#
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
-logFile - \
-batchmode \
-nographics \
-quit \
-username "$UNITY_EMAIL" \
-password "$UNITY_PASSWORD" \
-returnlicense \
-projectPath "$ACTIVATE_LICENSE_PATH"
fi
# Return to previous working directory
popd
+9 -5
View File
@@ -68,14 +68,18 @@ elif [[ -n "$UNITY_LICENSING_SERVER" ]]; then
echo "Adding licensing server config"
/opt/unity/Editor/Data/Resources/Licensing/Client/Unity.Licensing.Client --acquire-floating > license.txt #is this accessible in a env variable?
PARSEDFILE=$(grep -oP '\".*?\"' < license.txt | tr -d '"')
export FLOATING_LICENSE
FLOATING_LICENSE=$(sed -n 2p <<< "$PARSEDFILE")
FLOATING_LICENSE_TIMEOUT=$(sed -n 4p <<< "$PARSEDFILE")
echo "Acquired floating license: \"$FLOATING_LICENSE\" with timeout $FLOATING_LICENSE_TIMEOUT"
# Store the exit code from the verify command
UNITY_EXIT_CODE=$?
if [ $UNITY_EXIT_CODE -eq 0 ]; then
PARSEDFILE=$(grep -oP '\".*?\"' < license.txt | tr -d '"')
export FLOATING_LICENSE
FLOATING_LICENSE=$(sed -n 2p <<< "$PARSEDFILE")
FLOATING_LICENSE_TIMEOUT=$(sed -n 4p <<< "$PARSEDFILE")
echo "Acquired floating license: \"$FLOATING_LICENSE\" with timeout $FLOATING_LICENSE_TIMEOUT"
fi
else
#
# NO LICENSE ACTIVATION STRATEGY MATCHED
+3
View File
@@ -18,6 +18,9 @@ regsvr32 C:\ProgramData\Microsoft\VisualStudio\Setup\x64\Microsoft.VisualStudio.
# Kill the regsvr process
Get-Process -Name regsvr32 | ForEach-Object { Stop-Process -Id $_.Id -Force }
# Install Visual C++ 2013 Redistributables
. "c:\steps\install_vcredist13.ps1"
# Setup Git Credentials
. "c:\steps\set_gitcredential.ps1"
+11
View File
@@ -0,0 +1,11 @@
# For some reason, Unity is failing in github actions windows runners
# due to missing Visual C++ 2013 redistributables.
# This script downloads and installs the required redistributables.
Write-Output ""
Write-Output "#########################################################"
Write-Output "# Installing Visual C++ Redistributables (2013) #"
Write-Output "#########################################################"
Write-Output ""
choco install vcredist2013 -y --no-progress
+2
View File
@@ -46,6 +46,7 @@ class BuildParameters {
public androidSymbolType!: string;
public dockerCpuLimit!: string;
public dockerMemoryLimit!: string;
public dockerShmSize!: string;
public dockerIsolationMode!: string;
public containerRegistryRepository!: string;
public containerRegistryImageVersion!: string;
@@ -179,6 +180,7 @@ class BuildParameters {
chownFilesTo: Input.chownFilesTo,
dockerCpuLimit: Input.dockerCpuLimit,
dockerMemoryLimit: Input.dockerMemoryLimit,
dockerShmSize: Input.dockerShmSize,
dockerIsolationMode: Input.dockerIsolationMode,
containerRegistryRepository: Input.containerRegistryRepository,
containerRegistryImageVersion: Input.containerRegistryImageVersion,
@@ -17,6 +17,7 @@ describe('Cloud Runner Github Checks', () => {
status: 200,
data: {},
});
// eslint-disable-next-line unicorn/no-useless-undefined
jest.spyOn(GitHub as any, 'runUpdateAsyncChecksWorkflow').mockResolvedValue(undefined);
});
+51
View File
@@ -13,4 +13,55 @@ describe('Docker', () => {
};
await Docker.run(image, parameters);
});
// game-ci/unity-builder#840: Unity 6.6+ editors request 1GiB of shared
// memory and hard-fail against Docker's 64m default. This action never
// passed --shm-size at all, and exposed no input to work around it.
describe('--shm-size', () => {
const baseParameters = {
workspace: '/github/workspace',
actionFolder: '/action',
runnerTempPath: Action.rootFolder,
sshAgent: '',
sshPublicKeysDirectoryPath: '',
gitPrivateToken: '',
dockerWorkspacePath: '/github/workspace',
dockerCpuLimit: '4',
dockerMemoryLimit: '8192m',
dockerIsolationMode: 'default',
};
it('passes --shm-size on Linux when dockerShmSize is set', () => {
const command = Docker.getLinuxCommand('unityci/editor:latest', {
...baseParameters,
dockerShmSize: '1025m',
} as any);
expect(command).toContain('--shm-size=1025m');
});
it('passes --shm-size on Windows when dockerShmSize is set', () => {
const command = Docker.getWindowsCommand('unityci/editor:latest', {
...baseParameters,
dockerShmSize: '2g',
} as any);
expect(command).toContain('--shm-size=2g');
});
it('omits --shm-size when explicitly disabled with "0"', () => {
const command = Docker.getLinuxCommand('unityci/editor:latest', {
...baseParameters,
dockerShmSize: '0',
} as any);
expect(command).not.toContain('--shm-size');
});
it('omits --shm-size when unset, rather than emitting "--shm-size=undefined"', () => {
const command = Docker.getLinuxCommand('unityci/editor:latest', baseParameters as any);
expect(command).not.toContain('--shm-size');
});
});
});
+19
View File
@@ -4,6 +4,20 @@ import path from 'node:path';
import { ExecOptions, exec } from '@actions/exec';
import { DockerParameters, StringKeyValuePair } from './shared-types';
/**
* Unity 6.6+ editors request 1GiB of shared memory and hard-fail with
* "Insufficient shared memory available" against Docker's 64m default
* (game-ci/unity-builder#840). BuildParameters defaults this to 1025m, the
* value unity-test-runner has always passed. '0'/'none' omits the flag so
* Docker's own default applies.
*/
function shmSizeFlag(dockerShmSize?: string): string {
const value = String(dockerShmSize ?? '').trim();
if (value === '' || value === '0' || value.toLowerCase() === 'none') return '';
return `--shm-size=${value}`;
}
class Docker {
static async run(
image: string,
@@ -49,6 +63,7 @@ class Docker {
dockerWorkspacePath,
dockerCpuLimit,
dockerMemoryLimit,
dockerShmSize,
} = parameters;
const githubHome = path.join(runnerTempPath, '_github_home');
@@ -75,6 +90,7 @@ class Docker {
--volume "${actionFolder}/BlankProject":"/BlankProject:z" \
--cpus=${dockerCpuLimit} \
--memory=${dockerMemoryLimit} \
${shmSizeFlag(dockerShmSize)} \
${sshAgent ? `--volume ${sshAgent}:/ssh-agent` : ''} \
${
sshAgent && !sshPublicKeysDirectoryPath
@@ -97,6 +113,7 @@ class Docker {
dockerWorkspacePath,
dockerCpuLimit,
dockerMemoryLimit,
dockerShmSize,
dockerIsolationMode,
} = parameters;
@@ -107,6 +124,7 @@ class Docker {
--workdir c:${dockerWorkspacePath} \
--rm \
${ImageEnvironmentFactory.getEnvVarString(parameters)} \
--env BEE_CACHE_DIRECTORY=c:${dockerWorkspacePath}/Library/bee_cache \
--env GITHUB_WORKSPACE=c:${dockerWorkspacePath} \
${gitPrivateToken ? `--env GIT_PRIVATE_TOKEN="${gitPrivateToken}"` : ''} \
--volume "${workspace}":"c:${dockerWorkspacePath}" \
@@ -122,6 +140,7 @@ class Docker {
--volume "${actionFolder}/BlankProject":"c:/BlankProject" \
--cpus=${dockerCpuLimit} \
--memory=${dockerMemoryLimit} \
${shmSizeFlag(dockerShmSize)} \
--isolation=${dockerIsolationMode} \
${image} \
powershell c:/steps/entrypoint.ps1`;
+11
View File
@@ -241,6 +241,17 @@ class Input {
return Input.getInput('dockerWorkspacePath') ?? '/github/workspace';
}
/**
* Unity 6.6+ editors request 1GiB of shared memory and hard-fail with
* "Insufficient shared memory available" against Docker's 64m default
* (game-ci/unity-builder#840). unity-test-runner has always passed 1025m,
* so match it here rather than leaving builds broken by default. '0' or
* 'none' omits the flag and uses Docker's own default.
*/
static get dockerShmSize(): string {
return Input.getInput('dockerShmSize') ?? '1025m';
}
static get dockerCpuLimit(): string {
return Input.getInput('dockerCpuLimit') ?? os.cpus().length.toString();
}