diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index cb5b333da..ed3b55dec 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -14,6 +14,17 @@ Follow these rules for any work in this repo: - Repo structure + “where do I implement X?”: `.github/instructions/architecture.md` - Guardrails (agent operating rules): `.github/instructions/guardrails.md` +## Verification (compile & test before PR) +- How to verify changes (local + CI): `.github/instructions/verification.md` + - Cloud agent: push to work branch → wait for **Compile Check** workflow. + - Local: run `.github/scripts/verify-compilation.sh` (Linux/macOS) or `.github\scripts\verify-compilation.ps1` (Windows) after creating `unity-dev-settings.json` from the example: + ```json + { + "unity_executable": "", + "test_project_path": "" + } + ``` + ## Conventions & style - Coding conventions & style guide: `.github/instructions/style-guide.md` - Patterns cookbook (templates): `.github/instructions/patterns.md` diff --git a/.github/instructions/guardrails.md b/.github/instructions/guardrails.md index 9e24f13ed..ba7cb4880 100644 --- a/.github/instructions/guardrails.md +++ b/.github/instructions/guardrails.md @@ -22,6 +22,22 @@ Unless the task explicitly asks for it, do **not**: - Runtime code must be build-safe (no unguarded `UnityEditor` dependencies). - Put editor tooling under `Runtime/Editor/` (this repo’s editor-only area) and/or guard editor-only code with `#if UNITY_EDITOR`. +## Verification (compilation & tests) +Before opening a PR, verify that changes compile and pass tests. See +`.github/instructions/verification.md` for the full procedure. In short: + +- **Cloud agent**: push to your work branch and wait for the **`Compile Check`** + workflow (`.github/workflows/agent-sanity-check.yml`) to go green. +- **Local**: run `.github/scripts/verify-compilation.sh` (Linux/macOS) or + `.github\scripts\verify-compilation.ps1` (Windows), after setting up + `unity-dev-settings.json` from the provided example: + ```json + { + "unity_executable": "", + "test_project_path": "" + } + ``` + ## When unsure If a change would require guessing architecture, conventions, or customer-facing API behavior: - Stop and ask for clarification rather than inventing a new pattern. diff --git a/.github/instructions/verification.md b/.github/instructions/verification.md new file mode 100644 index 000000000..8e77c394e --- /dev/null +++ b/.github/instructions/verification.md @@ -0,0 +1,111 @@ +# Verifying Changes: Compilation & Tests + +This repo ships as a Unity UPM package. Unity C# must be verified through the Unity +Editor's compilation pipeline — `dotnet build` does **not** work on this codebase. + +--- + +## Cloud coding agent (GitHub Copilot agent / CI) + +When you push commits to a work branch the **`Compile Check`** workflow +(`.github/workflows/compile-check.yml`) runs automatically and verifies: + +1. All SDK C# code compiles without errors (including Samples). +2. The configured Unity **editmode** tests pass (see the workflow for exact coverage). + +### Workflow to follow after each batch of commits + +1. Push your work branch (e.g. `feat/my-feature`, `fix/something`). +2. Navigate to the **Actions** tab → **Compile Check** → your branch's run. +3. Wait for it to complete and confirm it is **green** before opening a PR. + +If the workflow fails: + +1. **Job Summary** (fastest) — the run's Summary page shows a compact + `Compilation Errors` table with file path, line number, and error code/message. + This is the primary place to read errors; no log-digging required. +2. **Annotations** — the Summary page also lists inline annotations (e.g. + `sdk/Runtime/Game/Requests/TriggersRequests.cs#L3`) that link directly to the + offending line. These also appear in the PR diff view. +3. **Raw log** (fallback) — if neither of the above is present the compile step itself + may have crashed before producing output; click the failed step and search for + `compilationhadfailure: True` to find the relevant section. + +Fix the reported errors and push again. + +> The full CI suite (`run-tests-and-package.yml`) runs on PRs to `dev` and `main`. +> The compile check is a smaller, faster subset for in-progress work branches. + +--- + +## Local (human developer or local Copilot instance) + +### Prerequisites + +1. Unity is installed locally (any Unity **2019.2+** version works; best to match the + project's minimum version in `package.json`). +2. You have a `unity-dev-settings.json` at the repo root (gitignored). +3. Python 3 is installed and available on your `PATH` as `python3` (used by the + local verification scripts to parse `unity-dev-settings.json`). + +### One-time setup + +Open or create `unity-dev-settings.json` and fill in the two fields: + +```json +{ + "unity_executable": "", + "test_project_path": "" +} +``` + +**`unity_executable` examples by platform:** + +| Platform | Example path | +|---|---| +| macOS | `/Applications/Unity/Hub/Editor/2022.3.22f1/Unity.app/Contents/MacOS/Unity` | +| Windows | `C:\Program Files\Unity\Hub\Editor\2022.3.22f1\Editor\Unity.exe` | +| Linux | `/opt/unity/Editor/Unity` | + +**`test_project_path`**: leave empty to let the script auto-create a temporary project +that references the SDK. Set to an absolute path only if you already maintain a +dedicated local Unity project that points at this SDK via a local package reference. + +### Running the check + +**Linux / macOS (bash):** +```bash +.github/scripts/verify-compilation.sh +``` + +**Windows (PowerShell):** +```powershell +.github\scripts\verify-compilation.ps1 +``` + +The script will: + +1. Read `unity-dev-settings.json`. +2. Create a temporary Unity project at `Temp~/VerificationProject` (gitignored) that + references the SDK as a local package and includes the Samples. +3. Launch Unity in batch mode with `-batchmode -nographics -quit`. +4. Print Unity's compilation output filtered to errors and warnings. +5. Exit `0` on success, `1` on any compilation error. + +--- + +## What counts as "verified" + +A change is verified when **either** of the following is true: + +- The `Compile Check` CI workflow is green on your branch, **or** +- The local verification script exits `0`. + +**Additionally** ensure: + +- No new `error CS` compiler errors appear. +- No existing public API signatures were changed without going through the deprecation + flow described in `.github/instructions/style-guide.md`. + +Running the full integration tests (`run-tests-and-package.yml`) is a CI-only step and +is not required for local verification. diff --git a/.github/scripts/verify-compilation.ps1 b/.github/scripts/verify-compilation.ps1 new file mode 100644 index 000000000..663e46447 --- /dev/null +++ b/.github/scripts/verify-compilation.ps1 @@ -0,0 +1,235 @@ +#Requires -Version 5.0 +<# +.SYNOPSIS + Verifies that the LootLocker Unity SDK compiles without errors. + +.DESCRIPTION + Reads unity-dev-settings.json from the repo root, optionally creates a temporary + Unity project referencing the SDK, then runs Unity in batch mode to check + compilation. + + See .github/instructions/verification.md for setup instructions. +#> + +$ErrorActionPreference = 'Stop' + +$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..\..")).Path +$SettingsFile = Join-Path $RepoRoot "unity-dev-settings.json" +$TempProject = Join-Path $RepoRoot "Temp~\VerificationProject" +$LogFile = Join-Path $TempProject "compilation.log" + +function Write-Step { param([string]$Msg) Write-Host $Msg } +function Write-Ok { param([string]$Msg) Write-Host $Msg -ForegroundColor Green } +function Write-Fail { param([string]$Msg) Write-Host $Msg -ForegroundColor Red } +function Write-Warn { param([string]$Msg) Write-Host $Msg -ForegroundColor Yellow } + +Write-Step "=========================================" +Write-Step " LootLocker SDK - Compilation Check" +Write-Step "=========================================" +Write-Step "" + +# --------------------------------------------------------------------------- +# 1. Load settings +# --------------------------------------------------------------------------- +if (-not (Test-Path $SettingsFile)) { + Write-Warn "SETUP REQUIRED: 'unity-dev-settings.json' not found at repo root." + Write-Step " Create unity-dev-settings.json with your Unity path." + Write-Step " See .github/instructions/verification.md for the required format and examples." + exit 1 +} + +$Settings = Get-Content $SettingsFile -Raw | ConvertFrom-Json +$UnityExe = $Settings.unity_executable +$CustomProject = $Settings.test_project_path + +if ([string]::IsNullOrWhiteSpace($UnityExe)) { + Write-Fail "ERROR: 'unity_executable' is empty in unity-dev-settings.json." + exit 1 +} +if (-not (Test-Path $UnityExe)) { + Write-Fail "ERROR: Unity executable not found: $UnityExe" + exit 1 +} + +# --------------------------------------------------------------------------- +# 2. Helper: create / refresh the temporary verification project +# --------------------------------------------------------------------------- +function Initialize-TempProject { + Write-Step "Creating temporary verification project at Temp~/VerificationProject ..." + + if (Test-Path $TempProject) { Remove-Item $TempProject -Recurse -Force } + New-Item -ItemType Directory -Path (Join-Path $TempProject "Assets") -Force | Out-Null + New-Item -ItemType Directory -Path (Join-Path $TempProject "Packages") -Force | Out-Null + New-Item -ItemType Directory -Path (Join-Path $TempProject "ProjectSettings") -Force | Out-Null + + $SdkRef = $RepoRoot -replace '\\', '/' + $nl = [char]10 + $manifestContent = '{' + $nl + ' "dependencies": {' + $nl + ' "com.lootlocker.lootlockersdk": "file:' + $SdkRef + '"' + $nl + ' }' + $nl + '}' + [IO.File]::WriteAllText((Join-Path $TempProject 'Packages\manifest.json'), $manifestContent) + + $psContent = '%YAML 1.1' + $nl + '%TAG !u! tag:unity3d.com,2011:' + $nl + '--- !u!129 &1' + $nl + 'PlayerSettings:' + $nl + ' companyName: LootLockerSDKVerification' + $nl + ' productName: LootLockerSDKVerification' + [IO.File]::WriteAllText((Join-Path $TempProject 'ProjectSettings\ProjectSettings.asset'), $psContent) + + $SamplesPath = Join-Path $RepoRoot "Samples~\LootLockerExamples" + if (Test-Path $SamplesPath) { + Copy-Item $SamplesPath (Join-Path $TempProject "Assets\") -Recurse -Force + } +} + +# --------------------------------------------------------------------------- +# 3. Determine project path +# --------------------------------------------------------------------------- +if (-not [string]::IsNullOrWhiteSpace($CustomProject) -and (Test-Path $CustomProject)) { + $ProjectPath = (Resolve-Path $CustomProject).Path + Write-Step "Using custom project: $ProjectPath" + + # Delete only the LootLocker compiled output artifacts so Tundra is forced to + # recompile the SDK from source. Deleting the entire Bee folder crashes Unity; + # deleting only outputs is safe — Tundra detects missing outputs and rebuilds them. + Write-Step "Removing cached LootLocker assemblies to force recompilation..." + $artifactsPath = Join-Path $ProjectPath "Library\Bee\artifacts" + Get-ChildItem $artifactsPath -Recurse -Filter "*lootlocker*" -ErrorAction SilentlyContinue | Remove-Item -Force -ErrorAction SilentlyContinue + $scriptAssemblies = Join-Path $ProjectPath "Library\ScriptAssemblies" + Get-ChildItem $scriptAssemblies -Filter "*lootlocker*" -ErrorAction SilentlyContinue | Remove-Item -Force -ErrorAction SilentlyContinue +} else { + Initialize-TempProject + $ProjectPath = $TempProject +} + +# --------------------------------------------------------------------------- +# 4. Run Unity in batch mode +# --------------------------------------------------------------------------- +function Invoke-UnityCompile { + param([string]$ProjectDir) + Write-Step "" + Write-Step "Unity: $UnityExe" + Write-Step "Project: $ProjectDir" + Write-Step "Log: $LogFile" + Write-Step "" + + # Ensure log directory exists; remove any stale log from a previous run. + if (-not (Test-Path (Split-Path $LogFile))) { New-Item -ItemType Directory -Path (Split-Path $LogFile) -Force | Out-Null } + if (Test-Path $LogFile) { Remove-Item $LogFile -Force } + + $prevEAP = $ErrorActionPreference + $ErrorActionPreference = 'Continue' + & $UnityExe -batchmode -nographics -projectPath $ProjectDir -logFile $LogFile -quit + $script:unityExitCode = if ($LASTEXITCODE -ne $null) { $LASTEXITCODE } else { 1 } + $ErrorActionPreference = $prevEAP +} + +$script:unityExitCode = 1 +Invoke-UnityCompile $ProjectPath + +# Wait for Unity to finish writing the log. Unity child processes (e.g. LicensingClient) +# may hold the file open after the main process exits. Poll until the log contains +# Unity's end-of-session marker. A full first-time compile can take 60-90 seconds so we +# wait up to 3 minutes. We also require the file size to be stable for 3 consecutive +# reads (1.5 s) before accepting the content, since LicensingClient may still be writing +# small amounts after the main process has exited. +$logContent = "" +$lastSize = -1 +$stableRuns = 0 +for ($i = 0; $i -lt 360; $i++) { + Start-Sleep -Milliseconds 500 + if (-not (Test-Path $LogFile)) { continue } + try { + $stream = [System.IO.File]::Open($LogFile, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite) + $reader = [System.IO.StreamReader]::new($stream, $true) # $true = detect encoding from BOM + $logContent = $reader.ReadToEnd() + $reader.Close(); $stream.Close() + + $currentSize = $logContent.Length + if ($currentSize -eq $lastSize) { $stableRuns++ } else { $stableRuns = 0 } + $lastSize = $currentSize + + # Stop once Unity's final line is present AND the file hasn't grown for 3 checks + if (($logContent -match "Application will terminate|Exiting batchmode") -and $stableRuns -ge 3) { break } + } catch { } +} + +# If the custom project crashed on startup (Package Manager never ran — log is tiny with +# no compilation output), fall back to the temp project automatically. +if ($ProjectPath -ne $TempProject -and ($logContent.Length -lt 5000 -or $logContent -notmatch "Package Manager")) { + Write-Warn "Custom project did not open correctly (startup crash). Falling back to temporary project." + Write-Warn "To fix: open '$ProjectPath' in the Unity Editor once, then re-run." + Initialize-TempProject + $ProjectPath = $TempProject + Invoke-UnityCompile $ProjectPath + + $lastSize = -1; $stableRuns = 0; $logContent = "" + for ($i = 0; $i -lt 360; $i++) { + Start-Sleep -Milliseconds 500 + if (-not (Test-Path $LogFile)) { continue } + try { + $stream = [System.IO.File]::Open($LogFile, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read, [System.IO.FileShare]::ReadWrite) + $reader = [System.IO.StreamReader]::new($stream, $true) + $logContent = $reader.ReadToEnd() + $reader.Close(); $stream.Close() + $currentSize = $logContent.Length + if ($currentSize -eq $lastSize) { $stableRuns++ } else { $stableRuns = 0 } + $lastSize = $currentSize + if (($logContent -match "Application will terminate|Exiting batchmode") -and $stableRuns -ge 3) { break } + } catch { } + } +} + +$logLines = $logContent -split "`n" + +# --------------------------------------------------------------------------- +# 5. Report results +# --------------------------------------------------------------------------- +Write-Step "" +Write-Step "--- Compilation result -------" + +$compileErrors = $logLines | Select-String -Pattern "error CS\d+" | Select-String -NotMatch "Licensing::" +$tundraSuccess = ($logLines | Select-String -Pattern "Tundra build success").Count -gt 0 +$tundraFailure = ($logLines | Select-String -Pattern "Tundra build failure|Tundra build failed").Count -gt 0 + +if ($compileErrors) { + $compileErrors | ForEach-Object { Write-Host $_.Line } +} + +Write-Step "-----------------------------------" +Write-Step "" + +# Determine outcome from log content: +# - Any "error CS####" line => compilation failed +# - "Tundra build failed" marker => compilation failed +# - No compiler errors and Unity exit code 0 => compilation succeeded (even if no Tundra marker) +# - "Tundra build success" found => compilation succeeded (Unity may still exit non-zero +# due to unrelated project setup issues unrelated to the SDK) +if ($compileErrors.Count -gt 0) { + Write-Fail "COMPILATION FAILED ($($compileErrors.Count) compiler error(s))" + Write-Step "Full log: $LogFile" + exit 1 +} +elseif ($tundraFailure) { + # Explicit Tundra failure should be treated as a hard failure regardless of exit code + if ($logLines) { + $logLines | Select-String -Pattern "error CS\d+|Scripts have compiler errors|error:" | Select-String -NotMatch "Licensing::" | + ForEach-Object { Write-Host $_.Line } + } + Write-Fail "COMPILATION FAILED (Tundra build failed)" + Write-Step "Full log: $LogFile" + exit 1 +} +elseif ($unityExitCode -eq 0) { + # Treat a clean Unity exit with no compiler errors as success, even if we did not see a Tundra marker + Write-Ok "COMPILATION SUCCEEDED" +} +elseif ($tundraSuccess) { + Write-Ok "COMPILATION SUCCEEDED" + Write-Warn "Note: Unity exited with code $unityExitCode after compilation (likely unrelated project setup - not an SDK issue)." +} +else { + # Non-zero Unity exit code with no clear Tundra success/failure marker; surface diagnostics and fail + if ($logLines) { + $logLines | Select-String -Pattern "error CS\d+|Scripts have compiler errors|error:" | Select-String -NotMatch "Licensing::" | + ForEach-Object { Write-Host $_.Line } + } + $reason = "exit code: $unityExitCode" + Write-Fail "COMPILATION FAILED ($reason)" + Write-Step "Full log: $LogFile" + exit 1 +} diff --git a/.github/scripts/verify-compilation.sh b/.github/scripts/verify-compilation.sh new file mode 100644 index 000000000..ace83874d --- /dev/null +++ b/.github/scripts/verify-compilation.sh @@ -0,0 +1,162 @@ +#!/usr/bin/env bash +# verify-compilation.sh — LootLocker Unity SDK local compilation check +# See .github/instructions/verification.md for setup instructions. +set -uo pipefail # intentionally no -e: we handle non-zero exits ourselves + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +SETTINGS_FILE="$REPO_ROOT/unity-dev-settings.json" +TEMP_PROJECT="$REPO_ROOT/Temp~/VerificationProject" + +RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m' + +echo "=========================================" +echo " LootLocker SDK - Compilation Check" +echo "=========================================" +echo "" + +# --------------------------------------------------------------------------- +# 1. Load settings +# --------------------------------------------------------------------------- +if [[ ! -f "$SETTINGS_FILE" ]]; then + echo -e "${YELLOW}SETUP REQUIRED:${NC} 'unity-dev-settings.json' not found at repo root." + echo " Create unity-dev-settings.json with your Unity path." + echo " See .github/instructions/verification.md for the required format and examples." + exit 1 +fi + +read_json_field() { + python3 -c "import json; d=json.load(open('$1')); print(d.get('$2',''))" 2>/dev/null || true +} + +UNITY_EXE=$(read_json_field "$SETTINGS_FILE" "unity_executable") +CUSTOM_PROJECT=$(read_json_field "$SETTINGS_FILE" "test_project_path") + +if [[ -z "$UNITY_EXE" ]]; then + echo -e "${RED}ERROR:${NC} 'unity_executable' is empty in unity-dev-settings.json." + exit 1 +fi +if [[ ! -f "$UNITY_EXE" || ! -x "$UNITY_EXE" ]]; then + echo -e "${RED}ERROR:${NC} Unity executable not found or not executable: $UNITY_EXE" + exit 1 +fi + +# --------------------------------------------------------------------------- +# 2. Helper: create / refresh the temporary verification project +# --------------------------------------------------------------------------- +init_temp_project() { + echo "Creating temporary verification project at Temp~/VerificationProject ..." + rm -rf "$TEMP_PROJECT" + mkdir -p "$TEMP_PROJECT/Assets" "$TEMP_PROJECT/Packages" "$TEMP_PROJECT/ProjectSettings" + + # manifest.json - local file: reference to the SDK root + cat > "$TEMP_PROJECT/Packages/manifest.json" < "$TEMP_PROJECT/ProjectSettings/ProjectSettings.asset" <<'EOF' +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!129 &1 +PlayerSettings: + companyName: LootLockerSDKVerification + productName: LootLockerSDKVerification +EOF + + # Copy Samples so they are also compiled + if [[ -d "$REPO_ROOT/Samples~/LootLockerExamples" ]]; then + cp -r "$REPO_ROOT/Samples~/LootLockerExamples" "$TEMP_PROJECT/Assets/" + fi +} + +# --------------------------------------------------------------------------- +# 3. Determine project path +# --------------------------------------------------------------------------- +if [[ -n "$CUSTOM_PROJECT" && -d "$CUSTOM_PROJECT" ]]; then + PROJECT_PATH="$CUSTOM_PROJECT" + echo "Using custom project: $PROJECT_PATH" + + # Delete only the LootLocker compiled output artifacts so Tundra is forced to + # recompile the SDK from source. Deleting the entire Bee folder crashes Unity; + # deleting only outputs is safe — Tundra detects missing outputs and rebuilds them. + echo "Removing cached LootLocker assemblies to force recompilation..." + find "$PROJECT_PATH/Library/Bee/artifacts" -iname "*lootlocker*" -delete 2>/dev/null || true + find "$PROJECT_PATH/Library/ScriptAssemblies" -iname "*lootlocker*" -delete 2>/dev/null || true +else + init_temp_project + PROJECT_PATH="$TEMP_PROJECT" +fi + +# --------------------------------------------------------------------------- +# 4. Run Unity in batch mode +# --------------------------------------------------------------------------- +# On Linux/macOS, -logFile - pipes Unity output directly to stdout, which is +# simpler and more reliable than polling a file. +run_unity() { + local proj="$1" + echo "" + echo "Unity: $UNITY_EXE" + echo "Project: $proj" + echo "" + UNITY_EXIT_CODE=0 + LOG_CONTENT=$("$UNITY_EXE" -batchmode -nographics -projectPath "$proj" -logFile - -quit 2>&1) \ + || UNITY_EXIT_CODE=$? +} + +run_unity "$PROJECT_PATH" + +# If the custom project crashed on startup (Package Manager never ran — output is tiny), +# fall back to the temp project automatically. +if [[ "$PROJECT_PATH" != "$TEMP_PROJECT" ]] && \ + ! echo "$LOG_CONTENT" | grep -q "Package Manager"; then + echo -e "${YELLOW}Custom project did not open correctly (startup crash). Falling back to temporary project.${NC}" + echo "To fix: open '$PROJECT_PATH' in the Unity Editor once, then re-run." + init_temp_project + PROJECT_PATH="$TEMP_PROJECT" + run_unity "$PROJECT_PATH" +fi + +# --------------------------------------------------------------------------- +# 5. Report results +# --------------------------------------------------------------------------- +echo "" +echo "--- Compilation result -------" + +# Collect compiler error lines, excluding Licensing noise +COMPILE_ERRORS=$(echo "$LOG_CONTENT" | grep -E "error CS[0-9]+" | grep -v "Licensing::" || true) +TUNDRA_SUCCESS=$(echo "$LOG_CONTENT" | grep -c "Tundra build success" || true) +TUNDRA_FAILURE=$(echo "$LOG_CONTENT" | grep -cE "Tundra build failure|Tundra build failed" || true) + +if [[ -n "$COMPILE_ERRORS" ]]; then + echo "$COMPILE_ERRORS" +fi +echo "-----------------------------------" +echo "" + +if [[ -n "$COMPILE_ERRORS" ]]; then + ERROR_COUNT=$(echo "$COMPILE_ERRORS" | wc -l | tr -d ' ') + echo -e "${RED}COMPILATION FAILED${NC} (${ERROR_COUNT} compiler error(s))" + exit 1 +elif [[ "$TUNDRA_FAILURE" -gt 0 ]]; then + # Explicit Tundra failure is a hard fail regardless of exit code + echo "$LOG_CONTENT" | grep -E "error CS[0-9]+|Scripts have compiler errors|error:" | grep -v "Licensing::" || true + echo -e "${RED}COMPILATION FAILED${NC} (Tundra build failed)" + exit 1 +elif [[ "$TUNDRA_SUCCESS" -gt 0 ]]; then + echo -e "${GREEN}COMPILATION SUCCEEDED${NC}" + if [[ $UNITY_EXIT_CODE -ne 0 ]]; then + echo -e "${YELLOW}Note: Unity exited with code $UNITY_EXIT_CODE after compilation (likely unrelated project setup - not an SDK issue).${NC}" + fi +elif [[ $UNITY_EXIT_CODE -eq 0 ]]; then + # No Tundra marker but Unity exited cleanly with no compiler errors — treat as success + echo -e "${GREEN}COMPILATION SUCCEEDED${NC}" +else + # Non-zero exit, no Tundra success, no compiler errors extracted — surface diagnostics + echo "$LOG_CONTENT" | grep -E "error CS[0-9]+|Scripts have compiler errors|error:" | grep -v "Licensing::" || true + echo -e "${RED}COMPILATION FAILED${NC} (exit code: $UNITY_EXIT_CODE)" + exit 1 +fi diff --git a/.github/workflows/compile-check.yml b/.github/workflows/compile-check.yml new file mode 100644 index 000000000..5db1c2834 --- /dev/null +++ b/.github/workflows/compile-check.yml @@ -0,0 +1,135 @@ +name: Compile Check +run-name: "Compile Check on ${{ github.ref_name }} (${{ github.sha }})" + +# Runs on every push to work branches so agents (and humans) get fast compilation +# feedback without waiting for the full CI pipeline. +# See .github/instructions/verification.md for details. +on: + push: + branches: + - 'feat/**' + - 'fix/**' + - 'docs/**' + - 'refactor/**' + - 'chore/**' + - 'test/**' + - 'scout/**' + - 'ci/**' + - 'copilot/**' + workflow_dispatch: + inputs: + unityVersion: + description: "Unity version to use (leave empty to use AGENT_CHECK_UNITY_VERSION variable)" + type: string + default: "" + +jobs: + compilation-check: + name: Verify SDK compiles + runs-on: ubuntu-latest + timeout-minutes: 10 + env: + UNITY_VERSION: ${{ github.event.inputs.unityVersion || vars.AGENT_CHECK_UNITY_VERSION || '2022.3.22f1' }} + + steps: + - name: Checkout SDK + uses: actions/checkout@v4 + with: + path: sdk + + - name: Create minimal test project + run: | + mkdir -p TestProject/Assets TestProject/Packages TestProject/ProjectSettings + + # Package manifest — local reference to the SDK, no external secrets needed + cat > TestProject/Packages/manifest.json <<'EOF' + { + "dependencies": { + "com.lootlocker.lootlockersdk": "file:../../sdk" + } + } + EOF + + # Minimal ProjectSettings — no game keys or environment settings required + printf '%%YAML 1.1\n%%TAG !u! tag:unity3d.com,2011:\n--- !u!129 &1\nPlayerSettings:\n companyName: LootLockerSDKVerification\n productName: LootLockerSDKVerification\n' \ + > TestProject/ProjectSettings/ProjectSettings.asset + + # Include Samples so they are also compiled + if [ -d "sdk/Samples~/LootLockerExamples" ]; then + cp -r sdk/Samples~/LootLockerExamples TestProject/Assets/ + fi + + - name: Cache Unity Library + uses: actions/cache@v4 + with: + path: TestProject/Library + key: Library-compile-check-${{ env.UNITY_VERSION }} + restore-keys: | + Library-compile-check- + + - name: Compile SDK (edit-mode via Unity test runner) + id: compile + continue-on-error: true + uses: game-ci/unity-test-runner@v4.3.1 + env: + UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }} + UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} + UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} + with: + projectPath: TestProject + unityVersion: ${{ env.UNITY_VERSION }} + testMode: editmode + githubToken: ${{ secrets.GITHUB_TOKEN }} + checkName: Compile Check (${{ env.UNITY_VERSION }}) + + - name: Report compilation errors + if: always() + run: | + # game-ci writes the Unity editor log to artifacts/ alongside test results. + # If it wrote a log file, parse it; otherwise skip gracefully. + LOG_FILE=$(grep -rl "compilationhadfailure: True" artifacts/ 2>/dev/null | head -1) + + if [ -z "$LOG_FILE" ]; then + if [ "${{ steps.compile.outcome }}" = "failure" ]; then + echo "::warning::Compile step failed but no Unity log file found in artifacts/ to parse." + echo "## ⚠️ Compile step failed — no parseable log found" >> "$GITHUB_STEP_SUMMARY" + echo "Check the raw step output above for details." >> "$GITHUB_STEP_SUMMARY" + fi + exit 0 + fi + + # Extract standard MSBuild-style CS compiler errors: + # /github/workspace/sdk/Some/File.cs(line,col): error CSxxxx: message + ERRORS=$(grep -E "^.+\([0-9]+,[0-9]+\): error CS[0-9]+:" "$LOG_FILE" \ + | sed 's|/github/workspace/sdk/||g') + + if [ -z "$ERRORS" ]; then + # Compile step may have failed for a non-CS-error reason; still report it + if [ "${{ steps.compile.outcome }}" = "failure" ]; then + echo "::error::Compile step failed but no CS errors were extracted from the log." + fi + exit 0 + fi + + # ── Job Summary (compact card, ideal for agent consumption) ────────── + { + echo "## ❌ Compilation Errors" + echo "" + echo "| File | Line | Error |" + echo "| ---- | ---- | ----- |" + while IFS= read -r line; do + if [[ "$line" =~ ^(.*)\(([0-9]+),[0-9]+\):\ error\ (CS[0-9]+):\ (.+)$ ]]; then + echo "| \`${BASH_REMATCH[1]}\` | ${BASH_REMATCH[2]} | **${BASH_REMATCH[3]}**: ${BASH_REMATCH[4]} |" + fi + done <<< "$ERRORS" + } >> "$GITHUB_STEP_SUMMARY" + + # ── Inline annotations (show up in PR diff and Annotations panel) ──── + while IFS= read -r line; do + if [[ "$line" =~ ^(.*)\(([0-9]+),([0-9]+)\):\ error\ (CS[0-9]+):\ (.+)$ ]]; then + echo "::error file=sdk/${BASH_REMATCH[1]},line=${BASH_REMATCH[2]},col=${BASH_REMATCH[3]}::${BASH_REMATCH[4]}: ${BASH_REMATCH[5]}" + fi + done <<< "$ERRORS" + + # Propagate failure so the job still turns red + exit 1 diff --git a/.gitignore b/.gitignore index 16d7b6b96..5ef217be9 100644 --- a/.gitignore +++ b/.gitignore @@ -60,6 +60,13 @@ crashlytics-build.properties .DS_Store +# Local Unity SDK dev settings (machine-specific, contains path to Unity installation) +unity-dev-settings.json +unity-dev-settings.json.meta + +# Temporary verification project created by .github/scripts/verify-compilation.* +Temp~/ + #IDE generated files .idea .vscode