name: LFX Release
run-name: LFX Release ${{ github.event.inputs.version || 'dev' }} by @${{ github.actor }}

on:
  workflow_dispatch:
    inputs:
      version:
        description: "Version to release (e.g., 0.1.0)"
        required: true
        type: string
      publish_pypi:
        description: "Publish to PyPI"
        required: true
        type: boolean
        default: true
      build_docker:
        description: "Build and publish Docker images"
        required: true
        type: boolean
        default: true
      pre_release:
        description: "Mark as pre-release"
        required: false
        type: boolean
        default: false
      create_github_release:
        description: "Create GitHub release"
        required: true
        type: boolean
        default: true

env:
  PYTHON_VERSION: "3.13"

permissions:
  contents: write
  packages: write

jobs:
  validate-version:
    name: Validate Version
    runs-on: ubuntu-latest
    outputs:
      should_release: ${{ steps.check.outputs.should_release }}
      current_version: ${{ steps.check.outputs.current_version }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v6

      - name: Setup Environment
        uses: astral-sh/setup-uv@v6
        with:
          enable-cache: true
          cache-dependency-glob: "uv.lock"
          python-version: ${{ env.PYTHON_VERSION }}
          prune-cache: false

      - name: Check version
        id: check
        run: |
          cd src/lfx
          # Use uv tree to get package info, consistent with nightly workflow
          name=$(uv tree | grep 'lfx' | head -n 1 | awk '{print $1}')
          version=$(uv tree | grep 'lfx' | head -n 1 | awk '{print $2}')

          # Strip leading 'v' if present
          version=$(echo $version | sed 's/^v//')
          echo "current_version=$version" >> $GITHUB_OUTPUT

          if [ "$version" != "${{ github.event.inputs.version }}" ]; then
            echo "❌ Version mismatch: package has $version but input is ${{ github.event.inputs.version }}"
            echo "Please update the version in pyproject.toml first"
            echo "should_release=false" >> $GITHUB_OUTPUT
            exit 1
          fi

          # Check if version already exists on PyPI
          if curl -s "https://pypi.org/pypi/lfx/json" | jq -r '.releases | keys[]' | grep -q "^${{ github.event.inputs.version }}$"; then
            echo "❌ Version ${{ github.event.inputs.version }} already exists on PyPI"
            echo "should_release=false" >> $GITHUB_OUTPUT
            exit 1
          fi

          echo "✅ Version ${{ github.event.inputs.version }} is valid and not yet released"
          echo "should_release=true" >> $GITHUB_OUTPUT

  run-tests:
    name: Run Tests
    needs: validate-version
    if: needs.validate-version.outputs.should_release == 'true'
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
    steps:
      - name: Checkout code
        uses: actions/checkout@v6

      - name: Setup Environment
        uses: astral-sh/setup-uv@v6
        with:
          enable-cache: true
          cache-dependency-glob: "uv.lock"
          python-version: ${{ matrix.python-version }}
          prune-cache: false

      - name: Run LFX tests
        run: |
          cd src/lfx
          make test

      - name: Test CLI installation
        run: |
          cd src/lfx
          uv pip install .
          uv run lfx --help
          uv run lfx run --help
          uv run lfx serve --help

  release-lfx:
    name: Build and Release LFX
    needs: [validate-version, run-tests]
    runs-on: ubuntu-latest
    outputs:
      version: ${{ steps.check-version.outputs.version }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v6

      - name: Setup Environment
        uses: astral-sh/setup-uv@v6
        with:
          enable-cache: true
          cache-dependency-glob: "uv.lock"
          python-version: ${{ env.PYTHON_VERSION }}
          prune-cache: false

      - name: Install LFX dependencies
        run: uv sync --dev --package lfx

      - name: Verify Version
        id: check-version
        run: |
          cd src/lfx
          # Use uv tree to get package info, consistent with nightly workflow
          name=$(uv tree | grep 'lfx' | head -n 1 | awk '{print $1}')
          version=$(uv tree | grep 'lfx' | head -n 1 | awk '{print $2}')

          # Verify package name
          if [ "$name" != "lfx" ]; then
            echo "Package name $name does not match lfx. Exiting the workflow."
            exit 1
          fi

          # Strip leading 'v' if present
          version=$(echo $version | sed 's/^v//')

          # Verify version matches input
          if [ "$version" != "${{ github.event.inputs.version }}" ]; then
            echo "Version $version does not match input ${{ github.event.inputs.version }}. Exiting the workflow."
            exit 1
          fi

          echo "version=$version" >> $GITHUB_OUTPUT

      - name: Build distribution
        run: |
          cd src/lfx
          rm -rf dist/
          uv build --wheel --out-dir dist

      - name: Check build artifacts
        run: |
          cd src/lfx
          ls -la dist/
          # Verify wheel contents
          unzip -l dist/*.whl | grep -E "(lfx/__main__.py|lfx/cli/run.py|lfx/cli/commands.py)"

      - name: Test installation from wheel
        run: |
          cd src/lfx
          uv pip install dist/*.whl --force-reinstall
          uv run lfx --help
          echo "LFX CLI test completed successfully"

      - name: Upload artifacts
        uses: actions/upload-artifact@v6
        with:
          name: lfx-dist
          path: src/lfx/dist/
          retention-days: 5

      - name: Publish to PyPI
        if: github.event.inputs.publish_pypi == 'true'
        env:
          UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
        run: |
          cd src/lfx
          uv publish dist/*.whl

  build-docker:
    name: Build Docker Images
    needs: [validate-version, run-tests]
    if: github.event.inputs.build_docker == 'true'
    runs-on: ubuntu-latest
    strategy:
      matrix:
        variant: [production, alpine]
    steps:
      - name: Checkout code
        uses: actions/checkout@v6

      - name: Set up QEMU
        uses: docker/setup-qemu-action@v3

      - name: Docker System Info and Cleanup
        run: |
          echo "=== Docker System Usage Before Cleanup ==="
          docker system df || true
          docker buildx du || true

          echo "=== Cleaning up Docker System ==="
          docker system prune -af --volumes || true
          docker buildx prune -af || true

          echo "=== Docker System Usage After Cleanup ==="
          docker system df || true
          docker buildx du || true

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - name: Login to GitHub Container Registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Prepare Docker metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: |
            langflowai/lfx
            ghcr.io/langflow-ai/lfx
          tags: |
            type=raw,value=${{ github.event.inputs.version }}${{ matrix.variant == 'alpine' && '-alpine' || '' }}
            type=raw,value=latest${{ matrix.variant == 'alpine' && '-alpine' || '' }},enable=${{ github.event.inputs.pre_release == 'false' }}
          labels: |
            org.opencontainers.image.title=LFX
            org.opencontainers.image.description=Langflow Executor - CLI tool for running Langflow AI workflows
            org.opencontainers.image.vendor=Langflow
            org.opencontainers.image.version=${{ github.event.inputs.version }}

      - name: Build and push Docker image
        uses: docker/build-push-action@v6
        with:
          context: .
          file: src/lfx/docker/Dockerfile${{ matrix.variant == 'alpine' && '.alpine' || '' }}
          platforms: linux/amd64,linux/arm64
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          build-args: |
            LFX_VERSION=${{ github.event.inputs.version }}

  create-release:
    name: Create GitHub Release
    needs: [release-lfx, build-docker]
    if: always() && github.event.inputs.create_github_release == 'true' && needs.release-lfx.result == 'success'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v6

      - name: Download artifacts
        uses: actions/download-artifact@v7
        with:
          name: lfx-dist
          path: dist/

      - name: Generate release notes
        id: notes
        run: |
          cat > release_notes.md << EOF
          # LFX ${{ github.event.inputs.version }}

          ## 🚀 Installation

          ### PyPI
          \`\`\`bash
          pip install lfx==${{ github.event.inputs.version }}
          # or
          uv pip install lfx==${{ github.event.inputs.version }}
          # or run without installing
          uvx lfx@${{ github.event.inputs.version }} --help
          \`\`\`

          ### Docker
          \`\`\`bash
          # Standard image
          docker pull langflowai/lfx:${{ github.event.inputs.version }}

          # Alpine image (smaller)
          docker pull langflowai/lfx:${{ github.event.inputs.version }}-alpine

          # Run a flow
          docker run --rm -v \$(pwd):/app/data langflowai/lfx:${{ github.event.inputs.version }} lfx run flow.json --input-value "Hello"
          \`\`\`

          ## 📦 What's New

          <!-- Add release notes here -->

          ## 📋 Checksums

          \`\`\`
          $(cd dist && sha256sum *)
          \`\`\`

          ---

          **Full Changelog**: https://github.com/${{ github.repository }}/compare/v${{ needs.validate-version.outputs.current_version }}...lfx-v${{ github.event.inputs.version }}
          EOF

      - name: Create Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: lfx-v${{ github.event.inputs.version }}
          name: LFX ${{ github.event.inputs.version }}
          body_path: release_notes.md
          draft: false
          prerelease: ${{ github.event.inputs.pre_release }}
          files: |
            dist/*
          generate_release_notes: true

  test-release:
    name: Test Release
    needs: [release-lfx, build-docker]
    if: always() && (needs.release-lfx.result == 'success' || needs.build-docker.result == 'success')
    runs-on: ubuntu-latest
    steps:
      - name: Wait for PyPI propagation
        if: needs.release-lfx.result == 'success'
        run: sleep 60

      - name: Test PyPI installation
        if: needs.release-lfx.result == 'success'
        run: |
          # Test installation using uv
          uv pip install lfx==${{ github.event.inputs.version }}
          uv run lfx --help

      - name: Test Docker image
        if: needs.build-docker.result == 'success'
        run: |
          # Test standard image
          docker run --rm langflowai/lfx:${{ github.event.inputs.version }} lfx --help

          # Test alpine image
          docker run --rm langflowai/lfx:${{ github.event.inputs.version }}-alpine lfx --help

          # Test with a simple flow
          cat > test_flow.json << 'EOF'
          {
            "nodes": [],
            "edges": []
          }
          EOF

          docker run --rm -v $(pwd):/app/data langflowai/lfx:${{ github.event.inputs.version }} \
            lfx run /app/data/test_flow.json --input-value "test" || true

  notify:
    name: Notify Release Status
    needs: [create-release, test-release]
    if: always()
    runs-on: ubuntu-latest
    steps:
      - name: Notify success
        if: needs.create-release.result == 'success'
        run: |
          echo "✅ LFX ${{ github.event.inputs.version }} released successfully!"
          echo "PyPI: https://pypi.org/project/lfx/${{ github.event.inputs.version }}/"
          echo "Docker Hub: https://hub.docker.com/r/langflowai/lfx/tags"
          echo "GitHub Release: https://github.com/${{ github.repository }}/releases/tag/lfx-v${{ github.event.inputs.version }}"

      - name: Notify failure
        if: needs.create-release.result != 'success'
        run: |
          echo "❌ LFX ${{ github.event.inputs.version }} release failed!"
          exit 1