diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml new file mode 100644 index 0000000..c48509c --- /dev/null +++ b/.github/workflows/auto-release.yml @@ -0,0 +1,53 @@ +name: Auto Release on CLI Bump + +on: + workflow_run: + workflows: ["Test"] + types: [completed] + branches: [main] + +jobs: + check-trigger: + runs-on: ubuntu-latest + if: | + github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.event == 'push' && + startsWith(github.event.workflow_run.head_commit.message, 'chore: bump bundled CLI version to') + outputs: + version: ${{ steps.version.outputs.version }} + previous_tag: ${{ steps.previous_tag.outputs.previous_tag }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Verify CLI version file was changed + run: | + if ! git diff --name-only HEAD~1 | grep -q '_cli_version.py'; then + echo "::error::CLI version file not changed in this commit" + exit 1 + fi + + - name: Get current SDK version and calculate next + id: version + run: | + CURRENT=$(python -c "import re; print(re.search(r'__version__ = \"([^\"]+)\"', open('src/claude_agent_sdk/_version.py').read()).group(1))") + IFS='.' read -ra PARTS <<< "$CURRENT" + NEXT="${PARTS[0]}.${PARTS[1]}.$((PARTS[2] + 1))" + echo "version=$NEXT" >> $GITHUB_OUTPUT + echo "Current: $CURRENT -> Next: $NEXT" + + - 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 + + release: + needs: check-trigger + uses: ./.github/workflows/build-and-publish.yml + with: + version: ${{ needs.check-trigger.outputs.version }} + previous_tag: ${{ needs.check-trigger.outputs.previous_tag }} + push_directly: true + secrets: inherit diff --git a/.github/workflows/build-and-publish.yml b/.github/workflows/build-and-publish.yml new file mode 100644 index 0000000..ce6524f --- /dev/null +++ b/.github/workflows/build-and-publish.yml @@ -0,0 +1,170 @@ +name: Build and Publish + +on: + workflow_call: + inputs: + version: + description: 'Version to publish' + required: true + type: string + previous_tag: + description: 'Previous release tag for changelog generation' + required: false + type: string + default: '' + push_directly: + description: 'Push directly to main (true) or create PR (false)' + required: false + type: boolean + default: false + +jobs: + build-wheels: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest] + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install build dependencies + run: pip install build twine wheel + shell: bash + + - name: Build wheel with bundled CLI + run: python scripts/build_wheel.py --version "${{ inputs.version }}" --skip-sdist --clean + shell: bash + + - uses: actions/upload-artifact@v4 + with: + name: wheel-${{ matrix.os }} + path: dist/*.whl + if-no-files-found: error + + publish: + needs: build-wheels + runs-on: ubuntu-latest + environment: production + permissions: + contents: write + pull-requests: write + env: + VERSION: ${{ inputs.version }} + steps: + - uses: actions/checkout@v4 + if: inputs.push_directly + with: + fetch-depth: 0 + ssh-key: ${{ secrets.DEPLOY_KEY }} + + - uses: actions/checkout@v4 + if: ${{ !inputs.push_directly }} + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Update version files + run: python scripts/update_version.py "$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 + + - uses: actions/download-artifact@v4 + with: + path: dist + pattern: wheel-* + merge-multiple: true + + - name: Build sdist and publish to PyPI + run: | + pip install build twine + python -m build --sdist + twine upload dist/* + env: + TWINE_USERNAME: __token__ + TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }} + + - name: Configure git + run: | + git config user.email "github-actions[bot]@users.noreply.github.com" + git config user.name "github-actions[bot]" + + - name: Commit version changes + run: | + git add pyproject.toml src/claude_agent_sdk/_version.py + git commit -m "chore: release v$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: ${{ inputs.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' + + # Direct push flow (auto-release) + - name: Push to main + if: inputs.push_directly + run: git push origin main + + - name: Create tag and GitHub Release + if: inputs.push_directly + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git tag -a "v$VERSION" -m "Release v$VERSION" + git push origin "v$VERSION" + + awk -v ver="$VERSION" '/^## / { if (found) exit; if ($2 == ver) found=1; next } found { print }' CHANGELOG.md > release_notes.md + echo -e "\n---\n\n**PyPI:** https://pypi.org/project/claude-agent-sdk/$VERSION/\n\n\`\`\`bash\npip install claude-agent-sdk==$VERSION\n\`\`\`" >> release_notes.md + + gh release create "v$VERSION" --title "v$VERSION" --notes-file release_notes.md + + # PR flow (manual publish) + - name: Create release branch + if: ${{ !inputs.push_directly }} + run: | + BRANCH_NAME="release/v$VERSION" + echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV + git checkout -b "$BRANCH_NAME" + + - name: Push branch and create PR + if: ${{ !inputs.push_directly }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git push origin "$BRANCH_NAME" + + PR_BODY="This PR updates the version to $VERSION after publishing to PyPI. + + ## Changes + - Updated version in \`pyproject.toml\` to $VERSION + - Updated version in \`src/claude_agent_sdk/_version.py\` to $VERSION + - Updated \`CHANGELOG.md\` with release notes + + ## Release Information + - Published to PyPI: https://pypi.org/project/claude-agent-sdk/$VERSION/ + - Bundled CLI version: ${{ steps.cli_version.outputs.cli_version }} + - Install with: \`pip install claude-agent-sdk==$VERSION\` + + 🤖 Generated by GitHub Actions" + + gh pr create \ + --title "chore: release v$VERSION" \ + --body "$PR_BODY" \ + --base main \ + --head "$BRANCH_NAME" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index b8b7e93..5be9954 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -7,6 +7,7 @@ on: description: 'Package version to publish (e.g., 0.1.4)' required: true type: string + jobs: test: runs-on: ubuntu-latest @@ -56,167 +57,26 @@ jobs: 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] + get-previous-tag: runs-on: ubuntu-latest - permissions: - contents: write - pull-requests: write - + outputs: + previous_tag: ${{ steps.previous_tag.outputs.previous_tag }} 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 }}") + - uses: actions/checkout@v4 + with: + fetch-depth: 0 - echo "PR created: $PR_URL" + - 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 + + release: + needs: [test, lint, get-previous-tag] + uses: ./.github/workflows/build-and-publish.yml + with: + version: ${{ github.event.inputs.version }} + previous_tag: ${{ needs.get-previous-tag.outputs.previous_tag }} + push_directly: false + secrets: inherit