Skip to content

DO NOT MERGE: fix: add mechanism to force stop stuck emotes #28417

DO NOT MERGE: fix: add mechanism to force stop stuck emotes

DO NOT MERGE: fix: add mechanism to force stop stuck emotes #28417

Workflow file for this run

name: Unity Test
on:
workflow_dispatch:
pull_request:
paths:
- 'Explorer/**'
types:
- opened
- reopened
- synchronize
- ready_for_review
- labeled
merge_group: {}
push: { branches: [dev] }
permissions:
contents: read
packages: write
checks: write
actions: read
env:
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
UNITY_LICENSE: ${{ secrets.UNITY_PERSONAL_LICENSE }}
UNITY_IMAGE_FLAVOR: linux-il2cpp
UNITY_IMAGE_VERSION: 3.2.0
GHCR_IMAGE_NAME: unityci-editor
INSPECT_REPORT_JSON_FILE_NAME: InspectCodeReport.json
WARNINGS_BASELINE_S3_KEY: '@dcl/unity-explorer/ci/warnings-baseline.json'
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
changes:
if: (github.event_name == 'push' && (github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev')) || (github.event.pull_request.draft == false) || (github.event.label.name == 'force-build') || (github.event.label.name == 'clean-build') || (github.event.label.name == 'force-lint')
name: Detect C# changes
runs-on: ubuntu-latest
outputs:
cs: ${{ steps.detect.outputs.cs }}
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
fetch-depth: 0
- name: Detect changed C# files
id: detect
env:
EVENT: ${{ github.event_name }}
BASE_REF: ${{ github.event.pull_request.base.ref }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
BEFORE_SHA: ${{ github.event.before }}
run: |
set -euo pipefail
if [ "$EVENT" = "pull_request" ]; then
base=$(git merge-base "origin/$BASE_REF" "$HEAD_SHA" 2>/dev/null || true)
if [ -z "$base" ]; then
echo "merge-base with origin/$BASE_REF unavailable - linting to be safe."
echo "cs=true" >> "$GITHUB_OUTPUT"; exit 0
fi
range="$base $HEAD_SHA"
elif [ "$EVENT" = "push" ] && [ -n "$BEFORE_SHA" ] && [ "$BEFORE_SHA" != "0000000000000000000000000000000000000000" ] && git cat-file -e "${BEFORE_SHA}^{commit}" 2>/dev/null; then
range="$BEFORE_SHA ${{ github.sha }}"
else
echo "Cannot determine a diff base for event '$EVENT' - linting to be safe."
echo "cs=true" >> "$GITHUB_OUTPUT"; exit 0
fi
if git diff --name-only $range -- '*.cs' | grep -q .; then
echo "C# files changed - lint will run."
echo "cs=true" >> "$GITHUB_OUTPUT"
else
echo "No C# files changed - skipping lint."
echo "cs=false" >> "$GITHUB_OUTPUT"
fi
lint:
needs: changes
if: needs.changes.outputs.cs == 'true' || github.event.label.name == 'force-lint'
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout CI actions
uses: actions/checkout@v6
with:
sparse-checkout: .github/actions
sparse-checkout-cone-mode: false
path: ci-actions
- name: Setup Docker disk
uses: ./ci-actions/.github/actions/setup-docker-disk
with:
keep-dotnet: 'true'
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
lfs: true
submodules: recursive
- name: Create LFS file list
run: git lfs ls-files -l | awk '{print $1}' | sort > .lfs-assets-id
- name: Restore LFS cache
uses: actions/cache@v4
id: lfs-cache-lint
with:
path: .git/lfs
key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}
- name: Git LFS Pull
run: |
git lfs pull
git add .
git reset --hard
- uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Add GitHub to the SSH known hosts file
run: |
mkdir -p -m 0700 /home/runner/.ssh
curl --silent https://api.github.com/meta | jq --raw-output '"github.com "+.ssh_keys[]' >> /home/runner/.ssh/known_hosts
chmod 600 /home/runner/.ssh/known_hosts
- name: Prepare SSH for Unity
run: |
rm -rf /tmp/ssh-unity
mkdir -p -m 0700 /tmp/ssh-unity
cp /home/runner/.ssh/known_hosts /tmp/ssh-unity/known_hosts
cp /home/runner/.ssh/key-* /tmp/ssh-unity/ 2>/dev/null || true
chmod 600 /tmp/ssh-unity/*
- name: Prepare Unity writable dirs
run: |
mkdir -p /tmp/unity-home/.local/share/unity3d
mkdir -p /tmp/unity-home/.cache/unity3d
mkdir -p /tmp/unity-home/.config/unity3d
chmod -R 777 /tmp/unity-home
- name: Read Unity version from ProjectVersion.txt
id: unity-version-lint
run: |
verFile="Explorer/ProjectSettings/ProjectVersion.txt"
ver=$(grep -o 'm_EditorVersion: [0-9a-f.]*' "$verFile" | cut -d' ' -f2)
echo "version=$ver" >> $GITHUB_OUTPUT
- name: Set image names
id: img-lint
run: |
version="${{ steps.unity-version-lint.outputs.version }}"
flavor="${{ env.UNITY_IMAGE_FLAVOR }}"
imgVer="${{ env.UNITY_IMAGE_VERSION }}"
tag="ubuntu-$version-$flavor-$imgVer"
owner=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
upstream="unityci/editor:$tag"
ghcr="ghcr.io/$owner/${{ env.GHCR_IMAGE_NAME }}:$tag"
echo "tag=$tag" >> $GITHUB_OUTPUT
echo "upstream=$upstream" >> $GITHUB_OUTPUT
echo "ghcr=$ghcr" >> $GITHUB_OUTPUT
- name: Log in to GitHub Container Registry
run: |
for i in 1 2 3; do
echo "${{ github.token }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin && exit 0
echo "GHCR login failed, retry $i/3"
sleep $((i * 10))
done
exit 1
- name: Restore Unity image from GHCR or seed from Docker Hub
run: |
set -o pipefail
ghcr="${{ steps.img-lint.outputs.ghcr }}"
upstream="${{ steps.img-lint.outputs.upstream }}"
if docker pull "$ghcr" 2>&1 | tee /tmp/ghcr-pull.log; then
exit 0
fi
if grep -qiE 'manifest unknown|not found' /tmp/ghcr-pull.log; then
echo "GHCR image missing, trying upstream..."
docker pull "$upstream"
docker tag "$upstream" "$ghcr"
docker push "$ghcr"
else
echo "GHCR pull failed, but not because the image is missing."
cat /tmp/ghcr-pull.log
exit 1
fi
# - name: Activate Unity license
# run: |
# docker run --rm \
# -v /tmp/unity-home/.local/share/unity3d:/root/.local/share/unity3d \
# -v /tmp/unity-home/.cache/unity3d:/root/.cache/unity3d \
# -v /tmp/unity-home/.config/unity3d:/root/.config/unity3d \
# -e UNITY_EMAIL="$UNITY_EMAIL" \
# -e UNITY_PASSWORD="$UNITY_PASSWORD" \
# -e UNITY_LICENSE="$UNITY_LICENSE" \
# ${{ steps.img-lint.outputs.ghcr }} \
# /bin/bash -lc 'source /opt/unity/activate.sh 2>/dev/null || true; unity-editor -batchmode -nographics -quit -logFile /dev/stdout'
# # C# Linting using JetBrains ReSharper CLI
# - name: Generate solution inside Unity Docker container
# run: |
# docker run --rm \
# -v "${{ github.workspace }}:/workspace" \
# -v $SSH_AUTH_SOCK:$SSH_AUTH_SOCK \
# -v /tmp/ssh-unity:/root/.ssh:ro \
# -v /tmp/unity-home/.local/share/unity3d:/root/.local/share/unity3d \
# -v /tmp/unity-home/.cache/unity3d:/root/.cache/unity3d \
# -v /tmp/unity-home/.config/unity3d:/root/.config/unity3d \
# -e SSH_AUTH_SOCK=$SSH_AUTH_SOCK \
# -e UNITY_EMAIL="$UNITY_EMAIL" \
# -e UNITY_PASSWORD="$UNITY_PASSWORD" \
# -e UNITY_LICENSE="$UNITY_LICENSE" \
# -w /workspace \
# ${{ steps.img-lint.outputs.ghcr }} \
# unity-editor \
# -batchmode \
# -nographics \
# -projectPath /workspace/Explorer \
# -executeMethod UnityEditor.SyncVS.SyncSolution \
# -quit \
# -logFile -
- name: Rewrite GitHub SSH URLs to HTTPS
run: |
git config --global url."https://x-access-token:${{ secrets.REPOS_READ_ONLY_TOKEN }}@github.com/".insteadOf "git@github.com:"
git config --global url."https://x-access-token:${{ secrets.REPOS_READ_ONLY_TOKEN }}@github.com/".insteadOf "ssh://git@github.com/"
- name: Generate solution with GameCI Builder
uses: game-ci/unity-builder@v4
env:
UNITY_LICENSE: ${{ secrets.UNITY_PERSONAL_LICENSE }}
UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
GIT_PRIVATE_TOKEN: ${{ secrets.REPOS_READ_ONLY_TOKEN }}
# GIT_CONFIG_EXTENSIONS: true
with:
projectPath: Explorer
customImage: ${{ steps.img-lint.outputs.ghcr }}
targetPlatform: StandaloneLinux64
buildMethod: Packages.Rider.Editor.RiderScriptEditor.SyncSolution
- name: Download ReSharper Command Line Tools
run: bash scripts/lint/download-resharper.sh rsharp
- name: Run ReSharper InspectCode (static analysis)
run: bash scripts/lint/run-inspectcode.sh Explorer/Explorer.sln "$INSPECT_REPORT_JSON_FILE_NAME"
# Filter false positives ('.CSharpErrors'/'.CppCompilerErrors' from '--no-build') and count.
- name: Count warnings
id: warnings
uses: ./.github/actions/count-warnings
with:
report: ${{ env.INSPECT_REPORT_JSON_FILE_NAME }}
filtered_output: filtered.json
- name: Read warnings baseline from S3
id: baseline
env:
AWS_MAX_ATTEMPTS: 3
AWS_RETRY_MODE: standard
AWS_ACCESS_KEY_ID: ${{ secrets.EXPLORER_TEAM_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.EXPLORER_TEAM_AWS_SECRET_ACCESS_KEY }}
EXPLORER_TEAM_S3_BUCKET: ${{ secrets.EXPLORER_TEAM_S3_BUCKET }}
run: |
# Read directly from s3 to avoid cache stales
baseline=$(aws s3 cp "s3://$EXPLORER_TEAM_S3_BUCKET/${{ env.WARNINGS_BASELINE_S3_KEY }}" - 2>/dev/null | jq -r '.count' 2>/dev/null || echo "")
echo "baseline=$baseline" >> "$GITHUB_OUTPUT"
echo "Baseline warnings: ${baseline:-<none>}"
# Hard gate: a PR may only merge if it strictly reduces the warning count.
# No baseline yet -> pass (the first dev push seeds it). Add the 'no-warning-ratchet'
# label to bypass on a PR that legitimately cannot touch warnings.
- name: Enforce warning reduction (PR)
if: github.event_name == 'pull_request'
env:
COUNT: ${{ steps.warnings.outputs.count }}
BASELINE: ${{ steps.baseline.outputs.baseline }}
BYPASS: ${{ contains(github.event.pull_request.labels.*.name, 'no-warning-ratchet') }}
run: |
if [ -z "$BASELINE" ]; then
echo "No baseline established yet - passing. The first push to dev will seed it."
exit 0
fi
if [ "$COUNT" -lt "$BASELINE" ]; then
echo "Warnings reduced: $COUNT < $BASELINE"
exit 0
fi
if [ "$BYPASS" = "true" ]; then
echo "::warning::Warnings not reduced ($COUNT >= $BASELINE) but 'no-warning-ratchet' label present - bypassing."
exit 0
fi
echo "::error::Compilation warnings ($COUNT) must be strictly LESS than the baseline ($BASELINE). Remove at least one warning to merge (or apply the 'no-warning-ratchet' label)."
exit 1
# Lower the baseline on merge to dev. Only ever decreases it.
- name: Update warnings baseline on S3 (dev)
if: github.event_name == 'push' && github.ref == 'refs/heads/dev'
env:
COUNT: ${{ steps.warnings.outputs.count }}
AWS_MAX_ATTEMPTS: 3
AWS_RETRY_MODE: standard
AWS_ACCESS_KEY_ID: ${{ secrets.EXPLORER_TEAM_AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.EXPLORER_TEAM_AWS_SECRET_ACCESS_KEY }}
EXPLORER_TEAM_S3_BUCKET: ${{ secrets.EXPLORER_TEAM_S3_BUCKET }}
run: |
set -euo pipefail
# Read directly from s3 to avoid cache stales
cur=$(aws s3 cp "s3://$EXPLORER_TEAM_S3_BUCKET/${{ env.WARNINGS_BASELINE_S3_KEY }}" - 2>/dev/null | jq -r '.count' 2>/dev/null || echo "")
if [ -n "$cur" ] && [ "$COUNT" -ge "$cur" ]; then
echo "No update: current count ($COUNT) is not less than existing baseline ($cur)."
exit 0
fi
echo "Updating baseline: ${cur:-<none>} => $COUNT"
jq -n --argjson c "$COUNT" \
'{count: $c, comment: "read and modified by CI/CD of explorer repo"}' > baseline.json
aws s3 cp baseline.json "s3://$EXPLORER_TEAM_S3_BUCKET/${{ env.WARNINGS_BASELINE_S3_KEY }}" \
--content-type application/json --cache-control no-cache
# Hand the numbers to the trusted workflow_run companion that posts the PR comment.
# Runs even when the gate above failed (!cancelled), so a failing run still refreshes the comment
# instead of leaving a stale message.
- name: Write warning result for PR comment
if: ${{ !cancelled() && github.event_name == 'pull_request' }}
env:
COUNT: ${{ steps.warnings.outputs.count }}
BASELINE: ${{ steps.baseline.outputs.baseline }}
run: |
if [ -z "$COUNT" ]; then
echo "No warning count computed (lint produced no result) - skipping comment payload."
exit 0
fi
jq -n \
--argjson pr "${{ github.event.pull_request.number }}" \
--argjson count "$COUNT" \
--arg baseline "$BASELINE" \
'{pr: $pr, count: $count, baseline: (if $baseline == "" then null else ($baseline | tonumber) end)}' \
> warning-result.json
cat warning-result.json
- name: Upload warning result
if: ${{ !cancelled() && github.event_name == 'pull_request' }}
uses: actions/upload-artifact@v4
with:
name: warning-result
path: warning-result.json
if-no-files-found: ignore
- name: Upload lint reports
uses: actions/upload-artifact@v4
with:
name: csharp-lint-reports
path: |
${{ env.INSPECT_REPORT_JSON_FILE_NAME }}
filtered.json
# CodeStyleReport.xml
# END: C# Linting using JetBrains ReSharper CLI
test:
# Skip when PR has 'perf_test' label (performance tests run in separate workflow)
if: |
!contains(github.event.pull_request.labels.*.name, 'perf_test') && (
(github.event_name == 'push' && github.ref == 'refs/heads/main') ||
(github.event.pull_request.draft == false) ||
(github.event.label.name == 'force-build') ||
(github.event.label.name == 'clean-build')
)
name: Test (${{ matrix.testMode }})
runs-on: ubuntu-latest
strategy:
matrix:
testMode: [playmode, editmode]
steps:
- name: Checkout CI actions
uses: actions/checkout@v6
with:
sparse-checkout: .github/actions
sparse-checkout-cone-mode: false
path: ci-actions
- name: Setup Docker disk
uses: ./ci-actions/.github/actions/setup-docker-disk
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
lfs: true
submodules: recursive
- name: Create LFS file list
run: git lfs ls-files -l | awk '{print $1}' | sort > .lfs-assets-id
- name: Restore LFS cache
uses: actions/cache@v5
id: lfs-cache
with:
path: .git/lfs
key: ${{ runner.os }}-lfs-${{ hashFiles('.lfs-assets-id') }}
- name: Git LFS Pull
run: |
git lfs pull
git add .
git reset --hard
- uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: 'Add GitHub to the SSH known hosts file'
run: |
mkdir -p -m 0700 ~/.ssh
echo 'github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl' >> ~/.ssh/known_hosts
echo 'github.com ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBEmKSENjQEezOmxkZMy7opKgwFB9nkt5YRrYMjNuG5N87uRgg6CLrbo5wAdT/y6v0mKV0U2w0WZ2YB/++Tpockg=' >> ~/.ssh/known_hosts
echo 'github.com ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCj7ndNxQowgcQnjshcLrqPEiiphnt+VTTvDP6mHBL9j1aNUkY4Ue1gvwnGLVlOhGeYrnZKAc/NKgzBh2bFNFMR0mVmYeeNR1cb+gZXH6N1gjEHGo2J3qjVg9rBJ0aM5st/22iqkAl7K6d9jMTi6COwJKqFHGmn7gwK+FBnfaYDwdOB8JqGz9eDNi2SegGaKie1jURLEuRPkLqlJrJ3AEdC5v7ibREzMQPGiHlIC5bE0GD/3mPN1Kul4a4EZhIC5RFxewRqDaK7be0wYMbSW7JzfLDMVtRAkKO5GjaKKv4RzGmrgVApFI/j6jJ0sTJZVgmmqODx0P+RcAfGV0RBCjMEdOPj4yr5ObHGjYCdaFEhXGTIlSGeFSEAFGFweZb9t80b8ABbeBRwx/dqI94aaFkMRkzT8VJ9B4yQvbWO7HP64Lxk6rab5Ygwypbcghmms/ATrc0CVh0zGw36VTzV2eBwboRa7G0sxgBudGrJ4BAOI/JrF4BnWcG5fvQewJ1Uc=' >> ~/.ssh/known_hosts
chmod 600 ~/.ssh/known_hosts
- name: Restore Library cache
uses: actions/cache@v5
with:
path: Explorer/Library
key: Library-Explorer-Ubuntu-${{ hashFiles('Explorer/Packages/packages-lock.json', 'Explorer/ProjectSettings/ProjectVersion.txt') }}
restore-keys: Library-Explorer-Ubuntu-
# --- Detect Unity version from the project ---
- name: Read Unity version from ProjectVersion.txt
id: unity-version
run: |
verFile="Explorer/ProjectSettings/ProjectVersion.txt"
if [ ! -f "$verFile" ]; then
echo "File not found: $verFile"
exit 1
fi
ver=$(grep -o 'm_EditorVersion: [0-9a-f.]*' "$verFile" | cut -d' ' -f2)
if [ -z "$ver" ]; then
echo "Could not parse Unity version from $verFile"
exit 1
fi
echo "version=$ver" >> $GITHUB_OUTPUT
echo "Detected Unity version: $ver"
# --- Build image references (GHCR + upstream) ---
- name: Set image names
id: img
run: |
version="${{ steps.unity-version.outputs.version }}"
flavor="${{ env.UNITY_IMAGE_FLAVOR }}"
imgVer="${{ env.UNITY_IMAGE_VERSION }}"
# ubuntu-<unityVersion>-<flavor>-<gameciVersion>
tag="ubuntu-$version-$flavor-$imgVer"
owner=$(echo "${{ github.repository_owner }}" | tr '[:upper:]' '[:lower:]')
upstream="unityci/editor:$tag"
ghcr="ghcr.io/$owner/${{ env.GHCR_IMAGE_NAME }}:$tag"
echo "tag=$tag" >> $GITHUB_OUTPUT
echo "upstream=$upstream" >> $GITHUB_OUTPUT
echo "ghcr=$ghcr" >> $GITHUB_OUTPUT
echo "Upstream image: $upstream"
echo "GHCR image: $ghcr"
# --- Log into GHCR ---
- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ github.token }}
# --- Restore Unity image from GHCR (fast path) ---
- name: Restore Unity image from GHCR
id: restore-image
continue-on-error: true
run: |
docker pull "${{ steps.img.outputs.ghcr }}"
# --- Seed GHCR from Docker Hub if missing (one-time per tag) ---
- name: Download from Docker Hub and upload to GHCR (if missing)
if: steps.restore-image.outcome == 'failure'
run: |
upstream="${{ steps.img.outputs.upstream }}"
ghcr="${{ steps.img.outputs.ghcr }}"
echo "GHCR miss. Pulling upstream: $upstream"
docker pull $upstream
docker tag $upstream $ghcr
docker push $ghcr
echo "Seeded GHCR: $ghcr"
- name: Install ripgrep for edit mode tests
if: matrix.testMode == 'editmode'
id: rg-img
run: |
base="${{ steps.img.outputs.ghcr }}"
derived="${base}-rg"
cat > /tmp/Dockerfile.rg <<EOF
FROM $base
RUN apt-get update \
&& apt-get install -y --no-install-recommends ripgrep \
&& rm -rf /var/lib/apt/lists/*
EOF
docker build -t "$derived" -f /tmp/Dockerfile.rg /tmp
echo "image=$derived" >> $GITHUB_OUTPUT
echo "Built derived image with ripgrep: $derived"
# Configure test runner
- uses: game-ci/unity-test-runner@v4.3.1
id: testRunner
timeout-minutes: 80
continue-on-error: true
env:
SHELL: /bin/bash
with:
projectPath: Explorer
testMode: ${{ matrix.testMode }}
sshAgent: ${{ env.SSH_AUTH_SOCK }}
customImage: ${{ matrix.testMode == 'editmode' && steps.rg-img.outputs.image || steps.img.outputs.ghcr }}
customParameters: -buildTarget StandaloneLinux64 -testCategory "!Performance" -burst-disable-compilation -accept-apiupdate
githubToken: ${{ env.GITHUB_TOKEN }}
- name: Dump Unity Editor log on failure
if: always() && steps.testRunner.outcome != 'success'
env:
ARTIFACTS_PATH: ${{ steps.testRunner.outputs.artifactsPath }}
TEST_MODE: ${{ matrix.testMode }}
run: |
echo "::group::Unity Editor log (last 200 lines)"
# game-ci runs the test editor with `-logFile <artifactsPath>/<mode>.log`,
# where artifactsPath resolves to "artifacts" at the workspace root
# (NOT Explorer/artifacts — projectPath does not prefix it). Prefer the
# action's own resolved output (matches the Report/Upload steps), but
# fall back to the literal path because on a hard timeout kill the action
# is killed before exporting its outputs (the Report step then fails with
# "Input required and not supplied: path"). Values are passed via env
# rather than interpolated into the shell directly.
LOG=""
for candidate in \
"$ARTIFACTS_PATH/$TEST_MODE.log" \
"artifacts/$TEST_MODE.log"; do
if [ -n "$candidate" ] && [ -f "$candidate" ]; then
LOG="$candidate"
break
fi
done
if [ -n "$LOG" ]; then
echo "Showing last 200 lines of: $LOG"
tail -200 "$LOG"
else
echo "No $TEST_MODE.log found in known locations."
echo "Searching workspace for any Unity logs (depth-bounded to skip Library/):"
matches=$(find . -maxdepth 3 -name "*.log" 2>/dev/null || true)
if [ -n "$matches" ]; then
echo "$matches" | head -50
else
echo "No .log files found"
fi
fi
echo "::endgroup::"
- name: Report test results
if: always()
uses: dorny/test-reporter@v2.1.1
with:
name: Unity Tests (${{ matrix.testMode }})
path: ${{ steps.testRunner.outputs.artifactsPath }}/*.xml
reporter: dotnet-nunit
fail-on-error: true
fail-on-empty: true
use-actions-summary: true
# Upload artifact
- uses: actions/upload-artifact@v6
if: always()
with:
name: Test results (${{ matrix.testMode }})
path: ${{ steps.testRunner.outputs.artifactsPath }}
if-no-files-found: error
watchdog:
runs-on: ubuntu-latest
needs: [lint, test]
if: |
needs.lint.result == 'failure' ||
needs.test.result == 'failure'
steps:
- run: exit 1