Local-first release CLI tools: ship_bump, ship_pypi, ship_release_gh
Tiny, local-first release tools for modern Python projects.
fastship gives you the same workflow feel as the nbdev nbdev_bump_version, release_pypi, and release_gh commands — but for plain (non-notebook) Python projects.
pip install fastship
Create a new project:
ship_new my-project
cd my-project
pip install -e .[dev]
This creates a complete project with pyproject.toml, __version__, LICENSE, README, and everything wired for fastship.
ship_bumpBump a version part (0=major, 1=minor, 2=patch):
ship_bump --part 2
ship_bump --part 1
ship_bump --part 0
Decrement instead:
ship_bump --part 2 --unbump
ship_pypiBuild + upload to PyPI:
ship_pypi
Upload to a named repository in ~/.pypirc (e.g. testpypi):
ship_pypi --repository testpypi
Quiet mode:
ship_pypi --quiet
ship_prCreate a PR from uncommitted or unpushed work, merge it immediately, and clean up:
ship_pr "Add new feature"
ship_pr "Fix bug" --label bug
ship_pr "Breaking change" --label breaking
This command:
enhancement)You must be on the default branch (usually main) with no unpulled changes.
ship_changelogGenerate or update CHANGELOG.md from closed GitHub issues since your last release:
ship_changelog
This is useful when you want to edit the changelog separately (e.g., in an editor or Claude Code) before releasing.
ship_release_ghThis is an interactive helper:
CHANGELOG.md from closed GitHub issues since your last GitHub release$EDITOR (defaults to nano) so you can edit the changeloggit commit -am release, git push__version__ship_release_gh
If you’ve already prepared the changelog (e.g., via ship_changelog), skip the changelog step:
ship_release_gh --no-changelog
ship_release_gh looks for a token in this order:
FASTSHIP_TOKEN./token file in your repo rootGITHUB_TOKENThe token must have permission to create releases (typically repo scope for classic PATs, or appropriate fine-grained permissions).
ship_releaseFull release workflow assuming changelog is ready:
ship_changelog # generate changelog, edit as needed
ship_release # release to GitHub + PyPI, bump version, push
This runs:
ship_release_gh --no_changelog (commit, push, create GitHub release)ship_pypi (upload to PyPI)ship_bump (bump patch version)ship_pypi does not bump your version for you — keep it explicit and boring.ship_release_gh requires that your project has a git origin remote pointing at GitHub (or use --repo OWNER/REPO).To add fastship to an existing project:
__init__.pyIn your package’s main __init__.py:
__version__ = "0.0.1"
pyproject.toml[project]
name = "my-project"
dynamic = ["version"]
[tool.setuptools.dynamic]
version = { attr = "my_project.__version__" }
Keep __version__ = "x.y.z" as a simple literal (don’t compute it). ship_bump will rewrite this line near the top of the file to keep builds happy.
Fastship infers your package name from [project].name (changing - to _). To override the release branch:
[tool.fastship]
branch = "main" # defaults to current git branch