π Automatic version bumping, changelog generation, and releases for UV Python projects using conventional commits.
graph LR
A[1. Push<br/>feat: new feature<br/>fix: bug fix] --> B[2. Analyze<br/>Parse commits<br/>Determine bump]
B --> C[3. Version<br/>Update pyproject.toml<br/>0.1.0 β 0.2.0]
C --> D[4. Changelog<br/>Generate notes<br/>CHANGELOG.md]
D --> E[5. Tag<br/>Create git tag<br/>v0.2.0]
E --> F[6. Release<br/>GitHub Release<br/>+ Artifacts]
F --> G[7. Build<br/>uv build<br/>.whl + .tar.gz]
G -.-> H[8. Publish<br/>PyPI optional<br/>uv publish]
style A fill:#e94560,stroke:#e94560,color:#fff
style B fill:#0ea5e9,stroke:#0ea5e9,color:#fff
style C fill:#22c55e,stroke:#22c55e,color:#fff
style D fill:#f59e0b,stroke:#f59e0b,color:#fff
style E fill:#a855f7,stroke:#a855f7,color:#fff
style F fill:#ec4899,stroke:#ec4899,color:#fff
style G fill:#14b8a6,stroke:#14b8a6,color:#fff
style H fill:#6366f1,stroke:#6366f1,stroke-dasharray: 5 5,color:#fff
- π Automatic Semantic Versioning - Version bumps based on conventional commits
- π Auto-generated Changelog - Beautiful CHANGELOG.md from commit history
- π·οΈ Git Tags - Automatic tag creation (e.g.,
v1.2.3) - π¦ GitHub Releases - Full releases with notes and artifacts
- π UV Native - Built specifically for UV Python projects
- π€ PyPI Publishing - Optional automatic PyPI deployment
- β‘ Zero Config - Sensible defaults, works out of the box
- π§ Highly Configurable - Customize every aspect of the release process
- Quick Start
- Usage
- Inputs
- Outputs
- Configuration
- Conventional Commits
- Examples
- Troubleshooting
- Contributing
- License
Create .github/workflows/release.yml:
name: Release
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: UV Semantic Release
uses: anomius/uv-semantic-release@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}[tool.semantic_release]
version_toml = ["pyproject.toml:project.version"]
branch = "main"
commit_parser = "conventional_commits"
tag_format = "v{version}"
[tool.semantic_release.changelog]
changelog_file = "CHANGELOG.md"git commit -m "feat: add user authentication"
git commit -m "fix: resolve login timeout issue"
git push origin mainThat's it! π The action will automatically create releases when you push to main.
- uses: anomius/uv-semantic-release@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}- uses: anomius/uv-semantic-release@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_to_pypi: 'true'
pypi_token: ${{ secrets.PYPI_TOKEN }}- uses: anomius/uv-semantic-release@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
python_version: '3.12'
uv_version: 'latest'
semantic_release_version: '9.14.0'
changelog: 'true'
push: 'true'
vcs_release: 'true'
build: 'true'
upload_to_release: 'true'
publish_to_pypi: 'false'
working_directory: '.'
git_committer_name: 'github-actions[bot]'
git_committer_email: 'github-actions[bot]@users.noreply.github.com'| Input | Description | Required | Default |
|---|---|---|---|
github_token |
GitHub token for releases and pushing | Yes | ${{ github.token }} |
pypi_token |
PyPI token for publishing | No | '' |
python_version |
Python version to use | No | '3.12' |
uv_version |
UV version to use | No | 'latest' |
semantic_release_version |
python-semantic-release version | No | '9.14.0' |
root_options |
Additional semantic-release options | No | '' |
changelog |
Generate changelog | No | 'true' |
push |
Push changes to remote | No | 'true' |
vcs_release |
Create GitHub release | No | 'true' |
build |
Build the package | No | 'true' |
publish_to_pypi |
Publish to PyPI | No | 'false' |
upload_to_release |
Upload artifacts to release | No | 'true' |
working_directory |
Working directory | No | '.' |
git_committer_name |
Git committer name | No | 'github-actions[bot]' |
git_committer_email |
Git committer email | No | '...[bot]@users.noreply.github.com' |
| Output | Description |
|---|---|
released |
Whether a new release was created ('true' or 'false') |
version |
The new version number (e.g., '1.2.3') |
previous_version |
The previous version number |
tag |
The git tag for the release (e.g., 'v1.2.3') |
- name: UV Semantic Release
id: release
uses: anomius/uv-semantic-release@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Post-release actions
if: steps.release.outputs.released == 'true'
run: |
echo "Released version ${{ steps.release.outputs.version }}"
echo "Tag: ${{ steps.release.outputs.tag }}"Add this to your pyproject.toml:
[project]
name = "your-package"
version = "0.1.0" # This will be auto-updated
[tool.semantic_release]
# Version location(s)
version_toml = ["pyproject.toml:project.version"]
# Branch configuration
branch = "main"
# Commit parser
commit_parser = "conventional_commits"
# Tag format
tag_format = "v{version}"
# Build command
build_command = "uv build"
# Release settings
upload_to_vcs_release = true
upload_to_pypi = false
# Commit message for version bumps
commit_message = "chore(release): bump version to {version} [skip ci]"
# Allow 0.x versions
allow_zero_version = true
major_on_zero = false
[tool.semantic_release.changelog]
changelog_file = "CHANGELOG.md"
exclude_commit_patterns = [
'''chore\(deps.*\):.*''',
'''chore\(release\):.*''',
'''Merge.*''',
]
[tool.semantic_release.branches.main]
match = "^(main|master)$"
prerelease = false
[tool.semantic_release.branches.develop]
match = "^(dev|develop)$"
prerelease = true
prerelease_token = "dev"
[tool.semantic_release.commit_parser_options]
allowed_tags = ["feat", "fix", "docs", "style", "refactor", "perf", "test", "build", "ci", "chore"]
minor_tags = ["feat"]
patch_tags = ["fix", "perf"]If you have version in multiple places:
[tool.semantic_release]
version_toml = ["pyproject.toml:project.version"]
version_variables = ["src/mypackage/__init__.py:__version__"]This action uses Conventional Commits to determine version bumps.
<type>(<scope>): <description>
[optional body]
[optional footer(s)]
| Commit Type | Example | Version Bump |
|---|---|---|
fix: |
fix: resolve null pointer |
PATCH (0.0.X) |
perf: |
perf: optimize queries |
PATCH (0.0.X) |
feat: |
feat: add dark mode |
MINOR (0.X.0) |
feat!: |
feat!: redesign API |
MAJOR (X.0.0) |
BREAKING CHANGE: |
See below | MAJOR (X.0.0) |
# Option 1: ! after type
git commit -m "feat!: remove deprecated endpoints"
# Option 2: Footer
git commit -m "refactor: update database schema
BREAKING CHANGE: requires database migration"These types don't trigger a release:
docs:- Documentation changesstyle:- Code style changesrefactor:- Code refactoringtest:- Test changesbuild:- Build system changesci:- CI configurationchore:- Maintenance tasks
name: Release
on:
push:
branches: [main]
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: anomius/uv-semantic-release@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}name: Release and Publish
on:
push:
branches: [main]
permissions:
contents: write
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Release
id: release
uses: anomius/uv-semantic-release@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_to_pypi: 'true'
pypi_token: ${{ secrets.PYPI_TOKEN }}
- name: Announce Release
if: steps.release.outputs.released == 'true'
run: |
echo "π Released v${{ steps.release.outputs.version }}!"name: Release Package A
on:
push:
branches: [main]
paths:
- 'packages/package-a/**'
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: anomius/uv-semantic-release@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
working_directory: 'packages/package-a'name: Manual Release
on:
workflow_dispatch:
inputs:
dry_run:
description: 'Dry run (no actual release)'
required: false
default: 'false'
type: boolean
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: anomius/uv-semantic-release@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
root_options: ${{ inputs.dry_run == 'true' && '--noop' || '' }}name: Pre-release
on:
push:
branches:
- 'develop'
- 'beta'
jobs:
prerelease:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: anomius/uv-semantic-release@v1
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
root_options: '--prerelease'Problem: Commits are pushed but no release is created.
Solutions:
- Ensure commits follow conventional commit format
- Check that commits include
feat:,fix:, orperf:types - Verify
fetch-depth: 0in checkout step - Check workflow has
contents: writepermission
Problem: Push or release creation fails with permission error.
Solutions:
-
For protected branches, use a Personal Access Token (PAT):
- uses: actions/checkout@v4 with: token: ${{ secrets.PAT_TOKEN }} fetch-depth: 0 - uses: anomius/uv-semantic-release@v1 with: github_token: ${{ secrets.PAT_TOKEN }}
-
Ensure the token has
reposcope
Problem: Release created but pyproject.toml version unchanged.
Solutions:
- Check
version_tomlpath is correct:- PEP 621:
["pyproject.toml:project.version"] - Poetry:
["pyproject.toml:tool.poetry.version"]
- PEP 621:
- Ensure pyproject.toml has a
versionfield
Problem: CHANGELOG.md not created or updated.
Solutions:
- Ensure
changelog: 'true'in action inputs - Check
[tool.semantic_release.changelog]config exists - Verify file permissions in repository
Problem: Release succeeds but PyPI upload fails.
Solutions:
- Verify
PYPI_TOKENsecret is set correctly - Check token has upload permissions
- Ensure package name is available on PyPI
- Verify
pyproject.tomlhas valid metadata
Contributions are welcome! Please see CONTRIBUTING.md for guidelines.
# Clone the repository
git clone https://github.com/anomius/uv-semantic-release.git
cd uv-semantic-release
# Install UV
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install dependencies
uv sync --devThis project is licensed under the MIT License - see the LICENSE file for details.
- python-semantic-release - The core release automation tool
- UV - The fast Python package manager
- Conventional Commits - The commit specification
Made with β€οΈ for the Python community