name: Release CLI

on:
  push:
    branches:
      - main
  workflow_dispatch:
    inputs:
      release_channel:
        description: npm dist-tag and prerelease lane.
        required: true
        type: choice
        default: stable
        options:
          - stable
          - beta
          - rc
          - nightly
      version_specifier:
        description: Nx release specifier. Use auto for conventional commits.
        required: true
        type: choice
        default: auto
        options:
          - auto
          - patch
          - minor
          - major
          - prerelease
          - prepatch
          - preminor
          - premajor
      dry_run:
        description: Preview versioning and npm publish without changing git or npm.
        required: true
        type: boolean
        default: true
      first_release:
        description: Use only for the first CLI package release.
        required: true
        type: boolean
        default: false
      allow_major:
        description: Required when intentionally publishing a major release.
        required: true
        type: boolean
        default: false

permissions:
  contents: write
  id-token: write

concurrency:
  # Shared group with release-npm so both workflows serialize on the same branch.
  # cancel-in-progress: false — never cancel a release mid-flight.
  group: release-main
  cancel-in-progress: false

jobs:
  check:
    name: Check if CLI is affected
    runs-on: ubuntu-latest
    if: >-
      github.event_name == 'workflow_dispatch' || (
        github.event_name == 'push' &&
        !startsWith(github.event.head_commit.message, 'chore(release):') &&
        !contains(github.event.head_commit.message, '[skip ci]')
      )
    outputs:
      affected: ${{ steps.nx.outputs.affected }}
    steps:
      - name: Checkout
        uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
        with:
          fetch-depth: 0
          fetch-tags: true
      - name: Setup workspace
        uses: ./.github/actions/setup-workspace
      - name: Detect affected
        id: nx
        shell: bash
        run: |
          if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
            echo "affected=true" >> "$GITHUB_OUTPUT"
          else
            affected=$(pnpm nx show projects --affected --base=HEAD~1 --head=HEAD --plain 2>/dev/null || echo '')
            if echo "$affected" | grep -qw "cli"; then
              echo "affected=true" >> "$GITHUB_OUTPUT"
            else
              echo "affected=false" >> "$GITHUB_OUTPUT"
            fi
          fi

  release:
    name: ${{ (inputs.dry_run == 'true' || inputs.dry_run == true) && 'Preview' || 'Publish' }} CLI ${{ inputs.release_channel || 'stable' }}
    needs: check
    if: needs.check.outputs.affected == 'true'
    runs-on: ubuntu-latest
    environment: npm-release

    steps:
      - name: Resolve effective inputs
        id: effective
        shell: bash
        run: |
          # push trigger: inputs are empty — use stable/auto/no-dry-run defaults.
          echo "dry_run=${{ inputs.dry_run == '' && 'false' || inputs.dry_run }}" >> "$GITHUB_OUTPUT"
          echo "release_channel=${{ inputs.release_channel == '' && 'stable' || inputs.release_channel }}" >> "$GITHUB_OUTPUT"
          echo "version_specifier=${{ inputs.version_specifier == '' && 'auto' || inputs.version_specifier }}" >> "$GITHUB_OUTPUT"
          echo "first_release=${{ inputs.first_release == '' && 'false' || inputs.first_release }}" >> "$GITHUB_OUTPUT"
          echo "allow_major=${{ inputs.allow_major == '' && 'false' || inputs.allow_major }}" >> "$GITHUB_OUTPUT"

      - name: Checkout
        uses: actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955 # v4.3.0
        with:
          fetch-depth: 0
          fetch-tags: true
          persist-credentials: true
          token: ${{ secrets.BOT_PAT || secrets.GITHUB_TOKEN }}

      - name: Release prelude
        uses: ./.github/actions/release-prelude
        with:
          dry_run: ${{ steps.effective.outputs.dry_run }}
          bot_pat: ${{ secrets.BOT_PAT }}

      - name: Configure release
        id: release
        uses: ./.github/actions/configure-release-version
        with:
          release_channel: ${{ steps.effective.outputs.release_channel }}
          version_specifier: ${{ steps.effective.outputs.version_specifier }}
          allow_major: ${{ steps.effective.outputs.allow_major }}
          git_ref: ${{ github.ref_name }}

      - name: Preflight release-critical tests
        run: bash tools/cli-release/preflight-tests.sh

      - name: Preflight package validation
        run: pnpm nx run cli:smoke-install

      - name: Preview Nx release and npm publish
        if: steps.effective.outputs.dry_run == 'true'
        shell: bash
        run: |
          set -euo pipefail
          args=(release --projects=cli --skip-publish --dry-run --yes)
          specifier='${{ steps.release.outputs.specifier }}'
          preid='${{ steps.release.outputs.preid }}'
          if [[ "$specifier" != 'auto' ]]; then args+=("$specifier"); fi
          if [[ -n "$preid" ]]; then args+=(--preid "$preid"); fi
          if [[ '${{ steps.effective.outputs.first_release }}' = 'true' ]]; then args+=(--first-release); fi
          pnpm nx "${args[@]}"
          npm publish dist/apps/cli --dry-run --access public --tag '${{ steps.release.outputs.dist_tag }}'

      - name: Release CLI package
        if: steps.effective.outputs.dry_run != 'true'
        id: cli
        uses: ./.github/actions/nx-release-package
        with:
          project: cli
          package_json_path: apps/cli/package.json
          dist_path: dist/apps/cli
          dist_tag: ${{ steps.release.outputs.dist_tag }}
          npm_access: public
          npm_token: ${{ secrets.NPM_TOKEN }}
          first_release: ${{ steps.effective.outputs.first_release }}
          specifier: ${{ steps.release.outputs.specifier }}
          preid: ${{ steps.release.outputs.preid }}
          git_ref: ${{ github.ref_name }}

      - name: Validate versioned package
        if: steps.effective.outputs.dry_run != 'true' && steps.cli.outputs.bumped == 'true'
        run: pnpm nx run cli:smoke-install

      - name: Generate release assets
        if: steps.cli.outputs.bumped != 'false'
        shell: bash
        run: |
          set -euo pipefail
          npx --yes @cyclonedx/cdxgen --required-only -t js -o dist/apps/cli/cli-sbom.cyclonedx.json
          node tools/cli-release/release-notes.mjs --package-dir dist/apps/cli --output dist/apps/cli/release-notes.md
          npm pack dist/apps/cli --pack-destination dist/apps/cli --json > dist/apps/cli/pack.json

      - name: Verify published CLI binary
        if: steps.effective.outputs.dry_run != 'true' && steps.cli.outputs.bumped == 'true'
        shell: bash
        run: |
          set -euo pipefail
          name="$(node -p "require('./apps/cli/package.json').name")"
          version='${{ steps.cli.outputs.version }}'
          for attempt in 1 2 3 4 5; do
            npm view "${name}@${version}" version dist.integrity dist.tarball --json > dist/apps/cli/npm-view.json 2>/dev/null && break
            sleep $((attempt * 10))
          done
          npm exec --yes --package "${name}@${version}" -- lenserfight --version

      - name: Create GitHub Release
        if: steps.effective.outputs.dry_run != 'true' && steps.cli.outputs.bumped == 'true'
        env:
          GH_TOKEN: ${{ github.token }}
        shell: bash
        run: |
          set -euo pipefail
          tag='${{ steps.cli.outputs.tag }}'
          tarball="$(node -e "const p=require('./dist/apps/cli/pack.json')[0]; console.log('dist/apps/cli/' + p.filename)")"
          prerelease_flag=()
          if [[ '${{ steps.effective.outputs.release_channel }}' != 'stable' ]]; then
            prerelease_flag=(--prerelease)
          fi
          gh release create "$tag" \
            "$tarball" \
            dist/apps/cli/cli-sbom.cyclonedx.json \
            dist/apps/cli/package-validation.json \
            dist/apps/cli/package-smoke.json \
            --title "$tag" \
            --notes-file dist/apps/cli/release-notes.md \
            "${prerelease_flag[@]}"

      - name: Upload release artifacts
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
        with:
          name: cli-release-${{ steps.effective.outputs.release_channel }}-${{ github.run_number }}
          path: |
            dist/apps/cli/*.tgz
            dist/apps/cli/pack.json
            dist/apps/cli/npm-view.json
            dist/apps/cli/package-validation.json
            dist/apps/cli/package-smoke.json
            dist/apps/cli/cli-sbom.cyclonedx.json
            dist/apps/cli/release-notes.md
