req-cli 0.4.0-rc.3

Managed requirements CLI for LLM agents and humans
name: release

on:
  push:
    tags:
      - "v*"
  # Manual trigger so the cross-platform build can be exercised without
  # creating a Release object. Defaults to publishing only when invoked
  # by a tag push; the dispatch form is dry-run unless you set the input.
  workflow_dispatch:
    inputs:
      publish_release:
        description: "Create a GitHub Release and attach archives (true|false)"
        required: false
        default: "false"
      ref_name:
        description: "Tag-like name to embed in archive filenames (e.g. v0.1.0-rc1)"
        required: false
        default: "dryrun"

permissions:
  contents: write

# Single source of truth for the archive base name across both trigger
# paths: tag push uses github.ref_name (e.g. v0.1.0); workflow_dispatch
# uses the provided input or "dryrun".
env:
  ARCHIVE_TAG: ${{ github.event_name == 'workflow_dispatch' && inputs.ref_name || github.ref_name }}

jobs:
  build:
    name: build ${{ matrix.target }}
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            archive: tar.gz
          - os: macos-latest
            target: x86_64-apple-darwin
            archive: tar.gz
          - os: macos-latest
            target: aarch64-apple-darwin
            archive: tar.gz
          - os: windows-latest
            target: x86_64-pc-windows-msvc
            archive: zip
    steps:
      - uses: actions/checkout@v5
      # rustup bootstrap; rust-toolchain.toml then pins the active channel.
      - uses: dtolnay/rust-toolchain@stable
      # Install the cross-compile target against the *pinned* toolchain
      # (rust-toolchain.toml). Passing `targets:` to dtolnay/rust-toolchain
      # would attach to `stable`, which the pin would then ignore.
      - name: install matrix target on pinned toolchain
        run: rustup target add ${{ matrix.target }}
      - uses: Swatinem/rust-cache@v2
      - run: cargo build --release --target ${{ matrix.target }}
      - name: package (unix)
        if: matrix.archive == 'tar.gz'
        run: |
          mkdir -p dist
          cp target/${{ matrix.target }}/release/req dist/
          cp README.md LICENSE dist/
          tar -czf req-${ARCHIVE_TAG}-${{ matrix.target }}.tar.gz -C dist .
      - name: package (windows)
        if: matrix.archive == 'zip'
        shell: pwsh
        run: |
          New-Item -ItemType Directory -Force -Path dist | Out-Null
          Copy-Item target/${{ matrix.target }}/release/req.exe dist/
          Copy-Item README.md, LICENSE dist/
          Compress-Archive -Path dist/* -DestinationPath "req-$env:ARCHIVE_TAG-${{ matrix.target }}.zip"
      # Always upload as a workflow artefact so dry-runs (and tag pushes
      # that later fail to publish) still leave the binaries downloadable
      # from the Actions UI for verification.
      - name: upload workflow artefact
        uses: actions/upload-artifact@v5
        with:
          name: req-${{ matrix.target }}
          path: |
            req-${{ env.ARCHIVE_TAG }}-${{ matrix.target }}.tar.gz
            req-${{ env.ARCHIVE_TAG }}-${{ matrix.target }}.zip
          if-no-files-found: ignore
      # Only attach to a GitHub Release on a real tag push, OR when the
      # manual dispatch explicitly opts in via publish_release=true.
      - name: attach to GitHub Release
        if: |
          github.event_name == 'push' ||
          (github.event_name == 'workflow_dispatch' && inputs.publish_release == 'true')
        uses: softprops/action-gh-release@v2
        with:
          files: |
            req-${{ env.ARCHIVE_TAG }}-${{ matrix.target }}.tar.gz
            req-${{ env.ARCHIVE_TAG }}-${{ matrix.target }}.zip
          draft: false
          prerelease: false
          fail_on_unmatched_files: false