# Release workflow — release-please orchestration + draft-until-all-green publish.
#
# Flow:
#   release-please  -> maintains the release PR; on merge cuts the tag + DRAFT release
#   build-binaries  -> cross-compiles every target, gated on the cut tag
#   publish-crate   -> publishes to crates.io, gated on the cut tag
#   upload-assets   -> renders git-cliff notes, attaches binaries to the DRAFT
#   publish-release -> flips DRAFT -> published ONLY if every prior job succeeded
name: Release

on:
  push:
    branches: [main]
  # Manual re-trigger to re-attach binaries to an ALREADY-released tag
  # (e.g. when the original build-binaries jobs failed).
  workflow_dispatch:
    inputs:
      tag_name:
        description: "Existing release tag to (re)build binaries for (e.g. v0.1.0)"
        required: true
        type: string
      version:
        description: "Version string for artifact names (e.g. 0.1.0)"
        required: true
        type: string

permissions:
  contents: write
  pull-requests: write

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: false

env:
  BINARY_NAME: scafgen

jobs:
  release-please:
    name: Release Please
    runs-on: ubuntu-latest
    outputs:
      build_artifacts: ${{ steps.gate.outputs.build_artifacts }}
      tag_name: ${{ steps.gate.outputs.tag_name }}
      version: ${{ steps.gate.outputs.version }}
    steps:
      - name: Run release-please
        id: release
        if: github.event_name == 'push'
        uses: googleapis/release-please-action@v4
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          config-file: release-please-config.json
          manifest-file: .release-please-manifest.json

      - name: Resolve build gate
        id: gate
        run: |
          if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
            echo "build_artifacts=true" >> "$GITHUB_OUTPUT"
            echo "tag_name=${{ inputs.tag_name }}" >> "$GITHUB_OUTPUT"
            echo "version=${{ inputs.version }}" >> "$GITHUB_OUTPUT"
          else
            echo "build_artifacts=${{ steps.release.outputs.release_created }}" >> "$GITHUB_OUTPUT"
            echo "tag_name=${{ steps.release.outputs.tag_name }}" >> "$GITHUB_OUTPUT"
            echo "version=${{ steps.release.outputs.version }}" >> "$GITHUB_OUTPUT"
          fi

  build-binaries:
    name: Build ${{ matrix.target }}
    needs: release-please
    if: needs.release-please.outputs.build_artifacts == 'true'
    runs-on: ${{ matrix.os }}
    timeout-minutes: 60
    strategy:
      fail-fast: false
      matrix:
        include:
          - target: x86_64-unknown-linux-musl
            os: ubuntu-latest
            use_zigbuild: true
            archive: tar.gz
          - target: aarch64-unknown-linux-musl
            os: ubuntu-latest
            use_zigbuild: true
            archive: tar.gz
          - target: x86_64-apple-darwin
            os: macos-latest
            use_zigbuild: false
            archive: tar.gz
          - target: aarch64-apple-darwin
            os: macos-latest
            use_zigbuild: false
            archive: tar.gz
          - target: x86_64-pc-windows-msvc
            os: windows-latest
            use_zigbuild: false
            archive: zip
          - target: aarch64-pc-windows-msvc
            os: windows-11-arm
            use_zigbuild: false
            archive: zip
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ needs.release-please.outputs.tag_name }}

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

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

      - name: Install Zig
        if: matrix.use_zigbuild
        uses: mlugg/setup-zig@v2
        with:
          version: 0.13.0

      - name: Install cargo-zigbuild
        if: matrix.use_zigbuild
        uses: taiki-e/install-action@v2
        with:
          tool: cargo-zigbuild

      - run: rustup target add ${{ matrix.target }}

      - name: Build (zigbuild)
        if: matrix.use_zigbuild
        run: cargo zigbuild --release --target ${{ matrix.target }}

      - name: Build (native)
        if: ${{ !matrix.use_zigbuild }}
        run: cargo build --release --target ${{ matrix.target }}

      - name: Package (unix tar.gz)
        if: matrix.archive == 'tar.gz'
        shell: bash
        run: |
          mkdir -p dist
          tar -czf "dist/${BINARY_NAME}-${{ needs.release-please.outputs.version }}-${{ matrix.target }}.tar.gz" \
            -C "target/${{ matrix.target }}/release" "${BINARY_NAME}"

      - name: Package (windows zip)
        if: matrix.archive == 'zip'
        shell: pwsh
        run: |
          New-Item -ItemType Directory -Force -Path dist | Out-Null
          Compress-Archive `
            -Path "target/${{ matrix.target }}/release/${env:BINARY_NAME}.exe" `
            -DestinationPath "dist/${env:BINARY_NAME}-${{ needs.release-please.outputs.version }}-${{ matrix.target }}.zip"

      - uses: actions/upload-artifact@v4
        with:
          name: dist-${{ matrix.target }}
          path: dist/*
          retention-days: 1

  publish-crate:
    name: Publish to crates.io
    needs: release-please
    if: needs.release-please.outputs.build_artifacts == 'true' && github.event_name == 'push'
    runs-on: ubuntu-latest
    timeout-minutes: 30
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ needs.release-please.outputs.tag_name }}

      - uses: dtolnay/rust-toolchain@stable

      - name: Publish to crates.io
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        # --allow-dirty: release-please bumps Cargo.toml but Cargo.lock is
        # regenerated during resolution, leaving the tree dirty; that drift is
        # expected and harmless for publishing.
        run: cargo publish --no-verify --allow-dirty

  upload-assets:
    name: Upload Release Assets
    needs: [release-please, build-binaries]
    if: needs.release-please.outputs.build_artifacts == 'true'
    runs-on: ubuntu-latest
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          ref: ${{ needs.release-please.outputs.tag_name }}

      - uses: actions/download-artifact@v4
        with:
          pattern: dist-*
          path: dist
          merge-multiple: true

      - name: Generate release notes
        uses: orhun/git-cliff-action@v4
        with:
          config: cliff.toml
          args: --latest --strip header
        env:
          OUTPUT: RELEASE_NOTES.md

      - name: Attach assets to the DRAFT release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ needs.release-please.outputs.tag_name }}
          body_path: RELEASE_NOTES.md
          draft: true # keep draft; publish-release flips it
          files: |
            dist/*.tar.gz
            dist/*.zip
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  publish-release:
    name: Publish Release
    needs: [release-please, build-binaries, upload-assets]
    if: needs.release-please.outputs.build_artifacts == 'true'
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - name: Publish the draft Release
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh release edit "${{ needs.release-please.outputs.tag_name }}" \
            --repo "${{ github.repository }}" \
            --draft=false \
            --latest
