name: Release Desktop Bridge

on:
  push:
    paths:
      - Cargo.toml
      - Cargo.lock
      - .github/workflows/release.yml
  workflow_dispatch:

permissions:
  contents: write

env:
  BIN_NAME: omni-code-bridge
  HOMEBREW_TAP_REPO: omni-stream-ai/homebrew-omni-code-bridge
  AUR_REPO: omni-code-bridge-bin
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true

jobs:
  prepare-release:
    name: Prepare Release
    runs-on: ubuntu-24.04
    outputs:
      should_release: ${{ steps.detect.outputs.should_release }}
      branch_name: ${{ steps.detect.outputs.branch_name }}
      cargo_version: ${{ steps.detect.outputs.cargo_version }}
      version: ${{ steps.detect.outputs.version }}
      tag: ${{ steps.detect.outputs.tag }}
      previous_tag: ${{ steps.detect.outputs.previous_tag }}
      prerelease: ${{ steps.detect.outputs.prerelease }}
      stable: ${{ steps.detect.outputs.stable }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Validate version and release channel
        id: detect
        shell: bash
        run: |
          set -euo pipefail

          branch_name="${GITHUB_REF_NAME}"
          cargo_version=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1)
          version="$cargo_version"
          tag="v$version"
          previous_tag=""
          prerelease="false"
          stable="false"

          if [[ "$version" == *-* ]]; then
            prerelease="true"
          else
            stable="true"
          fi

          if [[ "$branch_name" == "main" && "$prerelease" == "true" ]]; then
            echo "main cannot publish prerelease versions: $version" >&2
            exit 1
          fi

          if [[ "$branch_name" != "main" && "$stable" == "true" ]]; then
            echo "non-main branches must use a prerelease suffix: $version" >&2
            exit 1
          fi

          git fetch --tags --force
          previous_tag=$(git tag --sort=-version:refname | grep -Fxv "$tag" | head -n1 || true)
          if git rev-parse -q --verify "refs/tags/$tag" >/dev/null; then
            tagged_commit="$(git rev-list -n 1 "$tag")"
            if [[ "$tagged_commit" != "${GITHUB_SHA}" ]]; then
              echo "Tag $tag already exists on ${tagged_commit}; skipping release."
            else
              echo "Tag $tag already exists on the current commit; skipping release."
            fi
            should_release="false"
          else
            should_release="true"
          fi

          {
            echo "should_release=$should_release"
            echo "branch_name=$branch_name"
            echo "cargo_version=$cargo_version"
            echo "version=$version"
            echo "tag=$tag"
            echo "previous_tag=$previous_tag"
            echo "prerelease=$prerelease"
            echo "stable=$stable"
          } >> "$GITHUB_OUTPUT"

  build:
    name: Build ${{ matrix.asset_name }}
    needs: prepare-release
    if: needs.prepare-release.outputs.should_release == 'true'
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-24.04
            target: x86_64-unknown-linux-gnu
            asset_name: omni-code-bridge-linux-x64
            archive_ext: tar.gz
            binary_name: omni-code-bridge
          - os: ubuntu-24.04
            target: aarch64-unknown-linux-gnu
            asset_name: omni-code-bridge-linux-arm64
            archive_ext: tar.gz
            binary_name: omni-code-bridge
          - os: macos-14
            target: aarch64-apple-darwin
            asset_name: omni-code-bridge-macos-arm64
            archive_ext: tar.gz
            binary_name: omni-code-bridge
          - os: macos-14
            target: x86_64-apple-darwin
            asset_name: omni-code-bridge-macos-x64
            archive_ext: tar.gz
            binary_name: omni-code-bridge
          - os: windows-2022
            target: x86_64-pc-windows-msvc
            asset_name: omni-code-bridge-windows-x64
            archive_ext: zip
            binary_name: omni-code-bridge.exe
    steps:
      - name: Checkout
        uses: actions/checkout@v4

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

      - name: Install cross-compilation tools (Linux aarch64)
        if: matrix.target == 'aarch64-unknown-linux-gnu'
        run: |
          sudo apt-get update -q
          sudo apt-get install -y gcc-aarch64-linux-gnu g++-aarch64-linux-gnu

      - name: Build release binary
        env:
          CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
        run: cargo build --release --locked --target ${{ matrix.target }}

      - name: Package Unix artifact
        if: runner.os != 'Windows'
        shell: bash
        run: |
          mkdir -p dist/${{ matrix.asset_name }}
          cp target/${{ matrix.target }}/release/${{ matrix.binary_name }} dist/${{ matrix.asset_name }}/
          tar -C dist -czf dist/${{ matrix.asset_name }}.${{ matrix.archive_ext }} ${{ matrix.asset_name }}

      - name: Package Windows artifact
        if: runner.os == 'Windows'
        shell: pwsh
        run: |
          New-Item -ItemType Directory -Force -Path "dist/${{ matrix.asset_name }}" | Out-Null
          Copy-Item "target/${{ matrix.target }}/release/${{ matrix.binary_name }}" "dist/${{ matrix.asset_name }}/"
          Compress-Archive -Path "dist/${{ matrix.asset_name }}" -DestinationPath "dist/${{ matrix.asset_name }}.${{ matrix.archive_ext }}" -Force

      - name: Upload packaged artifact
        uses: actions/upload-artifact@v4
        with:
          name: ${{ matrix.asset_name }}
          path: dist/${{ matrix.asset_name }}.${{ matrix.archive_ext }}

  release:
    name: Publish Release
    needs:
      - prepare-release
      - build
    if: needs.prepare-release.outputs.should_release == 'true'
    runs-on: ubuntu-24.04
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Create and push tag
        env:
          TAG: ${{ needs.prepare-release.outputs.tag }}
        shell: bash
        run: |
          set -euo pipefail

          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git fetch --tags --force

          if git rev-parse -q --verify "refs/tags/${TAG}" >/dev/null; then
            tagged_commit="$(git rev-list -n 1 "${TAG}")"
            if [[ "$tagged_commit" != "${GITHUB_SHA}" ]]; then
              echo "Tag ${TAG} already exists on ${tagged_commit}, expected ${GITHUB_SHA}." >&2
              exit 1
            fi
          else
            git tag -a "${TAG}" -m "Release ${TAG}"
            git push origin "${TAG}"
          fi

      - name: Download build artifacts
        uses: actions/download-artifact@v4
        with:
          path: dist
          merge-multiple: true

      - name: Build release notes
        id: changelog
        uses: mikepenz/release-changelog-builder-action@v5
        with:
          configuration: .github/release-changelog-config.json
          mode: COMMIT
          toTag: ${{ needs.prepare-release.outputs.tag }}
          fromTag: ${{ needs.prepare-release.outputs.previous_tag }}
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Publish GitHub release assets
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ needs.prepare-release.outputs.tag }}
          name: ${{ needs.prepare-release.outputs.tag }}
          body: ${{ steps.changelog.outputs.changelog }}
          prerelease: ${{ needs.prepare-release.outputs.prerelease }}
          files: dist/*

      # ── crates.io ─────────────────────────────────────────────────────
      - name: Install Rust toolchain
        if: needs.prepare-release.outputs.stable == 'true'
        uses: dtolnay/rust-toolchain@stable

      - name: Publish to crates.io
        if: needs.prepare-release.outputs.stable == 'true'
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: cargo publish

      # ── Homebrew ──────────────────────────────────────────────────────
      - name: Checkout Homebrew tap repository
        if: needs.prepare-release.outputs.stable == 'true'
        uses: actions/checkout@v4
        with:
          repository: ${{ env.HOMEBREW_TAP_REPO }}
          token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
          path: homebrew-tap

      - name: Update Homebrew formula
        if: needs.prepare-release.outputs.stable == 'true'
        env:
          VERSION: ${{ needs.prepare-release.outputs.cargo_version }}
          TAG: ${{ needs.prepare-release.outputs.tag }}
        shell: bash
        run: |
          set -euo pipefail

          linux_x86_url="https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}/omni-code-bridge-linux-x64.tar.gz"
          linux_arm_url="https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}/omni-code-bridge-linux-arm64.tar.gz"
          macos_arm_url="https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}/omni-code-bridge-macos-arm64.tar.gz"
          macos_x86_url="https://github.com/${GITHUB_REPOSITORY}/releases/download/${TAG}/omni-code-bridge-macos-x64.tar.gz"

          linux_x86_sha=$(sha256sum dist/omni-code-bridge-linux-x64.tar.gz | awk '{print $1}')
          linux_arm_sha=$(sha256sum dist/omni-code-bridge-linux-arm64.tar.gz | awk '{print $1}')
          macos_arm_sha=$(sha256sum dist/omni-code-bridge-macos-arm64.tar.gz | awk '{print $1}')
          macos_x86_sha=$(sha256sum dist/omni-code-bridge-macos-x64.tar.gz | awk '{print $1}')

          mkdir -p homebrew-tap/Formula
          cat > homebrew-tap/Formula/omni-code-bridge.rb <<EOF
          class OmniCodeBridge < Formula
            desc "Rust bridge for Omni Code — connects mobile clients to local coding agents"
            homepage "https://github.com/${GITHUB_REPOSITORY}"
            version "${VERSION}"
            license "MIT"

            on_macos do
              on_arm do
                url "${macos_arm_url}"
                sha256 "${macos_arm_sha}"
              end

              on_intel do
                url "${macos_x86_url}"
                sha256 "${macos_x86_sha}"
              end
            end

            on_linux do
              on_arm do
                url "${linux_arm_url}"
                sha256 "${linux_arm_sha}"
              end

              on_intel do
                url "${linux_x86_url}"
                sha256 "${linux_x86_sha}"
              end
            end

            def install
              bin.install "omni-code-bridge"
            end

            test do
              assert_match version.to_s, shell_output("#{bin}/omni-code-bridge --version")
            end
          end
          EOF

      - name: Commit and push Homebrew formula update
        if: needs.prepare-release.outputs.stable == 'true'
        env:
          VERSION: ${{ needs.prepare-release.outputs.version }}
        shell: bash
        run: |
          set -euo pipefail
          cd homebrew-tap
          git add Formula/omni-code-bridge.rb
          if git diff --cached --quiet; then
            echo "No Homebrew formula changes to commit."
            exit 0
          fi

          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git commit -m "omni-code-bridge ${VERSION}"
          git push origin HEAD

      # ── AUR ──────────────────────────────────────────────────────────
      - name: Publish to AUR
        if: needs.prepare-release.outputs.stable == 'true'
        env:
          VERSION: ${{ needs.prepare-release.outputs.cargo_version }}
          TAG: ${{ needs.prepare-release.outputs.tag }}
          AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }}
        shell: bash
        run: |
          set -euo pipefail

          if [[ -z "${AUR_SSH_PRIVATE_KEY}" ]]; then
            echo "AUR_SSH_PRIVATE_KEY not set, skipping AUR publish."
            exit 0
          fi

          sudo apt-get update -q && sudo apt-get install -y openssh-client
          mkdir -p ~/.ssh
          chmod 700 ~/.ssh
          echo "${AUR_SSH_PRIVATE_KEY}" > ~/.ssh/aur
          chmod 600 ~/.ssh/aur
          ssh-keyscan aur.archlinux.org >> ~/.ssh/known_hosts 2>/dev/null

          export GIT_SSH_COMMAND="ssh -i ~/.ssh/aur -o IdentitiesOnly=yes -o StrictHostKeyChecking=no"

          git clone ssh://aur@aur.archlinux.org/${AUR_REPO}.git aur-pkg
          cd aur-pkg

          linux_x86_sha=$(sha256sum ../dist/omni-code-bridge-linux-x64.tar.gz | awk '{print $1}')
          linux_arm_sha=$(sha256sum ../dist/omni-code-bridge-linux-arm64.tar.gz | awk '{print $1}')
          license_sha=$(sha256sum ../LICENSE | awk '{print $1}')

          bash ../scripts/generate-aur-bin.sh "${VERSION}" "${GITHUB_REPOSITORY}" "${license_sha}" "${linux_x86_sha}" "${linux_arm_sha}" .

          git add PKGBUILD .SRCINFO omni-code-bridge.service
          if git diff --cached --quiet; then
            echo "No AUR changes to commit."
            exit 0
          fi

          git config user.email "junjie@omni-stream.ai"
          git config user.name "Junjie"
          git commit -m "Update omni-code-bridge-bin to ${VERSION}"
          git push origin master
