Skip to content

Commit 21428b4

Browse files
authored
Fix "Publish to PyPI" workflow: Add commit signing and improve diff (#82)
* Adds commit signing * Converts sed pattern matching to a script to ensure we don't update version values unrelated to PyPI * Remove the `Publish to Test PyPI first'` step since it no longer works
1 parent c4384ea commit 21428b4

File tree

2 files changed

+112
-47
lines changed

2 files changed

+112
-47
lines changed

.github/workflows/publish.yml

Lines changed: 63 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,6 @@ on:
77
description: 'Version to publish (e.g., 0.1.0)'
88
required: true
99
type: string
10-
test_pypi:
11-
description: 'Publish to Test PyPI first'
12-
required: false
13-
type: boolean
14-
default: true
15-
1610
jobs:
1711
test:
1812
runs-on: ubuntu-latest
@@ -79,11 +73,16 @@ jobs:
7973
with:
8074
python-version: '3.12'
8175

76+
- name: Set version
77+
id: version
78+
run: |
79+
VERSION="${{ github.event.inputs.version }}"
80+
echo "VERSION=$VERSION" >> $GITHUB_ENV
81+
echo "version=$VERSION" >> $GITHUB_OUTPUT
82+
8283
- name: Update version
8384
run: |
84-
# Update version in pyproject.toml
85-
sed -i 's/version = ".*"/version = "${{ github.event.inputs.version }}"/' pyproject.toml
86-
sed -i 's/__version__ = ".*"/__version__ = "${{ github.event.inputs.version }}"/' src/claude_code_sdk/__init__.py
85+
python scripts/update_version.py "${{ env.VERSION }}"
8786
8887
- name: Install build dependencies
8988
run: |
@@ -96,58 +95,75 @@ jobs:
9695
- name: Check package
9796
run: twine check dist/*
9897

99-
- name: Publish to Test PyPI
100-
if: ${{ github.event.inputs.test_pypi == 'true' }}
101-
env:
102-
TWINE_USERNAME: __token__
103-
TWINE_PASSWORD: ${{ secrets.TEST_PYPI_API_TOKEN }}
104-
run: |
105-
twine upload --repository testpypi dist/*
106-
echo "Package published to Test PyPI"
107-
echo "Install with: pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ claude-code-sdk==${{ github.event.inputs.version }}"
108-
10998
- name: Publish to PyPI
11099
env:
111100
TWINE_USERNAME: __token__
112101
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
113102
run: |
114103
twine upload dist/*
115104
echo "Package published to PyPI"
116-
echo "Install with: pip install claude-code-sdk==${{ github.event.inputs.version }}"
105+
echo "Install with: pip install claude-code-sdk==${{ env.VERSION }}"
117106
118107
- name: Create version update PR
119108
env:
120-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
109+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
121110
run: |
122111
# Create a new branch for the version update
123-
BRANCH_NAME="release/v${{ github.event.inputs.version }}"
124-
git checkout -b "$BRANCH_NAME"
112+
BRANCH_NAME="release/v${{ env.VERSION }}"
113+
echo "BRANCH_NAME=$BRANCH_NAME" >> $GITHUB_ENV
125114
126-
# Configure git
127-
git config --local user.email "github-actions[bot]@users.noreply.github.com"
128-
git config --local user.name "github-actions[bot]"
115+
# Create branch via API
116+
BASE_SHA=$(git rev-parse HEAD)
117+
gh api \
118+
--method POST \
119+
/repos/$GITHUB_REPOSITORY/git/refs \
120+
-f ref="refs/heads/$BRANCH_NAME" \
121+
-f sha="$BASE_SHA"
129122
130-
# Commit the version changes
131-
git add pyproject.toml src/claude_code_sdk/__init__.py
132-
git commit -m "chore: bump version to ${{ github.event.inputs.version }}"
123+
# Get current SHA values of files
124+
echo "Getting SHA for pyproject.toml"
125+
PYPROJECT_SHA=$(gh api /repos/$GITHUB_REPOSITORY/contents/pyproject.toml --jq '.sha')
126+
echo "Getting SHA for __init__.py"
127+
INIT_SHA=$(gh api /repos/$GITHUB_REPOSITORY/contents/src/claude_code_sdk/__init__.py --jq '.sha')
133128
134-
# Push the branch
135-
git push origin "$BRANCH_NAME"
129+
# Commit pyproject.toml via GitHub API (this creates signed commits)
130+
message="chore: bump version to ${{ env.VERSION }}"
131+
base64 -i pyproject.toml > pyproject.toml.b64
132+
gh api \
133+
--method PUT \
134+
/repos/$GITHUB_REPOSITORY/contents/pyproject.toml \
135+
-f message="$message" \
136+
137+
-f sha="$PYPROJECT_SHA" \
138+
-f branch="$BRANCH_NAME"
136139
137-
# Create PR using GitHub CLI (gh)
138-
gh pr create \
139-
--title "chore: bump version to ${{ github.event.inputs.version }}" \
140-
--body "This PR updates the version to ${{ github.event.inputs.version }} after publishing to PyPI.
141-
142-
## Changes
143-
- Updated version in \`pyproject.toml\`
144-
- Updated version in \`src/claude_code_sdk/__init__.py\`
145-
146-
## Release Information
147-
- Published to PyPI: https://pypi.org/project/claude-code-sdk/${{ github.event.inputs.version }}/
148-
- Install with: \`pip install claude-code-sdk==${{ github.event.inputs.version }}\`
149-
150-
## Next Steps
151-
After merging this PR, a release tag will be created automatically." \
140+
# Commit __init__.py via GitHub API
141+
base64 -i src/claude_code_sdk/__init__.py > init.py.b64
142+
gh api \
143+
--method PUT \
144+
/repos/$GITHUB_REPOSITORY/contents/src/claude_code_sdk/__init__.py \
145+
-f message="$message" \
146+
147+
-f sha="$INIT_SHA" \
148+
-f branch="$BRANCH_NAME"
149+
150+
# Create PR using GitHub CLI
151+
PR_BODY="This PR updates the version to ${{ env.VERSION }} after publishing to PyPI.
152+
153+
## Changes
154+
- Updated version in \`pyproject.toml\`
155+
- Updated version in \`src/claude_code_sdk/__init__.py\`
156+
157+
## Release Information
158+
- Published to PyPI: https://pypi.org/project/claude-code-sdk/${{ env.VERSION }}/
159+
- Install with: \`pip install claude-code-sdk==${{ env.VERSION }}\`
160+
161+
🤖 Generated by GitHub Actions"
162+
163+
PR_URL=$(gh pr create \
164+
--title "chore: bump version to ${{ env.VERSION }}" \
165+
--body "$PR_BODY" \
152166
--base main \
153-
--head "$BRANCH_NAME"
167+
--head "$BRANCH_NAME")
168+
169+
echo "PR created: $PR_URL"

scripts/update_version.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#!/usr/bin/env python3
2+
"""Update version in pyproject.toml and __init__.py files."""
3+
4+
import sys
5+
import re
6+
from pathlib import Path
7+
8+
9+
def update_version(new_version: str) -> None:
10+
"""Update version in project files."""
11+
# Update pyproject.toml
12+
pyproject_path = Path("pyproject.toml")
13+
content = pyproject_path.read_text()
14+
15+
# Only update the version field in [project] section
16+
content = re.sub(
17+
r'^version = "[^"]*"',
18+
f'version = "{new_version}"',
19+
content,
20+
count=1,
21+
flags=re.MULTILINE
22+
)
23+
24+
pyproject_path.write_text(content)
25+
print(f"Updated pyproject.toml to version {new_version}")
26+
27+
# Update __init__.py
28+
init_path = Path("src/claude_code_sdk/__init__.py")
29+
content = init_path.read_text()
30+
31+
# Only update __version__ assignment
32+
content = re.sub(
33+
r'^__version__ = "[^"]*"',
34+
f'__version__ = "{new_version}"',
35+
content,
36+
count=1,
37+
flags=re.MULTILINE
38+
)
39+
40+
init_path.write_text(content)
41+
print(f"Updated __init__.py to version {new_version}")
42+
43+
44+
if __name__ == "__main__":
45+
if len(sys.argv) != 2:
46+
print("Usage: python scripts/update_version.py <version>")
47+
sys.exit(1)
48+
49+
update_version(sys.argv[1])

0 commit comments

Comments
 (0)