name: Release binaries

# Build per-platform binaries and attach them to the GitHub Release for the
# pushed tag. No npm, no tokens — only the built-in GITHUB_TOKEN. Consumers
# install with: curl -fsSL .../install.sh | sh
on:
  push:
    tags:
      - 'v*'
  workflow_dispatch:
    inputs:
      tag:
        description: 'Existing tag to (re)build binaries for, e.g. v0.27.0-fork.12'
        required: true

permissions:
  contents: write

concurrency: release-binaries-${{ github.ref }}

jobs:
  build:
    name: Build ${{ matrix.name }}
    runs-on: ${{ matrix.os }}
    timeout-minutes: 30
    strategy:
      fail-fast: false
      matrix:
        include:
          - { name: Linux x64,        os: ubuntu-latest, target: x86_64-unknown-linux-gnu,    asset: chrome-use-linux-x64,        use_zigbuild: true,  ext: '' }
          - { name: Linux ARM64,      os: ubuntu-latest, target: aarch64-unknown-linux-gnu,   asset: chrome-use-linux-arm64,      use_zigbuild: true,  ext: '' }
          - { name: Linux musl x64,   os: ubuntu-latest, target: x86_64-unknown-linux-musl,   asset: chrome-use-linux-musl-x64,   use_zigbuild: true,  ext: '' }
          - { name: Linux musl ARM64, os: ubuntu-latest, target: aarch64-unknown-linux-musl,  asset: chrome-use-linux-musl-arm64, use_zigbuild: true,  ext: '' }
          - { name: Windows x64,      os: ubuntu-latest, target: x86_64-pc-windows-gnu,       asset: chrome-use-win32-x64,        use_zigbuild: false, ext: '.exe' }
          - { name: macOS x64,        os: macos-latest,  target: x86_64-apple-darwin,         asset: chrome-use-darwin-x64,       use_zigbuild: false, ext: '' }
          - { name: macOS ARM64,      os: macos-latest,  target: aarch64-apple-darwin,        asset: chrome-use-darwin-arm64,     use_zigbuild: false, ext: '' }
    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ github.event.inputs.tag || github.ref }}

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

      - name: Install cross-compilation tools (Linux)
        if: runner.os == 'Linux'
        run: |
          sudo apt-get update
          sudo apt-get install -y gcc-aarch64-linux-gnu gcc-x86-64-linux-gnu mingw-w64

      - name: Install cargo-zigbuild
        if: matrix.use_zigbuild
        run: |
          pip3 install ziglang
          cargo install cargo-zigbuild

      - name: Configure Rust linkers
        if: runner.os == 'Linux'
        run: |
          mkdir -p ~/.cargo
          cat >> ~/.cargo/config.toml << 'EOF'
          [target.aarch64-unknown-linux-gnu]
          linker = "aarch64-linux-gnu-gcc"

          [target.x86_64-pc-windows-gnu]
          linker = "x86_64-w64-mingw32-gcc"
          EOF

      - name: Cache Rust build artifacts
        uses: Swatinem/rust-cache@v2
        with:
          workspaces: cli

      - name: Build (zigbuild)
        if: matrix.use_zigbuild
        run: cargo zigbuild --release --manifest-path cli/Cargo.toml --target ${{ matrix.target }}

      - name: Build (cargo)
        if: '!matrix.use_zigbuild'
        run: cargo build --release --manifest-path cli/Cargo.toml --target ${{ matrix.target }}

      - name: Package (.tar.gz + .sha256)
        shell: bash
        run: |
          set -euo pipefail
          mkdir -p dist
          src="cli/target/${{ matrix.target }}/release/chrome-use${{ matrix.ext }}"
          # The binary inside every archive is named `chrome-use` (or .exe);
          # install.sh extracts that fixed name regardless of platform.
          cp "$src" "dist/chrome-use${{ matrix.ext }}"
          chmod +x "dist/chrome-use${{ matrix.ext }}" || true
          ( cd dist
            tar czf "${{ matrix.asset }}.tar.gz" "chrome-use${{ matrix.ext }}"
            if command -v sha256sum >/dev/null 2>&1; then
              sha256sum "${{ matrix.asset }}.tar.gz" > "${{ matrix.asset }}.tar.gz.sha256"
            else
              shasum -a 256 "${{ matrix.asset }}.tar.gz" > "${{ matrix.asset }}.tar.gz.sha256"
            fi
          )

      - name: Upload artifact
        uses: actions/upload-artifact@v7
        with:
          name: ${{ matrix.asset }}
          path: dist/${{ matrix.asset }}.tar.gz*
          retention-days: 3

  release:
    name: Attach binaries to GitHub Release
    needs: build
    runs-on: ubuntu-latest
    timeout-minutes: 10
    permissions:
      contents: write
    steps:
      # The release job is separate from the build matrix and has no repo by
      # default — check it out (full history + tags) so the changelog step has a
      # git repo to diff. Without this, `git` failed with "not a git repository"
      # and the changelog came out empty.
      - name: Checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ github.event.inputs.tag || github.ref }}
          fetch-depth: 0

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

      - name: List assets
        run: ls -la dist

      # Build the changelog from conventional-commit subjects since the previous
      # tag. GitHub's built-in generate_release_notes only lists merged PRs,
      # which is near-empty for this commit-to-main workflow — so we render the
      # commit log ourselves and every release shows what actually changed.
      - name: Generate changelog
        id: changelog
        run: |
          # fetch-depth:0 gets history, but the tag refs the changelog needs
          # aren't always present in a detached-HEAD tag checkout — pull them in.
          git fetch --tags --force --quiet origin 2>/dev/null || true
          TAG="${{ github.event.inputs.tag || github.ref_name }}"
          PREV="$(git describe --tags --abbrev=0 "${TAG}^" 2>/dev/null || true)"
          RANGE="${TAG}"
          [ -n "$PREV" ] && RANGE="${PREV}..${TAG}"
          # Group commit subjects by conventional-commit type so the notes are
          # scannable ("what's new / what's fixed") instead of a flat dev log.
          LOG="$(git log "$RANGE" --no-merges --pretty='%s' | grep -v '^chore(release)' || true)"
          section() { # $1=header  $2=grep-pattern
            local body; body="$(printf '%s\n' "$LOG" | grep -E "$2" | sed 's/^/- /')"
            [ -n "$body" ] && { printf '\n### %s\n%s\n' "$1" "$body"; }
          }
          {
            echo "notes<<__NOTES_EOF__"
            echo "## What changed"
            section "✨ Features" '^feat'
            section "🐛 Fixes" '^fix'
            section "🔧 Other" '^(perf|refactor|docs|build|ci|test|style|revert)'
            if [ -n "$PREV" ]; then
              echo ""
              echo "**Full changelog**: https://github.com/${{ github.repository }}/compare/${PREV}...${TAG}"
            fi
            echo "__NOTES_EOF__"
          } >> "$GITHUB_OUTPUT"

      - name: Attach to release
        uses: softprops/action-gh-release@v3
        with:
          tag_name: ${{ github.event.inputs.tag || github.ref_name }}
          files: |
            dist/*.tar.gz
            dist/*.tar.gz.sha256
          fail_on_unmatched_files: true
          # The commit-based changelog so every release shows what changed. The
          # first matrix job to run creates the release with these notes;
          # append_body:false keeps later platform jobs from duplicating them.
          body: ${{ steps.changelog.outputs.notes }}
          append_body: false
