# relkit: Opinionated Release Toolkit for Modern Python Projects
**"This Way or No Way"** - A strict, opinionated release workflow enforcer for Python projects using uv.
## Features
- 🚀 **Workspace Support**: Per-package versioning in monorepos
- 🔒 **Atomic Releases**: Version, changelog, commit, tag, and push in one command
- 🎯 **Explicit Operations**: No magic, every action is intentional
- ✅ **Pre-flight Checks**: Validates everything before release
- 📝 **Changelog Enforcement**: Required for all releases
- 🏷️ **Smart Tagging**: `v1.0.0` for single packages, `package-v1.0.0` for workspaces
- 🚫 **Safety First**: Blocks dangerous operations before they happen
## Installation
```bash
# Add to your project as a dev dependency
uv add --dev relkit
# Or install from GitHub
uv add --dev git+https://github.com/angelsen/relkit.git
```
## Quick Start
### Single Package Projects
```bash
# Initialize git hooks (recommended)
relkit init-hooks
# Check project status
relkit status
# Make changes, update CHANGELOG.md, then bump version
relkit bump patch # or minor/major
# Full release workflow
relkit release
```
### Workspace Projects
```bash
# Show workspace overview
relkit status
# Work with specific packages (--package is required)
relkit status --package termtap
relkit bump patch --package termtap
relkit build --package termtap
relkit publish --package termtap
# Package-specific tags are created automatically
# e.g., termtap-v1.0.0, webtap-v2.1.0
```
## Commands
### Core Commands
- `relkit status [--package NAME]` - Show release readiness
- `relkit bump <major|minor|patch> [--package NAME]` - Atomic version bump with changelog, commit, and tag
- `relkit release [--package NAME]` - Complete release workflow
- `relkit version` - Show current version
### Build & Publish
- `relkit build [--package NAME]` - Build distribution packages
- `relkit test` - Test built packages
- `relkit publish [--package NAME]` - Publish to PyPI (requires confirmation)
### Development Tools
- `relkit check <all|git|changelog|format|lint|types> [--fix]` - Run quality checks
- `relkit init-hooks` - Install git hooks
### Git Wrappers
- `relkit git <command>` - Pass-through to git with awareness
## Workspace Support
Relkit seamlessly handles three project types:
### 1. Single Package (default)
```toml
[project]
name = "mypackage"
version = "1.0.0"
```
- Commands work without `--package` flag
- Tags: `v1.0.0`
### 2. Pure Workspace
```toml
[tool.uv.workspace]
members = ["packages/*"]
# No [project] section in root
```
- All commands require `--package` flag
- Tags: `package-v1.0.0`
### 3. Hybrid Workspace
```toml
[project]
name = "root-package"
version = "2.0.0"
[tool.uv.workspace]
members = ["packages/*"]
```
- Root package and workspace members
- Use `--package root-package` or `--package member-name`
- Tags: `v2.0.0` for root, `member-v1.0.0` for members
## Philosophy
### Explicit Over Magic
- Workspace operations require explicit `--package` selection
- No automatic dependency cascades
- Clear errors when package selection is ambiguous
### Separation of Concerns
- **uv**: Manages dependencies and workspace setup
- **relkit**: Manages releases and versioning
- **git**: Version control (wrapped for safety)
### Atomic Operations
The `bump` command is atomic - it handles everything in one transaction:
1. Updates version in pyproject.toml
2. Updates CHANGELOG.md
3. Commits changes
4. Syncs lockfile if needed
5. Creates appropriate tag
6. Pushes to remote
## Safety Features
### Blocked Operations
❌ **Direct version edits**
```bash
# Editing version in pyproject.toml directly is blocked
git commit -am "bump version" # BLOCKED by pre-commit hook
```
❌ **Manual tag creation**
```bash
git tag v1.0.0 # BLOCKED by git hook
# Tags must be created via: relkit bump
```
❌ **Dirty working directory**
```bash
# With uncommitted changes:
relkit bump patch # BLOCKED - requires clean git
```
### Required Confirmations
- Publishing to PyPI requires explicit confirmation
- Major version bumps trigger breaking change warning
- All operations show clear next steps on failure
## Configuration
Relkit reads from `pyproject.toml`:
```toml
[project]
name = "your-package"
version = "0.1.0" # Never edit directly!
[tool.uv.workspace]
members = ["packages/*"] # Optional workspace config
```
Each package maintains its own:
- `pyproject.toml` with version
- `CHANGELOG.md` with release notes
- Git tags with appropriate naming
## Error Messages
All errors are actionable:
```
✗ Workspace requires --package
Available packages: termtap, webtap, logtap
Next steps:
1. Specify a package: relkit bump patch --package <name>
2. Use package name from pyproject.toml [project] section
```
## Development
```bash
# Clone repository
git clone https://github.com/angelsen/relkit.git
cd relkit
# Install in development mode
uv sync
# Run tests
uv run pytest
# Check types
uv run pyright
```
## Contributing
This tool is intentionally opinionated. We welcome contributions that:
- Improve error messages
- Add safety checks
- Enhance workspace support
- Fix bugs
We generally reject:
- Features that add "escape hatches"
- Options to bypass safety checks
- Implicit or magical behaviors
## License
MIT
## Credits
Created by Fredrik Angelsen. Built with Python 3.12+ and uv.
Raw data
{
"_id": null,
"home_page": null,
"name": "relkit",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.12",
"maintainer_email": null,
"keywords": "release, workflow, changelog, versioning, monorepo, automation",
"author": "Fredrik Angelsen",
"author_email": "Fredrik Angelsen <fredrikangelsen@gmail.com>",
"download_url": "https://files.pythonhosted.org/packages/80/9d/dc2563514bb3319630acd9020e2dd44d48c909796f30074405874b3fcbc4/relkit-1.5.0.tar.gz",
"platform": null,
"description": "# relkit: Opinionated Release Toolkit for Modern Python Projects\n\n**\"This Way or No Way\"** - A strict, opinionated release workflow enforcer for Python projects using uv.\n\n## Features\n\n- \ud83d\ude80 **Workspace Support**: Per-package versioning in monorepos\n- \ud83d\udd12 **Atomic Releases**: Version, changelog, commit, tag, and push in one command\n- \ud83c\udfaf **Explicit Operations**: No magic, every action is intentional\n- \u2705 **Pre-flight Checks**: Validates everything before release\n- \ud83d\udcdd **Changelog Enforcement**: Required for all releases\n- \ud83c\udff7\ufe0f **Smart Tagging**: `v1.0.0` for single packages, `package-v1.0.0` for workspaces\n- \ud83d\udeab **Safety First**: Blocks dangerous operations before they happen\n\n## Installation\n\n```bash\n# Add to your project as a dev dependency\nuv add --dev relkit\n\n# Or install from GitHub\nuv add --dev git+https://github.com/angelsen/relkit.git\n```\n\n## Quick Start\n\n### Single Package Projects\n\n```bash\n# Initialize git hooks (recommended)\nrelkit init-hooks\n\n# Check project status\nrelkit status\n\n# Make changes, update CHANGELOG.md, then bump version\nrelkit bump patch # or minor/major\n\n# Full release workflow\nrelkit release\n```\n\n### Workspace Projects\n\n```bash\n# Show workspace overview\nrelkit status\n\n# Work with specific packages (--package is required)\nrelkit status --package termtap\nrelkit bump patch --package termtap\nrelkit build --package termtap\nrelkit publish --package termtap\n\n# Package-specific tags are created automatically\n# e.g., termtap-v1.0.0, webtap-v2.1.0\n```\n\n## Commands\n\n### Core Commands\n\n- `relkit status [--package NAME]` - Show release readiness\n- `relkit bump <major|minor|patch> [--package NAME]` - Atomic version bump with changelog, commit, and tag\n- `relkit release [--package NAME]` - Complete release workflow\n- `relkit version` - Show current version\n\n### Build & Publish\n\n- `relkit build [--package NAME]` - Build distribution packages\n- `relkit test` - Test built packages\n- `relkit publish [--package NAME]` - Publish to PyPI (requires confirmation)\n\n### Development Tools\n\n- `relkit check <all|git|changelog|format|lint|types> [--fix]` - Run quality checks\n- `relkit init-hooks` - Install git hooks\n\n### Git Wrappers\n\n- `relkit git <command>` - Pass-through to git with awareness\n\n## Workspace Support\n\nRelkit seamlessly handles three project types:\n\n### 1. Single Package (default)\n```toml\n[project]\nname = \"mypackage\"\nversion = \"1.0.0\"\n```\n- Commands work without `--package` flag\n- Tags: `v1.0.0`\n\n### 2. Pure Workspace\n```toml\n[tool.uv.workspace]\nmembers = [\"packages/*\"]\n# No [project] section in root\n```\n- All commands require `--package` flag\n- Tags: `package-v1.0.0`\n\n### 3. Hybrid Workspace\n```toml\n[project]\nname = \"root-package\"\nversion = \"2.0.0\"\n\n[tool.uv.workspace]\nmembers = [\"packages/*\"]\n```\n- Root package and workspace members\n- Use `--package root-package` or `--package member-name`\n- Tags: `v2.0.0` for root, `member-v1.0.0` for members\n\n## Philosophy\n\n### Explicit Over Magic\n- Workspace operations require explicit `--package` selection\n- No automatic dependency cascades\n- Clear errors when package selection is ambiguous\n\n### Separation of Concerns\n- **uv**: Manages dependencies and workspace setup\n- **relkit**: Manages releases and versioning\n- **git**: Version control (wrapped for safety)\n\n### Atomic Operations\nThe `bump` command is atomic - it handles everything in one transaction:\n1. Updates version in pyproject.toml\n2. Updates CHANGELOG.md\n3. Commits changes\n4. Syncs lockfile if needed\n5. Creates appropriate tag\n6. Pushes to remote\n\n## Safety Features\n\n### Blocked Operations\n\n\u274c **Direct version edits**\n```bash\n# Editing version in pyproject.toml directly is blocked\ngit commit -am \"bump version\" # BLOCKED by pre-commit hook\n```\n\n\u274c **Manual tag creation**\n```bash\ngit tag v1.0.0 # BLOCKED by git hook\n# Tags must be created via: relkit bump\n```\n\n\u274c **Dirty working directory**\n```bash\n# With uncommitted changes:\nrelkit bump patch # BLOCKED - requires clean git\n```\n\n### Required Confirmations\n\n- Publishing to PyPI requires explicit confirmation\n- Major version bumps trigger breaking change warning\n- All operations show clear next steps on failure\n\n## Configuration\n\nRelkit reads from `pyproject.toml`:\n\n```toml\n[project]\nname = \"your-package\"\nversion = \"0.1.0\" # Never edit directly!\n\n[tool.uv.workspace]\nmembers = [\"packages/*\"] # Optional workspace config\n```\n\nEach package maintains its own:\n- `pyproject.toml` with version\n- `CHANGELOG.md` with release notes\n- Git tags with appropriate naming\n\n## Error Messages\n\nAll errors are actionable:\n\n```\n\u2717 Workspace requires --package\n\n Available packages: termtap, webtap, logtap\n\nNext steps:\n 1. Specify a package: relkit bump patch --package <name>\n 2. Use package name from pyproject.toml [project] section\n```\n\n## Development\n\n```bash\n# Clone repository\ngit clone https://github.com/angelsen/relkit.git\ncd relkit\n\n# Install in development mode\nuv sync\n\n# Run tests\nuv run pytest\n\n# Check types\nuv run pyright\n```\n\n## Contributing\n\nThis tool is intentionally opinionated. We welcome contributions that:\n- Improve error messages\n- Add safety checks\n- Enhance workspace support\n- Fix bugs\n\nWe generally reject:\n- Features that add \"escape hatches\"\n- Options to bypass safety checks\n- Implicit or magical behaviors\n\n## License\n\nMIT\n\n## Credits\n\nCreated by Fredrik Angelsen. Built with Python 3.12+ and uv.",
"bugtrack_url": null,
"license": "MIT",
"summary": "Opinionated release toolkit for modern Python projects",
"version": "1.5.0",
"project_urls": null,
"split_keywords": [
"release",
" workflow",
" changelog",
" versioning",
" monorepo",
" automation"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "2b0483d7d631292cf342775438e6a6150bc968097e341ddb45ed782f8e6ae8f7",
"md5": "f6628e03e3266b06538da52921a53775",
"sha256": "747df3c43929800482f463a8612f450883210bcb9734f81c467990036055ae1c"
},
"downloads": -1,
"filename": "relkit-1.5.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "f6628e03e3266b06538da52921a53775",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.12",
"size": 54302,
"upload_time": "2025-08-13T21:38:31",
"upload_time_iso_8601": "2025-08-13T21:38:31.480441Z",
"url": "https://files.pythonhosted.org/packages/2b/04/83d7d631292cf342775438e6a6150bc968097e341ddb45ed782f8e6ae8f7/relkit-1.5.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "809ddc2563514bb3319630acd9020e2dd44d48c909796f30074405874b3fcbc4",
"md5": "87b57762bd27dd6bac6330e3f99cefe1",
"sha256": "bf80463c0f010316fcb71d76befbf1d2c819914973c75b65de72eca35cebec03"
},
"downloads": -1,
"filename": "relkit-1.5.0.tar.gz",
"has_sig": false,
"md5_digest": "87b57762bd27dd6bac6330e3f99cefe1",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.12",
"size": 40057,
"upload_time": "2025-08-13T21:38:32",
"upload_time_iso_8601": "2025-08-13T21:38:32.976972Z",
"url": "https://files.pythonhosted.org/packages/80/9d/dc2563514bb3319630acd9020e2dd44d48c909796f30074405874b3fcbc4/relkit-1.5.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-08-13 21:38:32",
"github": false,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"lcname": "relkit"
}