name: Release

on:
  push:
    branches:
      - master
  workflow_dispatch:
    inputs:
      source_ref:
        description: Commit SHA, branch, or tag to publish as stable
        required: true
        type: string
        default: master
      stable_date:
        description: Enter a UTC date in YYYY-MM-DD format, for example 2026-03-18. Do not enter a version string. The workflow will resolve that date to a stable version such as 2026.318.0, then 2026.318.1 for the next same-day stable.
        required: false
        type: string
      dry_run:
        description: Preview the stable release without publishing
        required: true
        type: boolean
        default: false

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

jobs:
  verify_canary:
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    timeout-minutes: 30
    permissions:
      contents: read

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 9.15.4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: pnpm

      - name: Validate release package manifest
        run: node ./scripts/release-package-map.mjs check

      - name: Install dependencies
        run: pnpm install --no-frozen-lockfile

      - name: Typecheck
        run: pnpm -r typecheck

      - name: Run tests
        run: pnpm test:run

      - name: Build
        run: pnpm build

  publish_canary:
    if: github.event_name == 'push'
    needs: verify_canary
    runs-on: ubuntu-latest
    timeout-minutes: 45
    environment: npm-canary
    permissions:
      contents: write
      id-token: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 9.15.4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: pnpm

      - name: Validate release package manifest
        run: node ./scripts/release-package-map.mjs check

      - name: Install dependencies
        run: pnpm install --no-frozen-lockfile

      - name: Restore tracked install-time changes
        run: git checkout -- pnpm-lock.yaml

      - name: Configure git author
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

      - name: Publish canary
        env:
          GITHUB_ACTIONS: "true"
        run: ./scripts/release.sh canary --skip-verify

      - name: Push canary tag
        run: |
          tag="$(git tag --points-at HEAD | grep '^canary/v' | head -1)"
          if [ -z "$tag" ]; then
            echo "Error: no canary tag points at HEAD after release." >&2
            exit 1
          fi
          git push origin "refs/tags/${tag}"

  verify_stable:
    if: github.event_name == 'workflow_dispatch'
    runs-on: ubuntu-latest
    timeout-minutes: 30
    permissions:
      contents: read

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          ref: ${{ inputs.source_ref }}

      - name: Setup pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 9.15.4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: pnpm

      - name: Validate release package manifest
        run: node ./scripts/release-package-map.mjs check

      - name: Install dependencies
        run: pnpm install --no-frozen-lockfile

      - name: Typecheck
        run: pnpm -r typecheck

      - name: Run tests
        run: pnpm test:run

      - name: Build
        run: pnpm build

  preview_stable:
    if: github.event_name == 'workflow_dispatch' && inputs.dry_run
    needs: verify_stable
    runs-on: ubuntu-latest
    timeout-minutes: 45
    permissions:
      contents: read

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          ref: ${{ inputs.source_ref }}

      - name: Setup pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 9.15.4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: pnpm

      - name: Validate release package manifest
        run: node ./scripts/release-package-map.mjs check

      - name: Install dependencies
        run: pnpm install --no-frozen-lockfile

      - name: Dry-run stable release
        env:
          GITHUB_ACTIONS: "true"
        run: |
          args=(stable --skip-verify --dry-run)
          if [ -n "${{ inputs.stable_date }}" ]; then
            args+=(--date "${{ inputs.stable_date }}")
          fi
          ./scripts/release.sh "${args[@]}"

  publish_stable:
    if: github.event_name == 'workflow_dispatch' && !inputs.dry_run
    needs: verify_stable
    runs-on: ubuntu-latest
    timeout-minutes: 45
    environment: npm-stable
    permissions:
      contents: write
      id-token: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          ref: ${{ inputs.source_ref }}

      - name: Setup pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 9.15.4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 24
          cache: pnpm

      - name: Install dependencies
        run: pnpm install --no-frozen-lockfile

      - name: Restore tracked install-time changes
        run: git checkout -- pnpm-lock.yaml

      - name: Configure git author
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

      - name: Publish stable
        env:
          GITHUB_ACTIONS: "true"
        run: |
          args=(stable --skip-verify)
          if [ -n "${{ inputs.stable_date }}" ]; then
            args+=(--date "${{ inputs.stable_date }}")
          fi
          ./scripts/release.sh "${args[@]}"

      - name: Push stable tag
        run: |
          tag="$(git tag --points-at HEAD | grep '^v' | head -1)"
          if [ -z "$tag" ]; then
            echo "Error: no stable tag points at HEAD after release." >&2
            exit 1
          fi
          git push origin "refs/tags/${tag}"

      - name: Create GitHub Release
        env:
          GH_TOKEN: ${{ github.token }}
          PUBLISH_REMOTE: origin
        run: |
          version="$(git tag --points-at HEAD | grep '^v' | head -1 | sed 's/^v//')"
          if [ -z "$version" ]; then
            echo "Error: no v* tag points at HEAD after stable release." >&2
            exit 1
          fi
          ./scripts/create-github-release.sh "$version"
