name: Publish Package

on:
  push:
    branches:
      - main
      - 'v[0-9]*'

permissions: {}

env:
  VITEST_GENERATE_UI_TOKEN: 'true'
  VITE_TEST_WATCHER_DEBUG: 'false'

jobs:
  # Keep detection outside the Release environment. Environment approval is job-level,
  # so the publish job is only created after a release commit is detected.
  detect:
    if: github.repository == 'vitest-dev/vitest'
    name: Detect release commit
    runs-on: ubuntu-slim
    outputs:
      release: ${{ steps.detect.outputs.release }}
      version: ${{ steps.detect.outputs.version }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          persist-credentials: false

      - name: Detect release
        id: detect
        run: |
          SUBJECT="$(git log -1 --format=%s)"
          VERSION="$(jq -r .version package.json)"
          EXPECTED="chore: release v$VERSION"
          # use prefix match since commit has PR number trailer.
          if [[ "$SUBJECT" == "$EXPECTED"* ]]; then
            echo "release=true" >> "$GITHUB_OUTPUT"
            echo "version=$VERSION" >> "$GITHUB_OUTPUT"
            echo "Detected release v$VERSION"
          else
            echo "release=false" >> "$GITHUB_OUTPUT"
            echo "No release commit found at HEAD"
          fi

  publish:
    if: needs.detect.outputs.release == 'true'
    needs: detect
    name: Publish Vitest
    runs-on: ubuntu-latest
    permissions:
      contents: write # trusted publishing and changelog requirement
      id-token: write # trusted publishing requirement
    environment: Release
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-depth: 0
          persist-credentials: false

      - name: Check release tag
        env:
          VERSION: ${{ needs.detect.outputs.version }}
        run: |
          TAG="v$VERSION"

          if git rev-parse --verify --quiet "refs/tags/$TAG"; then
            echo "Tag $TAG already exists"
            exit 1
          fi

      - name: Set node version to 24
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
        with:
          node-version: 24
          registry-url: https://registry.npmjs.org/
          # disable cache to avoid cache poisoning
          package-manager-cache: false

      - name: Install pnpm
        uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0

      - name: Install
        run: pnpm install --frozen-lockfile --prefer-offline
        env:
          PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: '1'

      - name: Build
        run: pnpm build

      - name: Install pnpm for staged publishing
        uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4.2.0
        with:
          version: 11.6.0
          # Do not read package.json's pnpm@10 packageManager field when installing pnpm 11.
          package_json_file: .github/pnpm-publish-package.json

      - name: Stage publish to npm (dry run)
        env:
          VERSION: ${{ needs.detect.outputs.version }}
          PUBLISH_BRANCH: ${{ github.ref_name }}
          PUBLISH_DRY_RUN: 'true'
          # Keep pnpm 11 instead of switching to package.json's pnpm@10 during staged publishing.
          PNPM_CONFIG_PM_ON_FAIL: ignore
          # Avoid pnpm 11 rerunning install before pnpm run, which can fail on unapproved builds.
          PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN: 'false'
        run: pnpm run publish-ci "$VERSION"

      - name: Stage publish to npm
        env:
          VERSION: ${{ needs.detect.outputs.version }}
          PUBLISH_BRANCH: ${{ github.ref_name }}
          PNPM_CONFIG_PM_ON_FAIL: ignore
          PNPM_CONFIG_VERIFY_DEPS_BEFORE_RUN: 'false'
        run: pnpm run publish-ci "$VERSION"

      - id: generate-token
        uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
        with:
          client-id: ${{ secrets.RELEASE_GITHUB_APP_ID }}
          private-key: ${{ secrets.RELEASE_GITHUB_APP_PRIVATE_KEY }}
          owner: ${{ github.repository_owner }}
          repositories: ${{ github.event.repository.name }}
          permission-contents: write

      - name: Push release tag
        env:
          VERSION: ${{ needs.detect.outputs.version }}
          GH_TOKEN: ${{ steps.generate-token.outputs.token }}
        run: |
          TAG="v$VERSION"
          git config user.name "vitest-release-bot[bot]"
          git config user.email "292707936+vitest-release-bot[bot]@users.noreply.github.com"
          git tag "$TAG" "$GITHUB_SHA"
          git push "https://x-access-token:$GH_TOKEN@github.com/$GITHUB_REPOSITORY.git" "$TAG"

      - name: Generate Changelog
        env:
          VERSION: ${{ needs.detect.outputs.version }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          TAG="v$VERSION"
          npx changelogithub --to "$TAG" --name "$TAG"
