# Release workflow for ComfyUI frontend: version bump → PyPI publish → ComfyUI PR.
# Runs on a bi-weekly schedule for minor releases, or manually for patch/hotfix releases.
name: 'Release: ComfyUI'

on:
  # Bi-weekly schedule: Monday at 20:00 UTC
  schedule:
    - cron: '0 20 * * 1'

  # Manual trigger for both on-demand minor and patch/hotfix releases
  workflow_dispatch:
    inputs:
      release_type:
        description: 'minor = next minor version (bi-weekly cadence), patch = hotfix for current production version'
        required: true
        default: 'minor'
        type: choice
        options:
          - minor
          - patch
      comfyui_fork:
        description: 'ComfyUI fork to use for PR (e.g., Comfy-Org/ComfyUI)'
        required: false
        default: 'Comfy-Org/ComfyUI'
        type: string

jobs:
  check-release-week:
    runs-on: ubuntu-latest
    outputs:
      is_release_week: ${{ steps.check.outputs.is_release_week }}
    steps:
      - name: Check if release week
        id: check
        run: |
          # Anchor date: first bi-weekly release Monday
          ANCHOR="2025-12-22"

          ANCHOR_EPOCH=$(date -d "$ANCHOR" +%s)
          NOW_EPOCH=$(date +%s)
          WEEKS_SINCE=$(( (NOW_EPOCH - ANCHOR_EPOCH) / 604800 ))

          if [ $((WEEKS_SINCE % 2)) -eq 0 ]; then
            echo "Release week (week $WEEKS_SINCE since anchor $ANCHOR)"
            echo "is_release_week=true" >> $GITHUB_OUTPUT
          else
            echo "Not a release week (week $WEEKS_SINCE since anchor $ANCHOR)"
            echo "is_release_week=false" >> $GITHUB_OUTPUT
          fi

      - name: Summary
        run: |
          echo "## Release Check" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "- Is release week: ${{ steps.check.outputs.is_release_week }}" >> $GITHUB_STEP_SUMMARY
          echo "- Manual trigger: ${{ github.event_name == 'workflow_dispatch' }}" >> $GITHUB_STEP_SUMMARY
          echo "- Release type: ${{ inputs.release_type || 'minor (scheduled)' }}" >> $GITHUB_STEP_SUMMARY

  resolve-version:
    needs: check-release-week
    if: needs.check-release-week.outputs.is_release_week == 'true' || github.event_name == 'workflow_dispatch'
    runs-on: ubuntu-latest
    outputs:
      current_version: ${{ steps.resolve.outputs.current_version }}
      target_version: ${{ steps.resolve.outputs.target_version }}
      target_minor: ${{ steps.resolve.outputs.target_minor }}
      target_branch: ${{ steps.resolve.outputs.target_branch }}
      needs_release: ${{ steps.resolve.outputs.needs_release }}
      diff_url: ${{ steps.resolve.outputs.diff_url }}
      latest_patch_tag: ${{ steps.resolve.outputs.latest_patch_tag }}

    steps:
      - name: Checkout ComfyUI_frontend
        uses: actions/checkout@v6
        with:
          fetch-depth: 0
          path: frontend

      - name: Checkout ComfyUI (sparse)
        uses: actions/checkout@v6
        with:
          repository: Comfy-Org/ComfyUI
          sparse-checkout: |
            requirements.txt
          path: comfyui

      - name: Install pnpm
        uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0
        with:
          package_json_file: frontend/package.json

      - name: Setup Node.js
        uses: actions/setup-node@v6
        with:
          node-version-file: 'frontend/.nvmrc'

      - name: Install dependencies
        working-directory: frontend
        run: pnpm install --frozen-lockfile

      - name: Resolve release information
        id: resolve
        working-directory: frontend
        env:
          RELEASE_TYPE: ${{ inputs.release_type || 'minor' }}
        run: |
          set -euo pipefail

          # Run the resolver script
          if ! RESULT=$(pnpm exec tsx scripts/cicd/resolve-comfyui-release.ts ../comfyui .); then
            echo "Failed to resolve release information"
            exit 1
          fi

          echo "Resolver output:"
          echo "$RESULT"

          # Validate JSON output
          if ! echo "$RESULT" | jq empty 2>/dev/null; then
            echo "Invalid JSON output from resolver"
            exit 1
          fi

          # Parse JSON output and set outputs
          echo "current_version=$(echo "$RESULT" | jq -r '.current_version')" >> $GITHUB_OUTPUT
          echo "target_version=$(echo "$RESULT" | jq -r '.target_version')" >> $GITHUB_OUTPUT
          echo "target_minor=$(echo "$RESULT" | jq -r '.target_minor')" >> $GITHUB_OUTPUT
          echo "target_branch=$(echo "$RESULT" | jq -r '.target_branch')" >> $GITHUB_OUTPUT
          echo "needs_release=$(echo "$RESULT" | jq -r '.needs_release')" >> $GITHUB_OUTPUT
          echo "diff_url=$(echo "$RESULT" | jq -r '.diff_url')" >> $GITHUB_OUTPUT
          echo "latest_patch_tag=$(echo "$RESULT" | jq -r '.latest_patch_tag')" >> $GITHUB_OUTPUT

      - name: Summary
        run: |
          echo "## Release Information" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "- Current version: ${{ steps.resolve.outputs.current_version }}" >> $GITHUB_STEP_SUMMARY
          echo "- Target version: ${{ steps.resolve.outputs.target_version }}" >> $GITHUB_STEP_SUMMARY
          echo "- Target branch: ${{ steps.resolve.outputs.target_branch }}" >> $GITHUB_STEP_SUMMARY
          echo "- Needs release: ${{ steps.resolve.outputs.needs_release }}" >> $GITHUB_STEP_SUMMARY
          echo "- Diff: [${{ steps.resolve.outputs.current_version }}...${{ steps.resolve.outputs.target_version }}](${{ steps.resolve.outputs.diff_url }})" >> $GITHUB_STEP_SUMMARY

  trigger-release-if-needed:
    needs: resolve-version
    if: needs.resolve-version.outputs.needs_release == 'true'
    runs-on: ubuntu-latest

    steps:
      - name: Trigger release workflow
        env:
          GH_TOKEN: ${{ secrets.PR_GH_TOKEN }}
        run: |
          set -euo pipefail

          echo "Triggering release workflow for branch ${{ needs.resolve-version.outputs.target_branch }}"

          # Trigger the release-version-bump workflow
          if ! gh workflow run release-version-bump.yaml \
            --repo Comfy-Org/ComfyUI_frontend \
            --ref main \
            --field version_type=patch \
            --field branch=${{ needs.resolve-version.outputs.target_branch }}; then
            echo "Failed to trigger release workflow"
            exit 1
          fi

          echo "Release workflow triggered successfully for ${{ needs.resolve-version.outputs.target_branch }}"

      - name: Summary
        run: |
          echo "## Release Workflow Triggered" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "- Branch: ${{ needs.resolve-version.outputs.target_branch }}" >> $GITHUB_STEP_SUMMARY
          echo "- Target version: ${{ needs.resolve-version.outputs.target_version }}" >> $GITHUB_STEP_SUMMARY
          echo "- [View workflow runs](https://github.com/Comfy-Org/ComfyUI_frontend/actions/workflows/release-version-bump.yaml)" >> $GITHUB_STEP_SUMMARY

  publish-pypi:
    needs: [resolve-version, trigger-release-if-needed]
    if: >
      always() &&
      needs.resolve-version.result == 'success' &&
      (needs.trigger-release-if-needed.result == 'success' ||
       needs.trigger-release-if-needed.result == 'skipped')
    runs-on: ubuntu-latest
    steps:
      - name: Wait for release PR to be created and merged
        if: needs.trigger-release-if-needed.result == 'success'
        env:
          GH_TOKEN: ${{ secrets.PR_GH_TOKEN }}
        run: |
          set -euo pipefail

          TARGET_VERSION="${{ needs.resolve-version.outputs.target_version }}"
          TARGET_BRANCH="${{ needs.resolve-version.outputs.target_branch }}"
          echo "Waiting for version bump PR for v${TARGET_VERSION} on ${TARGET_BRANCH} to be merged..."

          # Poll for up to 30 minutes (a human or automation needs to merge the version bump PR)
          for i in $(seq 1 60); do
            # Check if the tag exists (release-draft-create creates a tag on merge)
            if gh api "repos/Comfy-Org/ComfyUI_frontend/git/ref/tags/v${TARGET_VERSION}" --silent 2>/dev/null; then
              echo "✅ Tag v${TARGET_VERSION} found — release PR has been merged"
              exit 0
            fi
            echo "Attempt $i/60: Tag v${TARGET_VERSION} not found yet, waiting 30s..."
            sleep 30
          done

          echo "❌ Timed out waiting for tag v${TARGET_VERSION}"
          exit 1

      - name: Checkout code at target version
        uses: actions/checkout@v6
        with:
          ref: v${{ needs.resolve-version.outputs.target_version }}

      - name: Install pnpm
        uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4.4.0

      - uses: actions/setup-node@v6
        with:
          node-version-file: '.nvmrc'
          cache: 'pnpm'

      - name: Build project
        env:
          SENTRY_DSN: ${{ secrets.SENTRY_DSN }}
          ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
          ALGOLIA_API_KEY: ${{ secrets.ALGOLIA_API_KEY }}
          ENABLE_MINIFY: 'true'
          USE_PROD_CONFIG: 'true'
        run: |
          pnpm install --frozen-lockfile
          pnpm build

      - name: Set up Python
        uses: actions/setup-python@v6
        with:
          python-version: '3.x'

      - name: Install build dependencies
        run: python -m pip install build

      - name: Build and publish PyPI package
        run: |
          set -euo pipefail
          mkdir -p comfyui_frontend_package/comfyui_frontend_package/static/
          cp -r dist/* comfyui_frontend_package/comfyui_frontend_package/static/

      - name: Build pypi package
        run: python -m build
        working-directory: comfyui_frontend_package
        env:
          COMFYUI_FRONTEND_VERSION: ${{ needs.resolve-version.outputs.target_version }}

      - name: Publish pypi package
        uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0
        with:
          skip-existing: true
          password: ${{ secrets.PYPI_TOKEN }}
          packages-dir: comfyui_frontend_package/dist

      - name: Wait for PyPI propagation
        run: |
          set -euo pipefail

          TARGET_VERSION="${{ needs.resolve-version.outputs.target_version }}"
          PACKAGE="comfyui-frontend-package"
          echo "Waiting for ${PACKAGE}==${TARGET_VERSION} to be available on PyPI..."

          # Wait up to 15 minutes (polling every 30 seconds)
          for i in $(seq 1 30); do
            HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "https://pypi.org/pypi/${PACKAGE}/${TARGET_VERSION}/json")
            if [ "$HTTP_CODE" = "200" ]; then
              echo "✅ ${PACKAGE}==${TARGET_VERSION} is available on PyPI"
              exit 0
            fi
            echo "Attempt $i/30: PyPI returned HTTP ${HTTP_CODE}, waiting 30s..."
            sleep 30
          done

          echo "❌ Timed out waiting for ${PACKAGE}==${TARGET_VERSION} on PyPI"
          exit 1

      - name: Summary
        run: |
          echo "## PyPI Publishing" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "- Package: comfyui-frontend-package" >> $GITHUB_STEP_SUMMARY
          echo "- Version: ${{ needs.resolve-version.outputs.target_version }}" >> $GITHUB_STEP_SUMMARY
          echo "- Status: ✅ Published and confirmed available" >> $GITHUB_STEP_SUMMARY

  create-comfyui-pr:
    needs:
      [
        check-release-week,
        resolve-version,
        trigger-release-if-needed,
        publish-pypi
      ]
    if: always() && needs.resolve-version.result == 'success' && needs.publish-pypi.result == 'success' && (needs.check-release-week.outputs.is_release_week == 'true' || github.event_name == 'workflow_dispatch')
    runs-on: ubuntu-latest

    steps:
      - name: Checkout ComfyUI fork
        uses: actions/checkout@v6
        with:
          repository: ${{ inputs.comfyui_fork || 'Comfy-Org/ComfyUI' }}
          token: ${{ secrets.PR_GH_TOKEN }}
          path: comfyui

      - name: Sync with upstream
        working-directory: comfyui
        run: |
          set -euo pipefail

          # Fetch latest upstream to base our branch on fresh code
          # Note: This only affects the local checkout, NOT the fork's master branch
          # We only push the automation branch, leaving the fork's master untouched
          echo "Fetching upstream master..."
          if ! git fetch https://github.com/Comfy-Org/ComfyUI.git master; then
            echo "Failed to fetch upstream master"
            exit 1
          fi

          echo "Checking out upstream master..."
          if ! git checkout FETCH_HEAD; then
            echo "Failed to checkout FETCH_HEAD"
            exit 1
          fi

          echo "Successfully synced with upstream master"

      - name: Update requirements.txt
        working-directory: comfyui
        run: |
          set -euo pipefail

          TARGET_VERSION="${{ needs.resolve-version.outputs.target_version }}"
          echo "Updating comfyui-frontend-package to ${TARGET_VERSION}"

          # Update the comfyui-frontend-package version (POSIX-compatible)
          sed -i.bak "s/comfyui-frontend-package==[0-9.][0-9.]*/comfyui-frontend-package==${TARGET_VERSION}/" requirements.txt
          rm requirements.txt.bak

          # Verify the change was made
          if ! grep -q "comfyui-frontend-package==${TARGET_VERSION}" requirements.txt; then
            echo "Failed to update requirements.txt"
            exit 1
          fi

          echo "Updated requirements.txt:"
          grep comfyui-frontend-package requirements.txt

      - name: Build PR description
        id: pr-body
        run: |
          BODY=$(cat <<'EOF'
          Bumps frontend to ${{ needs.resolve-version.outputs.target_version }}

          Test quickly:

          ```bash
          python main.py --front-end-version Comfy-Org/ComfyUI_frontend@${{ needs.resolve-version.outputs.target_version }}
          ```

          - Diff: [v${{ needs.resolve-version.outputs.current_version }}...v${{ needs.resolve-version.outputs.target_version }}](${{ needs.resolve-version.outputs.diff_url }})
          - PyPI: https://pypi.org/project/comfyui-frontend-package/${{ needs.resolve-version.outputs.target_version }}/
          - npm: https://www.npmjs.com/package/@comfyorg/comfyui-frontend-types/v/${{ needs.resolve-version.outputs.target_version }}
          EOF
          )

          PYPI_NOTE="✅ **PyPI package confirmed available** — \`comfyui-frontend-package==${{ needs.resolve-version.outputs.target_version }}\` has been published and verified."
          BODY=$''"${PYPI_NOTE}"$'\n\n'"${BODY}"

          # Save to file for later use
          printf '%s\n' "$BODY" > pr-body.txt
          cat pr-body.txt

      - name: Create PR to ComfyUI
        working-directory: comfyui
        env:
          GH_TOKEN: ${{ secrets.PR_GH_TOKEN }}
          COMFYUI_FORK: ${{ inputs.comfyui_fork || 'Comfy-Org/ComfyUI' }}
        run: |
          set -euo pipefail

          # Extract fork owner from repository name
          FORK_OWNER=$(echo "$COMFYUI_FORK" | cut -d'/' -f1)

          echo "Creating PR from ${COMFYUI_FORK} to Comfy-Org/ComfyUI"

          # Configure git
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"

          # Create/update branch (reuse same branch name each release cycle)
          BRANCH="automation/comfyui-frontend-bump"
          git checkout -B "$BRANCH"
          git add requirements.txt

          if ! git diff --cached --quiet; then
            git commit -m "Bump comfyui-frontend-package to ${{ needs.resolve-version.outputs.target_version }}"
          else
            echo "No changes to commit"
            exit 0
          fi

          # Force push to fork (overwrites previous release cycle's branch)
          # Note: This intentionally destroys branch history to maintain a single PR
          # Any review comments or manual commits will need to be re-applied
          if ! git push -f origin "$BRANCH"; then
            echo "Failed to push branch to fork"
            exit 1
          fi

          # Create draft PR from fork to upstream
          PR_BODY=$(cat ../pr-body.txt)

          # Try to create PR, ignore error if it already exists
          if ! gh pr create \
            --repo Comfy-Org/ComfyUI \
            --head "${FORK_OWNER}:${BRANCH}" \
            --base master \
            --title "Bump comfyui-frontend-package to ${{ needs.resolve-version.outputs.target_version }}" \
            --body "$PR_BODY" \
            --draft 2>&1; then

            # Check if PR already exists
            set +e
            EXISTING_PR=$(gh pr list --repo Comfy-Org/ComfyUI --head "${FORK_OWNER}:${BRANCH}" --json number --jq '.[0].number' 2>&1)
            PR_LIST_EXIT=$?
            set -e

            if [ $PR_LIST_EXIT -ne 0 ]; then
              echo "Failed to check for existing PR: $EXISTING_PR"
              exit 1
            fi

            if [ -n "$EXISTING_PR" ] && [ "$EXISTING_PR" != "null" ]; then
              echo "PR already exists (#${EXISTING_PR}), refreshing title/body"
              gh pr edit "$EXISTING_PR" \
                --repo Comfy-Org/ComfyUI \
                --title "Bump comfyui-frontend-package to ${{ needs.resolve-version.outputs.target_version }}" \
                --body-file ../pr-body.txt
            else
              echo "Failed to create PR and no existing PR found"
              exit 1
            fi
          fi

      - name: Summary
        run: |
          echo "## ComfyUI PR Created" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "Draft PR created in Comfy-Org/ComfyUI" >> $GITHUB_STEP_SUMMARY
          echo "" >> $GITHUB_STEP_SUMMARY
          echo "### PR Body:" >> $GITHUB_STEP_SUMMARY
          cat pr-body.txt >> $GITHUB_STEP_SUMMARY
