name: Create release

permissions:
  pull-requests: write
  contents: write

on:
  workflow_dispatch:
    inputs:
      bump_type:
        description: "Specify the type of version bump"
        required: true
        default: "patch"
        type: choice
        options:
          - patch
          - minor
          - major

jobs:
  bump-version:
    name: bump-version
    runs-on: ubuntu-latest
    steps:
      - name: Configure SSH for Git
        if: env.ACT != 'true'
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.RELEASE_BOT_SSH_KEY }}" > ~/.ssh/id_ed25519
          chmod 600 ~/.ssh/id_ed25519
          ssh-keyscan -H github.com >> ~/.ssh/known_hosts

      - name: Checkout repository
        if: env.ACT != 'true'
        uses: actions/checkout@v3
        with:
          ssh-key: ${{ secrets.RELEASE_BOT_SSH_KEY }}
          fetch-depth: 0

      - name: Checkout repository
        if: env.ACT == 'true'
        uses: actions/checkout@v3
        with:
          fetch-depth: 0

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: "3.10"

      - name: Install Commitizen
        run: |
          python -m pip install --upgrade pip
          pip install commitizen
          npm install -g conventional-changelog-cli

      - name: Configure Git user
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"

      - name: Bump version with Commitizen
        run: |
          cz bump --yes --increment ${{ github.event.inputs.bump_type }}

      - name: Amend commit message to include '[skip ci]'
        run: |
          git commit --amend --no-edit -m "$(git log -1 --pretty=%B) [skip ci]"

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

      - name: Get the new version tag
        id: version
        run: |
          mkdir -p artifacts
          NEW_TAG=$(cz version --project)
          echo "New version: $NEW_TAG"
          echo "version=$NEW_TAG" >> $GITHUB_ENV
          echo "$NEW_TAG" > artifacts/release-version

      - name: Get the previous version tag
        id: prev_version
        run: |
          PREV_TAG=$(git describe --tags --abbrev=0 ${GITHUB_SHA}^)
          echo "Previous tag: $PREV_TAG"
          echo "prev_version=$PREV_TAG" >> $GITHUB_ENV

      - name: Bump Cargo.toml version
        shell: bash
        working-directory: ${{ github.workspace }}
        env:
          VERSION: ${{ env.version }}
        run: |
          set -euo pipefail
          : "${VERSION:?env.version is empty}"

          # Ignore Act's local artifact dir noise
          echo artifacts/ >> .git/info/exclude || true

          # Edit the version line right after name="coyote"
          sed -E -i '
            /^[[:space:]]*name[[:space:]]*=[[:space:]]*"coyote"[[:space:]]*$/ {
              n
              s|^[[:space:]]*version[[:space:]]*=[[:space:]]*"[^"]*"|version = "'"$VERSION"'"|
            }
          ' Cargo.toml

          cargo update || true

          # Git config that helps in Act
          git config user.name  "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git config --global --add safe.directory "$GITHUB_WORKSPACE"

          git status --porcelain
          git diff --name-only -- Cargo.toml Cargo.lock || true

          if ! git diff --quiet -- Cargo.toml Cargo.lock; then
            git add -u -- Cargo.toml Cargo.lock
            git commit -m "chore: bump Cargo.toml to $VERSION"
          else
            echo "No changes to commit (already at $VERSION)"
          fi

      - name: Generate changelog for the version bump
        id: changelog
        run: |
          conventional-changelog -p conventionalcommits -i CHANGELOG.md --from ${{ env.prev_version }} --to v${{ env.version }} > artifacts/changelog.md

      - name: Push changes
        if: env.ACT != 'true'
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          git push origin --follow-tags

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          path: artifacts

      - name: Upload the changed Cargo files (Act)
        if: env.ACT == 'true'
        uses: actions/upload-artifact@v4
        with:
          name: bumped-cargo-files
          path: |
            Cargo.toml
            Cargo.lock

  publish-github-release:
    name: build-release
    needs: [bump-version]
    runs-on: ${{ matrix.os }}
    env:
      RUST_BACKTRACE: 1
      BUILD_CMD: cargo
    strategy:
      fail-fast: true
      matrix:
        include:
          - target: aarch64-unknown-linux-musl
            os: ubuntu-latest
            use-cross: true
            cargo-flags: ""
          - target: aarch64-apple-darwin
            os: macos-latest
            use-cross: true
            cargo-flags: ""
          - target: aarch64-pc-windows-msvc
            os: windows-latest
            use-cross: true
            cargo-flags: ""
          - target: x86_64-apple-darwin
            os: macos-latest
            cargo-flags: ""
          - target: x86_64-pc-windows-msvc
            os: windows-latest
            cargo-flags: ""
          - target: x86_64-unknown-linux-musl
            os: ubuntu-latest
            use-cross: true
            cargo-flags: ""
          - target: x86_64-unknown-linux-gnu
            os: ubuntu-latest
            cargo-flags: ""

    steps:
      - name: Check if actor is repository owner
        if: ${{ github.actor != github.repository_owner && env.ACT != 'true' }}
        run: |
          echo "You are not authorized to run this workflow."
          exit 1

      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1

      - name: Ensure repository is up-to-date
        if: env.ACT != 'true'
        run: |
          git fetch --all
          git pull

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

      - name: Ensure repository is up-to-date
        if: env.ACT != 'true'
        run: |
          git fetch --all
          git pull

      - name: Set environment variables
        shell: bash
        run: |
          release_version="$(cat ./artifacts/release-version)"
          echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV

      - name: Validate release environment variables
        run: |
          echo "Release version: ${{ env.RELEASE_VERSION }}"
          echo "Changelog body: $(cat artifacts/changelog.md)"

      - name: Get bumped Cargo files (Act)
        if: env.ACT == 'true'
        uses: actions/download-artifact@v4
        with:
          name: bumped-cargo-files
          path: ${{ github.workspace }}

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

      - name: Install cross
        if: matrix.use-cross
        uses: taiki-e/install-action@v2
        with:
          tool: cross

      - name: Overwrite build command env variable
        if: matrix.use-cross
        shell: bash
        run: echo "BUILD_CMD=cross" >> $GITHUB_ENV

      - name: Install latest LLVM/Clang
        if: matrix.os == 'ubuntu-latest'
        run: |
          wget https://apt.llvm.org/llvm.sh
          chmod +x llvm.sh
          # omit the version to get the latest stable for your Ubuntu (24.04 "noble" on ubuntu-latest)
          sudo ./llvm.sh all
          # ensure libclang dev package is present (adjust the "22" if a newer major exists)
          sudo apt-get update
          sudo apt-get install -y libclang-20-dev libclang-dev

      - name: Show Version Information (Rust, cargo, GCC)
        shell: bash
        run: |
          gcc --version || true
          rustup -V
          rustup toolchain list
          rustup default
          cargo -V
          rustc -V

      - name: Build
        shell: bash
        run: $BUILD_CMD build --locked --release --target=${{ matrix.target }} ${{ matrix.cargo-flags }}

      - name: Verify file
        shell: bash
        run: |
          file target/${{ matrix.target }}/release/coyote

      - name: Test
        if: matrix.target != 'aarch64-apple-darwin' && matrix.target != 'aarch64-pc-windows-msvc'
        shell: bash
        run: |
          set -euxo pipefail
          if [[ "${{ matrix.use-cross || 'false' }}" == 'true' ]]; then
            cross test --release --locked --target=${{ matrix.target }} --verbose
          else
            cargo test  --release --locked --target=${{ matrix.target }} --verbose
          fi

      - name: Build Archive
        shell: bash
        id: package
        env:
          target: ${{ matrix.target }}
        run: |
          set -euxo pipefail

          bin=${GITHUB_REPOSITORY##*/}
          dist_dir=`pwd`/dist
          name=$bin-$target
          executable=target/$target/release/$bin

          if [[ "$RUNNER_OS" == "Windows" ]]; then
            executable=$executable.exe
          fi

          mkdir $dist_dir
          cp $executable $dist_dir
          cd $dist_dir

          if [[ "$RUNNER_OS" == "Windows" ]]; then
            archive=$dist_dir/$name.zip
            sha=$dist_dir/$name.sha256
            7z a $archive *
            certutil -hashfile $archive sha256 | grep -E [A-Fa-f0-9]{64} > $sha
            echo "archive=dist/$name.zip" >> $GITHUB_OUTPUT
            echo "sha=dist/$name.sha256" >> $GITHUB_OUTPUT
          else
            archive=$dist_dir/$name.tar.gz
            sha=$dist_dir/$name.sha256
            tar -czf $archive *
            shasum -a 256 $archive > $sha
            echo "archive=dist/$name.tar.gz" >> $GITHUB_OUTPUT
            echo "sha=dist/$name.sha256" >> $GITHUB_OUTPUT
          fi

      - name: Publish Archive and SHA
        if: env.ACT != 'true'
        uses: softprops/action-gh-release@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          files: |
            ${{ steps.package.outputs.archive }} 
            ${{ steps.package.outputs.sha }}
          tag_name: v${{ env.RELEASE_VERSION }}
          name: "v${{ env.RELEASE_VERSION }}"
          body_path: artifacts/changelog.md
          prerelease: false

      - name: Add artifacts
        shell: bash
        run: |
          [[ -d artifacts ]] || mkdir -p artifacts
          cp ${{ steps.package.outputs.archive }} artifacts/
          cp ${{ steps.package.outputs.sha }} artifacts/

      - name: Upload artifacts
        uses: actions/upload-artifact@v4
        with:
          name: artifacts-v${{ env.RELEASE_VERSION }}-${{ matrix.target }}
          path: artifacts
          overwrite: true

  publish-homebrew-formula:
    needs: [publish-github-release]
    name: Update Homebrew formulas
    runs-on: ubuntu-latest
    steps:
      - name: Check if actor is repository owner
        if: ${{ github.actor != github.repository_owner  && env.ACT != 'true' }}
        run: |
          echo "You are not authorized to run this workflow."
          exit 1

      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 1

      - name: Get release artifacts
        uses: actions/download-artifact@v4
        with:
          path: artifacts
          merge-multiple: true

      - name: Set release assets and version
        shell: bash
        run: |
          # Set environment variables
          macos_sha="$(cat ./artifacts/coyote-x86_64-apple-darwin.sha256 | awk '{print $1}')"
          echo "MACOS_SHA=$macos_sha" >> $GITHUB_ENV
          macos_sha_arm="$(cat ./artifacts/coyote-aarch64-apple-darwin.sha256 | awk '{print $1}')"
          echo "MACOS_SHA_ARM=$macos_sha_arm" >> $GITHUB_ENV
          linux_sha="$(cat ./artifacts/coyote-x86_64-unknown-linux-musl.sha256 | awk '{print $1}')"
          echo "LINUX_SHA=$linux_sha" >> $GITHUB_ENV
          release_version="$(cat ./artifacts/release-version)"
          echo "RELEASE_VERSION=$release_version" >> $GITHUB_ENV

      - name: Validate release environment variables
        run: |
          echo "Release SHA macos: ${{ env.MACOS_SHA }}"
          echo "Release SHA macos-arm: ${{ env.MACOS_SHA_ARM }}"
          echo "Release SHA linux musl: ${{ env.LINUX_SHA }}"
          echo "Release version: ${{ env.RELEASE_VERSION }}"

      - name: Execute Homebrew packaging script
        if: env.ACT != 'true'
        run: |
          # run packaging script
          python "./deployment/homebrew/packager.py" ${{ env.RELEASE_VERSION }} "./deployment/homebrew/coyote.rb.template" "./coyote.rb" ${{ env.MACOS_SHA }} ${{ env.MACOS_SHA_ARM }} ${{ env.LINUX_SHA }}

      - name: Push changes to Homebrew tap
        if: env.ACT != 'true'
        env:
          TOKEN: ${{ secrets.COYOTE_GITHUB_TOKEN }}
        run: |
          # push to Git
          git config --global user.name "Dark-Alex-17"
          git config --global user.email "alex.j.tusa@gmail.com"
          git clone https://Dark-Alex-17:${{ secrets.COYOTE_GITHUB_TOKEN }}@github.com/Dark-Alex-17/homebrew-coyote.git
          rm homebrew-coyote/Formula/coyote.rb
          cp coyote.rb homebrew-coyote/Formula
          cd homebrew-coyote
          git add .
          git diff-index --quiet HEAD || git commit -am "Update formula for Coyote release ${{ env.RELEASE_VERSION }}"
          git push https://$TOKEN@github.com/Dark-Alex-17/homebrew-coyote.git

  publish-crate:
    needs: publish-github-release
    name: Publish Crate
    runs-on: ubuntu-latest
    steps:
      - name: Check if actor is repository owner
        if: ${{ github.actor != github.repository_owner  && env.ACT != 'true' }}
        run: |
          echo "You are not authorized to run this workflow."
          exit 1

      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Get bumped Cargo files (Act)
        if: env.ACT == 'true'
        uses: actions/download-artifact@v4
        with:
          name: bumped-cargo-files
          path: ${{ github.workspace }}

      - name: Ensure repository is up-to-date
        if: env.ACT != 'true'
        run: |
          git fetch --all
          git pull

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

      - uses: katyo/publish-crates@v2
        if: env.ACT != 'true'
        with:
          registry-token: ${{ secrets.CARGO_REGISTRY_TOKEN }}