Skip to content

Commit 863e598

Browse files
committed
ci: add PyPI publishing workflow with OIDC trusted publishing
- Add GitHub Actions workflow for automated PyPI publishing - Support both PyPI and TestPyPI with separate environments - Use OIDC (Trusted Publishing) for secure, token-free deployment - Build artifacts once, publish to multiple targets - Add comprehensive publishing documentation to README - Include setup instructions for PyPI trusted publishers - Document manual and automated release processes
1 parent e521865 commit 863e598

2 files changed

Lines changed: 164 additions & 0 deletions

File tree

.github/workflows/pypi-publish.yml

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
name: Publish to PyPI
2+
3+
on:
4+
release:
5+
types: [published]
6+
workflow_dispatch: # Allow manual trigger
7+
8+
jobs:
9+
build:
10+
name: Build distribution
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout repository
15+
uses: actions/checkout@v4
16+
17+
- name: Set up Python
18+
uses: actions/setup-python@v5
19+
with:
20+
python-version: '3.10'
21+
22+
- name: Install uv
23+
uses: astral-sh/setup-uv@v5
24+
with:
25+
enable-cache: true
26+
27+
- name: Build package
28+
run: uv build
29+
30+
- name: Store distribution packages
31+
uses: actions/upload-artifact@v4
32+
with:
33+
name: python-package-distributions
34+
path: dist/
35+
36+
publish-to-pypi:
37+
name: Publish to PyPI
38+
needs: [build]
39+
runs-on: ubuntu-latest
40+
41+
environment:
42+
name: pypi
43+
url: https://pypi.org/p/markdown-vault
44+
45+
permissions:
46+
id-token: write # REQUIRED for trusted publishing
47+
48+
steps:
49+
- name: Download distributions
50+
uses: actions/download-artifact@v4
51+
with:
52+
name: python-package-distributions
53+
path: dist/
54+
55+
- name: Publish to PyPI
56+
uses: pypa/gh-action-pypi-publish@release/v1
57+
58+
publish-to-testpypi:
59+
name: Publish to TestPyPI
60+
needs: [build]
61+
runs-on: ubuntu-latest
62+
63+
environment:
64+
name: testpypi
65+
url: https://test.pypi.org/p/markdown-vault
66+
67+
permissions:
68+
id-token: write # REQUIRED for trusted publishing
69+
70+
steps:
71+
- name: Download distributions
72+
uses: actions/download-artifact@v4
73+
with:
74+
name: python-package-distributions
75+
path: dist/
76+
77+
- name: Publish to TestPyPI
78+
uses: pypa/gh-action-pypi-publish@release/v1
79+
with:
80+
repository-url: https://test.pypi.org/legacy/

README.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,90 @@ Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
479479
6. Push (`git push origin feature/amazing-feature`)
480480
7. Open a Pull Request
481481

482+
## Publishing & Releases
483+
484+
This project uses automated publishing with **OIDC Trusted Publishing** for security and convenience.
485+
486+
### For Maintainers: Setting Up PyPI Trusted Publishing
487+
488+
**One-time setup** (requires PyPI account with project ownership):
489+
490+
1. **Go to PyPI Project Settings**
491+
- Visit: https://pypi.org/manage/project/markdown-vault/settings/publishing/
492+
- Or create pending publisher: https://pypi.org/manage/account/publishing/
493+
494+
2. **Add GitHub as Trusted Publisher**
495+
- **PyPI Project Name**: `markdown-vault`
496+
- **Owner**: `boxpositron`
497+
- **Repository name**: `markdown-vault`
498+
- **Workflow name**: `pypi-publish.yml`
499+
- **Environment name**: `pypi` (optional but recommended)
500+
501+
3. **Repeat for TestPyPI** (for testing)
502+
- Visit: https://test.pypi.org/manage/account/publishing/
503+
- Use same settings but with environment name: `testpypi`
504+
505+
### Creating a Release
506+
507+
Releases are fully automated via GitHub Actions:
508+
509+
```bash
510+
# 1. Update version in pyproject.toml
511+
# 2. Update CHANGELOG.md
512+
# 3. Commit changes
513+
git add pyproject.toml CHANGELOG.md
514+
git commit -m "chore: bump version to 0.0.2"
515+
516+
# 4. Create and push tag
517+
git tag v0.0.2
518+
git push origin main --tags
519+
520+
# 5. Create GitHub Release (triggers publishing)
521+
gh release create v0.0.2 \
522+
--title "v0.0.2" \
523+
--notes "See CHANGELOG.md for details" \
524+
dist/*.whl dist/*.tar.gz
525+
```
526+
527+
This automatically:
528+
- ✅ Builds Docker images (multi-platform: amd64/arm64)
529+
- ✅ Pushes to `ghcr.io/boxpositron/markdown-vault`
530+
- ✅ Publishes to TestPyPI (for verification)
531+
- ✅ Publishes to PyPI (production)
532+
- ✅ Generates SBOM and provenance attestations
533+
534+
### Manual Publishing (Alternative)
535+
536+
If you need to publish manually:
537+
538+
```bash
539+
# Build the package
540+
uv build
541+
542+
# Publish using trusted publishing (no token needed!)
543+
uv publish
544+
545+
# Or publish to TestPyPI first
546+
uv publish --publish-url https://test.pypi.org/legacy/
547+
```
548+
549+
**Note**: Manual publishing still uses OIDC when run from GitHub Actions, or requires API token when run locally.
550+
551+
### Verification
552+
553+
After publishing, verify the release:
554+
555+
```bash
556+
# Test PyPI installation
557+
pip install markdown-vault==0.0.2
558+
559+
# Test Docker image
560+
docker pull ghcr.io/boxpositron/markdown-vault:0.0.2
561+
562+
# Verify attestations
563+
docker buildx imagetools inspect ghcr.io/boxpositron/markdown-vault:0.0.2
564+
```
565+
482566
## Changelog
483567

484568
See [CHANGELOG.md](CHANGELOG.md) for version history.

0 commit comments

Comments
 (0)