name: Release

on:
  push:
    tags: ['v*.*.*']
  workflow_dispatch:

env:
  CARGO_TERM_COLOR: always

jobs:
  verify-version:
    # Tag-pushes only: refuse to build a release whose git tag does not
    # match `[package].version` in Cargo.toml. Catches the common "tagged
    # v0.2.0 but forgot to bump Cargo.toml" mistake before any build
    # minutes are spent.
    name: Verify tag matches Cargo.toml version
    if: startsWith(github.ref, 'refs/tags/v')
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Compare git tag with [package].version
        run: |
          tag="${GITHUB_REF_NAME#v}"
          pkg=$(awk '
              /^\[package\]/ { in_pkg=1; next }
              /^\[/          { in_pkg=0 }
              in_pkg && /^[[:space:]]*version[[:space:]]*=/ {
                  gsub(/^[[:space:]]*version[[:space:]]*=[[:space:]]*"/, "")
                  gsub(/".*$/, "")
                  print
                  exit
              }
          ' Cargo.toml)
          echo "Git tag (stripped):  $tag"
          echo "Cargo.toml version:  $pkg"
          if [ "$tag" != "$pkg" ]; then
              echo "::error::Git tag v$tag does not match [package].version $pkg — bump Cargo.toml before tagging."
              exit 1
          fi
          echo "OK: tag and Cargo.toml version match."

  build:
    name: Build ${{ matrix.target }}
    needs: verify-version
    # Run when the gate passed, or when it was skipped (workflow_dispatch
    # with no tag — useful as a manual smoke build).
    if: ${{ always() && (needs.verify-version.result == 'success' || needs.verify-version.result == 'skipped') }}
    runs-on: ${{ matrix.runner }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-unknown-linux-musl
            runner: ubuntu-latest
            apt_packages: musl-tools
            asset_suffix: x86_64-linux-musl
          - target: aarch64-unknown-linux-musl
            runner: ubuntu-24.04-arm
            apt_packages: musl-tools
            asset_suffix: aarch64-linux-musl
    steps:
      - uses: actions/checkout@v4

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

      - uses: Swatinem/rust-cache@v2
        with:
          key: ${{ matrix.target }}

      - name: Install apt packages
        if: matrix.apt_packages != ''
        run: |
          sudo apt-get update
          sudo apt-get install -y ${{ matrix.apt_packages }}

      - name: Build fifi
        run: cargo build --release --locked --target ${{ matrix.target }}

      - name: Verify static linkage
        if: contains(matrix.target, 'musl')
        run: |
          BIN=target/${{ matrix.target }}/release/fifi
          file "$BIN"
          # file(1) reports musl binaries as "statically linked" (classic)
          # or "static-pie linked" (modern PIE). Accept both.
          if ! file "$BIN" | grep -qE "static(ally|-pie)? linked"; then
            echo "ERROR: fifi is not statically linked"
            exit 1
          fi
          echo "OK: $(du -h "$BIN" | cut -f1) statically linked binary"

      - name: Strip release binary
        run: strip target/${{ matrix.target }}/release/fifi

      - name: Stage binary with versioned name
        run: |
          mkdir -p dist
          cp "target/${{ matrix.target }}/release/fifi" \
             "dist/fifi-${GITHUB_REF_NAME}-${{ matrix.asset_suffix }}"

      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: fifi-${{ matrix.asset_suffix }}
          path: dist/fifi-*
          if-no-files-found: error
          retention-days: 7

  release:
    name: Publish release
    if: startsWith(github.ref, 'refs/tags/v')
    needs: [build]
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v4
        with:
          # Need the tagged commit's full message for release notes;
          # default shallow clone strips the body.
          fetch-depth: 0

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

      - name: List assets
        run: ls -la dist/

      - name: Create GitHub release
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          # Release notes = body of the tagged release-prep commit,
          # minus the Co-Authored-By trailer.
          git log -1 --pretty=%b "$GITHUB_REF_NAME" \
            | sed '/^Co-Authored-By:/d' > release-notes.md
          gh release create "$GITHUB_REF_NAME" \
            --title "$GITHUB_REF_NAME" \
            --notes-file release-notes.md \
            dist/fifi-*
