name: Push Verification

on:
  push:
  workflow_dispatch:

permissions:
  contents: read

concurrency:
  group: openq4-push-verification-${{ github.ref }}
  cancel-in-progress: true

jobs:
  script-smoke:
    name: Validation Script Smoke
    runs-on: ubuntu-24.04
    env:
      OPENQ4_GAMELIBS_REPO: ${{ github.workspace }}/../openQ4-game

    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Fetch openQ4-game
        shell: bash
        run: |
          set -euo pipefail
          git clone --depth 1 https://github.com/themuffinator/openQ4-game.git "${OPENQ4_GAMELIBS_REPO}"

      - name: Setup Python
        uses: actions/setup-python@v6
        with:
          python-version: "3.x"

      - name: Check validation script syntax
        shell: bash
        run: |
          set -euo pipefail
          python -m py_compile \
            tools/build/build_openq4_pack.py \
            tools/build/build_pak0.py \
            tools/build/build_windows_installer.py \
            tools/build/check_staged_content_edits.py \
            tools/build/generate_pak_header.py \
            tools/build/generate_release_changelog.py \
            tools/build/generate_release_docs.py \
            tools/build/linux_metadata.py \
            tools/build/list_pak_sources.py \
            tools/build/openq4_pak.py \
            tools/build/openq4_release_version.py \
            tools/build/openq4_version.py \
            tools/build/package_nightly.py \
            tools/build/package_release.py \
            tools/build/stage_fast_install.py \
            tools/build/stage_gamelibs.py \
            tools/build/stage_windows_runtime.py \
            tools/build/sync_icons.py \
            tools/build/windows_runtime.py \
            tools/build/write_pak_manifest.py \
            src/sys/linux/pk4/id_utils.py \
            tools/validation/openq4_validate.py \
            tools/tests/renderer_validation_matrix.py \
            tools/tests/campaign_split_state_transition.py \
            tools/tests/docs_link_integrity.py \
            tools/tests/filesystem_case_segments.py \
            tools/tests/filesystem_mod_manifest.py \
            tools/tests/game_class_allocator_alignment.py \
            tools/tests/gamelibs_staging.py \
            tools/tests/hdr_postprocess_math.py \
            tools/tests/linux_arm64_ci_coverage.py \
            tools/tests/linux_gui_presentation_defaults.py \
            tools/tests/linux_highdpi_mouse.py \
            tools/tests/linux_metadata_fuzz.py \
            tools/tests/linux_packaging_guidance.py \
            tools/tests/linux_sanitizer_ci.py \
            tools/tests/linux_sdl3_glew_loader.py \
            tools/tests/linux_vsync_support.py \
            tools/tests/loading_continue_input.py \
            tools/tests/macos_apple_gl21_arb2_corridor.py \
            tools/tests/macos_evidence_plumbing.py \
            tools/tests/macos_evidence_recording.py \
            tools/tests/macos_gamelibs_alignment.py \
            tools/tests/macos_local_validation_track.py \
            tools/tests/macos_matrix_policy.py \
            tools/tests/macos_native_backend_guard.py \
            tools/tests/macos_native_backend_containment.py \
            tools/tests/macos_openal_provider_policy.py \
            tools/tests/macos_package_policy.py \
            tools/tests/macos_package_robustness.py \
            tools/tests/macos_renderer_backend_policy.py \
            tools/tests/macos_renderer_phase_breadcrumbs.py \
            tools/tests/macos_renderer_startup_guard.py \
            tools/tests/macos_sdl3_backend_guard.py \
            tools/tests/macos_shadow_policy.py \
            tools/tests/macos_signoff_archive.py \
            tools/tests/macos_static_policy.py \
            tools/tests/macos_support_claim_policy.py \
            tools/tests/macos_support_intake.py \
            tools/tests/macos_symbolication_policy.py \
            tools/tests/macos_metal_bridge.py \
            tools/tests/native_glx_shutdown.py \
            tools/tests/openq4_pure_pack.py \
            tools/tests/packaging_safety.py \
            tools/tests/preprocessor_macro_safety.py \
            tools/tests/posix_memory_management.py \
            tools/tests/posix_monotonic_time.py \
            tools/tests/posix_network_resolution.py \
            tools/tests/posix_thread_shutdown.py \
            tools/tests/release_tooling_safety.py \
            tools/tests/renderer_msaa_cvar_safety.py \
            tools/tests/renderer_supersampling_safety.py \
            tools/tests/savegame_corruption_contract.py \
            tools/tests/sdl3_input_parity.py \
            tools/tests/sdl3_multidisplay_windowing.py \
            tools/tests/settings_menu_coverage.py \
            tools/tests/steam_deck_support.py \
            tools/tests/startup_language_override.py \
            tools/tests/validation_hardening.py \
            tools/tests/vscode_fast_build.py
          bash -n tools/build/meson_setup.sh
          bash -n tools/macos/collect_macos_support_info.sh
          bash -n tools/validation/validate_macos_static.sh
          bash -n tools/validation/validate_push.sh
          bash -n tools/validation/validate_pr.sh
          python tools/tests/campaign_split_state_transition.py
          python tools/tests/docs_link_integrity.py
          python tools/tests/filesystem_case_segments.py
          python tools/tests/filesystem_mod_manifest.py
          python tools/tests/game_class_allocator_alignment.py
          python tools/tests/gamelibs_staging.py
          python tools/tests/hdr_postprocess_math.py
          python tools/tests/linux_arm64_ci_coverage.py
          python tools/tests/linux_gui_presentation_defaults.py
          python tools/tests/linux_highdpi_mouse.py
          python tools/tests/linux_metadata_fuzz.py
          python tools/tests/linux_packaging_guidance.py
          python tools/tests/linux_pk4_legacy_tools.py
          python tools/tests/linux_sanitizer_ci.py
          python tools/tests/linux_sdl3_glew_loader.py
          python tools/tests/linux_vsync_support.py
          python tools/tests/loading_continue_input.py
          python tools/tests/macos_apple_gl21_arb2_corridor.py
          python tools/tests/macos_evidence_plumbing.py
          python tools/tests/macos_evidence_recording.py
          python tools/tests/macos_gamelibs_alignment.py
          python tools/tests/macos_local_validation_track.py
          python tools/tests/macos_matrix_policy.py
          python tools/tests/macos_native_backend_guard.py
          python tools/tests/macos_native_backend_containment.py
          python tools/tests/macos_openal_provider_policy.py
          python tools/tests/macos_package_policy.py
          python tools/tests/macos_package_robustness.py
          python tools/tests/macos_renderer_backend_policy.py
          python tools/tests/macos_renderer_phase_breadcrumbs.py
          python tools/tests/macos_renderer_startup_guard.py
          python tools/tests/macos_sdl3_backend_guard.py
          python tools/tests/macos_shadow_policy.py
          python tools/tests/macos_signoff_archive.py
          python tools/tests/macos_static_policy.py
          python tools/tests/macos_support_claim_policy.py
          python tools/tests/macos_support_intake.py
          python tools/tests/macos_symbolication_policy.py
          python tools/tests/macos_metal_bridge.py
          python tools/tests/native_glx_shutdown.py
          python tools/tests/openq4_pure_pack.py
          python tools/tests/packaging_safety.py
          python tools/tests/preprocessor_macro_safety.py
          python tools/tests/release_tooling_safety.py
          python tools/tests/posix_memory_management.py
          python tools/tests/posix_monotonic_time.py
          python tools/tests/posix_network_resolution.py
          python tools/tests/posix_thread_shutdown.py
          python tools/tests/renderer_msaa_cvar_safety.py
          python tools/tests/renderer_supersampling_safety.py
          python tools/tests/savegame_corruption_contract.py
          python tools/tests/sdl3_input_parity.py
          python tools/tests/sdl3_multidisplay_windowing.py
          python tools/tests/settings_menu_coverage.py
          python tools/tests/steam_deck_support.py
          python tools/tests/startup_language_override.py
          python tools/tests/validation_hardening.py
          python tools/tests/vscode_fast_build.py

      - name: Dry-run push profile
        shell: bash
        run: |
          set -euo pipefail
          bash tools/validation/validate_push.sh --dry-run

  posix-builds:
    name: ${{ matrix.name }}
    needs: script-smoke
    runs-on: ${{ matrix.os }}
    timeout-minutes: 90
    env:
      OPENQ4_GAMELIBS_REPO: ${{ github.workspace }}/../openQ4-game
    strategy:
      fail-fast: false
      matrix:
        include:
          - name: Linux Push Verification
            os: ubuntu-24.04
            artifact_name: linux-x64
            runtime_smoke: false
            macos_graphics_bridge: ""
            macos_openal_provider: ""
          - name: Linux ARM64 Push Verification
            os: ubuntu-24.04-arm
            artifact_name: linux-arm64
            runtime_smoke: true
            macos_graphics_bridge: ""
            macos_openal_provider: ""
          - name: macOS OpenGL Push Verification
            os: macos-15
            artifact_name: macos-opengl
            runtime_smoke: false
            macos_graphics_bridge: opengl
            macos_openal_provider: apple_framework
          - name: macOS Metal Push Verification
            os: macos-15
            artifact_name: macos-metal
            runtime_smoke: false
            macos_graphics_bridge: metal
            macos_openal_provider: apple_framework
    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Fetch openQ4-game
        shell: bash
        run: |
          set -euo pipefail
          git clone --depth 1 https://github.com/themuffinator/openQ4-game.git "${OPENQ4_GAMELIBS_REPO}"
      - name: Setup Python
        uses: actions/setup-python@v6
        with:
          python-version: "3.x"

      - name: Install Meson and Ninja
        shell: bash
        run: |
          set -euo pipefail
          python -m pip install --upgrade pip
          python -m pip install meson ninja

      - name: Install Linux native dependencies
        if: startsWith(matrix.os, 'ubuntu-')
        shell: bash
        run: |
          set -euo pipefail
          for attempt in 1 2 3; do
            if sudo apt-get update && sudo apt-get install -y \
              binutils \
              libasound2-dev \
              libdbus-1-dev \
              libdecor-0-dev \
              libdrm-dev \
              libegl1-mesa-dev \
              libfribidi-dev \
              libgbm-dev \
              libgl1-mesa-dri \
              libgl1-mesa-dev \
              libglx-mesa0 \
              libibus-1.0-dev \
              libjack-dev \
              libopenal-dev \
              libpipewire-0.3-dev \
              libpulse-dev \
              libsndio-dev \
              libthai-dev \
              libudev-dev \
              libwayland-dev \
              libx11-dev \
              libxcursor-dev \
              libxext-dev \
              libxfixes-dev \
              libxi-dev \
              libxkbcommon-dev \
              libxrandr-dev \
              libxss-dev \
              libxtst-dev \
              libxxf86dga-dev \
              libxxf86vm-dev \
              xvfb; then
              exit 0
            fi

            if [ "${attempt}" -eq 3 ]; then
              echo "Failed to install Linux native dependencies after ${attempt} attempts."
              exit 1
            fi

            echo "Linux dependency installation attempt ${attempt} failed; retrying in 10 seconds..."
            sleep 10
          done

      - name: Run push verification
        shell: bash
        run: |
          set -euo pipefail
          validation_args=(--install --fail-on-dirty)
          if [[ -n "${{ matrix.macos_graphics_bridge }}" ]]; then
            validation_args+=(
              --platform-backend=sdl3
              --extra-setup-arg=-Dmacos_graphics_bridge=${{ matrix.macos_graphics_bridge }}
              --extra-setup-arg=-Dmacos_openal_provider=${{ matrix.macos_openal_provider }}
            )
          fi
          if [[ "${{ matrix.runtime_smoke }}" == "true" ]]; then
            validation_args+=(
              --runtime
              --runtime-cases renderer-default-safety-selftest,renderer-shadow-planner-selftest,renderer-shadow-projected-diagnostic,sdl3-x11-display-diagnostics
              --runtime-tiers auto
              --runtime-basepath ""
              --runtime-skip-official-pak-validation
            )
            env LIBGL_ALWAYS_SOFTWARE=1 SDL_VIDEO_DRIVER=x11 SDL_VIDEODRIVER=x11 xvfb-run -a bash tools/validation/validate_push.sh "${validation_args[@]}"
            env LIBGL_ALWAYS_SOFTWARE=1 OPENQ4_FORCE_X11=1 xvfb-run -a python tools/tests/renderer_validation_matrix.py \
              --cases sdl3-force-x11-display-diagnostics \
              --tiers auto \
              --basepath "" \
              --skip-official-pak-validation \
              --output-dir .tmp/renderer-validation/force-x11
          else
            bash tools/validation/validate_push.sh "${validation_args[@]}"
          fi

      - name: Publish staged payload
        if: always()
        uses: actions/upload-artifact@v6
        with:
          name: push-${{ matrix.artifact_name }}-payload
          path: .install
          if-no-files-found: warn
          retention-days: 14
          include-hidden-files: true

      - name: Publish renderer validation reports
        if: always() && matrix.runtime_smoke == true
        uses: actions/upload-artifact@v6
        with:
          name: push-${{ matrix.artifact_name }}-renderer-validation
          path: .tmp/renderer-validation
          if-no-files-found: warn
          retention-days: 14
          include-hidden-files: true

  windows-build:
    name: Windows Push Verification
    needs: script-smoke
    runs-on: windows-2025-vs2026
    timeout-minutes: 90
    env:
      OPENQ4_GAMELIBS_REPO: ${{ github.workspace }}/../openQ4-game
    steps:
      - name: Checkout
        uses: actions/checkout@v6

      - name: Fetch openQ4-game
        shell: pwsh
        run: |
          $ErrorActionPreference = "Stop"
          git clone --depth 1 https://github.com/themuffinator/openQ4-game.git "$env:OPENQ4_GAMELIBS_REPO"

      - name: Setup Python
        uses: actions/setup-python@v6
        with:
          python-version: "3.x"

      - name: Install Meson and Ninja
        shell: pwsh
        run: |
          $ErrorActionPreference = "Stop"
          python -m pip install --upgrade pip
          python -m pip install meson ninja

      - name: Prepare OpenAL Soft (Windows)
        shell: pwsh
        run: |
          $ErrorActionPreference = "Stop"
          $outputRoot = Join-Path $env:GITHUB_WORKSPACE ".tmp/openal-soft-x64"
          $env:OPENQ4_VS_TARGET_ARCH = "x64"
          powershell -ExecutionPolicy Bypass -File tools/build/prepare_windows_openal.ps1 -Architecture "x64" -OutputRoot $outputRoot
          "OPENQ4_OPENAL_ROOT=$outputRoot" >> $env:GITHUB_ENV

      - name: Run push verification
        shell: pwsh
        run: |
          $ErrorActionPreference = "Stop"
          powershell -NoProfile -ExecutionPolicy Bypass -File tools/validation/validate_push.ps1 --install --fail-on-dirty

      - name: Publish staged payload
        if: always()
        uses: actions/upload-artifact@v6
        with:
          name: push-windows-payload
          path: .install
          if-no-files-found: warn
          retention-days: 14
          include-hidden-files: true
