# Ship

> Commit, version bump, tag, and publish a release. Use when the user says 'ship', 'release', 'publish', 'bump version', 'tag a release', 'push a new version', 'let's ship this', 'cut a release', 'publish to PyPI', or any variation of wanting to commit and deploy a library release.

- Skill: `kklimuk/ship-2` (Agent Skill)
- Install (CLI): `npx skillmds@latest add kklimuk/ship-2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/kklimuk/ship-2/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: kklimuk (https://skillmd.com/u/kklimuk)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/kklimuk/ship-2

---


# Ship

Commit changes, bump the version, tag, and push to trigger a PyPI publish.

## Process

### Step 1: Pre-flight checks

Run `uv run ruff check .`, `uv run ruff format --check .`, and `uv run pytest`. All three must pass before proceeding. If any fails, stop and fix — do not skip checks (no `--no-verify`).

### Step 2: Stage and commit the changes

1. Run `git status` and `git diff` to understand what changed.
2. Run `git log --oneline -10` to match the existing commit message style.
3. If multiple distinct intents are mixed in the working tree, split into multiple commits — invoke `/commit` to do that cleanly, then return.
4. Stage specific files (never `git add -A`) and commit with a message that follows the project convention from CLAUDE.md.

**Always update CLAUDE.md and README.md** if conventions, file structure, or invariants changed in this release.

### Step 3: Determine the version bump

Ask the user whether this is a **major**, **minor**, or **patch** change. Use these guidelines to suggest one:

- **Major (`x.0.0`)**: Breaking API changes — removed exports, renamed functions, changed signatures or return types that consumers depend on. If still on `0.x.0`, minor bumps may carry breaking changes by convention.
- **Minor (`0.x.0`)**: New features, new exports, new options — anything additive.
- **Patch (`0.0.x`)**: Bug fixes, doc updates, internal refactors with no API change.

Present the suggestion but let the user decide.

### Step 4: Bump the version

1. Read the current version from `pyproject.toml` (`[project] version = "..."`).
2. Compute the new version.
3. Update `pyproject.toml`.
4. Commit with message: `chore: bump version to {new_version}`.

### Step 5: Tag and push

1. Create an annotated git tag: `git tag -a v{new_version} -m "v{new_version}: <one-line summary of the release>"`. The annotation should be enough that someone reading `git show v{new_version}` understands what shipped.
2. **Ask the user for confirmation before pushing.**
3. Push the commits and the tag: `git push && git push --tags`

This triggers `.github/workflows/publish.yml`, which publishes to PyPI via OIDC trusted publishing.

### Step 6: Verify

After pushing, report:

- The new version number and the tag (`v{new_version}`).
- A link to the publish run: `gh run list --workflow=publish.yml --limit=1`.

## Safety rules

- NEVER skip the pre-commit hook (`--no-verify`). If it fails, fix the issue and try again.
- NEVER amend a previous commit — always create new commits.
- NEVER force push.
- ALWAYS ask for confirmation before `git push` or `git push --tags`.
- ALWAYS update CLAUDE.md and README.md before committing if anything documented there has changed.
- The version tag format is `v{version}` (e.g. `v0.10.0`), NOT just the number.

