Compare commits

...

5 Commits
v4.6.3 ... main

Author SHA1 Message Date
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 Oh 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 <webber@takken.io>
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
mob-sakai d587557287
fix: XLTS versions on MacOS are not supported (#751) 2025-10-11 12:41:23 +02:00
mob-sakai 6e0bf17345
fix: upgrade `unity-changeset` to v3.0.1 for graphql dependency (#750)
unity-changeset@3.0.0 did not explicitly include graphql dependency. (#749)
2025-10-09 10:45:19 +02:00
12 changed files with 153 additions and 124 deletions

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

View File

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

128
dist/index.js generated vendored
View File

@ -6111,6 +6111,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}" \
@ -362276,17 +362277,27 @@ exports.UnityChangeset = unityChangeset_js_1.UnityChangeset;
/**
* Retrieves the Unity changeset for a specific version.
* @param version - The Unity version string (e.g., "2020.1.14f1").
* @param db - Optional database URL or true to use the default database.
* @returns A Promise that resolves to the UnityChangeset object.
* @throws Error if the version is not found.
*/
async function getUnityChangeset(version) {
async function getUnityChangeset(version, db) {
const sanitizedVersion = (0, utils_js_1.sanitizeVersion)(version);
let changesets;
// Database mode.
if (db) {
const dbUrl = db === true ? UNITY_CHANGESETS_DB_URL : db;
changesets = await getAllChangesetsFromDb(dbUrl);
}
else {
// GraphQL mode.
try {
changesets = await (0, unityGraphQL_js_1.getUnityReleases)(sanitizedVersion, []);
changesets = await (0, unityGraphQL_js_1.getUnityReleases)(sanitizedVersion, (0, utils_js_1.searchModeToStreams)(SearchMode.All), [unityChangeset_js_1.UnityReleaseEntitlement.XLTS]);
}
catch {
changesets = await getAllChangesetsFromDb();
// Fallback to default database mode.
changesets = await getAllChangesetsFromDb(UNITY_CHANGESETS_DB_URL);
}
}
if (!changesets || !Array.isArray(changesets)) {
throw new Error("Failed to retrieve changesets");
@ -362311,8 +362322,7 @@ var SearchMode;
SearchMode[SearchMode["Default"] = 2] = "Default";
SearchMode[SearchMode["PreRelease"] = 3] = "PreRelease";
SearchMode[SearchMode["LTS"] = 4] = "LTS";
SearchMode[SearchMode["XLTS"] = 5] = "XLTS";
SearchMode[SearchMode["SUPPORTED"] = 6] = "SUPPORTED";
SearchMode[SearchMode["Supported"] = 6] = "Supported";
})(SearchMode || (exports.SearchMode = SearchMode = {}));
/*
* Group mode.
@ -362362,10 +362372,11 @@ var FormatMode;
* @param groupMode - The group mode to use.
* @param outputMode - The output mode for the results.
* @param formatMode - The format mode for the output.
* @param db - Optional database URL or true to use the default database.
* @returns A Promise that resolves to a formatted string of the results.
*/
function listChangesets(searchMode, filterOptions, groupMode, outputMode, formatMode) {
return searchChangesets(searchMode)
function listChangesets(searchMode, filterOptions, groupMode, outputMode, formatMode, db) {
return searchChangesets(searchMode, db)
.then((results) => filterChangesets(results, filterOptions))
.then((results) => results.sort((a, b) => b.versionNumber - a.versionNumber))
.then((results) => groupChangesets(results, groupMode))
@ -362398,27 +362409,24 @@ exports.listChangesets = listChangesets;
/**
* Searches for Unity changesets based on the specified search mode.
* @param searchMode - The search mode to use.
* @param db - Optional database URL or true to use the default database.
* @returns A Promise that resolves to an array of UnityChangeset objects.
* @throws Error if the search mode is not supported.
*/
async function searchChangesets(searchMode) {
try {
switch (searchMode) {
case SearchMode.All:
case SearchMode.Default:
case SearchMode.PreRelease:
case SearchMode.SUPPORTED:
return await (0, unityGraphQL_js_1.getUnityReleases)(".", (0, utils_js_1.searchModeToStreams)(searchMode));
case SearchMode.LTS:
return await (0, unityGraphQL_js_1.getUnityReleasesInLTS)();
case SearchMode.XLTS:
return await (0, unityGraphQL_js_1.getUnityReleasesInLTS)([unityChangeset_js_1.UnityReleaseEntitlement.XLTS]);
default:
throw Error(`The given search mode '${searchMode}' was not supported`);
async function searchChangesets(searchMode, db) {
const streams = (0, utils_js_1.searchModeToStreams)(searchMode);
// Database mode.
if (db) {
const dbUrl = db === true ? UNITY_CHANGESETS_DB_URL : db;
return searchChangesetsFromDb(streams, dbUrl);
}
try {
// GraphQL mode.
return await (0, unityGraphQL_js_1.getUnityReleases)(".", streams, [unityChangeset_js_1.UnityReleaseEntitlement.XLTS]);
}
catch {
return await searchChangesetsFromDb(searchMode);
// Fallback to default database mode.
return await searchChangesetsFromDb(streams, UNITY_CHANGESETS_DB_URL);
}
}
exports.searchChangesets = searchChangesets;
@ -362450,7 +362458,7 @@ function filterChangesets(changesets, options) {
: Object.values((0, utils_js_1.groupBy)(changesets, (r) => r.minor)).map((g) => g[0]);
return changesets.filter((c) => min <= c.versionNumber &&
c.versionNumber <= max &&
(!options.lts || c.lts) &&
(options.xlts || !c.xlts) && // Include XLTS?
(!regex || regex.test(c.version)) &&
(!lc || lc.some((l) => l.minor == c.minor && l.lifecycle == c.lifecycle)));
}
@ -362487,13 +362495,15 @@ function groupChangesets(changesets, groupMode) {
exports.groupChangesets = groupChangesets;
/**
* Retrieves all Unity changesets from the database.
* @param db - Database URL. If not specified, use the default database.
* @returns A Promise that resolves to an array of all UnityChangeset objects from the database.
* @throws Error if the database cannot be fetched or is invalid.
*/
function getAllChangesetsFromDb() {
return fetch(UNITY_CHANGESETS_DB_URL)
.then((res) => {
function getAllChangesetsFromDb(db = UNITY_CHANGESETS_DB_URL) {
return fetch(db)
.then(async (res) => {
if (!res.ok) {
await res.text(); // Consume the response body to avoid leaks
throw Error(`The Unity changeset database could not be fetched: ${res.status} ${res.statusText}`);
}
return res.json();
@ -362502,26 +362512,24 @@ function getAllChangesetsFromDb() {
if (!Array.isArray(data)) {
throw new Error("Invalid changeset database format: expected array");
}
return data;
return data.map((item) => new exports.UnityChangeset(item.version, item.changeset, item.stream, item.entitlements));
});
}
exports.getAllChangesetsFromDb = getAllChangesetsFromDb;
/**
* Searches for Unity changesets from the database based on the specified search mode.
* @param searchMode - The search mode to use.
* @param streams - The array of release streams to filter by.
* @param db - Database URL. If not specified, use the default database.
* @returns A Promise that resolves to an array of UnityChangeset objects from the database.
*/
function searchChangesetsFromDb(searchMode) {
return getAllChangesetsFromDb()
function searchChangesetsFromDb(streams, db = UNITY_CHANGESETS_DB_URL) {
return getAllChangesetsFromDb(db)
.then((changesets) => {
if (!changesets || !Array.isArray(changesets))
return [];
if (changesets.length == 0)
return [];
const streams = (0, utils_js_1.searchModeToStreams)(searchMode);
return searchMode === SearchMode.XLTS
? changesets.filter((c) => streams.includes(c.stream))
: changesets.filter((c) => streams.includes(c.stream) && !c.xlts);
return changesets.filter((c) => streams.includes(c.stream));
});
}
exports.searchChangesetsFromDb = searchChangesetsFromDb;
@ -362689,7 +362697,7 @@ Object.defineProperty(UnityChangeset, "toNumber", {
"use strict";
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.getUnityReleasesInLTS = exports.getUnityReleases = void 0;
exports.getUnityReleases = void 0;
const unityChangeset_js_1 = __nccwpck_require__(52758);
const graphql_request_1 = __nccwpck_require__(5406);
const UNITY_GRAPHQL_ENDPOINT = "https://services.unity.com/graphql";
@ -362799,53 +362807,6 @@ query GetRelease($limit: Int, $skip: Int, $version: String!, $stream: [UnityRele
return results;
}
exports.getUnityReleases = getUnityReleases;
/**
* Retrieves Unity releases in LTS from the GraphQL API based on entitlements.
* @param entitlements - The array of entitlements to filter by.
* @returns A Promise that resolves to an array of UnityChangeset objects.
* @throws Error if the API response is invalid.
*/
async function getUnityReleasesInLTS(entitlements = []) {
const client = new graphql_request_1.GraphQLClient(UNITY_GRAPHQL_ENDPOINT);
const query = (0, graphql_request_1.gql) `
query GetReleaseMajorVersions($entitlements: [UnityReleaseEntitlement!])
{
getUnityReleaseMajorVersions(
stream: []
platform: []
architecture: []
entitlements: $entitlements
) {
version
}
}
`;
const variables = {
entitlements: entitlements,
};
const cacheKey = getCacheKey(query, variables);
let data;
const cached = cache.get(cacheKey);
if (cached && isCacheValid(cached.timestamp)) {
data = cached.data;
}
else {
data = await requestWithErrorHandling(client, query, variables);
cache.set(cacheKey, { data, timestamp: Date.now() });
}
if (!data.getUnityReleaseMajorVersions) {
throw new Error("Invalid response from Unity GraphQL API: missing getUnityReleaseMajorVersions");
}
const results = await Promise.all(data.getUnityReleaseMajorVersions
.map(async (v) => {
if (!v.version) {
throw new Error("Invalid response from Unity GraphQL API: missing version in major versions");
}
return await getUnityReleases(v.version, [unityChangeset_js_1.UnityReleaseStream.LTS], entitlements);
}));
return results.flat();
}
exports.getUnityReleasesInLTS = getUnityReleasesInLTS;
/***/ }),
@ -362892,11 +362853,10 @@ function searchModeToStreams(searchMode) {
unityChangeset_js_1.UnityReleaseStream.BETA,
];
case index_js_1.SearchMode.LTS:
case index_js_1.SearchMode.XLTS:
return [
unityChangeset_js_1.UnityReleaseStream.LTS,
];
case index_js_1.SearchMode.SUPPORTED:
case index_js_1.SearchMode.Supported:
return [
unityChangeset_js_1.UnityReleaseStream.SUPPORTED,
];

2
dist/index.js.map generated vendored

File diff suppressed because one or more lines are too long

View File

@ -4,10 +4,17 @@
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 \
echo "Requesting activation"
# Activate license
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
-logFile - \
-batchmode \
-nographics \
@ -17,8 +24,49 @@ echo "Requesting activation"
-password "$UNITY_PASSWORD" \
-projectPath "$ACTIVATE_LICENSE_PATH"
# Store the exit code from the verify command
UNITY_EXIT_CODE=$?
# 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

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

View File

@ -4,7 +4,20 @@
echo "Changing to \"$ACTIVATE_LICENSE_PATH\" directory."
pushd "$ACTIVATE_LICENSE_PATH"
/Applications/Unity/Hub/Editor/$UNITY_VERSION/Unity.app/Contents/MacOS/Unity \
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 \
@ -13,6 +26,7 @@ pushd "$ACTIVATE_LICENSE_PATH"
-password "$UNITY_PASSWORD" \
-returnlicense \
-projectPath "$ACTIVATE_LICENSE_PATH"
fi
# Return to previous working directory
popd

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?
# 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"
# Store the exit code from the verify command
UNITY_EXIT_CODE=$?
fi
else
#
# NO LICENSE ACTIVATION STRATEGY MATCHED

View File

@ -44,14 +44,13 @@
"base-64": "^1.0.0",
"commander": "^9.0.0",
"commander-ts": "^0.2.0",
"graphql": "^16.11.0",
"kubernetes-client": "^9.0.0",
"md5": "^2.3.0",
"nanoid": "^3.3.1",
"reflect-metadata": "^0.1.13",
"semver": "^7.5.2",
"ts-md5": "^1.3.1",
"unity-changeset": "^3.0.0",
"unity-changeset": "^3.1.0",
"uuid": "^9.0.0",
"yaml": "^2.2.2"
},

View File

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

View File

@ -107,6 +107,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}" \

View File

@ -4818,7 +4818,7 @@ graphql-request@6.1.0:
"@graphql-typed-document-node/core" "^3.2.0"
cross-fetch "^3.1.5"
graphql@^16.11.0:
graphql@^16.8.1:
version "16.11.0"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.11.0.tgz#96d17f66370678027fdf59b2d4c20b4efaa8a633"
integrity sha512-mS1lbMsxgQj6hge1XZ6p7GPhbrtFwUFYi3wRzXAC/FmYnyXMTvvI3td3rjmQ2u8ewXueaSvRPWaEcgVVOT9Jnw==
@ -7681,12 +7681,13 @@ undici@^5.25.4:
dependencies:
"@fastify/busboy" "^2.0.0"
unity-changeset@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/unity-changeset/-/unity-changeset-3.0.0.tgz#2a201ab76646a9c2d11aca3023108ddf2fbd4d77"
integrity sha512-NayBfB+Olb9UosItzonJKrvW7S7KQQ+sbz0dyHuAjT55jt8v7umtfk6pf/rvQziOdVUfk9ndwXdpuGJADD5+iA==
unity-changeset@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/unity-changeset/-/unity-changeset-3.1.0.tgz#7217915b995f1c441d8bdb1045c45c09f64ca94a"
integrity sha512-bFqaq3yuxeMROwqw81fi6S2f8l+4lracvnW8SkdJLGDx+QvWxQhjK4LsGYM19/plDDh7q3MwEbzQgmcqj5uxBg==
dependencies:
"@deno/shim-deno" "~0.18.0"
graphql "^16.8.1"
graphql-request "6.1.0"
universal-user-agent@^6.0.0: