name: E2E Bridge Smoke (deterministic, no LLM)

# Boots a headless Unity Editor, starts the Python MCP server's wire path, and
# drives a fixed sequence of real tool calls with exact assertions
# (Server/tests/e2e/bridge_smoke.py). Unlike claude-nl-suite.yml this needs
# NO Anthropic API key -- it is deterministic and cheap, so it can gate PRs and
# releases. It still needs Unity license secrets to boot the Editor.

on:
  workflow_dispatch:
  pull_request:
    paths:
      - "MCPForUnity/Editor/**"
      - "MCPForUnity/Runtime/**"
      - "Server/src/**"
      - "Server/tests/e2e/**"
      - "tools/local_harness.py"
      - ".github/workflows/e2e-bridge.yml"

permissions:
  contents: read

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

env:
  UNITY_IMAGE: unityci/editor:ubuntu-2021.3.45f2-linux-il2cpp-3

jobs:
  e2e-bridge:
    runs-on: ubuntu-24.04
    timeout-minutes: 40
    steps:
      - name: Detect Unity license secrets
        id: detect
        env:
          UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
          UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
          UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
          UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
        run: |
          set -e
          if [ -n "$UNITY_LICENSE" ] || { [ -n "$UNITY_EMAIL" ] && [ -n "$UNITY_PASSWORD" ] && [ -n "$UNITY_SERIAL" ]; }; then
            echo "unity_ok=true" >> "$GITHUB_OUTPUT"
          else
            echo "unity_ok=false" >> "$GITHUB_OUTPUT"
            echo "::warning::Unity license secrets absent; E2E bridge smoke will be skipped (not failed)."
          fi

      - uses: actions/checkout@v4
        if: steps.detect.outputs.unity_ok == 'true'
        with:
          fetch-depth: 0

      - uses: astral-sh/setup-uv@v4
        if: steps.detect.outputs.unity_ok == 'true'
        with:
          python-version: "3.11"

      - name: Install MCP server
        if: steps.detect.outputs.unity_ok == 'true'
        run: |
          set -eux
          uv venv
          echo "VIRTUAL_ENV=$GITHUB_WORKSPACE/.venv" >> "$GITHUB_ENV"
          echo "$GITHUB_WORKSPACE/.venv/bin" >> "$GITHUB_PATH"
          uv pip install -e Server

      # --- License staging (mirrors claude-nl-suite.yml) ---
      - name: Decide license sources
        if: steps.detect.outputs.unity_ok == 'true'
        id: lic
        shell: bash
        env:
          UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
          UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
          UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
          UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
        run: |
          set -eu
          use_ulf=false; use_ebl=false
          [[ -n "${UNITY_LICENSE:-}" ]] && use_ulf=true
          [[ -n "${UNITY_EMAIL:-}" && -n "${UNITY_PASSWORD:-}" && -n "${UNITY_SERIAL:-}" ]] && use_ebl=true
          echo "use_ulf=$use_ulf" >> "$GITHUB_OUTPUT"
          echo "use_ebl=$use_ebl" >> "$GITHUB_OUTPUT"

      - name: Stage Unity .ulf license (from secret)
        if: steps.detect.outputs.unity_ok == 'true' && steps.lic.outputs.use_ulf == 'true'
        id: ulf
        env:
          UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
        shell: bash
        run: |
          set -eu
          mkdir -p "$RUNNER_TEMP/unity-license-ulf" "$RUNNER_TEMP/unity-local/Unity"
          f="$RUNNER_TEMP/unity-license-ulf/Unity_lic.ulf"
          if printf "%s" "$UNITY_LICENSE" | base64 -d - >/dev/null 2>&1; then
            printf "%s" "$UNITY_LICENSE" | base64 -d - > "$f"
          else
            printf "%s" "$UNITY_LICENSE" > "$f"
          fi
          chmod 600 "$f" || true
          if grep -qi '<Signature>' "$f"; then
            cp -f "$f" "$RUNNER_TEMP/unity-local/Unity/Unity_lic.ulf"
            echo "ok=true" >> "$GITHUB_OUTPUT"
          else
            echo "ok=false" >> "$GITHUB_OUTPUT"
          fi

      - name: Activate Unity (EBL via container)
        if: steps.detect.outputs.unity_ok == 'true' && steps.lic.outputs.use_ebl == 'true'
        shell: bash
        env:
          UNITY_IMAGE: ${{ env.UNITY_IMAGE }}
          UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
          UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
          UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
        run: |
          set -euo pipefail
          mkdir -p "$RUNNER_TEMP/unity-config" "$RUNNER_TEMP/unity-local"
          docker run --rm --network host \
            -e HOME=/root -e UNITY_EMAIL -e UNITY_PASSWORD -e UNITY_SERIAL \
            -v "$RUNNER_TEMP/unity-config:/root/.config/unity3d" \
            -v "$RUNNER_TEMP/unity-local:/root/.local/share/unity3d" \
            "$UNITY_IMAGE" bash -lc '
              set -euxo pipefail
              /opt/unity/Editor/Unity -batchmode -nographics -logFile - \
                -username "$UNITY_EMAIL" -password "$UNITY_PASSWORD" -serial "$UNITY_SERIAL" -quit || true
            '

      - name: Warm up project (import Library once)
        if: steps.detect.outputs.unity_ok == 'true'
        shell: bash
        env:
          UNITY_IMAGE: ${{ env.UNITY_IMAGE }}
          ULF_OK: ${{ steps.ulf.outputs.ok }}
        run: |
          set -euxo pipefail
          manual_args=()
          if [[ "${ULF_OK:-false}" == "true" ]]; then
            manual_args=(-manualLicenseFile "/root/.local/share/unity3d/Unity/Unity_lic.ulf")
          fi
          docker run --rm --network host \
            -e HOME=/root \
            -v "${{ github.workspace }}:${{ github.workspace }}" -w "${{ github.workspace }}" \
            -v "$RUNNER_TEMP/unity-config:/root/.config/unity3d" \
            -v "$RUNNER_TEMP/unity-local:/root/.local/share/unity3d" \
            -v "$RUNNER_TEMP/unity-cache:/root/.cache/unity3d" \
            "$UNITY_IMAGE" /opt/unity/Editor/Unity -batchmode -nographics -logFile - \
              -projectPath "${{ github.workspace }}/TestProjects/UnityMCPTests" \
              "${manual_args[@]}" -quit

      - name: Clean old MCP status
        if: steps.detect.outputs.unity_ok == 'true'
        run: |
          set -eux
          mkdir -p "$GITHUB_WORKSPACE/.unity-mcp"
          rm -f "$GITHUB_WORKSPACE/.unity-mcp"/unity-mcp-status-*.json || true

      - name: Run headless bridge harness (boot + wait + smoke/editmode/playmode)
        if: steps.detect.outputs.unity_ok == 'true'
        shell: bash
        env:
          UNITY_IMAGE: ${{ env.UNITY_IMAGE }}
          ULF_OK: ${{ steps.ulf.outputs.ok }}
        run: |
          set -euxo pipefail
          # In --ci mode the harness drives the DockerLauncher: it runs the same
          # docker container (repo .unity-mcp status dir, docker liveness/teardown,
          # log redaction), waits on the status file, derives the instance, then
          # runs the smoke + EditMode + PlayMode legs over the bridge.
          license_args=()
          if [[ "${ULF_OK:-false}" == "true" ]]; then
            license_args=(--editor-arg -manualLicenseFile \
              --editor-arg "/root/.local/share/unity3d/Unity/Unity_lic.ulf")
          fi
          python3 tools/local_harness.py --ci \
            --legs smoke,editmode,playmode \
            --project-path TestProjects/UnityMCPTests \
            --reports reports \
            "${license_args[@]}"

      - name: Unity logs on failure
        if: failure() && steps.detect.outputs.unity_ok == 'true'
        run: docker logs unity-mcp --tail 200 | sed -E 's/((email|serial|license|password|token)[^[:space:]]*)/[REDACTED]/Ig' || true

      - name: Upload E2E report
        if: always() && steps.detect.outputs.unity_ok == 'true'
        uses: actions/upload-artifact@v4
        with:
          name: e2e-bridge-report
          path: reports/junit-*.xml
          if-no-files-found: ignore
