diff --git a/.github/workflows/publish-python-package.yml b/.github/workflows/publish-python-package.yml new file mode 100644 index 000000000..233a74aab --- /dev/null +++ b/.github/workflows/publish-python-package.yml @@ -0,0 +1,101 @@ +name: Publish Python Package to PyPI + +on: + # Manual trigger only - no automatic publishing + workflow_dispatch: + inputs: + package: + description: 'Package to publish' + required: true + type: choice + options: + - sdks/python + - integrations/adk-middleware/python + - integrations/agent-spec/python + - integrations/aws-strands/python + - integrations/crew-ai/python + - integrations/langgraph/python + dry_run: + description: 'Run in dry-run mode (build without uploading)' + required: false + type: boolean + default: false + +jobs: + publish: + runs-on: ubuntu-latest + + permissions: + contents: read + id-token: write # Required for PyPI trusted publishing + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install uv + uses: astral-sh/setup-uv@v4 + with: + version: ">=0.8.0" + + - name: Set up Python + run: uv python install 3.12 + + - name: Install dependencies + working-directory: ${{ inputs.package }} + run: uv sync + + - name: Run tests + working-directory: ${{ inputs.package }} + run: uv run python -m pytest + + - name: Build package + working-directory: ${{ inputs.package }} + run: uv build + + - name: Verify wheel permissions + working-directory: ${{ inputs.package }} + run: | + echo "## Wheel file permissions check" + uv run python -c " + import zipfile, glob, sys + whl = glob.glob('dist/*.whl')[0] + print(f'Checking {whl}') + bad = [] + for info in zipfile.ZipFile(whl).infolist(): + perms = (info.external_attr >> 16) & 0o777 + readable = perms & 0o444 + print(f' {oct(perms):>8s} {info.filename}') + if not readable: + bad.append(info.filename) + if bad: + print(f'ERROR: {len(bad)} file(s) missing read permissions:') + for f in bad: + print(f' - {f}') + sys.exit(1) + print('All files have correct permissions.') + " + + - name: Publish to PyPI + if: inputs.dry_run == false + working-directory: ${{ inputs.package }} + run: uv publish + env: + UV_PUBLISH_TOKEN: ${{ secrets.PYPI_API_TOKEN }} + + - name: Summary + if: success() + working-directory: ${{ inputs.package }} + run: | + NAME=$(uv run python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['name'])") + VERSION=$(uv run python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])") + + if [ "${{ inputs.dry_run }}" = "true" ]; then + echo "## ✅ Dry-run Complete!" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Built **${NAME} ${VERSION}** successfully. No artifacts were uploaded." >> $GITHUB_STEP_SUMMARY + else + echo "## ✅ Published ${NAME} ${VERSION} to PyPI" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Install with: \`pip install ${NAME}==${VERSION}\`" >> $GITHUB_STEP_SUMMARY + fi