Skip to content

Commit 21059be

Browse files
committed
feat: implement automated version bumping and PyPI publishing
- Add python-semantic-release with patch-only configuration - Create GitHub Actions workflows for PR testing and automated releases - Configure OIDC trusted publishing for PyPI (no API tokens needed) - Update CLAUDE.md with complete automation documentation - All commits now auto-increment patch version (0.1.14 → 0.1.15) - Manual control available for minor/major bumps when needed Resolves #14
1 parent a80f026 commit 21059be

File tree

5 files changed

+379
-120
lines changed

5 files changed

+379
-120
lines changed

.github/workflows/release.yml

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
name: Automated Release
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
7+
jobs:
8+
release:
9+
runs-on: ubuntu-latest
10+
concurrency: release
11+
permissions:
12+
id-token: write # OIDC for PyPI trusted publishing
13+
contents: write # Create releases and tags
14+
actions: write # Update workflow permissions if needed
15+
16+
steps:
17+
- name: Checkout code
18+
uses: actions/checkout@v4
19+
with:
20+
fetch-depth: 0 # Needed for semantic-release to work properly
21+
token: ${{ secrets.GITHUB_TOKEN }}
22+
23+
- name: Set up Python
24+
uses: actions/setup-python@v4
25+
with:
26+
python-version: '3.10'
27+
28+
- name: Install dependencies
29+
run: |
30+
python -m pip install --upgrade pip
31+
pip install -e .[dev]
32+
33+
- name: Check if version bump is needed
34+
id: release-check
35+
run: |
36+
# Check if there are commits since last tag that would trigger a release
37+
echo "Checking for version bump..."
38+
NEW_VERSION=$(semantic-release version --print --no-commit --no-tag --no-push --no-vcs-release 2>/dev/null || echo "")
39+
if [ "$NEW_VERSION" != "" ]; then
40+
echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT
41+
echo "should_release=true" >> $GITHUB_OUTPUT
42+
echo "New version will be: $NEW_VERSION"
43+
else
44+
echo "should_release=false" >> $GITHUB_OUTPUT
45+
echo "No version bump needed"
46+
fi
47+
48+
- name: Run tests
49+
if: steps.release-check.outputs.should_release == 'true'
50+
run: |
51+
echo "Running tests before release..."
52+
pytest idtap/tests/
53+
54+
- name: Configure git
55+
if: steps.release-check.outputs.should_release == 'true'
56+
run: |
57+
git config --global user.name "github-actions[bot]"
58+
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
59+
60+
- name: Bump version and create release
61+
if: steps.release-check.outputs.should_release == 'true'
62+
run: |
63+
echo "Creating release for version: ${{ steps.release-check.outputs.new_version }}"
64+
semantic-release version
65+
66+
- name: Build package
67+
if: steps.release-check.outputs.should_release == 'true'
68+
run: |
69+
echo "Building package..."
70+
python -m build
71+
72+
- name: Publish to PyPI
73+
if: steps.release-check.outputs.should_release == 'true'
74+
uses: pypa/gh-action-pypi-publish@release/v1
75+
with:
76+
# Using trusted publishing - no username/password needed
77+
# PyPI trusted publisher must be configured for this repo
78+
print-hash: true
79+
80+
- name: Output release info
81+
if: steps.release-check.outputs.should_release == 'true'
82+
run: |
83+
echo "🎉 Successfully released version ${{ steps.release-check.outputs.new_version }}"
84+
echo "📦 Package published to PyPI: https://pypi.org/project/idtap/${{ steps.release-check.outputs.new_version }}/"
85+
echo "🏷️ GitHub release created with tag: v${{ steps.release-check.outputs.new_version }}"

.github/workflows/test-pr.yml

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
name: Test PR
2+
3+
on:
4+
pull_request:
5+
branches: [ main ]
6+
7+
jobs:
8+
test:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- name: Checkout code
13+
uses: actions/checkout@v4
14+
with:
15+
fetch-depth: 0 # Needed for semantic-release
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v4
19+
with:
20+
python-version: '3.10'
21+
22+
- name: Install dependencies
23+
run: |
24+
python -m pip install --upgrade pip
25+
pip install -e .[dev]
26+
27+
- name: Run tests
28+
run: pytest idtap/tests/
29+
30+
- name: Build package
31+
run: python -m build
32+
33+
- name: Test semantic-release dry run
34+
run: |
35+
semantic-release version --print --no-commit --no-tag --no-push --no-vcs-release || echo "No version bump needed"
36+
37+
- name: Test upload to TestPyPI
38+
if: github.event.pull_request.head.repo.full_name == github.repository
39+
uses: pypa/gh-action-pypi-publish@release/v1
40+
with:
41+
repository-url: https://test.pypi.org/legacy/
42+
skip-existing: true
43+
password: ${{ secrets.TESTPYPI_API_TOKEN }}
44+
45+
- name: Comment PR with TestPyPI link
46+
if: github.event.pull_request.head.repo.full_name == github.repository
47+
uses: actions/github-script@v7
48+
with:
49+
script: |
50+
const prNumber = context.issue.number;
51+
const testPypiUrl = `https://test.pypi.org/project/idtap/`;
52+
53+
github.rest.issues.createComment({
54+
issue_number: prNumber,
55+
owner: context.repo.owner,
56+
repo: context.repo.repo,
57+
body: `📦 **Test Package Built Successfully!**
58+
59+
This PR has been automatically built and uploaded to TestPyPI for testing.
60+
61+
🔗 **TestPyPI Link**: ${testPypiUrl}
62+
63+
To test this version:
64+
\`\`\`bash
65+
pip install --index-url https://test.pypi.org/simple/ idtap
66+
\`\`\`
67+
68+
✅ All tests passed and package builds successfully.`
69+
});

CLAUDE.md

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ The Python API (`idtap`) is a sophisticated client library for interacting with
1616
- **Integration tests**: `python python/api_testing/api_test.py` (requires live server auth)
1717
- Test structure: Complete coverage of data models, client functionality, and authentication
1818

19-
### Build/Package/Publish
19+
### Build/Package/Publish - AUTOMATED via GitHub Actions
20+
**⚠️ IMPORTANT: Manual publishing is now automated. See "Automated Version Management" section below.**
21+
22+
Manual publishing (for local testing only):
2023
```bash
2124
python -m build
2225
python -m twine upload dist/* # or --repository testpypi for testing
@@ -86,12 +89,84 @@ python/idtap/
8689
- **Multi-instrument**: Sitar, Vocal (Male/Female) with instrument-specific features
8790
- **Analytical Tools**: Trajectory categorization, phrase grouping, temporal analysis
8891

89-
## Development Workflow
92+
## Automated Version Management & Publishing
93+
94+
### 🤖 How It Works (python-semantic-release + GitHub Actions)
95+
96+
**PATCH-ONLY MODE**: All commits automatically increment patch version (0.1.14 → 0.1.15) regardless of commit message.
97+
98+
#### Automatic Workflow
99+
1. **Make changes** → Commit with any message
100+
2. **Create PR** → GitHub Actions test + upload to TestPyPI automatically
101+
3. **Merge to main** → Version bumps + PyPI published + GitHub release created
102+
4. **Zero manual intervention** required!
103+
104+
#### Version Locations (Auto-Updated)
105+
- `idtap/__init__.py:3` - `__version__ = "0.1.14"`
106+
- `pyproject.toml:7` - `version = "0.1.14"`
107+
- `docs/conf.py:16-17` - `release = '0.1.14'` and `version = '0.1.14'`
108+
109+
### 🎛️ Manual Version Control Commands
110+
111+
**For normal development (patch bumps)**:
112+
```bash
113+
# Any commit message works - always bumps patch
114+
git commit -m "fix something" # 0.1.14 → 0.1.15
115+
git commit -m "add new feature" # 0.1.14 → 0.1.15
116+
git commit -m "update docs" # 0.1.14 → 0.1.15
117+
```
118+
119+
**When Jon wants to control version bumps manually**:
120+
```bash
121+
# Force minor version bump (0.1.15 → 0.2.0)
122+
semantic-release version --increment minor
123+
124+
# Force major version bump (0.2.0 → 1.0.0)
125+
semantic-release version --increment major
126+
127+
# Dry run to see what would happen
128+
semantic-release version --print --no-commit --no-tag --no-push --no-vcs-release
129+
```
130+
131+
### 🔧 Configuration Details
132+
133+
**Location**: `pyproject.toml` `[tool.semantic_release]` section
134+
- **patch_tags**: ALL commit types → patch version increment
135+
- **minor_tags**: `[]` (empty - no automatic minor bumps)
136+
- **major_tags**: `[]` (empty - no automatic major bumps)
137+
- **Files updated**: `__init__.py`, `pyproject.toml`, `docs/conf.py`
138+
139+
### 🚀 GitHub Actions Workflows
140+
141+
**`.github/workflows/test-pr.yml`**:
142+
- Runs on every PR
143+
- Tests + builds package + uploads to TestPyPI
144+
- Comments PR with TestPyPI install link
145+
146+
**`.github/workflows/release.yml`**:
147+
- Runs on merge to main
148+
- Tests → version bump → build → PyPI publish → GitHub release
149+
150+
### 📋 Required Setup (One-Time)
151+
152+
**GitHub Secrets**:
153+
- `TESTPYPI_API_TOKEN` - For PR testing uploads
154+
155+
**PyPI Trusted Publisher** (configured):
156+
- Owner: `UCSC-IDTAP`
157+
- Repository: `Python-API`
158+
- Workflow: `release.yml`
159+
- Uses OIDC - no API tokens needed for production PyPI
160+
161+
**⚠️ CLAUDE: When Jon asks to update version in special way, use the manual commands above!**
162+
163+
## Development Workflow (Updated for Automation)
90164
1. **Data Model Development**: Create/modify classes in `/classes/` with proper serialization
91-
2. **Client Method Development**: Add HTTP methods in `client.py` with authentication
92-
3. **Testing**: Write unit tests (mocked) + integration tests (live API)
93-
4. **Sync Dependencies**: Update both `Pipfile` and `pyproject.toml`
94-
5. **Build/Test/Publish**: Use standard Python packaging tools
165+
2. **Client Method Development**: Add HTTP methods in `client.py` with authentication
166+
3. **Testing**: Write unit tests (mocked) + integration tests (live API)
167+
4. **PR Creation**: GitHub Actions automatically test + upload to TestPyPI
168+
5. **Merge to main**: Automatic version bump + PyPI publish + GitHub release
169+
6. **No manual version management needed!**
95170

96171
## Installation Commands
97172
```bash

0 commit comments

Comments
 (0)