Best-in-class Python release automation, inspired by release-plz
Project description
releasio
Automated releases for Python projects
Version bumping, changelog generation, and PyPI publishing powered by conventional commits
Inspired by release-plz, releasio brings the same powerful release automation to the Python ecosystem. It analyzes your Conventional Commits to automatically determine version bumps, generate beautiful changelogs, and publish to PyPI.
Features
- Release PR Workflow - Automatically creates and maintains a release PR with version bump and changelog
- Conventional Commits - Automatic version bumping based on commit types (
feat:,fix:, etc.) - Beautiful Changelogs - Professional changelog generation with PR links and author attribution
- Zero Config - Works out of the box with sensible defaults
- GitHub Actions - First-class GitHub Actions support with outputs
- PyPI Trusted Publishing - Native OIDC support, no tokens required
- Pre-1.0.0 Semver - Proper handling of 0.x.y versions (breaking changes bump minor, not major)
- Pre-release Versions - Support for alpha, beta, and rc versions
- Fully Typed - Complete type annotations with
py.typedmarker
Installation
# Using uv (recommended)
uv tool install releasio
# Using pip
pip install releasio
# Using pipx
pipx install releasio
Quick Start
# 1. Check what would happen (always safe)
releasio check
# 2. Preview a release PR (safe - dry-run by default)
releasio release-pr
# 3. Create the release PR
releasio release-pr --execute
# 4. After merging the PR, preview the release
releasio release
# 5. Perform the actual release
releasio release --execute
All commands that make changes default to dry-run mode for safety. Use --execute to apply changes.
releasio handles version bumping, changelog generation, git tagging, PyPI publishing, and GitHub release creation.
CLI Commands
| Command | Description |
|---|---|
releasio check |
Preview what would happen during a release |
releasio update |
Update version and changelog locally |
releasio release-pr |
Create or update a release pull request |
releasio release |
Tag, publish to PyPI, and create GitHub release |
releasio do-release |
Complete workflow: update + commit + tag + publish (recommended) |
releasio check-pr |
Validate PR title follows conventional commits |
releasio init |
Initialize releasio configuration |
Common Options
# All commands that make changes default to dry-run (safe mode)
releasio update # Preview changes (dry-run)
releasio update --execute # Apply changes
releasio release-pr # Preview PR (dry-run)
releasio release-pr --execute # Create/update PR
releasio release # Preview release (dry-run)
releasio release --execute # Perform release
# Complete release workflow (recommended for simple projects)
releasio do-release # Preview complete release (dry-run)
releasio do-release --execute # Update + commit + tag + publish
# Version control
releasio update --version 2.0.0 # Force specific version
releasio update --prerelease alpha # Create pre-release (1.2.0a1)
releasio do-release --execute --version 2.0.0 # One-command release with version override
releasio release --execute --skip-publish # Skip PyPI publishing
releasio check-pr --require-scope # Require scope in PR title
GitHub Actions
releasio provides a GitHub Action for seamless CI/CD integration.
Recommended Workflow
Create .github/workflows/release.yml:
name: Release
on:
push:
branches: [main]
pull_request:
types: [closed]
branches: [main]
permissions:
contents: write
pull-requests: write
id-token: write # For PyPI trusted publishing
jobs:
# Create/update release PR on every push to main
release-pr:
if: github.event_name == 'push'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: mikeleppane/release-py@v1
with:
command: release-pr
# Perform release when release PR is merged
release:
if: |
github.event_name == 'pull_request' &&
github.event.pull_request.merged == true &&
contains(github.event.pull_request.labels.*.name, 'release')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: mikeleppane/release-py@v1
with:
command: release
# PyPI trusted publishing - no token needed!
Action Inputs
| Input | Description | Default |
|---|---|---|
command |
Command: release-pr, release, check, check-pr |
required |
github-token |
GitHub token for API access | github.token |
python-version |
Python version to use | 3.11 |
dry-run |
Run without making changes | false |
skip-publish |
Skip PyPI publishing | false |
Action Outputs
| Output | Description |
|---|---|
version |
The version released/to be released |
pr-number |
Created/updated PR number |
pr-url |
Created/updated PR URL |
release-url |
GitHub release URL |
tag |
Git tag created |
How It Works
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Push to main │────▶│ release-pr │────▶│ Release PR │
│ (commits) │ │ (automated) │ │ Created/Updated│
└─────────────────┘ └─────────────────┘ └────────┬────────┘
│
│ Merge PR
▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ GitHub Release │◀────│ release │◀────│ PR Merged │
│ + PyPI Publish │ │ (automated) │ │ (manual) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
Version Bumping Rules
| Commit Type | Version Bump | Example |
|---|---|---|
feat: |
Minor (0.1.0 → 0.2.0) | feat: add user authentication |
fix: |
Patch (0.1.0 → 0.1.1) | fix: handle null response |
perf: |
Patch | perf: optimize database queries |
feat!: or BREAKING CHANGE: |
Major* | feat!: redesign API |
*For 0.x.y versions, breaking changes bump minor instead of major to prevent accidental 1.0.0 releases.
Configuration
Configuration is optional. releasio works out of the box with sensible defaults.
Custom Configuration Files
releasio supports standalone TOML configuration files in addition to pyproject.toml. This is useful for projects that prefer dedicated config files or need monorepo-style configurations.
Configuration file precedence (highest to lowest):
.releasio.toml(dotfile, hidden)releasio.toml(visible file)pyproject.tomlunder[tool.releasio]section
Create a .releasio.toml or releasio.toml file in your project root:
# .releasio.toml
# Note: Custom config files use flattened structure (no [tool.releasio] wrapper)
default_branch = "main"
[version]
tag_prefix = "v"
[commits]
types_minor = ["feat"]
types_patch = ["fix", "perf", "docs"]
[changelog]
path = "CHANGELOG.md"
use_github_prs = false
[github]
release_pr_labels = ["release"]
draft_releases = false
[publish]
tool = "uv"
trusted_publishing = true
Important notes:
- Custom config files (
.releasio.tomlandreleasio.toml) use top-level keys (no[tool.releasio]wrapper) - You still need
pyproject.tomlfor project metadata (name, version) - Custom configs are only searched in the current directory
pyproject.tomlconfigs walk up the directory tree (existing behavior)- See
.releasio.toml.examplefor a complete example
pyproject.toml Configuration
Add to pyproject.toml if you prefer traditional configuration:
[tool.releasio]
default_branch = "main"
[tool.releasio.version]
tag_prefix = "v"
[tool.releasio.commits]
types_minor = ["feat"]
types_patch = ["fix", "perf", "docs"]
[tool.releasio.changelog]
path = "CHANGELOG.md"
use_github_prs = false # Set to true for squash merge workflows
[tool.releasio.github]
release_pr_labels = ["release"]
draft_releases = false
[tool.releasio.publish]
tool = "uv" # or "poetry", "pdm", "twine"
trusted_publishing = true
Using Poetry
If your project uses Poetry, configure releasio to use it for building and publishing:
[tool.releasio.publish]
tool = "poetry"
Configure authentication:
poetry config pypi-token.pypi your-token-here
Using PDM
If your project uses PDM, configure releasio to use it for building and publishing:
[tool.releasio.publish]
tool = "pdm"
Configure authentication:
export PDM_PUBLISH_PASSWORD=your-token-here
# or
pdm config pypi.token your-token-here
Multi-branch Release Channels
Automatically create pre-release versions based on the branch you're releasing from. This is useful for projects that maintain multiple release channels (e.g., stable, beta, alpha).
[tool.releasio.branches.main]
match = "main"
prerelease = false # Stable releases from main
[tool.releasio.branches.beta]
match = "beta"
prerelease = true
prerelease_token = "beta" # 1.2.0 → 1.2.0-beta.1
[tool.releasio.branches.alpha]
match = "alpha"
prerelease = true
prerelease_token = "alpha" # 1.2.0 → 1.2.0-alpha.1
[tool.releasio.branches.release]
match = "release/*" # Wildcard pattern
prerelease = true
prerelease_token = "rc" # 1.2.0 → 1.2.0-rc.1
When releasing from the beta branch, releasio will automatically detect it and append the pre-release token:
$ git checkout beta
$ releasio update --execute
Auto-detected pre-release channel beta from branch beta
Updating from 1.1.0 to 1.2.0-beta.1
Custom Changelog Templates
Customize how your changelog entries are formatted with section headers, author attribution, and custom templates.
[tool.releasio.changelog]
enabled = true
path = "CHANGELOG.md"
show_authors = true # Include author name: "- Add feature (@username)"
show_commit_hash = true # Include commit hash: "- Add feature (abc1234)"
# Custom template with all available variables
commit_template = "{description} by @{author} ({hash})"
# Customize section headers
[tool.releasio.changelog.section_headers]
feat = "🚀 New Features"
fix = "🐛 Bug Fixes"
perf = "⚡ Performance"
docs = "📚 Documentation"
refactor = "♻️ Refactoring"
breaking = "💥 Breaking Changes"
Available template variables:
{description}- Commit description{scope}- Commit scope (if present){author}- Author name{hash}- Short commit hash{body}- Full commit body{type}- Commit type (feat, fix, etc.)
Custom Commit Parsers
Support non-conventional commit formats like Gitmoji, Angular, or your own custom patterns. Custom parsers are tried first, with conventional commits as a fallback.
[tool.releasio.commits]
# Custom parsers for Gitmoji commits
commit_parsers = [
{ pattern = "^:sparkles:\\s*(?P<description>.+)$", type = "feat", group = "✨ Features" },
{ pattern = "^:bug:\\s*(?P<description>.+)$", type = "fix", group = "🐛 Bug Fixes" },
{ pattern = "^:boom:\\s*(?P<description>.+)$", type = "breaking", group = "💥 Breaking Changes", breaking_indicator = ":boom:" },
{ pattern = "^:recycle:\\s*(?P<description>.+)$", type = "refactor", group = "♻️ Refactoring" },
{ pattern = "^:memo:\\s*(?P<description>.+)$", type = "docs", group = "📚 Documentation" },
]
# Fall back to conventional commits if no custom parser matches (default: true)
use_conventional_fallback = true
Each parser supports:
pattern- Regex with named capture groups (must includedescriptiongroup)type- Commit type for version bumping (e.g., "feat", "fix")group- Changelog section headerscope_group- Optional: name of regex group containing scopedescription_group- Group name for description (default: "description")breaking_indicator- If set, marks commits as breaking changes
Native Changelog Fallback
releasio can generate changelogs natively when git-cliff is not installed. This uses your section_headers and commit_template settings.
[tool.releasio.changelog]
# Generate changelog natively if git-cliff unavailable (default: true)
native_fallback = true
# Auto-generate git-cliff config from releasio settings
generate_cliff_config = false
Build Command Hook
Customize the build command used during release. By default, releasio uses uv build, but you can specify any build command.
[tool.releasio.hooks]
# Custom build command (replaces default uv build)
build = "python -m build --sdist --wheel"
# Or use template variables
build = "hatch build -t wheel && echo 'Built version {version}'"
Available template variables:
{version}- Version being built{project_path}- Path to the project directory
Version File Management
By default, releasio updates the version in pyproject.toml. You can also update version strings in other files.
Explicit Version Files
Specify additional files that contain version strings:
[tool.releasio.version]
version_files = [
"src/mypackage/__init__.py", # __version__ = "1.0.0"
"src/mypackage/__version__.py", # __version__ = "1.0.0"
"VERSION", # Plain text file with just the version
]
Supported patterns in Python files:
__version__ = "1.0.0"VERSION = "1.0.0"version = "1.0.0"
Auto-Detection
Enable automatic detection of version files in your package:
[tool.releasio.version]
auto_detect_version_files = true
When enabled, releasio automatically finds and updates version strings in:
src/<package>/__init__.pysrc/<package>/__version__.pysrc/<package>/_version.py<package>/__init__.py(flat layout)VERSION(plain text file in project root)
Lock File Updates
releasio automatically updates your lock file after bumping the version to keep dependencies in sync. This works with multiple package managers:
| Package Manager | Lock File | Command |
|---|---|---|
| uv | uv.lock |
uv lock |
| Poetry | poetry.lock |
poetry lock --no-update |
| PDM | pdm.lock |
pdm lock --no-update |
| Hatch | none | skipped |
The package manager is auto-detected based on:
- Existing lock files (e.g.,
uv.lock,poetry.lock) - Tool configuration in
pyproject.toml(e.g.,[tool.poetry])
To disable lock file updates:
[tool.releasio.version]
update_lock_file = false
Full Configuration Reference
[tool.releasio]
default_branch = "main" # Branch for releases
allow_dirty = false # Allow releases from dirty working directory
[tool.releasio.version]
tag_prefix = "v" # Git tag prefix (v1.0.0)
initial_version = "0.1.0" # Version for first release
version_files = [] # Additional files to update version in
auto_detect_version_files = false # Auto-detect __init__.py, __version__.py, etc.
update_lock_file = true # Update uv.lock/poetry.lock/pdm.lock after bump
[tool.releasio.commits]
types_minor = ["feat"] # Commit types triggering minor bump
types_patch = ["fix", "perf"] # Commit types triggering patch bump
breaking_pattern = "BREAKING[ -]CHANGE:"
skip_release_patterns = ["[skip release]", "[release skip]"]
commit_parsers = [] # Custom parsers for non-conventional commits
use_conventional_fallback = true # Fall back to conventional if no parser matches
[tool.releasio.changelog]
enabled = true
path = "CHANGELOG.md"
use_github_prs = false # Use PR-based changelog (for squash merges)
show_authors = false # Include author in changelog entries
show_commit_hash = false # Include commit hash in changelog entries
commit_template = "" # Custom template: "{description} by @{author}"
native_fallback = true # Generate natively if git-cliff unavailable
generate_cliff_config = false # Auto-generate git-cliff config
[tool.releasio.changelog.section_headers]
feat = "✨ Features"
fix = "🐛 Bug Fixes"
breaking = "⚠️ Breaking Changes"
[tool.releasio.github]
owner = "" # Auto-detected from git remote
repo = "" # Auto-detected from git remote
api_url = "https://api.github.com"
release_pr_branch = "releasio/release"
release_pr_labels = ["release"]
draft_releases = false
[tool.releasio.publish]
enabled = true
registry = "https://upload.pypi.org/legacy/"
tool = "uv" # Options: "uv", "poetry", "pdm", "twine"
trusted_publishing = true
[tool.releasio.hooks]
pre_bump = [] # Commands before version bump
post_bump = [] # Commands after version bump
pre_release = [] # Commands before release
post_release = [] # Commands after release
build = "" # Custom build command (replaces uv build)
# Multi-branch release channels (optional)
[tool.releasio.branches.main]
match = "main"
prerelease = false
[tool.releasio.branches.beta]
match = "beta"
prerelease = true
prerelease_token = "beta"
Advanced Features
Release Asset Uploads
Upload files to GitHub releases automatically (e.g., binaries, documentation archives, wheel files).
[tool.releasio.github]
release_assets = [
"dist/*.whl", # Upload all wheel files
"dist/*.tar.gz", # Upload source distributions
"docs/build/html.zip", # Upload documentation archive
]
Supports glob patterns for flexible file matching. Assets are uploaded after the GitHub release is created. If an upload fails, a warning is shown but the release continues.
Example use cases:
- Upload compiled binaries for different platforms
- Attach documentation archives
- Include license files or additional resources
- Publish distribution files directly to GitHub releases
Package Validation
Validate distribution files before publishing to PyPI using twine check.
[tool.releasio.publish]
validate_before_publish = true # Run twine check before publishing
check_existing_version = true # Check if version already exists on PyPI
What it does:
validate_before_publish: Runstwine checkon built packages to ensure metadata is valid and follows PyPI requirementscheck_existing_version: Queries PyPI API to verify the version hasn't been published yet, preventing accidental republishing
Falls back to basic checks (file existence, extension validation) if twine is not installed.
Future Features (Coming Soon)
The following features are planned and have configuration fields available, but are not yet implemented. See TODO comments in the codebase for implementation details:
First-Time Contributor Recognition
[tool.releasio.changelog]
show_first_time_contributors = true
first_contributor_badge = "🎉 First contribution!"
Highlights first-time contributors in changelogs with a customizable badge. Perfect for celebrating new community members.
Dependency Update Notifications
[tool.releasio.changelog]
include_dependency_updates = true
Automatically detects and includes dependency updates in the changelog by comparing lock files (uv.lock, poetry.lock, pdm.lock) between releases.
Security Advisory Integration
[tool.releasio.security]
enabled = true
auto_create_advisory = true
security_patterns = [
"fix\\(security\\):",
"security:",
"CVE-\\d{4}-\\d+",
]
Automatically creates GitHub Security Advisories when security-related commits are detected in a release.
Requirements
- Python 3.11+
- Git repository with conventional commits
pyproject.tomlwith[project]section
Contributing
Contributions are welcome! Please read our Contributing Guide for details.
git clone https://github.com/mikeleppane/release-py.git
cd release-py
uv sync --all-extras
uv run pytest
License
MIT License - see LICENSE for details.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file releasio-2.0.0.tar.gz.
File metadata
- Download URL: releasio-2.0.0.tar.gz
- Upload date:
- Size: 611.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.21 {"installer":{"name":"uv","version":"0.9.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c494229ee18c36d2a19c43c08543464a24e562abcc9a52adf560a593d482e052
|
|
| MD5 |
7c5160c59e269d4b0612c1b3c59cee2b
|
|
| BLAKE2b-256 |
522f6cdbda5c933c0bba4c0f2c3185e409206d23bb23e773e163a04ff865d09b
|
File details
Details for the file releasio-2.0.0-py3-none-any.whl.
File metadata
- Download URL: releasio-2.0.0-py3-none-any.whl
- Upload date:
- Size: 91.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.21 {"installer":{"name":"uv","version":"0.9.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2fa38f8dbdee5299920f4b832008369229ef4588fd3d1d2d18d7ae48a5ef86b6
|
|
| MD5 |
8d1bb0db0364e61ab996380cd7738c10
|
|
| BLAKE2b-256 |
9864f2b8155d740c1e93e70f6984f1b14505aa314d63b631a2150d14bca4c124
|