Skip to content

Publish Python SDK to PyPI #3

Publish Python SDK to PyPI

Publish Python SDK to PyPI #3

Workflow file for this run

name: Publish Python SDK to PyPI
on:
workflow_dispatch:
inputs:
tag:
description: Existing annotated sdk-vX.Y.Z tag contained in GitHub main
required: true
type: string
permissions: {}
concurrency:
group: publish-pypi-agenttool-sdk
cancel-in-progress: false
jobs:
prepare:
name: Build and verify exact Python distributions
runs-on: ubuntu-24.04
timeout-minutes: 30
permissions:
contents: read
steps:
- name: Check out the annotated SDK release tag
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.tag }}
fetch-depth: 0
persist-credentials: false
- name: Set up Bun 1.3.5
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: 1.3.5
- name: Set up Python 3.14.5
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: 3.14.5
- name: Set up uv 0.9.26 without a shared cache
uses: astral-sh/setup-uv@1e862dfacbd1d6d858c55d9b792c756523627244 # v7.1.4
with:
version: 0.9.26
enable-cache: false
- name: Prepare exact distributions without publication authority
shell: bash
env:
RELEASE_TAG: ${{ inputs.tag }}
run: |
set -euo pipefail
output="${RUNNER_TEMP}/agenttool-pypi-release"
bun bin/pypi-release.ts prepare \
--tag "$RELEASE_TAG" \
--output "$output"
- name: Transfer the exact release receipt
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: pypi-release-receipt-${{ github.run_id }}
path: ${{ runner.temp }}/agenttool-pypi-release/receipt.json
if-no-files-found: error
compression-level: 0
retention-days: 1
- name: Transfer the exact wheel independently
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: pypi-release-wheel-${{ github.run_id }}
path: ${{ runner.temp }}/agenttool-pypi-release/dist/*.whl
if-no-files-found: error
compression-level: 0
retention-days: 1
- name: Transfer the exact source distribution independently
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6
with:
name: pypi-release-sdist-${{ github.run_id }}
path: ${{ runner.temp }}/agenttool-pypi-release/dist/*.tar.gz
if-no-files-found: error
compression-level: 0
retention-days: 1
preflight:
name: Check exact public PyPI state
needs: prepare
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
contents: read
outputs:
publish_required: ${{ steps.registry.outputs.publish_required }}
wheel_required: ${{ steps.registry.outputs.wheel_required }}
sdist_required: ${{ steps.registry.outputs.sdist_required }}
steps:
- name: Check out the same annotated SDK release tag
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.tag }}
fetch-depth: 0
persist-credentials: false
- name: Set up Bun 1.3.5
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: 1.3.5
- name: Set up Python 3.14.5
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: 3.14.5
- name: Download the prepared receipt
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
with:
name: pypi-release-receipt-${{ github.run_id }}
path: ${{ runner.temp }}/agenttool-pypi-release
- name: Download the prepared wheel
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
with:
name: pypi-release-wheel-${{ github.run_id }}
path: ${{ runner.temp }}/agenttool-pypi-release/dist
- name: Download the prepared source distribution
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
with:
name: pypi-release-sdist-${{ github.run_id }}
path: ${{ runner.temp }}/agenttool-pypi-release/dist
- name: Decide whether exact publication is still required
id: registry
shell: bash
run: |
set -euo pipefail
bun bin/pypi-release.ts check \
--receipt "${RUNNER_TEMP}/agenttool-pypi-release/receipt.json"
publish:
name: Approve and publish exact Python distributions
needs: preflight
if: ${{ needs.preflight.outputs.publish_required == 'true' }}
runs-on: ubuntu-24.04
timeout-minutes: 10
environment: pypi
permissions:
id-token: write
steps:
- name: Download the missing wheel only
if: ${{ needs.preflight.outputs.wheel_required == 'true' }}
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
with:
name: pypi-release-wheel-${{ github.run_id }}
path: ${{ runner.temp }}/agenttool-pypi-upload
- name: Download the missing source distribution only
if: ${{ needs.preflight.outputs.sdist_required == 'true' }}
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
with:
name: pypi-release-sdist-${{ github.run_id }}
path: ${{ runner.temp }}/agenttool-pypi-upload
- name: Publish through PyPI trusted publishing
uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 # v1.14.1
with:
packages-dir: ${{ runner.temp }}/agenttool-pypi-upload
skip-existing: true
verify:
name: Re-download and verify exact public PyPI bytes
needs:
- preflight
- publish
if: ${{ always() && needs.preflight.result == 'success' && (needs.publish.result == 'success' || needs.publish.result == 'skipped') }}
runs-on: ubuntu-24.04
timeout-minutes: 15
permissions:
contents: read
steps:
- name: Check out the same annotated SDK release tag
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.tag }}
fetch-depth: 0
persist-credentials: false
- name: Set up Bun 1.3.5
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: 1.3.5
- name: Set up Python 3.14.5
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: 3.14.5
- name: Download the prepared receipt
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
with:
name: pypi-release-receipt-${{ github.run_id }}
path: ${{ runner.temp }}/agenttool-pypi-release
- name: Download the prepared wheel
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
with:
name: pypi-release-wheel-${{ github.run_id }}
path: ${{ runner.temp }}/agenttool-pypi-release/dist
- name: Download the prepared source distribution
uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6
with:
name: pypi-release-sdist-${{ github.run_id }}
path: ${{ runner.temp }}/agenttool-pypi-release/dist
- name: Verify public hashes and archive identities without package execution
shell: bash
run: |
set -euo pipefail
bun bin/pypi-release.ts verify \
--receipt "${RUNNER_TEMP}/agenttool-pypi-release/receipt.json"
- name: Record the non-secret verified release receipt
if: ${{ always() }}
shell: bash
run: |
receipt="${RUNNER_TEMP}/agenttool-pypi-release/receipt.json"
if test -f "$receipt"; then
{
echo '## PyPI release receipt'
echo '```json'
cat "$receipt"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
fi