flakestorm/docs/PUBLISHING.md

562 lines
12 KiB
Markdown

# Publishing flakestorm to PyPI
This guide explains how to publish flakestorm so users can install it with `pip install flakestorm`.
---
## Table of Contents
1. [Understanding PyPI](#understanding-pypi)
2. [Prerequisites](#prerequisites)
3. [Project Structure for Publishing](#project-structure-for-publishing)
4. [Step-by-Step Publishing Guide](#step-by-step-publishing-guide)
5. [Automated Publishing with GitHub Actions](#automated-publishing-with-github-actions)
6. [Publishing the Rust Extension](#publishing-the-rust-extension)
7. [Version Management](#version-management)
8. [Testing Before Publishing](#testing-before-publishing)
9. [Common Issues](#common-issues)
---
## Understanding PyPI
### What is PyPI?
**PyPI** (Python Package Index) is the official repository for Python packages. When users run:
```bash
pip install flakestorm
```
pip downloads the package from PyPI (https://pypi.org).
### What Gets Published?
A Python package is distributed as either:
- **Source Distribution (sdist)**: `.tar.gz` file with source code
- **Wheel (bdist_wheel)**: `.whl` file, pre-built for specific platforms
For flakestorm:
- **Pure Python code**: Published as universal wheel (works everywhere)
- **Rust extension**: Published as platform-specific wheels (separate process)
---
## Prerequisites
### 1. PyPI Account
Create accounts on:
- **Test PyPI**: https://test.pypi.org/account/register/ (for testing)
- **PyPI**: https://pypi.org/account/register/ (for production)
### 2. API Tokens
Generate API tokens (more secure than username/password):
1. Go to https://pypi.org/manage/account/token/
2. Create a token with scope "Entire account" or project-specific
3. Save the token securely (you'll only see it once!)
### 3. Install Build Tools
```bash
pip install build twine hatch
```
---
## Project Structure for Publishing
flakestorm is already set up correctly. Here's what makes it publishable:
### pyproject.toml (Key Sections)
```toml
[build-system]
requires = ["hatchling", "hatch-fancy-pypi-readme"]
build-backend = "hatchling.build"
[project]
name = "flakestorm" # Package name on PyPI
version = "0.1.0" # Version number
description = "The Agent Reliability Engine"
readme = "README.md" # Shown on PyPI page
license = "Apache-2.0"
requires-python = ">=3.10"
dependencies = [ # Auto-installed with package
"typer>=0.9.0",
"rich>=13.0.0",
# ...
]
[project.scripts]
flakestorm = "flakestorm.cli.main:app" # Creates `flakestorm` command
[tool.hatch.build.targets.wheel]
packages = ["src/flakestorm"] # What to include in wheel
```
### Directory Structure
```
flakestorm/
├── pyproject.toml # Package metadata (required)
├── README.md # PyPI description
├── LICENSE # License file
├── src/
│ └── flakestorm/ # Your package code
│ ├── __init__.py # Must exist for package
│ ├── core/
│ ├── mutations/
│ └── ...
└── tests/ # Not included in package
```
### `src/flakestorm/__init__.py` (Package Entry Point)
```python
"""flakestorm - The Agent Reliability Engine"""
__version__ = "0.1.0"
from flakestorm.core.config import load_config, FlakeStormConfig
from flakestorm.core.runner import FlakeStormRunner
__all__ = ["load_config", "FlakeStormConfig", "FlakeStormRunner", "__version__"]
```
---
## Step-by-Step Publishing Guide
### Step 1: Verify Package Metadata
```bash
# Check pyproject.toml is valid
# NOTE: Use editable mode for development, regular install for testing wheel builds
pip install -e . # Editable mode (recommended for development)
# OR test the wheel build process:
python -m pip install build
python -m build --wheel
python -m pip install dist/*.whl
# Verify the package works
flakestorm --version
```
**Important:** If you get `ModuleNotFoundError: No module named 'flakestorm.reports'` when using `pip install .` (non-editable), it means the wheel build didn't include all subpackages. Use `pip install -e .` for development, or ensure `pyproject.toml` has the correct `packages` configuration.
### Step 2: Build the Package
```bash
# Install build tools (if not already installed)
pip install build
# Clean previous builds
rm -rf dist/ build/ *.egg-info src/*.egg-info
# Build source distribution and wheel
python -m build
# You should see:
# dist/
# flakestorm-0.1.0.tar.gz (source)
# flakestorm-0.1.0-py3-none-any.whl (wheel)
# Verify all subpackages are included (especially reports)
unzip -l dist/*.whl | grep "flakestorm/reports"
```
### Step 3: Check the Build
```bash
# Install twine for checking (if not already installed)
pip install twine
# Verify the package contents
twine check dist/*
# List files in the wheel
unzip -l dist/*.whl
# Ensure it contains all subpackages:
# - flakestorm/__init__.py
# - flakestorm/core/*.py
# - flakestorm/mutations/*.py
# - flakestorm/reports/*.py (important: check this exists!)
# - flakestorm/assertions/*.py
# - etc.
# Quick check for reports module:
unzip -l dist/*.whl | grep "flakestorm/reports"
```
### Step 4: Test on Test PyPI (Recommended)
```bash
# Upload to Test PyPI first
twine upload --repository testpypi dist/*
# You'll be prompted for:
# Username: __token__
# Password: pypi-your-test-token-here
# Install from Test PyPI to verify
pip install --index-url https://test.pypi.org/simple/ flakestorm
```
### Step 5: Publish to Production PyPI
```bash
# Upload to real PyPI
twine upload dist/*
# Username: __token__
# Password: pypi-your-real-token-here
```
### Step 6: Verify Installation
```bash
# In a fresh virtual environment
python -m venv test_env
source test_env/bin/activate
pip install flakestorm
flakestorm --version
```
🎉 **Congratulations!** Users can now `pip install flakestorm`!
---
## Automated Publishing with GitHub Actions
Set up automatic publishing when you create a release:
### `.github/workflows/publish.yml`
```yaml
name: Publish to PyPI
on:
release:
types: [published]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install build tools
run: pip install build twine
- name: Build package
run: python -m build
- name: Check package
run: twine check dist/*
- name: Publish to PyPI
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }}
run: twine upload dist/*
```
### Setting Up the Secret
1. Go to your GitHub repo → Settings → Secrets → Actions
2. Add a new secret named `PYPI_TOKEN`
3. Paste your PyPI API token as the value
### Creating a Release
1. Go to GitHub → Releases → Create new release
2. Create a new tag (e.g., `v0.1.0`)
3. Add release notes
4. Publish release
5. GitHub Actions will automatically publish to PyPI
---
## Publishing the Rust Extension
The Rust extension (if implemented) would be published separately because it requires platform-specific binaries.
### Using `maturin`
```bash
cd rust/
# Build wheels for your current platform
maturin build --release
# The wheel would be in: ../target/wheels/flakestorm_rust-0.1.0-cp311-*.whl
# (or cp310, cp312 depending on Python version used)
```
### Multi-Platform Publishing with GitHub Actions
```yaml
# .github/workflows/rust-publish.yml
name: Publish Rust Extension
on:
release:
types: [published]
jobs:
linux:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: PyO3/maturin-action@v1
with:
manylinux: auto
command: build
args: --release --manifest-path rust/Cargo.toml -o dist
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-linux
path: dist
macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: PyO3/maturin-action@v1
with:
command: build
args: --release --manifest-path rust/Cargo.toml -o dist
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-macos
path: dist
windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: PyO3/maturin-action@v1
with:
command: build
args: --release --manifest-path rust/Cargo.toml -o dist
- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-windows
path: dist
publish:
needs: [linux, macos, windows]
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
path: dist
merge-multiple: true
- name: Publish to PyPI
uses: PyO3/maturin-action@v1
with:
command: upload
args: --skip-existing dist/*
env:
MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }}
```
---
## Version Management
### Semantic Versioning
Follow [Semantic Versioning](https://semver.org/):
```
MAJOR.MINOR.PATCH
0.1.0 - Initial release
0.1.1 - Bug fixes
0.2.0 - New features (backward compatible)
1.0.0 - Stable release / Breaking changes
```
### Where Version is Defined
Update version in TWO places:
1. **`pyproject.toml`**:
```toml
[project]
version = "0.2.0"
```
2. **`src/flakestorm/__init__.py`**:
```python
__version__ = "0.2.0"
```
### Automating Version Sync (Optional)
Use `hatch-vcs` to automatically get version from git tags:
```toml
# pyproject.toml
[build-system]
requires = ["hatchling", "hatch-vcs"]
[tool.hatch.version]
source = "vcs"
```
Then just create a git tag and the version is set automatically:
```bash
git tag v0.2.0
git push --tags
```
---
## Testing Before Publishing
### Local Testing
```bash
# Create a fresh virtual environment
python -m venv test_install
source test_install/bin/activate
# Install from local build
pip install dist/flakestorm-0.1.0-py3-none-any.whl
# Test it works
flakestorm --help
flakestorm init
python -c "from flakestorm import load_config; print('OK')"
```
### Test PyPI
Always test on Test PyPI first:
```bash
# Upload to Test PyPI
twine upload --repository testpypi dist/*
# Install from Test PyPI
pip install --index-url https://test.pypi.org/simple/ \
--extra-index-url https://pypi.org/simple/ \
flakestorm
```
The `--extra-index-url` is needed because Test PyPI may not have all dependencies.
---
## Common Issues
### "Package name already taken"
Package names on PyPI are unique. If `flakestorm` is taken:
- Check https://pypi.org/project/flakestorm/
- Choose a different name: `flakestorm-cli`, `py-flakestorm`, etc.
### "Invalid distribution file"
```bash
# Check what's wrong
twine check dist/*
# Common fixes:
# - Ensure README.md is valid markdown
# - Ensure LICENSE file exists
# - Ensure version is valid format
```
### "Missing files in wheel"
```bash
# List wheel contents
unzip -l dist/*.whl
# If files are missing, check pyproject.toml:
[tool.hatch.build.targets.wheel]
packages = ["src/flakestorm"] # Make sure path is correct
```
### "Command not found after install"
Ensure `project.scripts` is set in pyproject.toml:
```toml
[project.scripts]
flakestorm = "flakestorm.cli.main:app"
```
---
## Quick Reference
### One-Time Setup
```bash
# Install tools
pip install build twine
# Create PyPI account and token
# Store token securely
```
### Each Release
```bash
# 1. Update version in pyproject.toml and __init__.py
# 2. Commit and push
git add -A && git commit -m "Release 0.2.0" && git push
# 3. Build
python -m build
# 4. Check
twine check dist/*
# 5. Test (optional but recommended)
twine upload --repository testpypi dist/*
pip install --index-url https://test.pypi.org/simple/ flakestorm
# 6. Publish
twine upload dist/*
# 7. Tag release
git tag v0.2.0
git push --tags
```
### With GitHub Actions
Just create a release on GitHub and everything happens automatically!
---
## Next Steps After Publishing
1. **Announce**: Post on social media, Reddit, Hacker News
2. **Documentation**: Update docs with install instructions
3. **Monitor**: Watch for issues and PyPI download stats
4. **Iterate**: Fix bugs, add features, release new versions
---
*Happy publishing! 🚀*