name: Publish to PyPI

on:
  workflow_dispatch:
    inputs:
      release_tag:
        description: 'Release tag to publish'
        required: false
        type: string

jobs:
  build:
    name: Build packages
    runs-on: ubuntu-latest
    permissions:
        contents: read
    steps:
      - name: Determine release tag
        id: release
        run: |
          if [ -n "${{ inputs.release_tag }}" ]; then
            echo "tag=${{ inputs.release_tag }}" >> $GITHUB_OUTPUT
          else
            echo "tag=" >> $GITHUB_OUTPUT
          fi

      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
        with:
          ref: ${{ steps.release.outputs.tag || github.ref }}

      - name: Set up Python
        uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0
        with:
          python-version: "3.12"

      - name: Install uv
        uses: astral-sh/setup-uv@38f3f104447c67c051c4a08e39b64a148898af3a # v4

      - name: Build packages
        run: |
          uv build --all-packages
          rm dist/.gitignore

      - name: Upload artifacts
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
        with:
          name: dist
          path: dist/

  publish:
    name: Publish to PyPI
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: pypi
      url: https://pypi.org/p/crewai
    permissions:
      id-token: write
      contents: read
    steps:
      - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
        with:
          ref: ${{ inputs.release_tag || github.ref }}

      - name: Install uv
        uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6
        with:
          version: "0.11.3"
          python-version: "3.12"
          enable-cache: false

      - name: Download artifacts
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: dist
          path: dist

      - name: Publish to PyPI
        env:
          UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
        run: |
          failed=0
          for package in dist/*; do
            if [[ "$package" == *"crewai_devtools"* ]]; then
              echo "Skipping private package: $package"
              continue
            fi
            echo "Publishing $package"
            if ! uv publish "$package"; then
              echo "Failed to publish $package"
              failed=1
            fi
          done
          if [ $failed -eq 1 ]; then
            echo "Some packages failed to publish"
            exit 1
          fi

      - name: Build Slack payload
        if: success()
        id: slack
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          RELEASE_TAG: ${{ inputs.release_tag }}
        run: |
          payload=$(uv run python -c "
          import json, re, subprocess, sys

          with open('lib/crewai/src/crewai/__init__.py') as f:
              m = re.search(r\"__version__\s*=\s*[\\\"']([^\\\"']+)\", f.read())
          version = m.group(1) if m else 'unknown'

          import os
          tag = os.environ.get('RELEASE_TAG') or version

          try:
              r = subprocess.run(['gh','release','view',tag,'--json','body','-q','.body'],
                                 capture_output=True, text=True, check=True)
              body = r.stdout.strip()
          except Exception:
              body = ''

          blocks = [
              {'type':'section','text':{'type':'mrkdwn',
                  'text':f':rocket: \`crewai v{version}\` published to PyPI'}},
              {'type':'section','text':{'type':'mrkdwn',
                  'text':f'<https://pypi.org/project/crewai/{version}/|View on PyPI> · <https://github.com/crewAIInc/crewAI/releases/tag/{tag}|Release notes>'}},
              {'type':'divider'},
          ]

          if body:
              heading, items = '', []
              for line in body.split('\n'):
                  line = line.strip()
                  if not line: continue
                  hm = re.match(r'^#{2,3}\s+(.*)', line)
                  if hm:
                      if heading and items:
                          skip = heading in ('What\\'s Changed','') or 'Contributors' in heading
                          if not skip:
                              txt = f'*{heading}*\n' + '\n'.join(f'• {i}' for i in items)
                              blocks.append({'type':'section','text':{'type':'mrkdwn','text':txt}})
                      heading, items = hm.group(1), []
                  elif line.startswith('- ') or line.startswith('* '):
                      items.append(re.sub(r'\*\*([^*]*)\*\*', r'*\1*', line[2:]))
              if heading and items:
                  skip = heading in ('What\\'s Changed','') or 'Contributors' in heading
                  if not skip:
                      txt = f'*{heading}*\n' + '\n'.join(f'• {i}' for i in items)
                      blocks.append({'type':'section','text':{'type':'mrkdwn','text':txt}})

          blocks.append({'type':'divider'})
          blocks.append({'type':'section','text':{'type':'mrkdwn',
              'text':f'\`\`\`uv add \"crewai[tools]=={version}\"\`\`\`'}})

          print(json.dumps({'blocks':blocks}))
          ")
          echo "payload=$payload" >> $GITHUB_OUTPUT

      - name: Notify Slack
        if: success()
        uses: slackapi/slack-github-action@b0fa283ad8fea605de13dc3f449259339835fc52 # v2.1.0
        with:
          webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
          webhook-type: incoming-webhook
          payload: ${{ steps.slack.outputs.payload }}
