name: Release

on:
  push:
    tags:
      - "v*"

permissions:
  contents: write

env:
  CARGO_TERM_COLOR: always

jobs:
  build:
    name: Build - ${{ matrix.target }}
    runs-on: ${{ matrix.runner }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: aarch64-apple-darwin
            runner: macos-14
            npm-pkg: darwin-arm64
            binary: codefidence
          - target: x86_64-apple-darwin
            runner: macos-14
            npm-pkg: darwin-x64
            binary: codefidence
          - target: x86_64-unknown-linux-gnu
            runner: ubuntu-latest
            npm-pkg: linux-x64
            binary: codefidence
          - target: aarch64-unknown-linux-gnu
            runner: ubuntu-latest
            npm-pkg: linux-arm64
            binary: codefidence
            cross: true
          - target: x86_64-pc-windows-msvc
            runner: windows-latest
            npm-pkg: win32-x64
            binary: codefidence.exe

    steps:
      - uses: actions/checkout@v4

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

      - name: Install cross
        if: matrix.cross
        run: cargo install cross --git https://github.com/cross-rs/cross

      - name: Build binary
        run: |
          if [ "${{ matrix.cross }}" = "true" ]; then
            cross build --release --target ${{ matrix.target }}
          else
            cargo build --release --target ${{ matrix.target }}
          fi
        shell: bash

      - name: Copy binary to npm package
        run: |
          cp target/${{ matrix.target }}/release/${{ matrix.binary }} npm/${{ matrix.npm-pkg }}/${{ matrix.binary }}
        shell: bash

      - name: Create tarball (unix)
        if: runner.os != 'Windows'
        run: |
          cd target/${{ matrix.target }}/release
          tar czf ../../../codefidence-${{ matrix.target }}.tar.gz ${{ matrix.binary }}

      - name: Create zip (windows)
        if: runner.os == 'Windows'
        run: |
          cd target/${{ matrix.target }}/release
          7z a ../../../codefidence-${{ matrix.target }}.zip ${{ matrix.binary }}

      - name: Upload binary artifact
        uses: actions/upload-artifact@v4
        with:
          name: binary-${{ matrix.target }}
          path: npm/${{ matrix.npm-pkg }}/${{ matrix.binary }}

      - name: Upload release archive
        uses: actions/upload-artifact@v4
        with:
          name: archive-${{ matrix.target }}
          path: |
            codefidence-${{ matrix.target }}.tar.gz
            codefidence-${{ matrix.target }}.zip

  release:
    name: Create GitHub Release
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Download all archives
        uses: actions/download-artifact@v4
        with:
          pattern: archive-*
          merge-multiple: true
          path: archives

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          generate_release_notes: true
          files: archives/*
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  update-homebrew:
    name: Update Homebrew formula
    needs: release
    runs-on: ubuntu-latest
    steps:
      - name: Download release archives
        uses: actions/download-artifact@v4
        with:
          pattern: archive-*
          merge-multiple: true
          path: archives

      - name: Generate Homebrew formula
        run: |
          VERSION="${GITHUB_REF_NAME#v}"

          sha_darwin_arm64=$(sha256sum archives/codefidence-aarch64-apple-darwin.tar.gz | cut -d' ' -f1)
          sha_darwin_x64=$(sha256sum archives/codefidence-x86_64-apple-darwin.tar.gz | cut -d' ' -f1)
          sha_linux_x64=$(sha256sum archives/codefidence-x86_64-unknown-linux-gnu.tar.gz | cut -d' ' -f1)
          sha_linux_arm64=$(sha256sum archives/codefidence-aarch64-unknown-linux-gnu.tar.gz | cut -d' ' -f1)

          BASE_URL="https://github.com/agencedebord/codefidence/releases/download/v${VERSION}"

          cat > codefidence.rb << FORMULA
          class Codefidence < Formula
            desc "Structured project knowledge wiki for AI-assisted development"
            homepage "https://github.com/agencedebord/codefidence"
            version "${VERSION}"
            license any_of: ["MIT", "Apache-2.0"]

            on_macos do
              if Hardware::CPU.arm?
                url "${BASE_URL}/codefidence-aarch64-apple-darwin.tar.gz"
                sha256 "${sha_darwin_arm64}"
              else
                url "${BASE_URL}/codefidence-x86_64-apple-darwin.tar.gz"
                sha256 "${sha_darwin_x64}"
              end
            end

            on_linux do
              if Hardware::CPU.arm?
                url "${BASE_URL}/codefidence-aarch64-unknown-linux-gnu.tar.gz"
                sha256 "${sha_linux_arm64}"
              else
                url "${BASE_URL}/codefidence-x86_64-unknown-linux-gnu.tar.gz"
                sha256 "${sha_linux_x64}"
              end
            end

            def install
              bin.install "codefidence"
            end

            test do
              assert_match "codefidence", shell_output("#{bin}/codefidence --version")
            end
          end
          FORMULA

          # Remove leading whitespace from heredoc
          sed -i 's/^          //' codefidence.rb

      - name: Push formula to homebrew-tap
        uses: dmnemec/copy_file_to_another_repo_action@main
        env:
          API_TOKEN_GITHUB: ${{ secrets.HOMEBREW_TAP_TOKEN }}
        with:
          source_file: codefidence.rb
          destination_repo: agencedebord/homebrew-tap
          destination_folder: Formula
          user_email: github-actions[bot]@users.noreply.github.com
          user_name: github-actions[bot]
          commit_message: "Update codefidence to ${{ github.ref_name }}"

  publish-crate:
    name: Publish to crates.io
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

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

      - name: Publish to crates.io
        run: cargo publish
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

  publish-npm:
    name: Publish npm packages
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: "20"
          registry-url: "https://registry.npmjs.org"

      - name: Download all binaries
        uses: actions/download-artifact@v4
        with:
          pattern: binary-*
          path: binaries

      - name: Copy binaries to npm packages
        run: |
          cp binaries/binary-aarch64-apple-darwin/codefidence npm/darwin-arm64/codefidence
          cp binaries/binary-x86_64-apple-darwin/codefidence npm/darwin-x64/codefidence
          cp binaries/binary-x86_64-unknown-linux-gnu/codefidence npm/linux-x64/codefidence
          cp binaries/binary-aarch64-unknown-linux-gnu/codefidence npm/linux-arm64/codefidence
          cp binaries/binary-x86_64-pc-windows-msvc/codefidence.exe npm/win32-x64/codefidence.exe

      - name: Set version from tag
        run: |
          VERSION="${GITHUB_REF_NAME#v}"
          for pkg in darwin-arm64 darwin-x64 linux-x64 linux-arm64 win32-x64 cli; do
            cd npm/$pkg
            npm version "$VERSION" --no-git-tag-version --allow-same-version
            cd ../..
          done
          # Update optionalDependencies versions in cli package
          cd npm/cli
          node -e "
            const pkg = require('./package.json');
            for (const dep of Object.keys(pkg.optionalDependencies || {})) {
              pkg.optionalDependencies[dep] = '$VERSION';
            }
            require('fs').writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
          "
          cd ../..

      - name: Publish platform packages
        run: |
          for pkg in darwin-arm64 darwin-x64 linux-x64 linux-arm64 win32-x64; do
            cd npm/$pkg
            chmod +x codefidence 2>/dev/null || true
            npm publish --access public
            cd ../..
          done
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

      - name: Publish CLI package
        run: |
          cd npm/cli
          npm publish --access public
        env:
          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
