name: Release App

on:
  push:
    tags:
      - "v*"
  workflow_dispatch:
    inputs:
      tag:
        description: "Tag to release (e.g., v0.1.2). Leave empty to use current ref."
        required: false
        type: string
      release_name:
        description: "Release title (defaults to OpenWork <tag>)"
        required: false
        type: string
      release_body:
        description: "Release notes body in Markdown (defaults to a short placeholder)"
        required: false
        type: string
      draft:
        description: "Create the GitHub Release as a draft"
        required: false
        type: boolean
        default: false
      prerelease:
        description: "Mark the GitHub Release as a prerelease"
        required: false
        type: boolean
        default: false
      notarize:
        description: "Notarize macOS builds (requires Apple team configured)"
        required: false
        type: boolean
        default: false

permissions:
  contents: write
  pull-requests: write

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

jobs:
  prepare-release:
    name: Prepare GitHub Release
    runs-on: ubuntu-latest
    steps:
      - name: Set release metadata
        shell: bash
        env:
          INPUT_TAG: ${{ github.event.inputs.tag }}
          INPUT_RELEASE_NAME: ${{ github.event.inputs.release_name }}
          INPUT_RELEASE_BODY: ${{ github.event.inputs.release_body }}
        run: |
          set -euo pipefail

          TAG_INPUT="${INPUT_TAG:-}"
          if [ -n "$TAG_INPUT" ]; then
            if [[ "$TAG_INPUT" == v* ]]; then
              TAG="$TAG_INPUT"
            else
              TAG="v$TAG_INPUT"
            fi
          else
            TAG="${GITHUB_REF_NAME}"
          fi

          RELEASE_NAME_INPUT="${INPUT_RELEASE_NAME:-}"
          if [ -n "$RELEASE_NAME_INPUT" ]; then
            RELEASE_NAME="$RELEASE_NAME_INPUT"
          else
            RELEASE_NAME="OpenWork $TAG"
          fi

          RELEASE_BODY_INPUT="${INPUT_RELEASE_BODY:-}"
          if [ -n "$RELEASE_BODY_INPUT" ]; then
            RELEASE_BODY="$RELEASE_BODY_INPUT"
          else
            RELEASE_BODY="See the assets to download this version and install."
          fi

          # Guard against multiline env var injection
          TAG="${TAG//$'\n'/}"
          TAG="${TAG//$'\r'/}"
          RELEASE_NAME="${RELEASE_NAME//$'\n'/ }"
          RELEASE_NAME="${RELEASE_NAME//$'\r'/ }"

          echo "RELEASE_TAG=$TAG" >> "$GITHUB_ENV"
          echo "RELEASE_NAME=$RELEASE_NAME" >> "$GITHUB_ENV"

          {
            echo "RELEASE_BODY<<__OPENWORK_RELEASE_BODY_EOF__"
            printf '%s\n' "$RELEASE_BODY"
            echo "__OPENWORK_RELEASE_BODY_EOF__"
          } >> "$GITHUB_ENV"

      - name: Create release if missing
        shell: bash
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          set -euo pipefail

          BODY_FILE="$RUNNER_TEMP/release_body.md"
          printf '%s\n' "$RELEASE_BODY" > "$BODY_FILE"

          if gh release view "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
            echo "Release $RELEASE_TAG already exists; skipping create."
            exit 0
          fi

          DRAFT_FLAG=""
          PRERELEASE_FLAG=""
          if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.draft }}" == "true" ]]; then
            DRAFT_FLAG="--draft"
          fi
          if [[ "${{ github.event_name }}" == "workflow_dispatch" && "${{ github.event.inputs.prerelease }}" == "true" ]]; then
            PRERELEASE_FLAG="--prerelease"
          fi

          gh release create "$RELEASE_TAG" \
            --repo "$GITHUB_REPOSITORY" \
            --title "$RELEASE_NAME" \
            --notes-file "$BODY_FILE" \
            $DRAFT_FLAG $PRERELEASE_FLAG

  publish-tauri:
    name: Build + Publish (${{ matrix.target }})
    needs: prepare-release
    runs-on: ${{ matrix.platform }}
    timeout-minutes: 360
    env:
      MACOS_NOTARIZE: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.notarize || vars.MACOS_NOTARIZE || 'false' }}

    strategy:
      fail-fast: false
      matrix:
        include:
          - platform: macos-14
            os_type: macos
            target: aarch64-apple-darwin
            args: "--target aarch64-apple-darwin --bundles dmg,app"
          - platform: macos-14
            os_type: macos
            target: x86_64-apple-darwin
            args: "--target x86_64-apple-darwin --bundles dmg,app"
          - platform: ubuntu-22.04
            os_type: linux
            target: x86_64-unknown-linux-gnu
            args: "--target x86_64-unknown-linux-gnu --bundles deb,rpm"
          - platform: windows-2022
            os_type: windows
            target: x86_64-pc-windows-msvc
            args: "--target x86_64-pc-windows-msvc --bundles msi"

    steps:
      - name: Set release metadata
        shell: bash
        env:
          INPUT_TAG: ${{ github.event.inputs.tag }}
          INPUT_RELEASE_NAME: ${{ github.event.inputs.release_name }}
          INPUT_RELEASE_BODY: ${{ github.event.inputs.release_body }}
        run: |
          set -euo pipefail

          TAG_INPUT="${INPUT_TAG:-}"
          if [ -n "$TAG_INPUT" ]; then
            if [[ "$TAG_INPUT" == v* ]]; then
              TAG="$TAG_INPUT"
            else
              TAG="v$TAG_INPUT"
            fi
          else
            TAG="${GITHUB_REF_NAME}"
          fi

          RELEASE_NAME_INPUT="${INPUT_RELEASE_NAME:-}"
          if [ -n "$RELEASE_NAME_INPUT" ]; then
            RELEASE_NAME="$RELEASE_NAME_INPUT"
          else
            RELEASE_NAME="OpenWork $TAG"
          fi

          RELEASE_BODY_INPUT="${INPUT_RELEASE_BODY:-}"
          if [ -n "$RELEASE_BODY_INPUT" ]; then
            RELEASE_BODY="$RELEASE_BODY_INPUT"
          else
            RELEASE_BODY="See the assets to download this version and install."
          fi

          # Guard against multiline env var injection
          TAG="${TAG//$'\n'/}"
          TAG="${TAG//$'\r'/}"
          RELEASE_NAME="${RELEASE_NAME//$'\n'/ }"
          RELEASE_NAME="${RELEASE_NAME//$'\r'/ }"

          echo "RELEASE_TAG=$TAG" >> "$GITHUB_ENV"
          echo "RELEASE_NAME=$RELEASE_NAME" >> "$GITHUB_ENV"

          {
            echo "RELEASE_BODY<<__OPENWORK_RELEASE_BODY_EOF__"
            printf '%s\n' "$RELEASE_BODY"
            echo "__OPENWORK_RELEASE_BODY_EOF__"
          } >> "$GITHUB_ENV"

      - name: Enable git long paths (Windows)
        if: matrix.os_type == 'windows'
        shell: pwsh
        run: git config --global core.longpaths true

      - name: Checkout
        uses: actions/checkout@v4
        with:
          ref: ${{ env.RELEASE_TAG }}

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Setup pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 10.27.0

      - name: Setup Bun
        uses: oven-sh/setup-bun@v1
        with:
          bun-version: "1.3.6"

      - name: Install dependencies
        run: pnpm install --frozen-lockfile

      - name: Install Linux build dependencies
        if: matrix.os_type == 'linux'
        run: |
          sudo apt-get update
          sudo apt-get install -y \
            libgtk-3-dev \
            libglib2.0-dev \
            libayatana-appindicator3-dev \
            libsoup-3.0-dev \
            libwebkit2gtk-4.1-dev \
            libssl-dev \
            rpm \
            libdbus-1-dev \
            librsvg2-dev

      - name: Setup Rust
        uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

      - name: Download OpenCode sidecar
        shell: bash
        env:
          OPENCODE_VERSION: 1.1.36
          GITHUB_TOKEN: ${{ github.token }}
        run: |
          set -euo pipefail

          version="${OPENCODE_VERSION}"
          if [ -n "${GITHUB_TOKEN:-}" ]; then
            latest=$(curl -fsSL \
              -H "Authorization: Bearer ${GITHUB_TOKEN}" \
              -H "X-GitHub-Api-Version: 2022-11-28" \
              https://api.github.com/repos/anomalyco/opencode/releases/latest \
              | sed -n 's/.*"tag_name": *"v\([^"]*\)".*/\1/p')
            if [ -n "$latest" ]; then
              version="$latest"
            fi
          fi

          case "${{ matrix.target }}" in
            aarch64-apple-darwin)
              opencode_asset="opencode-darwin-arm64.zip"
              ;;
            x86_64-apple-darwin)
              opencode_asset="opencode-darwin-x64-baseline.zip"
              ;;
            x86_64-unknown-linux-gnu)
              opencode_asset="opencode-linux-x64-baseline.tar.gz"
              ;;
            x86_64-pc-windows-msvc)
              opencode_asset="opencode-windows-x64-baseline.zip"
              ;;
            *)
              echo "Unsupported target: ${{ matrix.target }}"
              exit 1
              ;;
          esac

          url="https://github.com/anomalyco/opencode/releases/download/v${version}/${opencode_asset}"
          tmp_dir="$RUNNER_TEMP/opencode"
          extract_dir="$tmp_dir/extracted"
          rm -rf "$tmp_dir"
          mkdir -p "$extract_dir"
          curl -fsSL -o "$tmp_dir/$opencode_asset" "$url"

          if [[ "$opencode_asset" == *.tar.gz ]]; then
            tar -xzf "$tmp_dir/$opencode_asset" -C "$extract_dir"
          else
            if command -v unzip >/dev/null 2>&1; then
              unzip -q "$tmp_dir/$opencode_asset" -d "$extract_dir"
            elif command -v 7z >/dev/null 2>&1; then
              7z x "$tmp_dir/$opencode_asset" -o"$extract_dir" >/dev/null
            else
              echo "No unzip utility available"
              exit 1
            fi
          fi

          if [ -f "$extract_dir/opencode" ]; then
            bin_path="$extract_dir/opencode"
          elif [ -f "$extract_dir/opencode.exe" ]; then
            bin_path="$extract_dir/opencode.exe"
          else
            echo "OpenCode binary not found in archive"
            ls -la "$extract_dir"
            exit 1
          fi

          target_name="opencode-${{ matrix.target }}"
          if [ "${{ matrix.os_type }}" = "windows" ]; then
            target_name="${target_name}.exe"
          fi

          mkdir -p packages/desktop/src-tauri/sidecars
          cp "$bin_path" "packages/desktop/src-tauri/sidecars/${target_name}"
          chmod 755 "packages/desktop/src-tauri/sidecars/${target_name}"

      - name: Write notary API key
        if: matrix.os_type == 'macos' && env.MACOS_NOTARIZE == 'true'
        env:
          APPLE_NOTARY_API_KEY_P8_BASE64: ${{ secrets.APPLE_NOTARY_API_KEY_P8_BASE64 }}
        run: |
          set -euo pipefail

          NOTARY_KEY_PATH="$RUNNER_TEMP/AuthKey.p8"
          printf '%s' "$APPLE_NOTARY_API_KEY_P8_BASE64" | base64 --decode > "$NOTARY_KEY_PATH"
          chmod 600 "$NOTARY_KEY_PATH"

          echo "NOTARY_KEY_PATH=$NOTARY_KEY_PATH" >> "$GITHUB_ENV"

      - name: Build + upload (notarized)
        if: matrix.os_type == 'macos' && env.MACOS_NOTARIZE == 'true'
        uses: tauri-apps/tauri-action@390cbe447412ced1303d35abe75287949e43437a
        env:
          CI: true
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

          # Tauri updater signing
          TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
          TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}

          # macOS signing
          APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
          APPLE_CERTIFICATE: ${{ secrets.APPLE_CODESIGN_CERT_P12_BASE64 }}
          APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CODESIGN_CERT_PASSWORD }}

          # macOS notarization (App Store Connect API key)
          APPLE_API_KEY: ${{ secrets.APPLE_NOTARY_API_KEY_ID }}
          APPLE_API_ISSUER: ${{ secrets.APPLE_NOTARY_API_ISSUER_ID }}
          APPLE_API_KEY_PATH: ${{ env.NOTARY_KEY_PATH }}
        with:
          tagName: ${{ env.RELEASE_TAG }}
          releaseName: ${{ env.RELEASE_NAME }}
          releaseBody: ${{ env.RELEASE_BODY }}
          releaseDraft: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.draft == 'true' }}
          prerelease: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.prerelease == 'true' }}
          projectPath: packages/desktop
          tauriScript: pnpm exec tauri -vvv
          args: ${{ matrix.args }}
          retryAttempts: 3
          includeUpdaterJson: true
          updaterJsonPreferNsis: true
          releaseAssetNamePattern: openwork-desktop-[platform]-[arch][ext]

      - name: Build + upload
        if: matrix.os_type != 'macos' || env.MACOS_NOTARIZE != 'true'
        uses: tauri-apps/tauri-action@390cbe447412ced1303d35abe75287949e43437a
        env:
          CI: true
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

          # Tauri updater signing
          TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
          TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}

          # macOS signing
          APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
          APPLE_CERTIFICATE: ${{ secrets.APPLE_CODESIGN_CERT_P12_BASE64 }}
          APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CODESIGN_CERT_PASSWORD }}
        with:
          tagName: ${{ env.RELEASE_TAG }}
          releaseName: ${{ env.RELEASE_NAME }}
          releaseBody: ${{ env.RELEASE_BODY }}
          releaseDraft: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.draft == 'true' }}
          prerelease: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.prerelease == 'true' }}
          projectPath: packages/desktop
          tauriScript: pnpm exec tauri -vvv
          args: ${{ matrix.args }}
          retryAttempts: 3
          includeUpdaterJson: true
          updaterJsonPreferNsis: true
          releaseAssetNamePattern: openwork-desktop-[platform]-[arch][ext]

  aur-pr:
    name: Open AUR update PR
    needs: publish-tauri
    runs-on: ubuntu-latest

    permissions:
      contents: write
      pull-requests: write

    steps:
      - name: Set release metadata
        shell: bash
        env:
          INPUT_TAG: ${{ github.event.inputs.tag }}
        run: |
          set -euo pipefail

          TAG_INPUT="${INPUT_TAG:-}"
          if [ -n "$TAG_INPUT" ]; then
            if [[ "$TAG_INPUT" == v* ]]; then
              TAG="$TAG_INPUT"
            else
              TAG="v$TAG_INPUT"
            fi
          else
            TAG="${GITHUB_REF_NAME}"
          fi

          # Guard against multiline env var injection
          TAG="${TAG//$'\n'/}"
          TAG="${TAG//$'\r'/}"

          echo "RELEASE_TAG=$TAG" >> "$GITHUB_ENV"

      - name: Checkout
        uses: actions/checkout@v4
        with:
          ref: ${{ env.RELEASE_TAG }}

      - name: Open AUR update PR
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: scripts/aur/open-pr.sh "$RELEASE_TAG"
