diff --git a/.github/workflows/create-release-tag.yml b/.github/workflows/create-release-tag.yml deleted file mode 100644 index 8d6b8e1..0000000 --- a/.github/workflows/create-release-tag.yml +++ /dev/null @@ -1,59 +0,0 @@ -name: Create Release Tag - -on: - pull_request: - types: [closed] - branches: [main] - -jobs: - create-tag: - if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release/v') - runs-on: ubuntu-latest - permissions: - contents: write - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Extract version from branch name - id: extract_version - run: | - BRANCH_NAME="${{ github.event.pull_request.head.ref }}" - VERSION="${BRANCH_NAME#release/v}" - echo "version=$VERSION" >> $GITHUB_OUTPUT - - - name: Get previous release tag - id: previous_tag - run: | - PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD~1 2>/dev/null || echo "") - echo "previous_tag=$PREVIOUS_TAG" >> $GITHUB_OUTPUT - - - name: Create and push tag - run: | - git config --local user.email "github-actions[bot]@users.noreply.github.com" - git config --local user.name "github-actions[bot]" - - # Create annotated tag - git tag -a "v${{ steps.extract_version.outputs.version }}" \ - -m "Release v${{ steps.extract_version.outputs.version }}" - - # Push tag - git push origin "v${{ steps.extract_version.outputs.version }}" - - - name: Create GitHub Release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # Create release with auto-generated notes - gh release create "v${{ steps.extract_version.outputs.version }}" \ - --title "Release v${{ steps.extract_version.outputs.version }}" \ - --generate-notes \ - --notes-start-tag "${{ steps.previous_tag.outputs.previous_tag }}" \ - --notes "Published to PyPI: https://pypi.org/project/claude-agent-sdk/${{ steps.extract_version.outputs.version }}/ - - ### Installation - \`\`\`bash - pip install claude-agent-sdk==${{ steps.extract_version.outputs.version }} - \`\`\`" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index b8b7e93..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,222 +0,0 @@ -name: Publish to PyPI - -on: - workflow_dispatch: - inputs: - version: - description: 'Package version to publish (e.g., 0.1.4)' - required: true - type: string -jobs: - test: - runs-on: ubuntu-latest - strategy: - matrix: - python-version: ["3.10", "3.11", "3.12", "3.13"] - - steps: - - uses: actions/checkout@v4 - - - 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: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - 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/ - - build-wheels: - needs: [test, lint] - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest] - permissions: - contents: write - pull-requests: write - - steps: - - uses: actions/checkout@v4 - - - 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 "${{ github.event.inputs.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: - needs: [build-wheels] - runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - - steps: - - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - fetch-depth: 0 # Fetch all history including tags for changelog generation - - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Set version - id: version - run: | - VERSION="${{ github.event.inputs.version }}" - echo "VERSION=$VERSION" >> $GITHUB_ENV - echo "version=$VERSION" >> $GITHUB_OUTPUT - - - name: Update version - run: | - python scripts/update_version.py "${{ env.VERSION }}" - - - name: Read CLI version from code - id: cli_version - run: | - CLI_VERSION=$(python -c "import re; print(re.search(r'__cli_version__ = \"([^\"]+)\"', open('src/claude_agent_sdk/_cli_version.py').read()).group(1))") - echo "cli_version=$CLI_VERSION" >> $GITHUB_OUTPUT - echo "Bundled CLI version: $CLI_VERSION" - - - 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 - env: - TWINE_USERNAME: __token__ - TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} - run: | - twine upload dist/* - echo "Package published to PyPI" - echo "Install with: pip install claude-agent-sdk==${{ env.VERSION }}" - - - name: Get previous release tag - id: previous_tag - run: | - PREVIOUS_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") - echo "previous_tag=$PREVIOUS_TAG" >> $GITHUB_OUTPUT - echo "Previous release: $PREVIOUS_TAG" - - - name: Create release branch and commit version changes - run: | - # Create a new branch for the version update - BRANCH_NAME="release/v${{ env.VERSION }}" - echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV - - # Configure git - git config --local user.email "github-actions[bot]@users.noreply.github.com" - git config --local user.name "github-actions[bot]" - - # Create and switch to new branch - git checkout -b "$BRANCH_NAME" - - # Commit version changes - git add pyproject.toml src/claude_agent_sdk/_version.py - git commit -m "chore: release v${{ env.VERSION }}" - - - name: Update changelog with Claude - continue-on-error: true - uses: anthropics/claude-code-action@v1 - with: - prompt: "/generate-changelog new version: ${{ env.VERSION }}, old version: ${{ steps.previous_tag.outputs.previous_tag }}" - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - github_token: ${{ secrets.GITHUB_TOKEN }} - claude_args: | - --model claude-opus-4-5 - --allowedTools 'Bash(git add:*),Bash(git commit:*),Edit' - - - name: Push branch and create PR - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - # Push the branch with all commits - git push origin "${{ env.BRANCH_NAME }}" - - # Create PR using GitHub CLI - PR_BODY="This PR updates the version to ${{ env.VERSION }} after publishing to PyPI. - - ## Changes - - Updated version in \`pyproject.toml\` to ${{ env.VERSION }} - - Updated version in \`src/claude_agent_sdk/_version.py\` to ${{ env.VERSION }} - - Updated \`CHANGELOG.md\` with release notes - - ## Release Information - - Published to PyPI: https://pypi.org/project/claude-agent-sdk/${{ env.VERSION }}/ - - Bundled CLI version: ${{ steps.cli_version.outputs.cli_version }} - - Install with: \`pip install claude-agent-sdk==${{ env.VERSION }}\` - - 🤖 Generated by GitHub Actions" - - PR_URL=$(gh pr create \ - --title "chore: release v${{ env.VERSION }}" \ - --body "$PR_BODY" \ - --base main \ - --head "${{ env.BRANCH_NAME }}") - - echo "PR created: $PR_URL" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..9e4cf4f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,327 @@ +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 diff --git a/scripts/update_version.py b/scripts/update_version.py index b980d52..b01502e 100755 --- a/scripts/update_version.py +++ b/scripts/update_version.py @@ -1,11 +1,31 @@ #!/usr/bin/env python3 """Update version in pyproject.toml and __init__.py files.""" +import argparse import re import sys from pathlib import Path +def get_current_version() -> str: + """Get current version from pyproject.toml.""" + pyproject_path = Path("pyproject.toml") + content = pyproject_path.read_text() + match = re.search(r'^version = "([^"]+)"', content, re.MULTILINE) + if match: + return match.group(1) + raise ValueError("Could not find version in pyproject.toml") + + +def increment_patch(version: str) -> str: + """Increment the patch version.""" + parts = version.split(".") + if len(parts) != 3: + raise ValueError(f"Invalid version format: {version}") + major, minor, patch = parts + return f"{major}.{minor}.{int(patch) + 1}" + + def update_version(new_version: str) -> None: """Update version in project files.""" # Update pyproject.toml @@ -42,8 +62,23 @@ def update_version(new_version: str) -> None: if __name__ == "__main__": - if len(sys.argv) != 2: - print("Usage: python scripts/update_version.py ") - sys.exit(1) + parser = argparse.ArgumentParser(description="Update package version") + parser.add_argument("version", nargs="?", help="New version to set") + parser.add_argument("--get", action="store_true", help="Print current version") + parser.add_argument( + "--patch", action="store_true", help="Auto-increment patch version" + ) - update_version(sys.argv[1]) + args = parser.parse_args() + + if args.get: + print(get_current_version()) + elif args.patch: + current = get_current_version() + new_ver = increment_patch(current) + update_version(new_ver) + elif args.version: + update_version(args.version) + else: + parser.print_help() + sys.exit(1)