# Publishing sanicode to PyPI

## One-Time Setup

### 1. Configure PyPI Trusted Publisher

Trusted publishing uses OpenID Connect (OIDC) — no API tokens needed.

1. Go to https://pypi.org/manage/account/publishing/
2. Click "Add a new pending publisher"
3. Fill in:
   - **PyPI Project Name:** `sanicode`
   - **Owner:** `rdwj`
   - **Repository name:** `sanicode`
   - **Workflow name:** `release.yml`
   - **Environment name:** `pypi`
4. Click "Add"

### 2. Create GitHub Environment

1. Go to https://github.com/rdwj/sanicode/settings/environments
2. Create environment named `pypi`
3. (Optional) Add yourself as a required reviewer for manual approval before publishing
4. Set deployment branches to "main" only

## Releasing

### Automated (Recommended)

From the project root:

```bash
./scripts/release.sh <version> "<description>"

# Example:
./scripts/release.sh 0.2.0 "Add API server and Prometheus metrics"
```

The script:
1. Validates version format (x.y.z)
2. Updates `src/sanicode/version.py` and `pyproject.toml`
3. Verifies both files match
4. Commits, creates annotated tag `v<version>`, pushes both

GitHub Actions then:
1. Verifies tag/version.py/pyproject.toml all agree
2. Runs full test suite across Python 3.10-3.13
3. Creates a GitHub Release with auto-generated notes
4. Builds sdist + wheel
5. Publishes to PyPI via OIDC

### Manual Fallback

If GitHub Actions is unavailable:

```bash
# Build
rm -rf dist/
python -m build

# Validate
twine check dist/*

# Upload (requires PyPI API token)
twine upload dist/*
```

## Version Numbering

Semantic versioning: `MAJOR.MINOR.PATCH`

- **MAJOR**: Breaking API changes
- **MINOR**: New features (backward compatible)
- **PATCH**: Bug fixes

The version lives in two files that must stay in sync:
- `src/sanicode/version.py` — `__version__ = "x.y.z"`
- `pyproject.toml` — `version = "x.y.z"`

The release script and GitHub Actions both enforce this invariant.

## Verification

After a release:

```bash
# Check PyPI
pip install sanicode==<version>
sanicode --version

# Check GitHub
# https://github.com/rdwj/sanicode/releases
# https://github.com/rdwj/sanicode/actions
```
