Skip to content

Commit c9d3ddb

Browse files
ci(audience): move playmode-linux to self-hosted runner with real GPU (SDK-340)
- Adds 6 StandaloneLinux64 entries to set-matrix's playmode_full, each pinned to [self-hosted, Linux, X64]. - Drops the playmode_linux output and the entire playmode-linux job (docker + xvfb + watchdog + ci-provenance scaffolding goes away). - Adds three Linux steps to the playmode job: * Install Unity Hub (idempotent, apt path per https://docs.unity.com/en-us/hub/install-hub-linux) * Resolve Unity (linux-il2cpp module via Hub headless) * Run PlayMode tests (mirrors the macOS step) - Real GPU + active session means no software OpenGL, no xvfb, no minimum-screen workaround. Cells should land in 5-10 min like macOS / Windows. TODO before merge: - Capture player log (Linux) step (mirror macOS:364-380) - Surface Unity compile errors as annotations (Linux) step (mirror macOS:398-422) - Confirm Hub install path /opt/unityhub/unityhub matches the apt package layout. Adjust if .AppImage chosen instead. - One smoke run on the runner to verify Hub install + activation + editor install all complete cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 4017213 commit c9d3ddb

1 file changed

Lines changed: 118 additions & 69 deletions

File tree

.github/workflows/test-audience-sample-app.yml

Lines changed: 118 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@ jobs:
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

Comments
 (0)