name: Publish

on:
  push:
    tags: ['v*']
  workflow_dispatch:
    inputs:
      release_tag:
        description: Release tag to publish, for example v0.2.0
        required: true
        type: string

jobs:
  github-release:
    name: Create GitHub release
    runs-on: ubuntu-latest
    permissions:
      contents: write
    outputs:
      tag: ${{ steps.release.outputs.tag }}
    steps:
      - name: Resolve release tag
        id: release
        env:
          INPUT_RELEASE_TAG: ${{ inputs.release_tag }}
        run: |
          set -euo pipefail
          if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then
            tag="$INPUT_RELEASE_TAG"
          else
            tag="${GITHUB_REF_NAME}"
          fi
          if [[ ! "$tag" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then
            echo "Invalid release tag: $tag" >&2
            exit 1
          fi
          echo "tag=${tag}" >> "$GITHUB_OUTPUT"
      - name: Create release if needed
        env:
          GH_TOKEN: ${{ github.token }}
          RELEASE_TAG: ${{ steps.release.outputs.tag }}
          GH_REPO: ${{ github.repository }}
        run: |
          set -euo pipefail
          gh release view "$RELEASE_TAG" --repo "$GH_REPO" >/dev/null 2>&1 || \
            gh release create "$RELEASE_TAG" --repo "$GH_REPO" --title "$RELEASE_TAG" --generate-notes

  binary-assets:
    name: Publish native binary (${{ matrix.target }})
    needs: github-release
    runs-on: ${{ matrix.os }}
    permissions:
      contents: write
    strategy:
      fail-fast: false
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
          - os: ubuntu-24.04-arm
            target: aarch64-unknown-linux-gnu
          - os: macos-13
            target: x86_64-apple-darwin
          - os: macos-latest
            target: aarch64-apple-darwin
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ inputs.release_tag || github.ref }}
      - uses: actions-rust-lang/setup-rust-toolchain@v1
        with:
          target: ${{ matrix.target }}
      - name: Resolve release tag
        id: release
        run: echo "tag=${{ needs.github-release.outputs.tag }}" >> "$GITHUB_OUTPUT"
      - name: Set crate version from release tag
        env:
          RELEASE_TAG: ${{ steps.release.outputs.tag }}
        run: python3 scripts/set-release-version.py "$RELEASE_TAG"
      - name: Build native binary
        run: cargo build --locked --release --target ${{ matrix.target }}
      - name: Package native binary
        run: |
          set -euo pipefail
          mkdir -p dist
          cp "target/${{ matrix.target }}/release/jbx" dist/jbx
          chmod +x dist/jbx
          tar -C dist -czf "jbx-${{ matrix.target }}.tar.gz" jbx
      - name: Upload release asset
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ steps.release.outputs.tag }}
          files: jbx-${{ matrix.target }}.tar.gz

  crates-io:
    name: Publish to crates.io
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ inputs.release_tag || github.ref }}
      - uses: actions-rust-lang/setup-rust-toolchain@v1
      - name: Resolve release tag
        id: release
        env:
          INPUT_RELEASE_TAG: ${{ inputs.release_tag }}
        run: |
          set -euo pipefail
          if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then
            tag="$INPUT_RELEASE_TAG"
          else
            tag="${GITHUB_REF_NAME}"
          fi
          if [[ ! "$tag" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then
            echo "Invalid release tag: $tag" >&2
            exit 1
          fi
          version="${tag#v}"
          echo "tag=${tag}" >> "$GITHUB_OUTPUT"
          echo "version=${version}" >> "$GITHUB_OUTPUT"
      - name: Set crate version from release tag
        env:
          RELEASE_TAG: ${{ steps.release.outputs.tag }}
        run: python3 scripts/set-release-version.py "$RELEASE_TAG"
      - name: Check crates.io version
        id: crates
        env:
          RELEASE_VERSION: ${{ steps.release.outputs.version }}
        run: |
          python3 - <<'PY'
          import os
          import urllib.error
          import urllib.request

          version = os.environ["RELEASE_VERSION"]
          url = f"https://crates.io/api/v1/crates/jbx/{version}"
          try:
              with urllib.request.urlopen(url, timeout=20) as response:
                  exists = response.status == 200
          except urllib.error.HTTPError as error:
              if error.code == 404:
                  exists = False
              else:
                  raise

          with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output:
              print(f"exists={str(exists).lower()}", file=output)
          if exists:
              print(f"jbx {version} already exists on crates.io; skipping publish")
          PY
      - name: Publish
        if: steps.crates.outputs.exists != 'true'
        run: cargo publish --locked --allow-dirty
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

  docker:
    name: Publish Docker image
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
        with:
          ref: ${{ inputs.release_tag || github.ref }}
      - name: Resolve release tag
        id: release
        env:
          INPUT_RELEASE_TAG: ${{ inputs.release_tag }}
        run: |
          set -euo pipefail
          if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then
            tag="$INPUT_RELEASE_TAG"
          else
            tag="${GITHUB_REF_NAME}"
          fi
          if [[ ! "$tag" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+([-+][0-9A-Za-z.-]+)?$ ]]; then
            echo "Invalid release tag: $tag" >&2
            exit 1
          fi
          version="${tag#v}"
          echo "tag=${tag}" >> "$GITHUB_OUTPUT"
          echo "version=${version}" >> "$GITHUB_OUTPUT"
      - name: Set crate version from release tag
        env:
          RELEASE_TAG: ${{ steps.release.outputs.tag }}
        run: python3 scripts/set-release-version.py "$RELEASE_TAG"
      - name: Use release Docker builder toolchain
        run: |
          python3 - <<'PY'
          from pathlib import Path

          path = Path("Dockerfile")
          content = path.read_text()
          path.write_text(content.replace("rust:1.86-bookworm", "rust:1.88-bookworm"))
          PY
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/metadata-action@v5
        id: meta
        with:
          images: ghcr.io/${{ github.repository }}
          tags: |
            type=semver,pattern={{version}},value=${{ steps.release.outputs.tag }}
            type=semver,pattern={{major}}.{{minor}},value=${{ steps.release.outputs.tag }}
            type=semver,pattern={{major}},value=${{ steps.release.outputs.tag }},enable=${{ !startsWith(steps.release.outputs.version, '0.') }}
            type=sha
      - uses: docker/build-push-action@v6
        with:
          push: true
          platforms: linux/amd64,linux/arm64
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
