cargo-copter 0.3.0

Test dependents against multiple versions of your crate (or your local WIP before publishing). Inspired by the cargo-crusader
name: Release

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

permissions:
  contents: write

jobs:
  verify-version:
    name: Verify tag matches Cargo.toml
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/v')
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Check version match
        run: |

          TAG_VERSION="${GITHUB_REF#refs/tags/v}"
          CARGO_VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)".*/\1/')
          echo "Tag version: $TAG_VERSION"
          echo "Cargo.toml version: $CARGO_VERSION"
          if [ "$TAG_VERSION" != "$CARGO_VERSION" ]; then
            echo "::error::Version mismatch! Tag is v$TAG_VERSION but Cargo.toml has $CARGO_VERSION"
            exit 1
          fi
          echo "✓ Versions match"

  build-release:
    name: Build Release Artifacts
    needs: verify-version
    if: always() && (needs.verify-version.result == 'success' || needs.verify-version.result == 'skipped')
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          # Linux x86_64 (GNU) - use cross for older glibc compatibility
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            artifact_name: cargo-copter
            cross: true

          # Linux x86_64 (musl - static binary)
          - os: ubuntu-latest
            target: x86_64-unknown-linux-musl
            artifact_name: cargo-copter

          # Linux ARM64 (GNU) - use cross for older glibc compatibility
          - os: ubuntu-latest
            target: aarch64-unknown-linux-gnu
            artifact_name: cargo-copter
            cross: true

          # Linux ARM64 (musl - static binary) - native ARM runner
          - os: ubuntu-24.04-arm
            target: aarch64-unknown-linux-musl
            artifact_name: cargo-copter

          # macOS ARM64 (Apple Silicon)
          - os: macos-latest
            target: aarch64-apple-darwin
            artifact_name: cargo-copter

          # Windows x86_64
          - os: windows-latest
            target: x86_64-pc-windows-msvc
            artifact_name: cargo-copter.exe

          # Windows ARM64 (MSVC can cross-compile natively)
          - os: windows-latest
            target: aarch64-pc-windows-msvc
            artifact_name: cargo-copter.exe

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

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

      - name: Install musl tools (Linux musl targets)
        if: contains(matrix.target, 'musl')
        run: |

          sudo apt-get update
          sudo apt-get install -y musl-tools

      - name: Cache cargo registry
        uses: actions/cache@v4
        with:
          path: ~/.cargo/registry
          key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}

      - name: Cache cargo index
        uses: actions/cache@v4
        with:
          path: ~/.cargo/git
          key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }}

      - name: Test build
        run: cargo test

      - name: Build release binary (native)
        if: matrix.cross != true
        run: cargo build --release --target ${{ matrix.target }}

      - name: Build release binary (cross)
        if: matrix.cross == true
        uses: houseabsolute/actions-rust-cross@v1.0.5
        with:
          command: build
          target: ${{ matrix.target }}
          args: --release

      - name: Strip binary (Unix, native only)
        if: runner.os != 'Windows' && matrix.cross != true
        run: strip target/${{ matrix.target }}/release/${{ matrix.artifact_name }}

      - name: Create cargo-binstall compatible archive
        shell: bash
        run: |

          cd target/${{ matrix.target }}/release

          # cargo-binstall expects: {bin}-{target}.{tar.gz|zip}
          if [ "${{ runner.os }}" = "Windows" ]; then
            ARCHIVE="cargo-copter-${{ matrix.target }}.zip"
            7z a "$ARCHIVE" "${{ matrix.artifact_name }}"
            echo "ASSET=$ARCHIVE" >> $GITHUB_ENV
          else
            ARCHIVE="cargo-copter-${{ matrix.target }}.tar.gz"
            tar czf "$ARCHIVE" "${{ matrix.artifact_name }}"
            echo "ASSET=$ARCHIVE" >> $GITHUB_ENV
          fi

          # Generate SHA256 checksum for cargo-binstall verification
          if [ "${{ runner.os }}" = "Windows" ]; then
            certutil -hashfile "$ARCHIVE" SHA256 | grep -v "hash" | tr -d '[:space:]' > "$ARCHIVE.sha256"
          else
            shasum -a 256 "$ARCHIVE" | cut -d ' ' -f 1 > "$ARCHIVE.sha256"
          fi

          cd -

      - name: Upload release asset
        uses: softprops/action-gh-release@v1
        with:
          files: |

            target/${{ matrix.target }}/release/${{ env.ASSET }}
            target/${{ matrix.target }}/release/${{ env.ASSET }}.sha256
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  publish-crate:
    name: Publish to crates.io
    needs: build-release
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/v')
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

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

      - name: Publish to crates.io
        run: cargo publish --token ${{ secrets.CARGO_REGISTRY_TOKEN }}
        continue-on-error: true