name: Release on: repository_dispatch: types: [new-version-published] workflow_dispatch: inputs: version: description: 'SDK version to release (e.g., 0.2.0)' required: true type: string cli_version: description: 'CLI version to bundle (optional, uses current if empty)' required: false type: string concurrency: group: release cancel-in-progress: false jobs: bump-versions: name: Bump Versions runs-on: ubuntu-latest permissions: contents: write outputs: sdk_version: ${{ steps.versions.outputs.sdk_version }} cli_version: ${{ steps.versions.outputs.cli_version }} steps: - uses: actions/checkout@v4 with: token: ${{ secrets.GITHUB_TOKEN }} - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.12' - name: Determine versions id: versions run: | # Get current versions CURRENT_SDK=$(grep -Po '(?<=^version = ")[^"]+' pyproject.toml) CURRENT_CLI=$(grep -Po '(?<=__cli_version__ = ")[^"]+' src/claude_agent_sdk/_cli_version.py) if [ "${{ github.event_name }}" = "repository_dispatch" ]; then # Auto release from CLI bump: increment SDK patch, use dispatched CLI version CLI_VERSION="${{ github.event.client_payload.version }}" IFS='.' read -r major minor patch <<< "$CURRENT_SDK" SDK_VERSION="${major}.${minor}.$((patch + 1))" echo "Auto release: CLI $CLI_VERSION, SDK $CURRENT_SDK -> $SDK_VERSION" else # Manual release: use provided version SDK_VERSION="${{ github.event.inputs.version }}" CLI_VERSION="${{ github.event.inputs.cli_version }}" if [ -z "$CLI_VERSION" ]; then CLI_VERSION="$CURRENT_CLI" fi echo "Manual release: SDK $SDK_VERSION, CLI $CLI_VERSION" fi echo "sdk_version=$SDK_VERSION" >> $GITHUB_OUTPUT echo "cli_version=$CLI_VERSION" >> $GITHUB_OUTPUT - name: Update version files run: | # Update SDK version python scripts/update_version.py "${{ steps.versions.outputs.sdk_version }}" # Update CLI version cat > src/claude_agent_sdk/_cli_version.py << EOF """Bundled Claude Code CLI version.""" __cli_version__ = "${{ steps.versions.outputs.cli_version }}" EOF - name: Commit version changes run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git add pyproject.toml src/claude_agent_sdk/_version.py src/claude_agent_sdk/_cli_version.py if git diff --staged --quiet; then echo "No version changes to commit" else git commit -m "chore: release v${{ steps.versions.outputs.sdk_version }}" git push origin main fi test: name: Test (Python ${{ matrix.python-version }}) needs: [bump-versions] runs-on: ubuntu-latest strategy: matrix: python-version: ["3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 with: ref: main # Get latest after version bump - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e ".[dev]" - name: Run tests run: python -m pytest tests/ -v lint: name: Lint needs: [bump-versions] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: main - name: Set up Python uses: actions/setup-python@v5 with: python-version: "3.12" - name: Install dependencies run: | python -m pip install --upgrade pip pip install -e ".[dev]" - name: Run ruff run: | ruff check src/ tests/ ruff format --check src/ tests/ - name: Run mypy run: mypy src/ create-tag: name: Create Tag needs: [bump-versions, test, lint] runs-on: ubuntu-latest permissions: contents: write outputs: tag: ${{ steps.tag.outputs.tag }} steps: - uses: actions/checkout@v4 with: ref: main fetch-depth: 0 - name: Create and push tag id: tag run: | VERSION="${{ needs.bump-versions.outputs.sdk_version }}" TAG="v$VERSION" git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git tag -a "$TAG" -m "Release $TAG" git push origin "$TAG" echo "tag=$TAG" >> $GITHUB_OUTPUT build-wheels: name: Build Wheel (${{ matrix.os }}) needs: [bump-versions, create-tag] runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest] steps: - uses: actions/checkout@v4 with: ref: ${{ needs.create-tag.outputs.tag }} - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.12' - name: Install build dependencies run: | python -m pip install --upgrade pip pip install build twine wheel shell: bash - name: Build wheel with bundled CLI run: | python scripts/build_wheel.py \ --version "${{ needs.bump-versions.outputs.sdk_version }}" \ --cli-version "${{ needs.bump-versions.outputs.cli_version }}" \ --skip-sdist \ --clean shell: bash - name: Upload wheel artifact uses: actions/upload-artifact@v4 with: name: wheel-${{ matrix.os }} path: dist/*.whl if-no-files-found: error compression-level: 0 publish: name: Publish to PyPI needs: [bump-versions, create-tag, build-wheels] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: ref: ${{ needs.create-tag.outputs.tag }} - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.12' - name: Download all wheel artifacts uses: actions/download-artifact@v4 with: path: dist pattern: wheel-* merge-multiple: true - name: Install build dependencies run: | python -m pip install --upgrade pip pip install build twine - name: Build source distribution run: python -m build --sdist - name: Publish to PyPI id: publish env: TWINE_USERNAME: __token__ TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} run: | twine upload dist/* echo "Published claude-agent-sdk==${{ needs.bump-versions.outputs.sdk_version }}" - name: Delete tag on publish failure if: failure() && steps.publish.outcome == 'failure' run: | git push --delete origin "${{ needs.create-tag.outputs.tag }}" || true echo "::error::Publish failed - tag has been deleted" github-release: name: Create GitHub Release needs: [bump-versions, create-tag, publish] runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - name: Create GitHub Release env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | VERSION="${{ needs.bump-versions.outputs.sdk_version }}" CLI_VERSION="${{ needs.bump-versions.outputs.cli_version }}" TAG="${{ needs.create-tag.outputs.tag }}" gh release create "$TAG" \ --title "Release $TAG" \ --generate-notes \ --notes "## Installation \`\`\`bash pip install claude-agent-sdk==$VERSION \`\`\` **Bundled CLI version:** $CLI_VERSION **PyPI:** https://pypi.org/project/claude-agent-sdk/$VERSION/" update-changelog: name: Update Changelog needs: [bump-versions, create-tag, publish] runs-on: ubuntu-latest permissions: contents: write steps: - uses: actions/checkout@v4 with: ref: main fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} - name: Get previous tag id: prev_tag run: | PREV_TAG=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null || echo "") echo "tag=$PREV_TAG" >> $GITHUB_OUTPUT - name: Update changelog with Claude continue-on-error: true uses: anthropics/claude-code-action@v1 with: prompt: "/generate-changelog new version: ${{ needs.bump-versions.outputs.sdk_version }}, old version: ${{ steps.prev_tag.outputs.tag }}" anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} github_token: ${{ secrets.GITHUB_TOKEN }} claude_args: | --model claude-sonnet-4-20250514 --allowedTools 'Bash(git add:*),Bash(git commit:*),Bash(git push:*),Edit,Read' - name: Push changelog if updated run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" if ! git diff --quiet CHANGELOG.md 2>/dev/null; then git add CHANGELOG.md git commit -m "docs: update changelog for v${{ needs.bump-versions.outputs.sdk_version }}" git push origin main fi