name: release

# End-to-end release pipeline.
#
# Per version:
#   1. Conventional commits land on main.
#   2. release-plz opens a release PR (Cargo.toml + CHANGELOG bump).
#   3. PR merge → release-plz publishes the source crate to crates.io
#      (--no-verify, so we skip the heavy sherpa-onnx-sys C++ build),
#      pushes a `vX.Y.Z` git tag, and emits the version as a job output.
#   4. The reusable _build.yml runs the cross-platform napi matrix.
#   5. The publish job lays out per-target sub-packages, syncs every
#      version field to the released version, and runs `npm publish
#      --provenance` for each `@otoji/core-<triple>` plus the umbrella
#      `@otoji/core` package via OIDC trusted publishing.
#
# Required secrets:
#   - CARGO_REGISTRY_TOKEN  (crates.io publish, used by release-plz)
#
# npm publishing uses OIDC — no NPM_TOKEN. Each `@otoji/core*` package on
# npmjs.com must have a Trusted Publisher pointing at this repo + this
# workflow file (https://docs.npmjs.com/trusted-publishers).
#
# Required repo settings:
#   Settings → Actions → General → Workflow permissions:
#     * Read and write permissions
#     * Allow GitHub Actions to create and approve pull requests

on:
  push:
    branches: [main]
  workflow_dispatch:
    inputs:
      force_npm:
        description: "Force npm build + publish even when release-plz didn't cut a new version"
        type: boolean
        default: false

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

env:
  CARGO_TERM_COLOR: always
  CARGO_INCREMENTAL: "0"
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"

jobs:
  # ─── Step 1: Cargo.toml bump + crates.io publish ──────────────────────
  release-plz:
    name: release-plz (crates.io)
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
    outputs:
      released_version: ${{ steps.released.outputs.version }}
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
          token: ${{ secrets.GITHUB_TOKEN }}
      - uses: dtolnay/rust-toolchain@stable

      - id: release-pr
        uses: release-plz/action@v0.5
        with:
          command: release-pr
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

      # Auto-merge the release PR so feat/fix commits on main ship
      # without manual intervention (semantic-release-style flow).
      - name: Auto-merge release PR
        if: steps.release-pr.outputs.prs != '' && steps.release-pr.outputs.prs != '[]'
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          set -euo pipefail
          pr_number=$(echo '${{ steps.release-pr.outputs.prs }}' | jq -r '.[0].number // empty')
          if [ -n "$pr_number" ]; then
            echo "Auto-merging release PR #$pr_number"
            gh pr merge "$pr_number" --repo "$GITHUB_REPOSITORY" --squash --admin || \
              gh pr merge "$pr_number" --repo "$GITHUB_REPOSITORY" --squash
          fi

      - id: release
        uses: release-plz/action@v0.5
        with:
          command: release
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

      - id: released
        name: Extract published version
        run: |
          set -euo pipefail
          version=$(echo '${{ steps.release.outputs.releases }}' | jq -r '.[0].version // empty')
          echo "Released version: '${version:-<none>}'"
          echo "version=$version" >> "$GITHUB_OUTPUT"

  # ─── Step 2: cross-platform napi build (reusable matrix) ─────────────
  build:
    needs: release-plz
    if: |
      always() && (
        (needs.release-plz.result == 'success' && needs.release-plz.outputs.released_version != '')
        || (github.event_name == 'workflow_dispatch' && inputs.force_npm)
      )
    uses: ./.github/workflows/_build.yml

  # ─── Step 3: per-platform sub-packages + umbrella publish ────────────
  publish:
    name: publish to npm
    needs: [release-plz, build]
    if: always() && needs.build.result == 'success'
    runs-on: ubuntu-latest
    permissions:
      contents: write
      id-token: write
    steps:
      - uses: actions/checkout@v4
      - uses: oven-sh/setup-bun@v2
      - uses: actions/setup-node@v4
        with:
          node-version: latest
          registry-url: https://registry.npmjs.org

      - run: bun install

      - uses: actions/download-artifact@v4
        with:
          path: artifacts

      - name: Sync versions to released_version
        env:
          VERSION: ${{ needs.release-plz.outputs.released_version }}
        run: |
          set -euxo pipefail
          if [ -z "${VERSION:-}" ]; then
            VERSION=$(node -p "require('./package.json').version")
          fi
          echo "Publishing version: $VERSION"
          node -e '
            const fs = require("fs");
            const v = process.env.VERSION;
            const pkg = JSON.parse(fs.readFileSync("package.json", "utf8"));
            pkg.version = v;
            for (const k of Object.keys(pkg.optionalDependencies || {})) {
              pkg.optionalDependencies[k] = v;
            }
            fs.writeFileSync("package.json", JSON.stringify(pkg, null, 2) + "\n");
          '

      - name: Lay out per-platform npm/<triple>/ dirs
        run: |
          set -euxo pipefail
          npx --no-install napi create-npm-dir -t .
          npx --no-install napi artifacts -d artifacts
          ls -R npm

      - name: Publish per-platform packages with provenance
        run: |
          set -euxo pipefail
          for dir in npm/*/; do
            (cd "$dir" && npm publish --access public --tag latest --provenance)
          done

      - name: Publish umbrella package with provenance
        run: npm publish --access public --tag latest --provenance
