name: Unix Release

on:
  workflow_dispatch:
  push:
    tags:
      - "v*"

permissions:
  contents: write

jobs:
  build-unix-release:
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, macos-latest]

    runs-on: ${{ matrix.os }}

    steps:
      - name: Check out repository
        uses: actions/checkout@v4

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@stable

      - name: Cache Rust build outputs
        uses: Swatinem/rust-cache@v2

      - name: Install Linux build dependencies
        if: runner.os == 'Linux'
        run: |
          sudo apt-get update
          sudo apt-get install -y libasound2-dev libpcaudio-dev libsonic-dev

      - name: Restore voice model assets cache
        uses: actions/cache/restore@v4
        id: cache-voice
        with:
          path: .hematite/assets/voice
          key: kokoro-voice-assets-v1.0
          restore-keys: |
            kokoro-voice-assets-

      - name: Download voice model assets
        run: |
          mkdir -p .hematite/assets/voice
          base="https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files-v1.0"
          [[ -f .hematite/assets/voice/kokoro-v1.0.onnx ]] || curl -L --retry 3 --max-time 300 -o .hematite/assets/voice/kokoro-v1.0.onnx "$base/kokoro-v1.0.onnx"
          [[ -f .hematite/assets/voice/voices.bin ]] || curl -L --retry 3 --max-time 120 -o .hematite/assets/voice/voices.bin "$base/voices-v1.0.bin"

      - name: Save voice model assets cache
        if: always() && steps.cache-voice.outputs.cache-hit != 'true'
        uses: actions/cache/save@v4
        with:
          path: .hematite/assets/voice
          key: kokoro-voice-assets-v1.0

      # ── ORT Linux workaround ────────────────────────────────────────────────────
      # cdn.pyke.io (the CDN that serves pyke's custom ORT builds for the `ort`
      # Rust crate) broke for the Linux target in April 2026 while macOS/Windows
      # continued to work. We bypass it entirely by downloading the identical
      # ORT 1.24.2 binary from Microsoft's official GitHub releases instead.
      #
      # Four non-obvious requirements to make this work:
      # 1. Create a bare libonnxruntime.so symlink — Microsoft ships only the
      #    versioned .so.X.Y.Z; the linker needs the unversioned name.
      # 2. Set ORT_PREFER_DYNAMIC_LINK=1 — ort-sys defaults to static (.a) when
      #    ORT_LIB_LOCATION is set; Microsoft's release is dynamic-only.
      # 3. ORT_LIB_LOCATION points to the root dir (containing lib/); ort-sys
      #    searches {root}/lib/ for the .so.
      # 4. LIBRARY_PATH + LD_LIBRARY_PATH — lld needs the explicit search path
      #    even after ort-sys validates the location successfully.
      #
      # The cache key is stable (not tied to Cargo.lock) so subsequent releases
      # hit the cache and never contact pyke.io or GitHub for ORT again.
      # If pyke.io recovers, this path still works and is strictly more reliable.
      # To revert to the default CDN path: remove these four steps entirely.
      # ────────────────────────────────────────────────────────────────────────────
      - name: Cache ORT library (Linux)
        if: runner.os == 'Linux'
        uses: actions/cache@v4
        id: cache-ort-lib
        with:
          path: ~/.cache/ort-lib
          key: ort-ms-1.24.2-x86_64-linux-v5

      - name: Pre-fetch ORT library (Linux)
        if: runner.os == 'Linux' && steps.cache-ort-lib.outputs.cache-hit != 'true'
        shell: bash
        run: |
          rm -rf ~/.cache/ort-lib && mkdir -p ~/.cache/ort-lib
          GH_URL="https://github.com/microsoft/onnxruntime/releases/download/v1.24.2/onnxruntime-linux-x64-1.24.2.tgz"
          echo "Downloading ORT 1.24.2 from Microsoft GitHub releases..."
          curl -fSL --retry 3 --retry-delay 10 --max-time 180 -L -o /tmp/ort.tgz "$GH_URL"
          tar -xzf /tmp/ort.tgz -C ~/.cache/ort-lib --strip-components=1
          echo "=== ORT extraction layout ==="
          find ~/.cache/ort-lib -name "libonnxruntime*" | sort
          echo "============================="
          # Find the directory containing the versioned .so and ensure bare symlink exists
          VERSIONED=$(find ~/.cache/ort-lib -name "libonnxruntime.so.*" -not -type l | head -1)
          if [ -z "$VERSIONED" ]; then
            echo "ERROR: libonnxruntime.so.* not found after extraction" >&2
            exit 1
          fi
          LIB_DIR=$(dirname "$VERSIONED")
          echo "Library directory: $LIB_DIR"
          if [ ! -f "$LIB_DIR/libonnxruntime.so" ]; then
            ln -sf "$(basename "$VERSIONED")" "$LIB_DIR/libonnxruntime.so"
            echo "Created symlink: libonnxruntime.so -> $(basename "$VERSIONED")"
          fi
          # Store the resolved path for the next step
          echo "$LIB_DIR" > ~/.cache/ort-lib/.ort_lib_dir

      - name: Set ORT_LIB_LOCATION (Linux)
        if: runner.os == 'Linux'
        shell: bash
        run: |
          if [ -f "$HOME/.cache/ort-lib/.ort_lib_dir" ]; then
            LIB_DIR=$(cat "$HOME/.cache/ort-lib/.ort_lib_dir")
            ORT_ROOT=$(dirname "$LIB_DIR")
            echo "ORT_LIB_LOCATION=$ORT_ROOT" >> "$GITHUB_ENV"
            echo "ORT_PREFER_DYNAMIC_LINK=1" >> "$GITHUB_ENV"
            # LIBRARY_PATH: tells the C linker (lld) where to find -lonnxruntime at link time.
            # LD_LIBRARY_PATH: tells the dynamic loader where to find it at runtime/test time.
            echo "LIBRARY_PATH=$LIB_DIR" >> "$GITHUB_ENV"
            echo "LD_LIBRARY_PATH=$LIB_DIR" >> "$GITHUB_ENV"
            echo "ORT_LIB_LOCATION=$ORT_ROOT  LIBRARY_PATH=$LIB_DIR"
            ls "$LIB_DIR/libonnxruntime.so" && echo "symlink confirmed" || echo "WARNING: symlink missing"
          else
            echo "::warning::ORT pre-fetch did not run or failed - build will attempt CDN download"
          fi

      - name: Build portable archive
        shell: bash
        run: |
          for attempt in 1 2 3; do
            echo "Build attempt $attempt..."
            bash ./scripts/package-unix.sh && break
            if [ $attempt -lt 3 ]; then
              echo "Build failed (attempt $attempt), retrying in 30s..."
              sleep 30
            else
              echo "All build attempts failed."
              exit 1
            fi
          done

      - name: Upload packaged artifacts
        uses: actions/upload-artifact@v4
        with:
          name: hematite-${{ runner.os }}
          path: dist/*/*.tar.gz

      - name: Publish GitHub release assets
        if: startsWith(github.ref, 'refs/tags/')
        uses: softprops/action-gh-release@v2
        with:
          files: dist/*/*.tar.gz
