3333 runs-on : ubuntu-latest
3434 outputs :
3535 playmode : ${{ steps.set.outputs.playmode }}
36- playmode_linux : ${{ steps.set.outputs.playmode_linux }}
3736 mobile : ${{ steps.set.outputs.mobile }}
3837 steps :
3938 - id : set
@@ -51,15 +50,13 @@ jobs:
5150 {"target":"StandaloneWindows64","backend":"IL2CPP","unity":"2022.3.62f2","changeset":"7670c08855a9","runner":["self-hosted","Windows","X64"]},
5251 {"target":"StandaloneWindows64","backend":"Mono2x","unity":"2022.3.62f2","changeset":"7670c08855a9","runner":["self-hosted","Windows","X64"]},
5352 {"target":"StandaloneOSX","backend":"IL2CPP","unity":"2022.3.62f2","changeset":"7670c08855a9","runner":["self-hosted","macOS","ARM64"]},
54- {"target":"StandaloneOSX","backend":"Mono2x","unity":"2022.3.62f2","changeset":"7670c08855a9","runner":["self-hosted","macOS","ARM64"]}
55- ]'
56- playmode_linux_full='[
57- {"target":"StandaloneLinux64","backend":"IL2CPP","unity":"2021.3.45f2"},
58- {"target":"StandaloneLinux64","backend":"Mono2x","unity":"2021.3.45f2"},
59- {"target":"StandaloneLinux64","backend":"IL2CPP","unity":"6000.4.0f1"},
60- {"target":"StandaloneLinux64","backend":"Mono2x","unity":"6000.4.0f1"},
61- {"target":"StandaloneLinux64","backend":"IL2CPP","unity":"2022.3.62f2"},
62- {"target":"StandaloneLinux64","backend":"Mono2x","unity":"2022.3.62f2"}
53+ {"target":"StandaloneOSX","backend":"Mono2x","unity":"2022.3.62f2","changeset":"7670c08855a9","runner":["self-hosted","macOS","ARM64"]},
54+ {"target":"StandaloneLinux64","backend":"IL2CPP","unity":"2021.3.45f2","changeset":"88f88f591b2e","runner":["self-hosted","Linux","X64"]},
55+ {"target":"StandaloneLinux64","backend":"Mono2x","unity":"2021.3.45f2","changeset":"88f88f591b2e","runner":["self-hosted","Linux","X64"]},
56+ {"target":"StandaloneLinux64","backend":"IL2CPP","unity":"6000.4.0f1","changeset":"8cf496087c8f","runner":["self-hosted","Linux","X64"]},
57+ {"target":"StandaloneLinux64","backend":"Mono2x","unity":"6000.4.0f1","changeset":"8cf496087c8f","runner":["self-hosted","Linux","X64"]},
58+ {"target":"StandaloneLinux64","backend":"IL2CPP","unity":"2022.3.62f2","changeset":"7670c08855a9","runner":["self-hosted","Linux","X64"]},
59+ {"target":"StandaloneLinux64","backend":"Mono2x","unity":"2022.3.62f2","changeset":"7670c08855a9","runner":["self-hosted","Linux","X64"]}
6360 ]'
6461 mobile_full='[
6562 {"target":"Android","unity":"2021.3.45f2","method":"AndroidBuilder.Build"},
@@ -72,16 +69,13 @@ jobs:
7269 if [[ "${{ github.event_name }}" == "pull_request" ]]; then
7370 filter='[.[] | select(.unity != "2022.3.62f2")]'
7471 playmode=$(jq -c "$filter" <<<"$playmode_full")
75- playmode_linux=$(jq -c "$filter" <<<"$playmode_linux_full")
7672 mobile=$(jq -c "$filter" <<<"$mobile_full")
7773 else
7874 playmode=$(jq -c '.' <<<"$playmode_full")
79- playmode_linux=$(jq -c '.' <<<"$playmode_linux_full")
8075 mobile=$(jq -c '.' <<<"$mobile_full")
8176 fi
8277 {
8378 echo "playmode=$playmode"
84- echo "playmode_linux=$playmode_linux"
8579 echo "mobile=$mobile"
8680 } >> "$GITHUB_OUTPUT"
8781
@@ -312,6 +306,66 @@ jobs:
312306 if ('${{ matrix.backend }}' -eq 'IL2CPP') { Write-Host "Found IL2CPP: $il2cpp" }
313307 "UNITY_PATH=$editor" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
314308
309+ - name : Install Unity Hub (Linux, idempotent)
310+ if : runner.os == 'Linux'
311+ shell : bash
312+ run : |
313+ # Install per https://docs.unity.com/en-us/hub/install-hub-linux.
314+ # Idempotent: skip if already present so subsequent runs cost
315+ # nothing. Requires passwordless sudo for the runner user.
316+ set -uo pipefail
317+ if [ -x /opt/unityhub/unityhub ]; then
318+ echo "Unity Hub already installed."
319+ exit 0
320+ fi
321+ sudo install -d -m 0755 /etc/apt/keyrings
322+ wget -qO- https://hub.unity3d.com/linux/keys/public \
323+ | sudo gpg --dearmor --yes -o /etc/apt/keyrings/Unity_Technologies_ApS.gpg
324+ echo "deb [signed-by=/etc/apt/keyrings/Unity_Technologies_ApS.gpg] https://hub.unity3d.com/linux/repos/deb stable main" \
325+ | sudo tee /etc/apt/sources.list.d/unityhub.list >/dev/null
326+ sudo apt-get update -qq
327+ sudo apt-get install -y --no-install-recommends unityhub
328+
329+ - name : Resolve Unity ${{ matrix.unity }} (Linux)
330+ if : runner.os == 'Linux'
331+ shell : bash
332+ env :
333+ UNITY_VER : ${{ matrix.unity }}
334+ UNITY_CS : ${{ matrix.changeset }}
335+ run : |
336+ set -uo pipefail
337+ HUB=/opt/unityhub/unityhub
338+
339+ echo "::group::install editor"
340+ "$HUB" --headless install \
341+ --version "$UNITY_VER" --changeset "$UNITY_CS" --architecture x86_64 \
342+ || echo "(install non-zero — OK if 'Editor already installed in this location')"
343+ echo "::endgroup::"
344+
345+ if [ "${{ matrix.backend }}" = "IL2CPP" ]; then
346+ echo "::group::install linux-il2cpp module"
347+ "$HUB" --headless install-modules \
348+ --version "$UNITY_VER" --changeset "$UNITY_CS" --architecture x86_64 \
349+ --module linux-il2cpp \
350+ || echo "(install-modules non-zero — OK if 'No modules found to install')"
351+ echo "::endgroup::"
352+ fi
353+
354+ EDITOR=""
355+ for cand in \
356+ "$HOME/Unity/Hub/Editor/$UNITY_VER/Editor/Unity" \
357+ "/opt/unity/editors/$UNITY_VER/Editor/Unity"; do
358+ if [ -x "$cand" ]; then EDITOR="$cand"; break; fi
359+ done
360+
361+ if [ -z "$EDITOR" ]; then
362+ echo "::error::Unity $UNITY_VER not found after install"
363+ "$HUB" --headless editors --installed 2>&1 || true
364+ exit 1
365+ fi
366+ echo "Found Unity: $EDITOR"
367+ echo "UNITY_PATH=$EDITOR" >> "$GITHUB_ENV"
368+
315369 - name : Run PlayMode tests (macOS)
316370 if : runner.os == 'macOS'
317371 shell : bash
@@ -335,6 +389,27 @@ jobs:
335389 -testResults "$(pwd)/artifacts/test-results.xml" \
336390 -logFile - 2>&1 | tee "$(pwd)/artifacts/unity.log"
337391
392+ - name : Run PlayMode tests (Linux)
393+ if : runner.os == 'Linux'
394+ shell : bash
395+ env :
396+ AUDIENCE_TEST_PUBLISHABLE_KEY : ${{ secrets.AUDIENCE_TEST_PUBLISHABLE_KEY }}
397+ AUDIENCE_SCRIPTING_BACKEND : ${{ matrix.backend }}
398+ run : |
399+ set -euo pipefail
400+ mkdir -p artifacts
401+ # Same shape as the macOS step. -nographics is safe because the
402+ # runner has a real GPU + active session; Unity opens a hardware
403+ # GL context via that session if needed (it does not for this
404+ # suite).
405+ "$UNITY_PATH" \
406+ -batchmode -nographics \
407+ -projectPath examples/audience \
408+ -runTests \
409+ -testPlatform ${{ matrix.target }} \
410+ -testResults "$(pwd)/artifacts/test-results.xml" \
411+ -logFile - 2>&1 | tee "$(pwd)/artifacts/unity.log"
412+
338413 - name : Run PlayMode tests (Windows)
339414 if : runner.os == 'Windows'
340415 shell : pwsh
@@ -385,6 +460,20 @@ jobs:
385460 done
386461 fi
387462
463+ - name : Capture player log (Linux)
464+ if : always() && runner.os == 'Linux'
465+ shell : bash
466+ run : |
467+ # Linux convention: ~/.config/unity3d/<Company>/<Product>/Player.log.
468+ # Mirror macOS step.
469+ mkdir -p artifacts
470+ src="$HOME/.config/unity3d"
471+ if [ -d "$src" ]; then
472+ find "$src" -name "Player.log" 2>/dev/null | while IFS= read -r f; do
473+ cp "$f" "artifacts/Player-$(basename "$(dirname "$f")").log" 2>/dev/null || true
474+ done
475+ fi
476+
388477 - name : Capture player log (Windows)
389478 if : always() && runner.os == 'Windows'
390479 shell : pwsh
@@ -426,6 +515,22 @@ jobs:
426515 echo "::error::$sanitized"
427516 done || true
428517
518+ - name : Surface Unity compile errors as annotations (Linux)
519+ if : always() && runner.os == 'Linux'
520+ shell : bash
521+ run : |
522+ set -uo pipefail
523+ LOG_FILE="artifacts/unity.log"
524+ if [ ! -f "$LOG_FILE" ]; then
525+ echo "::notice::No Unity log file at $LOG_FILE."
526+ exit 0
527+ fi
528+ grep -E '(error CS[0-9]+:|Compilation failed:)' "$LOG_FILE" | sort -u | while IFS= read -r line; do
529+ trimmed="${line#"${line%%[![:space:]]*}"}"
530+ sanitized="${trimmed//::/%3A%3A}"
531+ echo "::error::$sanitized"
532+ done || true
533+
429534 - name : Surface Unity compile errors as annotations (Windows)
430535 if : always() && runner.os == 'Windows'
431536 shell : pwsh
@@ -465,62 +570,6 @@ jobs:
465570 artifacts/Player-*.log
466571 examples/audience/Logs/**
467572
468- playmode-linux :
469- needs : set-matrix
470- if : |
471- (github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false)
472- || github.event_name == 'schedule'
473- || github.event_name == 'workflow_dispatch'
474- name : ${{ matrix.target }} / ${{ matrix.backend }} / Unity ${{ matrix.unity }}
475- runs-on : ubuntu-latest-8-cores
476- strategy :
477- fail-fast : false
478- matrix :
479- include : ${{ fromJSON(needs.set-matrix.outputs.playmode_linux) }}
480-
481- steps :
482- - uses : actions/checkout@v4
483- with :
484- lfs : true
485-
486- - uses : actions/cache@v4
487- with :
488- path : examples/audience/Library
489- key : Library-${{ matrix.backend }}-${{ matrix.target }}-${{ matrix.unity }}-${{ hashFiles('examples/audience/Assets/**', 'examples/audience/Packages/**', 'examples/audience/ProjectSettings/**', 'src/Packages/Audience/**') }}
490- restore-keys : |
491- Library-${{ matrix.backend }}-${{ matrix.target }}-${{ matrix.unity }}-
492- Library-${{ matrix.backend }}-${{ matrix.target }}-
493-
494- - uses : game-ci/unity-test-runner@v4
495- id : playmode
496- env :
497- UNITY_EMAIL : ${{ secrets.UNITY_EMAIL }}
498- UNITY_PASSWORD : ${{ secrets.UNITY_PASSWORD }}
499- UNITY_SERIAL : ${{ secrets.UNITY_SERIAL }}
500- AUDIENCE_TEST_PUBLISHABLE_KEY : ${{ secrets.AUDIENCE_TEST_PUBLISHABLE_KEY }}
501- AUDIENCE_SCRIPTING_BACKEND : ${{ matrix.backend }}
502- with :
503- unityVersion : ${{ matrix.unity }}
504- targetPlatform : ${{ matrix.target }}
505- projectPath : examples/audience
506- testMode : playmode
507- githubToken : ${{ secrets.GITHUB_TOKEN }}
508-
509- - name : Publish test report
510- uses : dorny/test-reporter@v3
511- if : always()
512- with :
513- name : PlayMode (${{ matrix.backend }} / ${{ matrix.target }})
514- path : ${{ steps.playmode.outputs.artifactsPath }}/playmode-results.xml
515- reporter : dotnet-nunit
516- fail-on-error : true
517-
518- - uses : actions/upload-artifact@v4
519- if : always()
520- with :
521- name : playmode-${{ matrix.backend }}-${{ matrix.target }}-${{ matrix.unity }}
522- path : ${{ steps.playmode.outputs.artifactsPath }}
523-
524573 # Mobile IL2CPP build validation — runs on GitHub-hosted Ubuntu via GameCI Docker
525574 # containers so self-hosted macOS/Windows machines are not occupied.
526575 # Scope: IL2CPP compile pipeline only. Runtime tests require a real device and
0 commit comments