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 fastshipCreate 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.
Bump a version part (0=major, 1=minor, 2=patch):
ship-bump --part 2
ship-bump --part 1
ship-bump --part 0Decrement instead:
ship-bump --part 2 --unbumpBuild + upload to PyPI:
ship-pypiUpload to a named repository in ~/.pypirc (e.g. testpypi):
ship-pypi --repository testpypiQuiet mode:
ship-pypi --quietCreate 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 breakingThis command:
- Creates a new branch from your current work
- Commits any uncommitted changes (using the title as commit message)
- Pushes to origin and creates a PR
- Adds the specified label (default:
enhancement) - Squash-merges the PR
- Deletes the remote branch and resets local to updated main
You must be on the default branch (usually main) with no unpulled changes.
Generate or update CHANGELOG.md from closed GitHub issues since your last release:
ship-changelogThis is useful when you want to edit the changelog separately (e.g., in an editor or Claude Code) before releasing.
This is an interactive helper:
- Creates/updates
CHANGELOG.mdfrom closed GitHub issues since your last GitHub release - Opens your
$EDITOR(defaults tonano) so you can edit the changelog - Prompts you to confirm
- Runs
git commit -am release,git push - Creates a GitHub release tagged with your current
__version__
ship-release-ghIf you've already prepared the changelog (e.g., via ship-changelog), skip the changelog step:
ship-release-gh --no-changelogThis still opens CHANGELOG.md in your editor for final review before the release is created.
ship-release-gh looks for a token in this order:
FASTSHIP_TOKEN- a
./tokenfile in your repo root GITHUB_TOKEN
The token must have permission to create releases (typically repo scope for classic PATs, or appropriate fine-grained permissions).
Full release workflow assuming changelog is ready:
ship-changelog # generate changelog, edit as needed
ship-release # release to GitHub + PyPI, bump version, pushThis runs:
ship-release-gh --no_changelog(openCHANGELOG.mdfor final review, commit if needed, push, create GitHub release)ship-pypi(upload to PyPI)ship-bump(bump patch version)- Commit and push the version bump
ship-pypidoes not bump your version for you — keep it explicit and boring.ship-release-ghrequires that your project has a gitoriginremote pointing at GitHub (or use--repo OWNER/REPO).
To add fastship to an existing project:
In your package's main __init__.py:
__version__ = "0.0.1"[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