name: Release

on:
  push:
    branches:
      - main
  workflow_dispatch:

concurrency: ${{ github.workflow }}-${{ github.ref }}

jobs:
  check-release:
    name: Check for new version
    runs-on: ubuntu-latest
    timeout-minutes: 5
    permissions:
      contents: read
    outputs:
      should_release: ${{ steps.check.outputs.should_release }}
      needs_github_release: ${{ steps.check.outputs.needs_github_release }}
      version: ${{ steps.check.outputs.version }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "24"
          registry-url: "https://registry.npmjs.org"

      - name: Compare package.json version to npm and check GitHub release
        id: check
        run: |
          LOCAL_VERSION=$(node -p "require('./packages/zero-native/package.json').version")
          echo "Local version: $LOCAL_VERSION"

          NPM_VERSION=$(npm view zero-native version 2>/dev/null || echo "0.0.0")
          echo "npm version: $NPM_VERSION"

          if [ "$LOCAL_VERSION" != "$NPM_VERSION" ]; then
            echo "Version changed: $NPM_VERSION -> $LOCAL_VERSION"
            echo "should_release=true" >> "$GITHUB_OUTPUT"
            echo "needs_github_release=true" >> "$GITHUB_OUTPUT"
          else
            echo "Version unchanged on npm, skipping publish"
            echo "should_release=false" >> "$GITHUB_OUTPUT"

            TAG="v$LOCAL_VERSION"
            EXISTING_ASSETS=$(gh release view "$TAG" --json assets --jq '.assets[].name' 2>/dev/null || true)
            missing=0
            for asset in \
              CHECKSUMS.txt \
              zero-native-darwin-arm64 \
              zero-native-darwin-x64 \
              zero-native-linux-arm64 \
              zero-native-linux-x64 \
              zero-native-linux-musl-arm64 \
              zero-native-linux-musl-x64 \
              zero-native-win32-arm64.exe \
              zero-native-win32-x64.exe
            do
              if ! printf '%s\n' "$EXISTING_ASSETS" | grep -Fx "$asset" >/dev/null; then
                echo "Missing release asset: $asset"
                missing=1
              fi
            done

            if [ "$missing" -eq 0 ]; then
              echo "GitHub release $TAG exists with native assets"
              echo "needs_github_release=false" >> "$GITHUB_OUTPUT"
            else
              echo "GitHub release $TAG is missing native assets, will create/update it"
              echo "needs_github_release=true" >> "$GITHUB_OUTPUT"
            fi
          fi

          echo "version=$LOCAL_VERSION" >> "$GITHUB_OUTPUT"
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  github-release:
    name: Create GitHub Release
    needs: check-release
    if: needs.check-release.outputs.needs_github_release == 'true'
    runs-on: macos-14
    timeout-minutes: 10
    permissions:
      contents: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Zig
        uses: mlugg/setup-zig@v2
        with:
          version: 0.16.0

      - name: Extract changelog entry
        run: |
          VERSION="${{ needs.check-release.outputs.version }}"
          awk '/<!-- release:start -->/{found=1; next} /<!-- release:end -->/{exit} found{print}' CHANGELOG.md > /tmp/release-notes.md

          LINES=$(wc -l < /tmp/release-notes.md | tr -d ' ')
          if [ "$LINES" -lt 2 ]; then
            echo "Error: No release notes found between <!-- release:start --> and <!-- release:end --> markers in CHANGELOG.md"
            exit 1
          fi
          echo "Extracted release notes for $VERSION ($LINES lines)"

      - name: Build native release asset
        run: |
          mkdir -p /tmp/zero-native-release

          build_asset() {
            target="$1"
            name="$2"
            rm -rf zig-out
            zig build -Dtarget="$target" -Doptimize=ReleaseSmall

            src="zig-out/bin/zero-native"
            case "$name" in
              *.exe) src="${src}.exe" ;;
            esac

            cp "$src" "/tmp/zero-native-release/$name"
            chmod 755 "/tmp/zero-native-release/$name"
          }

          build_asset aarch64-macos zero-native-darwin-arm64
          build_asset x86_64-macos zero-native-darwin-x64
          build_asset aarch64-linux-gnu zero-native-linux-arm64
          build_asset x86_64-linux-gnu zero-native-linux-x64
          build_asset aarch64-linux-musl zero-native-linux-musl-arm64
          build_asset x86_64-linux-musl zero-native-linux-musl-x64
          build_asset aarch64-windows zero-native-win32-arm64.exe
          build_asset x86_64-windows zero-native-win32-x64.exe

          (cd /tmp/zero-native-release && shasum -a 256 zero-native-* > CHECKSUMS.txt)

      - name: Create GitHub Release
        run: |
          VERSION="${{ needs.check-release.outputs.version }}"
          TAG="v$VERSION"

          if gh release view "$TAG" &>/dev/null; then
            echo "Release $TAG already exists, updating assets"
          else
            echo "Creating release $TAG..."
            gh release create "$TAG" \
              --title "$TAG" \
              --notes-file /tmp/release-notes.md
          fi

          gh release upload "$TAG" \
            /tmp/zero-native-release/zero-native-* \
            /tmp/zero-native-release/CHECKSUMS.txt \
            --clobber
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  publish:
    name: Publish CLI to npm
    needs: [check-release, github-release]
    if: >-
      always()
      && needs.check-release.outputs.should_release == 'true'
      && (needs.github-release.result == 'success' || needs.github-release.result == 'skipped')
    runs-on: ubuntu-latest
    timeout-minutes: 10
    environment: Release
    permissions:
      contents: read
      id-token: write
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "24"
          registry-url: "https://registry.npmjs.org"

      - name: Check version sync
        run: npm --prefix packages/zero-native run version:check

      - name: Check package scripts
        run: npm --prefix packages/zero-native run scripts:check

      - name: Publish to npm
        run: |
          if [ "${{ github.event.repository.visibility }}" = "public" ]; then
            npm publish --provenance --access public
          else
            npm publish --access public
          fi
        working-directory: packages/zero-native
