uv-upsync - is a tool for automated dependency updates and version bumping in pyproject.toml.
Project description
uv-upsync
Overview
uv-upsync is a uv-native tool for automated dependency updates and version bumping in pyproject.toml.
uv lock --upgrade refreshes your lockfile but leaves the lower bounds in pyproject.toml untouched, so httpx>=0.24.0 stays >=0.24.0 forever. uv-upsync raises those human-authored bounds to the latest published version, re-locks with uv, and rolls back if the resolution fails — all while preserving your formatting, comments, operators, extras and environment markers.
Features
- Built for the uv ecosystem — familiar flags (
--project,--upgrade-package,--all-groups,--offline,--no-cache,--color), uv-style output and auv lockround-trip with automatic rollback on failure - Index-aware — resolves versions from the PEP 691 index configured for your project via
[[tool.uv.index]], so private indexes work out of the box - Correct by construction — specifiers are parsed with
packaging, the canonical PEP 440/508 implementation, not regular expressions - Conservative — only raises lower bounds (
>=,>,~=); pinned (==) requirements are never touched - Range-aware — compound specifiers like
>=1.2,<2.0have their floor raised to the latest version that still satisfies the cap (<2.0) and any exclusions (!=) - Controlled —
--max-bump patch|minor|majorholds back larger jumps (auto-apply minors, review majors), and--prereleaseopts into pre-release versions - Format-preserving — only the version token is rewritten; everything else, including comments and markers, is kept verbatim
- Fast — version lookups are fetched concurrently and cached
- Selective — target specific groups or packages, or exclude packages
- Configurable — persist defaults in a
[tool.uv-upsync]table, overridable per run - Resilient — by default an upgrade that does not resolve is held back individually instead of failing the whole run (
--strictto opt out) - Safe —
--dry-runto preview and--checkfor CI - Scriptable —
--format jsonfor tooling and--format markdownfor pull request bodies - Integrated — ships a pre-commit hook and a GitHub Action
Installation
Run it without installing:
uvx uv-upsync
Or add it to your development dependencies:
uv add --dev uv-upsync
Usage
The examples below assume
uv-upsyncis installed. If it isn't, prefix any command withuvxto run it without installing (e.g.uvx uv-upsync --dry-run).
By default, uv-upsync upgrades every dependency in the pyproject.toml found in the current directory:
$ uv-upsync
Updated click v8.1.8 -> v8.2.1
Updated httpx v0.27.0 -> v0.28.1
Resolved 12 packages in 184ms
Updated 2 dependencies in pyproject.toml
Locked dependencies
Nothing to do is reported the way uv reports it:
$ uv-upsync
Resolved 12 packages in 121ms
Audited 12 dependencies, all up to date
Options
| Option | Description |
|---|---|
--project <DIR> |
Path to the project directory containing the pyproject.toml |
--directory <DIR> |
Change to DIR before running |
-P, --upgrade-package <PKG> |
Allow upgrades for only the given package(s) |
--exclude <PKG> |
Package(s) to exclude from upgrading |
--group <NAME> |
Upgrade dependencies in the given group(s) only |
--all-groups |
Upgrade dependencies in all groups |
--max-bump <level> |
Limit upgrades to at most patch, minor, or major |
--prerelease |
Allow upgrading to pre-release versions |
--index-url <URL> |
Base URL of the PEP 691 package index (defaults to the project's uv index or PyPI) |
--offline |
Disable network access, using only cached data |
-n, --no-cache |
Avoid reading from or writing to the cache |
--dry-run |
Preview the upgrades without writing to pyproject.toml |
--check |
Exit with a non-zero status if any upgrades are available |
--strict |
Roll back every upgrade and fail if the result does not lock |
--no-lock |
Write the upgrades without running uv lock |
--resolve |
When an upgrade does not lock, bump to the latest version that does |
-q, --quiet |
Use quiet output |
-v, --verbose |
Use verbose output (shows skipped dependencies) |
--format <text|json|markdown> |
Output format for the upgrade summary |
--color <auto|always|never> |
Control the use of color in output |
-V, --version |
Show the version and exit |
uv-upsync understands all three dependency tables: project.dependencies, project.optional-dependencies, and dependency-groups (PEP 735).
Resolution
After bumping the specifiers, uv-upsync re-locks with uv. If the combined upgrade does not resolve, the default best-effort mode keeps the largest subset of upgrades that locks and reports which ones were held back (naming the conflicting dependency when it can) — so a single incompatible dependency never costs you the rest:
$ uv-upsync
Resolved 2 packages in 153ms
Updated idna v2.0 -> v3.17
Updated 1 dependency in pyproject.toml
warning: Held back docutils v0.16 -> v0.23 (conflicts with sphinx)
Locked dependencies
With --resolve, an upgrade that does not lock at its latest version is bisected for the latest version that does, instead of being held back entirely:
$ uv-upsync --resolve
Updated idna v2.0 -> v3.17
Updated numpy v1.20 -> v2.0.2 # 2.4.6 needs a newer Python; 2.0.2 is the latest that fits
Updated 2 dependencies in pyproject.toml
Locked dependencies
Use --strict to instead roll back every upgrade and fail (exit code 2) if the result does not lock, or --no-lock to write the upgrades and skip locking entirely.
Configuration
uv-upsync reads defaults from a [tool.uv-upsync] table in your pyproject.toml, so you don't have to repeat them on every run:
[tool.uv-upsync]
exclude = ["click", "ruff"] # never upgrade these packages
group = ["project", "test"] # only these groups (omit for all)
upgrade-package = ["httpx"] # only these packages
all-groups = true # upgrade every group
max-bump = "minor" # hold back major upgrades
prerelease = false # allow pre-release versions
resolve = false # bisect for the latest version that locks
index-url = "https://example.com/simple" # PEP 691 index to query
Settings are resolved with the precedence command line > [tool.uv-upsync] > defaults: passing a flag always overrides the matching setting. The index is resolved as --index-url > [tool.uv-upsync].index-url > [[tool.uv.index]] > PyPI.
Examples
Preview the upgrades
uv-upsync --dry-run
Upgrade a single package
uv-upsync --upgrade-package httpx
Exclude packages
uv-upsync --exclude click --exclude ruff
Upgrade specific groups
# Only the project dependencies
uv-upsync --group project
# A couple of named groups
uv-upsync --group test --group docs
Fail CI when dependencies are stale
uv-upsync --check
--check writes nothing and exits with a non-zero status when upgrades are available, which makes it easy to wire into a scheduled job or pre-merge gate.
Pre-commit
Add uv-upsync to your pre-commit configuration:
repos:
- repo: https://github.com/pivoshenko/uv-upsync
rev: v2.3.2
hooks:
- id: uv-upsync
Two hooks are available:
uv-upsync— upgrade the bounds inpyproject.tomland re-lock (runsuv lock, souvmust be on yourPATH)uv-upsync-check— fail the commit if any dependency can be upgraded, without writing changes
GitHub Action
Keep dependencies fresh on a schedule and open a pull request with the results:
name: upgrade-dependencies
on:
schedule:
- cron: "0 6 * * 1" # every Monday
workflow_dispatch:
jobs:
upsync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- id: upsync
uses: pivoshenko/uv-upsync@v2.3.2
with:
args: --all-groups --format markdown
- uses: peter-evans/create-pull-request@v6
with:
commit-message: "build: upgrade dependencies"
title: "build: upgrade dependencies"
body: ${{ steps.upsync.outputs.summary }}
branch: build/uv-upsync
With --format markdown the action's summary output is a ready-made pull request body (⬆️ click 8.1.8 → 8.2.1 …). To gate pull requests instead, run the action with args: --check and drop the pull request step.
| Input | Description | Default |
|---|---|---|
args |
Additional arguments passed to uv-upsync |
"" |
version |
Version of uv-upsync to run |
latest |
working-directory |
Directory to run in | . |
| Output | Description |
|---|---|
summary |
The upgrade summary printed by uv-upsync |
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 uv_upsync-2.4.1.tar.gz.
File metadata
- Download URL: uv_upsync-2.4.1.tar.gz
- Upload date:
- Size: 65.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","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":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c2fd8bca2bacacea68f0aa4dbcdf59970ee4ae19b565867e7e36440576e497f0
|
|
| MD5 |
56f0ef2e727da119acb927bb237b95e0
|
|
| BLAKE2b-256 |
bc94592d660893359a7f95e6eb7220f2b0d36cf96e64db7753bfdbbfb7e0321f
|
File details
Details for the file uv_upsync-2.4.1-py3-none-any.whl.
File metadata
- Download URL: uv_upsync-2.4.1-py3-none-any.whl
- Upload date:
- Size: 21.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.17 {"installer":{"name":"uv","version":"0.11.17","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":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
41a0002161cc126a6573b450a3cde425c0ac63d62968614d908b9ae19beedc03
|
|
| MD5 |
ab9a0d77bea0996d2bd6701b0f11072f
|
|
| BLAKE2b-256 |
a3fe62bdf28e97836edf2879a685880875bca08a7167e6748308d4e76c97d25c
|