Skip to content

fix: license server toolset input + runAsHostUser HOME/USER (closes #739)#838

Open
frostebite wants to merge 1 commit into
mainfrom
fix/issue-739-license-server-toolset-and-host-user
Open

fix: license server toolset input + runAsHostUser HOME/USER (closes #739)#838
frostebite wants to merge 1 commit into
mainfrom
fix/issue-739-license-server-toolset-and-host-user

Conversation

@frostebite
Copy link
Copy Markdown
Member

@frostebite frostebite commented May 7, 2026

What this is

Two opt-in, backwards-compatible fixes for the issues users hit on Linux Docker builds with a Unity floating license server, surfaced in #739.

The original symptom in #739 was -buildTarget Android being silently ignored on Linux. Investigation in the issue thread revealed it's actually a licensing problem: when the Licensing Client can't resolve the right entitlement, Unity falls through to a license that lacks the requested platform support, and the editor produces a Linux Standalone artifact without raising an error. Two distinct root causes surfaced — addressed below.

1. unityLicensingToolset input

Floating-license servers can host multiple toolsets. Without a toolset field in services-config.json, the Licensing Client relies on the server-side default toolset; if the server doesn't have one set (or the wrong one is set) it returns 404 / Found 0 entitlement groups, and Unity falls through.

This PR adds an optional unityLicensingToolset input, threaded through InputBuildParametersSetupShared, where it's injected into the rendered services-config.json only when set. The Licensing Client then requests entitlements from the named toolset.

2. runAsHostUser license-client malfunction

When runAsHostUser: true, entrypoint.sh invoked su $USERNAME -c "..." without explicit HOME/USER. The host user inherited root's environment (HOME=/root, USER unset), so the Unity Licensing Client — which writes to ~/.config/unity3d — could not resolve a writable home directory. This matches OP's observation in the issue thread that dropping runAsHostUser: true resolved their licensing failure even after they reverted the toolset workaround.

Fix: set HOME/USER/LOGNAME explicitly on the inner shell command, while preserving the rest of root's env via su -p.

Are both opt-in? Are both backwards compatible?

Fix Opt-in Backwards compatible Notes
unityLicensingToolset input Yes — default empty, field omitted from services-config.json when unset Yes — byte-for-byte identical config when unset None
runAsHostUser HOME/USER fix Yes — entirely inside the if RUN_AS_HOST_USER == "true" branch Mostly. Only changes env for users currently using runAsHostUser: true. Anyone whose pipeline silently relies on the broken HOME=/root would see paths shift to /home/$USER. Almost certainly nobody does — but it is technically a behavior change for that opt-in branch.

Users not using a license server and not using runAsHostUser see zero change.

What I'm uncertain about

  • No reproduction. I can't reproduce either issue locally — none of the Linux license-server flows are exercisable from a maintainer machine without a live floating-license server. The fixes are derived from the logs and behavior reported in BuildTarget is ignored on linux #739.
  • The runAsHostUser fix is plausible but not confirmed. OP (sebas77) hedged on the cause ("almost definitively") and moved off game-ci before verifying. The /home/UNKNOWN log line plus inspection of entrypoint.sh makes this the strongest hypothesis — but it would be ideal if a reviewer with access to a license server validated before merge. Happy to split it into a follow-up PR if you'd prefer to land the toolset fix alone first.

What I considered and dropped

  • A general "merge arbitrary JSON into services-config.json" input. Larger API surface, conflicts with existing unityLicensingServer, footgun in YAML. Single named input is narrower and safer; if more fields become useful, add them one at a time.
  • Fail-fast on Licensing Client errors in the Unity log. The pattern that fired here (com.unity.editor.headless not found) isn't always fatal — Unity probes entitlements during startup and may succeed on a later one — and log formats drift across Unity 5/2019/2022/6000/7000. False-firing would turn currently-green builds red. Worth its own investigation.

Companion change

Mirrored across the orchestrator CLI surface: game-ci/orchestrator#TBD (linked once opened). The entrypoint.sh HOME/USER fix flows through automatically because orchestrator copies it from unity-builder/dist/platforms/ubuntu/.

Test plan

  • yarn typecheck clean
  • yarn lint clean (only pre-existing warnings)
  • yarn test:ci — 347 passed, 2 skipped (no failures)
  • yarn build regenerates dist/index.js containing the new field
  • Verify on a real floating-license server that unityLicensingToolset is honored (cannot do locally — need a reviewer with access)
  • Verify on a real Linux runner with runAsHostUser: true that licensing succeeds (cannot do locally — need a reviewer with access)

Closes #739.

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features

    • Added optional unityLicensingToolset input for configuring the named toolset identifier in Unity floating-license server scenarios.
  • Documentation

    • Updated StandaloneLinux64 input description to explicitly note restoration of extensionless behavior from v4.

… runAsHostUser

Addresses #739. Two opt-in, backwards-compatible changes for users on Linux
Docker builds with a Unity floating-license server:

1. Floating license servers that host multiple toolsets had no way to tell
   the Licensing Client which toolset to request — Unity could fall through
   to an entitlement that lacks build-target support (e.g. Android), then
   silently produce a Linux Standalone artifact. The action now accepts an
   optional unityLicensingToolset input that is written into
   services-config.json. When unset, the rendered config is byte-for-byte
   identical to before.

2. With runAsHostUser: true, su was invoked without explicit HOME/USER, so
   the host user inherited root's environment (HOME=/root, USER unset). The
   Unity Licensing Client, which writes to ~/.config/unity3d, could not
   resolve a writable home directory, leading to intermittent license
   activation failures. Set HOME/USER/LOGNAME explicitly before sourcing
   build steps. Change lives entirely inside the existing runAsHostUser
   branch.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a new unityLicensingToolset input parameter for Unity floating-license server configurations. The parameter flows from the action input declaration through the Input getter, BuildParameters model, and is written to the services configuration JSON during platform setup, with test coverage verifying the data propagation.

Changes

Unity Licensing Toolset Feature

Layer / File(s) Summary
Input Declaration
action.yml
New optional input unityLicensingToolset is declared for specifying a named toolset identifier in floating-license server scenarios. Description for StandaloneLinux64 extension-removal input is clarified.
Input Getter
src/model/input.ts
Static getter Input.unityLicensingToolset reads the workflow input and defaults to empty string when unset.
Build Parameters Model & Factory
src/model/build-parameters.ts
BuildParameters class adds unityLicensingToolset property; factory method populates it from Input.unityLicensingToolset.
Platform Setup Integration
src/model/platform-setup.ts
PlatformSetup.SetupShared conditionally parses the services config template JSON, sets the toolset field from build parameters, and writes the updated configuration.
Test Coverage
src/model/build-parameters.test.ts
Test suite verifies that BuildParameters.create() correctly propagates the unityLicensingToolset input value into the returned parameters object.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

  • game-ci/unity-builder#685: Both PRs add new Input getters and corresponding BuildParameters properties with propagation into platform setup code following the same architectural pattern.

Suggested labels

run-integration

Suggested reviewers

  • webbertakken
  • GabLeRoux

Poem

🐰 A toolset arrives on the licensing shore,
Through inputs and getters, it flows to the core,
Services config now knows its true name,
Floating-license servers rejoice all the same! 🎯

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the main changes: adding a license server toolset input and fixing runAsHostUser HOME/USER environment variables, and references the closed issue.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description is comprehensive, detailed, and addresses all key template sections including changes, rationale, and test results.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-739-license-server-toolset-and-host-user

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/model/platform-setup.ts (1)

36-40: ⚡ Quick win

Add contextual error handling around services config parsing.

Line 37 can throw a raw JSON.parse error with limited context. Wrapping this block to emit a targeted error (template path + invalid JSON/toolset context) will make failures much easier to diagnose in CI logs.

Proposed patch
     if (buildParameters.unityLicensingToolset) {
-      const parsed = JSON.parse(servicesConfig);
-      parsed.toolset = buildParameters.unityLicensingToolset;
-      servicesConfig = JSON.stringify(parsed, undefined, 2);
+      try {
+        const parsed = JSON.parse(servicesConfig);
+        parsed.toolset = buildParameters.unityLicensingToolset;
+        servicesConfig = JSON.stringify(parsed, undefined, 2);
+      } catch (error) {
+        core.error(
+          `Failed to render services config from template ${servicesConfigPathTemplate}: ${
+            error instanceof Error ? error.message : String(error)
+          }`,
+        );
+        throw error;
+      }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/model/platform-setup.ts` around lines 36 - 40, The JSON.parse call that
modifies servicesConfig can throw with no contextual info; wrap the block that
reads buildParameters.unityLicensingToolset and calls JSON.parse in a try/catch
inside the function or module where servicesConfig is handled (referencing
buildParameters.unityLicensingToolset, servicesConfig, and parsed), and in the
catch emit/throw a new error or processLogger.error that includes the template
path/identifier and the offending unityLicensingToolset value (e.g., "Invalid
servicesConfig JSON for template X with toolset Y: <original error message>") so
CI logs show clear context for the failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/model/platform-setup.ts`:
- Around line 36-40: The JSON.parse call that modifies servicesConfig can throw
with no contextual info; wrap the block that reads
buildParameters.unityLicensingToolset and calls JSON.parse in a try/catch inside
the function or module where servicesConfig is handled (referencing
buildParameters.unityLicensingToolset, servicesConfig, and parsed), and in the
catch emit/throw a new error or processLogger.error that includes the template
path/identifier and the offending unityLicensingToolset value (e.g., "Invalid
servicesConfig JSON for template X with toolset Y: <original error message>") so
CI logs show clear context for the failure.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 39c3fb1f-9352-43e5-8034-ba0b603658a1

📥 Commits

Reviewing files that changed from the base of the PR and between d829bfc and dd95ad9.

⛔ Files ignored due to path filters (4)
  • dist/index.js is excluded by !**/dist/**
  • dist/index.js.map is excluded by !**/dist/**, !**/*.map
  • dist/licenses.txt is excluded by !**/dist/**
  • dist/platforms/ubuntu/entrypoint.sh is excluded by !**/dist/**
📒 Files selected for processing (5)
  • action.yml
  • src/model/build-parameters.test.ts
  • src/model/build-parameters.ts
  • src/model/input.ts
  • src/model/platform-setup.ts

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 7, 2026

Cat Gif

@codecov
Copy link
Copy Markdown

codecov Bot commented May 7, 2026

Codecov Report

❌ Patch coverage is 20.00000% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 45.16%. Comparing base (d829bfc) to head (dd95ad9).

Files with missing lines Patch % Lines
src/model/platform-setup.ts 0.00% 4 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #838      +/-   ##
==========================================
- Coverage   45.34%   45.16%   -0.19%     
==========================================
  Files          36       36              
  Lines         688      693       +5     
  Branches      199      201       +2     
==========================================
+ Hits          312      313       +1     
- Misses        337      341       +4     
  Partials       39       39              
Files with missing lines Coverage Δ
src/model/build-parameters.ts 77.27% <ø> (ø)
src/model/input.ts 74.19% <100.00%> (+0.28%) ⬆️
src/model/platform-setup.ts 0.00% <0.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

frostebite added a commit to game-ci/orchestrator that referenced this pull request May 7, 2026
Remove Unity-specific licensing fields from orchestrator's typed surfaces
(BuildParameters, OrchestratorConfig, CLI input mapper, build/orchestrate
yargs commands, cli-plugin adapter):

- unitySerial
- unityLicensingServer
- skipActivation

These were vestigial — BuildParameters.create() hardcoded them to empty
strings, no orchestrator service read them for logic. They existed only
to mirror unity-builder's BuildParameters shape, which is exactly the
boundary violation: orchestrator's domain is dispatch + providers, not
engine-specific licensing.

The plugin contract (coreParams: Record<string, any>) is already opaque
and engine-agnostic. The host (unity-builder today, @game-ci/cli in the
future) passes its full BuildParameters object through; orchestrator
reads only generic build context (targetPlatform, projectPath, etc.) and
its plugin-owned config from env/inputs. Engine-specific keys ride in
the dict untouched.

No companion change needed in unity-builder: it continues to construct
its own BuildParameters with whatever fields it wants and pass it as
coreParams. The dict's index signature accepts everything.

Documentation:
- Tracking issue #25 lays out the full architecture, today/future state,
  and migration runway.
- Code comments in plugin-lifecycle.ts, interfaces.ts, build-parameters.ts,
  build.ts, orchestrate.ts, input-mapper.ts, build-parameters-adapter.ts
  reference the issue and explain the boundary intent so the next
  contributor understands why these fields are not (and must not be)
  declared here.

Out of scope (separate cleanups, noted in tracking issue):
- cacheUnityInstallationOnMac / unityHubVersionOnMac in input-mapper
  (Mac runtime install caching, more entangled)
- task-parameter-serializer.ts UNITY_SERIAL well-known-secret list
  (well-known-secrets generalization)
- activate CLI command (Unity-specific legacy helper, leave as-is)

Refs game-ci/unity-builder#739 and game-ci/unity-builder#838 (the user-
facing fix that motivated this boundary cleanup).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BuildTarget is ignored on linux

1 participant