Skip to content

Commit 20ac092

Browse files
committed
Implement GitHub Actions workflows for release creation and publishing to PyPI. Update CLAUDE.md with a quick release process and required secrets setup. Add detailed documentation in WORKFLOWS.md for CI testing, release creation, and publishing steps.
1 parent a21a061 commit 20ac092

4 files changed

Lines changed: 517 additions & 5 deletions

File tree

.github/WORKFLOWS.md

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
# GitHub Actions Workflows
2+
3+
This repository uses GitHub Actions for automated testing, releasing, and publishing to PyPI.
4+
5+
## Workflows Overview
6+
7+
### 1. CI Testing (`test.yml`)
8+
9+
**Triggers:**
10+
- Push to `main` or `develop` branches
11+
- Pull requests to `main` or `develop` branches
12+
13+
**What it does:**
14+
- Runs tests across multiple OS (Ubuntu, macOS, Windows) and Python versions (3.8-3.12)
15+
- Performs linting with black and flake8
16+
- Runs type checking with mypy
17+
- Executes test suite with coverage reporting
18+
- Uploads coverage to Codecov
19+
- Builds and validates the package
20+
21+
**Status:** Automatically runs on every push and PR
22+
23+
---
24+
25+
### 2. Create Release (`release.yml`)
26+
27+
**Triggers:**
28+
- Manual workflow dispatch via GitHub Actions UI
29+
30+
**Required Inputs:**
31+
- `version`: Version number (e.g., `1.0.0`, `1.2.3-beta.1`)
32+
- `prerelease`: Boolean flag for pre-release versions (default: `false`)
33+
34+
**What it does:**
35+
1. Validates version format (semantic versioning)
36+
2. Checks that the tag doesn't already exist
37+
3. Runs full test suite
38+
4. Updates version in `pyproject.toml` and `src/qastudio_pytest/__init__.py`
39+
5. Builds the package
40+
6. Commits version bump
41+
7. Creates and pushes a git tag (`v{version}`)
42+
8. Generates release notes from git commits
43+
9. Creates a GitHub Release with built artifacts
44+
45+
**How to use:**
46+
1. Go to **Actions****Create Release**
47+
2. Click **Run workflow**
48+
3. Enter the version number (e.g., `1.0.0`)
49+
4. Select if it's a pre-release
50+
5. Click **Run workflow**
51+
52+
**Example versions:**
53+
- Release: `1.0.0`, `2.1.3`
54+
- Pre-release: `1.0.0-beta.1`, `2.0.0-rc.1`
55+
56+
---
57+
58+
### 3. Publish to PyPI (`publish.yml`)
59+
60+
**Triggers:**
61+
- Automatically when a GitHub Release is published
62+
- Manual workflow dispatch via GitHub Actions UI
63+
64+
**Required Secrets:**
65+
- `PYPI_API_TOKEN`: PyPI API token for publishing to production PyPI
66+
- `TEST_PYPI_API_TOKEN`: (Optional) TestPyPI API token for testing
67+
68+
**What it does:**
69+
1. Runs full test suite
70+
2. Performs linting and type checking
71+
3. Builds the package
72+
4. Validates package with twine
73+
5. Publishes to PyPI or TestPyPI
74+
75+
**Automatic Publishing (on release):**
76+
- When you create a release using `release.yml`, this workflow automatically triggers
77+
- Publishes the package to PyPI using the `PYPI_API_TOKEN` secret
78+
79+
**Manual Publishing:**
80+
1. Go to **Actions****Publish to PyPI**
81+
2. Click **Run workflow**
82+
3. Select environment:
83+
- `testpypi`: Publish to TestPyPI (for testing)
84+
- `pypi`: Publish to production PyPI
85+
4. Click **Run workflow**
86+
87+
---
88+
89+
## Complete Release Process
90+
91+
### Standard Release Flow
92+
93+
1. **Create a Release**
94+
```
95+
GitHub → Actions → Create Release → Run workflow
96+
Enter version: 1.0.0
97+
```
98+
99+
2. **GitHub Actions will:**
100+
- Run tests
101+
- Update version files
102+
- Create git tag
103+
- Create GitHub Release
104+
105+
3. **Publish to PyPI** (automatic)
106+
- The `publish.yml` workflow triggers automatically
107+
- Publishes to PyPI
108+
109+
### Testing Before Production
110+
111+
To test the release process before publishing to production PyPI:
112+
113+
1. **Manually run Publish workflow**
114+
```
115+
GitHub → Actions → Publish to PyPI → Run workflow
116+
Environment: testpypi
117+
```
118+
119+
2. **Test installation from TestPyPI**
120+
```bash
121+
pip install --index-url https://test.pypi.org/simple/ qastudio-pytest
122+
```
123+
124+
3. **If everything works, publish to production**
125+
```
126+
GitHub → Actions → Publish to PyPI → Run workflow
127+
Environment: pypi
128+
```
129+
130+
---
131+
132+
## Required GitHub Secrets
133+
134+
You need to configure these secrets in your GitHub repository:
135+
136+
### `PYPI_API_TOKEN` (Required)
137+
138+
1. Go to [PyPI Account Settings](https://pypi.org/manage/account/)
139+
2. Scroll to **API tokens**
140+
3. Click **Add API token**
141+
4. Name: `github-actions-qastudio-pytest`
142+
5. Scope: **Project: qastudio-pytest** (or Entire account)
143+
6. Copy the token (starts with `pypi-`)
144+
7. Add to GitHub:
145+
- Go to your repo → **Settings****Secrets and variables****Actions**
146+
- Click **New repository secret**
147+
- Name: `PYPI_API_TOKEN`
148+
- Value: (paste the token)
149+
150+
### `TEST_PYPI_API_TOKEN` (Optional)
151+
152+
Same process as above, but use [TestPyPI](https://test.pypi.org/manage/account/).
153+
154+
---
155+
156+
## Workflow Permissions
157+
158+
The workflows require the following permissions:
159+
160+
- **release.yml**: `contents: write` (to create releases and tags)
161+
- **publish.yml**: `contents: read`, `id-token: write` (for trusted publishing)
162+
163+
These are configured in the workflow files.
164+
165+
---
166+
167+
## Troubleshooting
168+
169+
### "Tag already exists" error
170+
171+
The tag `v{version}` already exists. Either:
172+
- Use a different version number
173+
- Delete the existing tag: `git tag -d v1.0.0 && git push origin :refs/tags/v1.0.0`
174+
175+
### "Invalid version format" error
176+
177+
Version must follow semantic versioning:
178+
- Valid: `1.0.0`, `2.1.3`, `1.0.0-beta.1`
179+
- Invalid: `v1.0.0`, `1.0`, `version-1.0.0`
180+
181+
### "Package already exists on PyPI" error
182+
183+
PyPI doesn't allow re-uploading the same version. You need to:
184+
- Bump the version number
185+
- Use a new version for the release
186+
187+
### Authentication errors with PyPI
188+
189+
Check that:
190+
1. `PYPI_API_TOKEN` secret is set correctly
191+
2. Token hasn't expired
192+
3. Token has correct scope (project or entire account)
193+
194+
---
195+
196+
## Version Numbering Guidelines
197+
198+
Follow [Semantic Versioning](https://semver.org/):
199+
200+
- **MAJOR** version (1.0.0 → 2.0.0): Breaking changes
201+
- **MINOR** version (1.0.0 → 1.1.0): New features, backward compatible
202+
- **PATCH** version (1.0.0 → 1.0.1): Bug fixes, backward compatible
203+
204+
**Pre-release versions:**
205+
- Alpha: `1.0.0-alpha.1`
206+
- Beta: `1.0.0-beta.1`
207+
- Release Candidate: `1.0.0-rc.1`
208+
209+
---
210+
211+
## Manual Publishing (without GitHub Actions)
212+
213+
If you need to publish manually:
214+
215+
```bash
216+
# Install build tools
217+
pip install --upgrade build twine
218+
219+
# Build the package
220+
python -m build
221+
222+
# Check the build
223+
twine check dist/*
224+
225+
# Test on TestPyPI
226+
twine upload --repository testpypi dist/*
227+
228+
# Publish to PyPI
229+
twine upload dist/*
230+
```
231+
232+
Configure `~/.pypirc`:
233+
```ini
234+
[pypi]
235+
username = __token__
236+
password = pypi-YOUR_TOKEN_HERE
237+
238+
[testpypi]
239+
username = __token__
240+
password = pypi-YOUR_TESTPYPI_TOKEN_HERE
241+
```
242+
243+
---
244+
245+
## Monitoring Workflow Runs
246+
247+
- View all workflows: **Actions** tab in GitHub
248+
- Check individual runs for logs and errors
249+
- Failed runs will show error messages
250+
- Download build artifacts from workflow runs if needed
251+
252+
---
253+
254+
## Best Practices
255+
256+
1. **Always test on TestPyPI first** before publishing to production
257+
2. **Run tests locally** before creating a release
258+
3. **Write meaningful release notes** in the GitHub Release
259+
4. **Follow semantic versioning** strictly
260+
5. **Tag pre-releases correctly** to avoid confusion
261+
6. **Keep secrets secure** - never commit them to the repository
262+
7. **Monitor PyPI downloads** and user feedback after releases
263+
264+
---
265+
266+
## References
267+
268+
- [PyPI Publishing Guide](https://packaging.python.org/tutorials/packaging-projects/)
269+
- [GitHub Actions Documentation](https://docs.github.com/en/actions)
270+
- [Semantic Versioning](https://semver.org/)
271+
- [PyPI API Tokens](https://pypi.org/help/#apitoken)

.github/workflows/publish.yml

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
name: Publish to PyPI
2+
3+
on:
4+
release:
5+
types: [published]
6+
workflow_dispatch:
7+
inputs:
8+
environment:
9+
description: 'Publish to TestPyPI or PyPI'
10+
required: true
11+
type: choice
12+
options:
13+
- testpypi
14+
- pypi
15+
default: testpypi
16+
17+
jobs:
18+
publish:
19+
runs-on: ubuntu-latest
20+
permissions:
21+
contents: read
22+
id-token: write # Required for trusted publishing
23+
24+
steps:
25+
- name: Checkout code
26+
uses: actions/checkout@v4
27+
28+
- name: Set up Python
29+
uses: actions/setup-python@v5
30+
with:
31+
python-version: '3.11'
32+
33+
- name: Install dependencies
34+
run: |
35+
python -m pip install --upgrade pip
36+
pip install -e .[dev]
37+
38+
- name: Run tests
39+
run: |
40+
pytest tests/ -v --cov=qastudio_pytest --cov-report=term
41+
42+
- name: Run linting
43+
run: |
44+
black --check src/ tests/
45+
flake8 src/ tests/
46+
47+
- name: Run type checking
48+
run: |
49+
mypy src/
50+
51+
- name: Build package
52+
run: |
53+
pip install build
54+
python -m build
55+
56+
- name: Check package
57+
run: |
58+
pip install twine
59+
twine check dist/*
60+
61+
- name: Publish to TestPyPI (manual trigger)
62+
if: github.event_name == 'workflow_dispatch' && inputs.environment == 'testpypi'
63+
uses: pypa/gh-action-pypi-publish@release/v1
64+
with:
65+
repository-url: https://test.pypi.org/legacy/
66+
password: ${{ secrets.TEST_PYPI_API_TOKEN }}
67+
skip-existing: true
68+
69+
- name: Publish to PyPI (manual trigger)
70+
if: github.event_name == 'workflow_dispatch' && inputs.environment == 'pypi'
71+
uses: pypa/gh-action-pypi-publish@release/v1
72+
with:
73+
password: ${{ secrets.PYPI_API_TOKEN }}
74+
75+
- name: Publish to PyPI (on release)
76+
if: github.event_name == 'release'
77+
uses: pypa/gh-action-pypi-publish@release/v1
78+
with:
79+
password: ${{ secrets.PYPI_API_TOKEN }}
80+
81+
- name: Create success comment
82+
if: success() && github.event_name == 'release'
83+
uses: actions/github-script@v7
84+
with:
85+
script: |
86+
const version = context.ref.replace('refs/tags/v', '');
87+
github.rest.issues.createComment({
88+
issue_number: context.payload.release.id,
89+
owner: context.repo.owner,
90+
repo: context.repo.repo,
91+
body: `🚀 Successfully published version ${version} to PyPI!\n\nInstall with:\n\`\`\`bash\npip install qastudio-pytest==${version}\n\`\`\``
92+
})

0 commit comments

Comments
 (0)