uv-upsync 2.3.2__tar.gz → 2.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- uv_upsync-2.4.0/.github/workflows/demo.yaml +38 -0
- uv_upsync-2.4.0/.pre-commit-hooks.yaml +17 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/CHANGELOG.md +79 -0
- uv_upsync-2.4.0/CLAUDE.md +58 -0
- uv_upsync-2.4.0/PKG-INFO +341 -0
- uv_upsync-2.4.0/README.md +305 -0
- uv_upsync-2.4.0/action.yml +46 -0
- uv_upsync-2.4.0/assets/demo/pyproject.toml +10 -0
- uv_upsync-2.4.0/assets/demo.tape +19 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/justfile +4 -1
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/pyproject.toml +13 -20
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/src/uv_upsync/__init__.py +1 -1
- uv_upsync-2.4.0/src/uv_upsync/__main__.py +519 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/src/uv_upsync/commands.py +5 -11
- uv_upsync-2.4.0/src/uv_upsync/config.py +102 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/src/uv_upsync/exceptions.py +1 -18
- uv_upsync-2.4.0/src/uv_upsync/logging.py +73 -0
- uv_upsync-2.4.0/src/uv_upsync/parsers.py +380 -0
- uv_upsync-2.4.0/src/uv_upsync/pypi.py +133 -0
- uv_upsync-2.4.0/src/uv_upsync/report.py +67 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/src/uv_upsync/uv.py +15 -4
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/tests/conftest.py +2 -2
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/tests/test_commands.py +8 -30
- uv_upsync-2.4.0/tests/test_config.py +78 -0
- uv_upsync-2.4.0/tests/test_exceptions.py +55 -0
- uv_upsync-2.4.0/tests/test_logging.py +152 -0
- uv_upsync-2.4.0/tests/test_parsers.py +378 -0
- uv_upsync-2.4.0/tests/test_pypi.py +149 -0
- uv_upsync-2.4.0/tests/test_report.py +44 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/tests/test_uv.py +24 -19
- uv_upsync-2.4.0/uv.lock +1167 -0
- uv_upsync-2.3.2/PKG-INFO +0 -189
- uv_upsync-2.3.2/README.md +0 -154
- uv_upsync-2.3.2/src/uv_upsync/__main__.py +0 -155
- uv_upsync-2.3.2/src/uv_upsync/logging.py +0 -34
- uv_upsync-2.3.2/src/uv_upsync/parsers.py +0 -155
- uv_upsync-2.3.2/src/uv_upsync/pypi.py +0 -67
- uv_upsync-2.3.2/tests/test_exceptions.py +0 -109
- uv_upsync-2.3.2/tests/test_logging.py +0 -120
- uv_upsync-2.3.2/tests/test_parsers.py +0 -269
- uv_upsync-2.3.2/tests/test_pypi.py +0 -99
- uv_upsync-2.3.2/uv.lock +0 -1320
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/.editorconfig +0 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/.github/.codecov.yaml +0 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/.github/ISSUE_TEMPLATE/---bug-report.yaml +0 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/.github/ISSUE_TEMPLATE/---documentation.yaml +0 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/.github/ISSUE_TEMPLATE/---feature-request.yaml +0 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/.github/ISSUE_TEMPLATE/config.yaml +0 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/.github/labels.yaml +0 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/.github/workflows/ci.yaml +0 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/.github/workflows/release.yaml +0 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/.github/workflows/update-labels.yaml +0 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/.gitignore +0 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/CODE_OF_CONDUCT.md +0 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/CONTRIBUTING.md +0 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/LICENSE +0 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/SECURITY.md +0 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/assets/logo.svg +0 -0
- {uv_upsync-2.3.2 → uv_upsync-2.4.0}/tests/__init__.py +0 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: Demo
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
push:
|
|
6
|
+
branches: [main]
|
|
7
|
+
paths:
|
|
8
|
+
- assets/demo.tape
|
|
9
|
+
- assets/demo/**
|
|
10
|
+
- src/**
|
|
11
|
+
|
|
12
|
+
permissions:
|
|
13
|
+
contents: write
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
vhs:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v4
|
|
20
|
+
|
|
21
|
+
- name: Install uv
|
|
22
|
+
uses: astral-sh/setup-uv@v5
|
|
23
|
+
|
|
24
|
+
- name: Install uv-upsync
|
|
25
|
+
run: |
|
|
26
|
+
uv tool install --from . uv-upsync
|
|
27
|
+
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
|
28
|
+
|
|
29
|
+
- name: Render demo
|
|
30
|
+
uses: charmbracelet/vhs-action@v2
|
|
31
|
+
with:
|
|
32
|
+
path: assets/demo.tape
|
|
33
|
+
|
|
34
|
+
- name: Commit demo
|
|
35
|
+
uses: stefanzweifel/git-auto-commit-action@v5
|
|
36
|
+
with:
|
|
37
|
+
commit_message: "docs: regenerate demo gif"
|
|
38
|
+
file_pattern: assets/demo.gif
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
- id: uv-upsync
|
|
2
|
+
name: uv-upsync
|
|
3
|
+
description: Upgrade dependency lower bounds in pyproject.toml to the latest versions
|
|
4
|
+
entry: uv-upsync
|
|
5
|
+
language: python
|
|
6
|
+
files: ^pyproject\.toml$
|
|
7
|
+
pass_filenames: false
|
|
8
|
+
require_serial: true
|
|
9
|
+
|
|
10
|
+
- id: uv-upsync-check
|
|
11
|
+
name: uv-upsync (check)
|
|
12
|
+
description: Fail if any dependency in pyproject.toml can be upgraded
|
|
13
|
+
entry: uv-upsync --check
|
|
14
|
+
language: python
|
|
15
|
+
files: ^pyproject\.toml$
|
|
16
|
+
pass_filenames: false
|
|
17
|
+
require_serial: true
|
|
@@ -2,6 +2,85 @@
|
|
|
2
2
|
|
|
3
3
|
<!-- version list -->
|
|
4
4
|
|
|
5
|
+
## v2.4.0 (2026-05-30)
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
|
|
9
|
+
- Replace singleton metaclass with __new__-based singleton
|
|
10
|
+
([`53b7737`](https://github.com/pivoshenko/uv-upsync/commit/53b77372e484a3695f5bbac0cb4abfaf58821a39))
|
|
11
|
+
|
|
12
|
+
- **ci**: Configure ty python-version to match ruff target
|
|
13
|
+
([`1cc34f1`](https://github.com/pivoshenko/uv-upsync/commit/1cc34f10645df6885c484aeb844d9cf25defdace))
|
|
14
|
+
|
|
15
|
+
### Build System
|
|
16
|
+
|
|
17
|
+
- Bump pytest to 9.0.3 to fix GHSA-6w46-j5rx-g56g
|
|
18
|
+
([`4479cf6`](https://github.com/pivoshenko/uv-upsync/commit/4479cf679006e7fd3224221441dc70eb29e41865))
|
|
19
|
+
|
|
20
|
+
- Drop unused pytest-lazy-fixture (incompatible with pytest 9)
|
|
21
|
+
([`fe3176a`](https://github.com/pivoshenko/uv-upsync/commit/fe3176a69c5d86f67324fadd60500e6249dcc3b1))
|
|
22
|
+
|
|
23
|
+
- Update dev dependencies
|
|
24
|
+
([`b0813fa`](https://github.com/pivoshenko/uv-upsync/commit/b0813fa8191f364761833cb5f83c22fad71af146))
|
|
25
|
+
|
|
26
|
+
- Update dev dependencies
|
|
27
|
+
([`5ef0fe5`](https://github.com/pivoshenko/uv-upsync/commit/5ef0fe5b9eb2f7c77a5218c4a979667e7f9a5021))
|
|
28
|
+
|
|
29
|
+
- Update dev dependencies
|
|
30
|
+
([`dd39a19`](https://github.com/pivoshenko/uv-upsync/commit/dd39a191d129b9a712bb674b1b73a5630c997fc7))
|
|
31
|
+
|
|
32
|
+
- Update dev dependencies
|
|
33
|
+
([`dcd9ed7`](https://github.com/pivoshenko/uv-upsync/commit/dcd9ed7c4d66e55b714b00835030d81d9f8554b5))
|
|
34
|
+
|
|
35
|
+
- Update dev dependencies
|
|
36
|
+
([`2b8bb0b`](https://github.com/pivoshenko/uv-upsync/commit/2b8bb0bb732d7d8dd1b478e0db01a6e5055f94bf))
|
|
37
|
+
|
|
38
|
+
### Chores
|
|
39
|
+
|
|
40
|
+
- Align pyproject keywords with github repository topics
|
|
41
|
+
([`2f420db`](https://github.com/pivoshenko/uv-upsync/commit/2f420db49e3c0c4f4c4c3e8604eef2088026885f))
|
|
42
|
+
|
|
43
|
+
- Keep dependencies-upgrade and packaging-upgrade keywords
|
|
44
|
+
([`9140317`](https://github.com/pivoshenko/uv-upsync/commit/9140317c0bdcf042620338153547d925547d14ac))
|
|
45
|
+
|
|
46
|
+
### Documentation
|
|
47
|
+
|
|
48
|
+
- Clearer tagline
|
|
49
|
+
([`d445a73`](https://github.com/pivoshenko/uv-upsync/commit/d445a73bf570082606f3ba43100d1ddae170d775))
|
|
50
|
+
|
|
51
|
+
- Drop manual table of contents in favor of github's built-in
|
|
52
|
+
([`aa5c0a6`](https://github.com/pivoshenko/uv-upsync/commit/aa5c0a60fb2c7d27db40de3238a79a5cc452826e))
|
|
53
|
+
|
|
54
|
+
- Note that examples can be run with uvx without installing
|
|
55
|
+
([`a6c20a8`](https://github.com/pivoshenko/uv-upsync/commit/a6c20a8a7ec33e1872c5d4e801953a5a868bc4a7))
|
|
56
|
+
|
|
57
|
+
- Reorder badges, add table of contents, simplify overview
|
|
58
|
+
([`88b5592`](https://github.com/pivoshenko/uv-upsync/commit/88b55923883e530d11827e32243598bc8a85c100))
|
|
59
|
+
|
|
60
|
+
### Features
|
|
61
|
+
|
|
62
|
+
- Add --format json/markdown output and expose it from the action
|
|
63
|
+
([`618bb44`](https://github.com/pivoshenko/uv-upsync/commit/618bb44faf66554c4dce817f284ba096caf2a7d8))
|
|
64
|
+
|
|
65
|
+
- Add [tool.uv-upsync] config, pre-commit hook, and GitHub Action
|
|
66
|
+
([`9d718a9`](https://github.com/pivoshenko/uv-upsync/commit/9d718a9573cec997bb5826e2ea4c9c4cd747b075))
|
|
67
|
+
|
|
68
|
+
- Align uv-upsync with the uv ecosystem
|
|
69
|
+
([`3557c71`](https://github.com/pivoshenko/uv-upsync/commit/3557c71a9163f35abb57f25d7897ced593bdc888))
|
|
70
|
+
|
|
71
|
+
- Best-effort resolution that isolates un-lockable upgrades
|
|
72
|
+
([`2e5bfc8`](https://github.com/pivoshenko/uv-upsync/commit/2e5bfc8a68b1708c1339dfb08f88ead5f0fada72))
|
|
73
|
+
|
|
74
|
+
- Render failures as uv-style errors instead of tracebacks
|
|
75
|
+
([`7ad19d6`](https://github.com/pivoshenko/uv-upsync/commit/7ad19d6f07762385b5b8f834cd1e7623eb0e42fa))
|
|
76
|
+
|
|
77
|
+
- Resolver-aware --resolve, conflict naming, and a VHS demo
|
|
78
|
+
([`07ad6fd`](https://github.com/pivoshenko/uv-upsync/commit/07ad6fd3600d3836a4b484e7422de221eb2a9d31))
|
|
79
|
+
|
|
80
|
+
- Support compound ranges, --max-bump, and --prerelease
|
|
81
|
+
([`973e8fa`](https://github.com/pivoshenko/uv-upsync/commit/973e8fa351d743a7dd9c1d577fdd5dc835789445))
|
|
82
|
+
|
|
83
|
+
|
|
5
84
|
## v2.3.2 (2026-03-29)
|
|
6
85
|
|
|
7
86
|
### Chores
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
|
4
|
+
|
|
5
|
+
## Project
|
|
6
|
+
|
|
7
|
+
uv-upsync is a CLI tool for automated dependency updates and version bumping in `pyproject.toml` files. Think of it as `uv lock --upgrade` for the human-authored version specifiers: it raises the lower bounds in `pyproject.toml` to the latest published versions, then re-locks with `uv`. It is designed to feel native to the uv ecosystem — uv-style flags, output, and a `uv lock` round-trip with rollback on failure. Built with Click, httpx, tomlkit, and packaging.
|
|
8
|
+
|
|
9
|
+
## Commands
|
|
10
|
+
|
|
11
|
+
All commands use `uv run` (no virtual env activation needed). Task runner: `just`.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
just format # pyupgrade + ruff format
|
|
15
|
+
just lint # ty check + ruff check
|
|
16
|
+
just test # pytest with coverage
|
|
17
|
+
just update # uv lock --upgrade + uv-upsync
|
|
18
|
+
|
|
19
|
+
uv run pytest tests/test_parsers.py # single test file
|
|
20
|
+
uv run pytest tests/test_parsers.py::test_name # single test
|
|
21
|
+
uv run ruff check --no-fix . # lint without auto-fix
|
|
22
|
+
uv run ty check . # type check
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Architecture
|
|
26
|
+
|
|
27
|
+
Entry point: `uv_upsync.__main__:main` (Click CLI).
|
|
28
|
+
|
|
29
|
+
Source in `src/uv_upsync/` with clear separation:
|
|
30
|
+
|
|
31
|
+
- **`__main__.py`** — uv-style Click CLI and orchestration. Resolves the target pyproject, collects upgradable package names across the selected groups, fetches latest versions concurrently, plans updates, then locks. Default is **best-effort**: try the full set, and on a lock failure keep the maximal subset that resolves (greedy), naming the conflicting peer (via `find_conflicts`) for held-back packages. `--resolve` bisects a held-back dependency's versions for the latest that locks; `--strict` rolls back everything and exits 2; `--no-lock` writes without locking. Supports `--check` (CI gate) and `--dry-run`
|
|
32
|
+
- **`parsers.py`** — TOML iteration and version-bumping logic. Parses specifiers with `packaging` (`Requirement`/`Specifier`), not regex. `upgradable_specifier` returns the single raisable lower-bound clause plus a residual `SpecifierSet` (caps/exclusions), so compound ranges like `>=1.2,<2.0` are supported. `select_new_version` picks the highest version that beats the floor, satisfies the residual, and respects `--prerelease`/`--max-bump`. `plan_updates` computes a list of `Update`s (group label, index, new text) without mutating; `apply_updates` returns a deep-copied document with a chosen subset applied. Handles `project.dependencies`, `project.optional-dependencies`, `dependency-groups` (PEP 735). Only raises lower bounds (`>=`, `>`, `~=`); pinned (`==`) requirements are never touched. `_replace_version` rewrites only the version token, preserving formatting, extras and markers. Inline tables (e.g. `include-group`) are ignored
|
|
33
|
+
- **`pypi.py`** — `PyPIClient` querying the PEP 691 simple JSON API (`Accept: application/vnd.pypi.simple.v1+json`). Index-aware: `index_url_from_pyproject` reads `[[tool.uv.index]]` / `tool.uv.index-url`. Fetches concurrently (thread pool), caches in memory, supports `--offline`/`--no-cache`. Picks the highest stable version via `packaging.version`
|
|
34
|
+
- **`report.py`** — Renders the applied/held-back updates as JSON or Markdown for `--format` (text output is handled by the logger). The GitHub Action exposes the Markdown as a `summary` output for PR bodies
|
|
35
|
+
- **`uv.py`** — Subprocess wrapper for `uv lock` (accepts `cwd` and `--offline`)
|
|
36
|
+
- **`config.py`** — Reads `[tool.uv-upsync]` from pyproject (`exclude`, `group`, `upgrade-package`, `all-groups`, `index-url`) with validation. CLI args take precedence over config, which takes precedence over defaults
|
|
37
|
+
- **`commands.py`** — Custom Click command/formatter classes; help mirrors uv's clap layout (bold headers, cyan flags)
|
|
38
|
+
- **`logging.py`** — Singleton logger with uv-style output: `status` (green verb), `update` (`Updated <name> v<old> -> v<new>`), `warning:`/`error:` prefixes to stderr (with dimmed `Caused by:` chains), verbose-only `skip`. Honors `--quiet`/`--verbose`/`--color`
|
|
39
|
+
- **`exceptions.py`** — `BaseError` and `UVCommandError`
|
|
40
|
+
|
|
41
|
+
The entry point (`main`) wraps the Click command so failures render as `error:` lines (never tracebacks). Repo root also ships `.pre-commit-hooks.yaml` and a composite `action.yml`.
|
|
42
|
+
|
|
43
|
+
## Code Conventions
|
|
44
|
+
|
|
45
|
+
- `from __future__ import annotations` required in every file (enforced by ruff isort)
|
|
46
|
+
- Python 3.13 target for ruff; requires-python >= 3.10
|
|
47
|
+
- All ruff lint rules enabled (`select = ["ALL"]`), specific ignores in pyproject.toml
|
|
48
|
+
- Line length: 100
|
|
49
|
+
- Double quotes, single-line imports, imports sorted by length
|
|
50
|
+
- Type annotations throughout; type checker is `ty` (not mypy)
|
|
51
|
+
- Coverage target: 95% (`__main__.py` and `__init__.py` are omitted from coverage)
|
|
52
|
+
- Run the test suite with all dependency groups installed (e.g. `uv run --all-groups pytest`) so `pytest-cov` is available
|
|
53
|
+
|
|
54
|
+
## Commit Convention
|
|
55
|
+
|
|
56
|
+
Conventional Commits: `type(scope): description`
|
|
57
|
+
|
|
58
|
+
Types: `feat`, `fix`, `docs`, `refactor`, `test`, `chore`, `ci`, `build`, `perf`, `style`, `revert`. Semantic-release uses these for automatic versioning (`feat` = major, `fix`/`perf`/`refactor` = minor, `docs`/`style` = patch).
|
uv_upsync-2.4.0/PKG-INFO
ADDED
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: uv-upsync
|
|
3
|
+
Version: 2.4.0
|
|
4
|
+
Summary: uv-upsync - is a tool for automated dependency updates and version bumping in pyproject.toml.
|
|
5
|
+
Project-URL: Homepage, https://github.com/pivoshenko/uv-upsync
|
|
6
|
+
Project-URL: Repository, https://github.com/pivoshenko/uv-upsync
|
|
7
|
+
Author-email: Volodymyr Pivoshenko <contact@pivoshenko.dev>
|
|
8
|
+
Maintainer-email: Volodymyr Pivoshenko <contact@pivoshenko.dev>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: dependencies,dependencies-management,dependencies-update,dependencies-upgrade,hacktoberfest,packaging,packaging-management,packaging-update,packaging-upgrade,pypi,python,uv,uv-plugin,uv-plugins,uv-tool
|
|
12
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
13
|
+
Classifier: Environment :: Other Environment
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Information Technology
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Natural Language :: English
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Programming Language :: Python
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
25
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
26
|
+
Classifier: Topic :: Scientific/Engineering
|
|
27
|
+
Classifier: Topic :: Software Development
|
|
28
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
29
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
30
|
+
Requires-Python: >=3.10
|
|
31
|
+
Requires-Dist: click~=8.1.8
|
|
32
|
+
Requires-Dist: httpx~=0.28.1
|
|
33
|
+
Requires-Dist: packaging~=26.2
|
|
34
|
+
Requires-Dist: tomlkit~=0.13.3
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
<div align="center">
|
|
38
|
+
<img alt="logo" src="https://github.com/pivoshenko/uv-upsync/blob/main/assets/logo.svg?raw=True" height=200>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<br>
|
|
42
|
+
|
|
43
|
+
<p align="center">
|
|
44
|
+
<a href="https://pypi.org/project/uv-upsync">
|
|
45
|
+
<img alt="PyPI" src="https://img.shields.io/pypi/v/uv-upsync?style=flat-square&logo=pypi&logoColor=white&color=4856CD&label=PyPI">
|
|
46
|
+
</a>
|
|
47
|
+
<a href="https://pypi.org/project/uv-upsync">
|
|
48
|
+
<img alt="Python" src="https://img.shields.io/pypi/pyversions/uv-upsync?style=flat-square&logo=python&logoColor=white&color=4856CD&label=Python">
|
|
49
|
+
</a>
|
|
50
|
+
<a href="https://github.com/astral-sh/uv">
|
|
51
|
+
<img alt="uv" src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/uv/main/assets/badge/v0.json&style=flat-square&label=uv">
|
|
52
|
+
</a>
|
|
53
|
+
<a href="https://docs.astral.sh/ruff">
|
|
54
|
+
<img alt="Ruff" src="https://img.shields.io/badge/Style-ruff-black.svg?style=flat-square&logo=ruff&logoColor=white&color=D7FF64">
|
|
55
|
+
</a>
|
|
56
|
+
<a href="https://github.com/pivoshenko/uv-upsync/actions/workflows/ci.yaml">
|
|
57
|
+
<img alt="CI" src="https://img.shields.io/github/actions/workflow/status/pivoshenko/uv-upsync/ci.yaml?label=CI&style=flat-square&logo=githubactions&logoColor=white&color=0A6847">
|
|
58
|
+
</a>
|
|
59
|
+
<a href="https://codecov.io/gh/pivoshenko/uv-upsync">
|
|
60
|
+
<img alt="Coverage" src="https://img.shields.io/codecov/c/gh/pivoshenko/uv-upsync?token=cqRQxVnDR6&style=flat-square&logo=codecov&logoColor=white&color=0A6847&label=Coverage">
|
|
61
|
+
</a>
|
|
62
|
+
<a href="https://opensource.org/licenses/MIT">
|
|
63
|
+
<img alt="License" src="https://img.shields.io/pypi/l/uv-upsync?style=flat-square&logo=opensourceinitiative&logoColor=white&color=0A6847&label=License">
|
|
64
|
+
</a>
|
|
65
|
+
<a href="https://stand-with-ukraine.pp.ua">
|
|
66
|
+
<img alt="StandWithUkraine" src="https://img.shields.io/badge/Support-Ukraine-FFC93C?style=flat-square&labelColor=07689F">
|
|
67
|
+
</a>
|
|
68
|
+
</p>
|
|
69
|
+
|
|
70
|
+
<p align="center">
|
|
71
|
+
<em>Automated dependency upgrades for <code>pyproject.toml</code>, native to uv.</em>
|
|
72
|
+
</p>
|
|
73
|
+
|
|
74
|
+
<div align="center">
|
|
75
|
+
<img alt="demo" src="https://github.com/pivoshenko/uv-upsync/blob/main/assets/demo.gif?raw=true">
|
|
76
|
+
</div>
|
|
77
|
+
|
|
78
|
+
## Overview
|
|
79
|
+
|
|
80
|
+
`uv-upsync` is a [uv]-native tool for automated dependency updates and version
|
|
81
|
+
bumping in `pyproject.toml`.
|
|
82
|
+
|
|
83
|
+
`uv lock --upgrade` refreshes your **lockfile** but leaves the lower bounds in
|
|
84
|
+
`pyproject.toml` untouched, so `httpx>=0.24.0` stays `>=0.24.0` forever.
|
|
85
|
+
`uv-upsync` raises those human-authored bounds to the latest published version,
|
|
86
|
+
re-locks with `uv`, and rolls back if the resolution fails — all while
|
|
87
|
+
preserving your formatting, comments, operators, extras and environment markers.
|
|
88
|
+
|
|
89
|
+
[uv]: https://github.com/astral-sh/uv
|
|
90
|
+
|
|
91
|
+
### Features
|
|
92
|
+
|
|
93
|
+
- **Built for the uv ecosystem** — familiar flags (`--project`, `--upgrade-package`,
|
|
94
|
+
`--all-groups`, `--offline`, `--no-cache`, `--color`), uv-style output and a
|
|
95
|
+
`uv lock` round-trip with automatic rollback on failure
|
|
96
|
+
- **Index-aware** — resolves versions from the [PEP 691] index configured for
|
|
97
|
+
your project via `[[tool.uv.index]]`, so private indexes work out of the box
|
|
98
|
+
- **Correct by construction** — specifiers are parsed with [`packaging`], the
|
|
99
|
+
canonical PEP 440/508 implementation, not regular expressions
|
|
100
|
+
- **Conservative** — only raises lower bounds (`>=`, `>`, `~=`); pinned (`==`)
|
|
101
|
+
requirements are never touched
|
|
102
|
+
- **Range-aware** — compound specifiers like `>=1.2,<2.0` have their floor raised
|
|
103
|
+
to the latest version that still satisfies the cap (`<2.0`) and any exclusions (`!=`)
|
|
104
|
+
- **Controlled** — `--max-bump patch|minor|major` holds back larger jumps (auto-apply
|
|
105
|
+
minors, review majors), and `--prerelease` opts into pre-release versions
|
|
106
|
+
- **Format-preserving** — only the version token is rewritten; everything else,
|
|
107
|
+
including comments and markers, is kept verbatim
|
|
108
|
+
- **Fast** — version lookups are fetched concurrently and cached
|
|
109
|
+
- **Selective** — target specific groups or packages, or exclude packages
|
|
110
|
+
- **Configurable** — persist defaults in a `[tool.uv-upsync]` table, overridable per run
|
|
111
|
+
- **Resilient** — by default an upgrade that does not resolve is held back individually instead of failing the whole run (`--strict` to opt out)
|
|
112
|
+
- **Safe** — `--dry-run` to preview and `--check` for CI
|
|
113
|
+
- **Scriptable** — `--format json` for tooling and `--format markdown` for pull request bodies
|
|
114
|
+
- **Integrated** — ships a [pre-commit] hook and a GitHub Action
|
|
115
|
+
|
|
116
|
+
## Installation
|
|
117
|
+
|
|
118
|
+
Run it without installing:
|
|
119
|
+
|
|
120
|
+
```shell
|
|
121
|
+
uvx uv-upsync
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Or add it to your development dependencies:
|
|
125
|
+
|
|
126
|
+
```shell
|
|
127
|
+
uv add --dev uv-upsync
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Usage
|
|
131
|
+
|
|
132
|
+
> The examples below assume `uv-upsync` is installed. If it isn't, prefix any
|
|
133
|
+
> command with `uvx` to run it without installing (e.g. `uvx uv-upsync --dry-run`).
|
|
134
|
+
|
|
135
|
+
By default, `uv-upsync` upgrades every dependency in the `pyproject.toml` found
|
|
136
|
+
in the current directory:
|
|
137
|
+
|
|
138
|
+
```shell
|
|
139
|
+
$ uv-upsync
|
|
140
|
+
Updated click v8.1.8 -> v8.2.1
|
|
141
|
+
Updated httpx v0.27.0 -> v0.28.1
|
|
142
|
+
Resolved 12 packages in 184ms
|
|
143
|
+
Updated 2 dependencies in pyproject.toml
|
|
144
|
+
Locked dependencies
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Nothing to do is reported the way uv reports it:
|
|
148
|
+
|
|
149
|
+
```shell
|
|
150
|
+
$ uv-upsync
|
|
151
|
+
Resolved 12 packages in 121ms
|
|
152
|
+
Audited 12 dependencies, all up to date
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Options
|
|
156
|
+
|
|
157
|
+
| Option | Description |
|
|
158
|
+
| --- | --- |
|
|
159
|
+
| `--project <DIR>` | Path to the project directory containing the `pyproject.toml` |
|
|
160
|
+
| `--directory <DIR>` | Change to `DIR` before running |
|
|
161
|
+
| `-P`, `--upgrade-package <PKG>` | Allow upgrades for only the given package(s) |
|
|
162
|
+
| `--exclude <PKG>` | Package(s) to exclude from upgrading |
|
|
163
|
+
| `--group <NAME>` | Upgrade dependencies in the given group(s) only |
|
|
164
|
+
| `--all-groups` | Upgrade dependencies in all groups |
|
|
165
|
+
| `--max-bump <level>` | Limit upgrades to at most `patch`, `minor`, or `major` |
|
|
166
|
+
| `--prerelease` | Allow upgrading to pre-release versions |
|
|
167
|
+
| `--index-url <URL>` | Base URL of the [PEP 691] package index (defaults to the project's uv index or PyPI) |
|
|
168
|
+
| `--offline` | Disable network access, using only cached data |
|
|
169
|
+
| `-n`, `--no-cache` | Avoid reading from or writing to the cache |
|
|
170
|
+
| `--dry-run` | Preview the upgrades without writing to `pyproject.toml` |
|
|
171
|
+
| `--check` | Exit with a non-zero status if any upgrades are available |
|
|
172
|
+
| `--strict` | Roll back every upgrade and fail if the result does not lock |
|
|
173
|
+
| `--no-lock` | Write the upgrades without running `uv lock` |
|
|
174
|
+
| `--resolve` | When an upgrade does not lock, bump to the latest version that does |
|
|
175
|
+
| `-q`, `--quiet` | Use quiet output |
|
|
176
|
+
| `-v`, `--verbose` | Use verbose output (shows skipped dependencies) |
|
|
177
|
+
| `--format <text\|json\|markdown>` | Output format for the upgrade summary |
|
|
178
|
+
| `--color <auto\|always\|never>` | Control the use of color in output |
|
|
179
|
+
| `-V`, `--version` | Show the version and exit |
|
|
180
|
+
|
|
181
|
+
`uv-upsync` understands all three dependency tables: `project.dependencies`,
|
|
182
|
+
`project.optional-dependencies`, and `dependency-groups` ([PEP 735]).
|
|
183
|
+
|
|
184
|
+
### Resolution
|
|
185
|
+
|
|
186
|
+
After bumping the specifiers, `uv-upsync` re-locks with `uv`. If the combined
|
|
187
|
+
upgrade does not resolve, the default **best-effort** mode keeps the largest
|
|
188
|
+
subset of upgrades that locks and reports which ones were held back (naming the
|
|
189
|
+
conflicting dependency when it can) — so a single incompatible dependency never
|
|
190
|
+
costs you the rest:
|
|
191
|
+
|
|
192
|
+
```console
|
|
193
|
+
$ uv-upsync
|
|
194
|
+
Resolved 2 packages in 153ms
|
|
195
|
+
Updated idna v2.0 -> v3.17
|
|
196
|
+
Updated 1 dependency in pyproject.toml
|
|
197
|
+
warning: Held back docutils v0.16 -> v0.23 (conflicts with sphinx)
|
|
198
|
+
Locked dependencies
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
With `--resolve`, an upgrade that does not lock at its latest version is bisected
|
|
202
|
+
for the **latest version that does**, instead of being held back entirely:
|
|
203
|
+
|
|
204
|
+
```console
|
|
205
|
+
$ uv-upsync --resolve
|
|
206
|
+
Updated idna v2.0 -> v3.17
|
|
207
|
+
Updated numpy v1.20 -> v2.0.2 # 2.4.6 needs a newer Python; 2.0.2 is the latest that fits
|
|
208
|
+
Updated 2 dependencies in pyproject.toml
|
|
209
|
+
Locked dependencies
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Use `--strict` to instead roll back every upgrade and fail (exit code `2`) if the
|
|
213
|
+
result does not lock, or `--no-lock` to write the upgrades and skip locking
|
|
214
|
+
entirely.
|
|
215
|
+
|
|
216
|
+
## Configuration
|
|
217
|
+
|
|
218
|
+
`uv-upsync` reads defaults from a `[tool.uv-upsync]` table in your
|
|
219
|
+
`pyproject.toml`, so you don't have to repeat them on every run:
|
|
220
|
+
|
|
221
|
+
```toml
|
|
222
|
+
[tool.uv-upsync]
|
|
223
|
+
exclude = ["click", "ruff"] # never upgrade these packages
|
|
224
|
+
group = ["project", "test"] # only these groups (omit for all)
|
|
225
|
+
upgrade-package = ["httpx"] # only these packages
|
|
226
|
+
all-groups = true # upgrade every group
|
|
227
|
+
max-bump = "minor" # hold back major upgrades
|
|
228
|
+
prerelease = false # allow pre-release versions
|
|
229
|
+
resolve = false # bisect for the latest version that locks
|
|
230
|
+
index-url = "https://example.com/simple" # PEP 691 index to query
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
Settings are resolved with the precedence **command line > `[tool.uv-upsync]` >
|
|
234
|
+
defaults**: passing a flag always overrides the matching setting. The index is
|
|
235
|
+
resolved as `--index-url` > `[tool.uv-upsync].index-url` > `[[tool.uv.index]]` >
|
|
236
|
+
PyPI.
|
|
237
|
+
|
|
238
|
+
## Examples
|
|
239
|
+
|
|
240
|
+
### Preview the upgrades
|
|
241
|
+
|
|
242
|
+
```shell
|
|
243
|
+
uv-upsync --dry-run
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Upgrade a single package
|
|
247
|
+
|
|
248
|
+
```shell
|
|
249
|
+
uv-upsync --upgrade-package httpx
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Exclude packages
|
|
253
|
+
|
|
254
|
+
```shell
|
|
255
|
+
uv-upsync --exclude click --exclude ruff
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### Upgrade specific groups
|
|
259
|
+
|
|
260
|
+
```shell
|
|
261
|
+
# Only the project dependencies
|
|
262
|
+
uv-upsync --group project
|
|
263
|
+
|
|
264
|
+
# A couple of named groups
|
|
265
|
+
uv-upsync --group test --group docs
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Fail CI when dependencies are stale
|
|
269
|
+
|
|
270
|
+
```shell
|
|
271
|
+
uv-upsync --check
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
`--check` writes nothing and exits with a non-zero status when upgrades are
|
|
275
|
+
available, which makes it easy to wire into a scheduled job or pre-merge gate.
|
|
276
|
+
|
|
277
|
+
## Pre-commit
|
|
278
|
+
|
|
279
|
+
Add `uv-upsync` to your [pre-commit] configuration:
|
|
280
|
+
|
|
281
|
+
```yaml
|
|
282
|
+
repos:
|
|
283
|
+
- repo: https://github.com/pivoshenko/uv-upsync
|
|
284
|
+
rev: v2.3.2
|
|
285
|
+
hooks:
|
|
286
|
+
- id: uv-upsync
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Two hooks are available:
|
|
290
|
+
|
|
291
|
+
- **`uv-upsync`** — upgrade the bounds in `pyproject.toml` and re-lock (runs
|
|
292
|
+
`uv lock`, so `uv` must be on your `PATH`)
|
|
293
|
+
- **`uv-upsync-check`** — fail the commit if any dependency can be upgraded,
|
|
294
|
+
without writing changes
|
|
295
|
+
|
|
296
|
+
## GitHub Action
|
|
297
|
+
|
|
298
|
+
Keep dependencies fresh on a schedule and open a pull request with the results:
|
|
299
|
+
|
|
300
|
+
```yaml
|
|
301
|
+
name: upgrade-dependencies
|
|
302
|
+
on:
|
|
303
|
+
schedule:
|
|
304
|
+
- cron: "0 6 * * 1" # every Monday
|
|
305
|
+
workflow_dispatch:
|
|
306
|
+
|
|
307
|
+
jobs:
|
|
308
|
+
upsync:
|
|
309
|
+
runs-on: ubuntu-latest
|
|
310
|
+
steps:
|
|
311
|
+
- uses: actions/checkout@v4
|
|
312
|
+
- id: upsync
|
|
313
|
+
uses: pivoshenko/uv-upsync@v2.3.2
|
|
314
|
+
with:
|
|
315
|
+
args: --all-groups --format markdown
|
|
316
|
+
- uses: peter-evans/create-pull-request@v6
|
|
317
|
+
with:
|
|
318
|
+
commit-message: "build: upgrade dependencies"
|
|
319
|
+
title: "build: upgrade dependencies"
|
|
320
|
+
body: ${{ steps.upsync.outputs.summary }}
|
|
321
|
+
branch: build/uv-upsync
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
With `--format markdown` the action's `summary` output is a ready-made pull
|
|
325
|
+
request body (`⬆️ click 8.1.8 → 8.2.1 …`). To gate pull requests instead, run
|
|
326
|
+
the action with `args: --check` and drop the pull request step.
|
|
327
|
+
|
|
328
|
+
| Input | Description | Default |
|
|
329
|
+
| --- | --- | --- |
|
|
330
|
+
| `args` | Additional arguments passed to `uv-upsync` | `""` |
|
|
331
|
+
| `version` | Version of `uv-upsync` to run | latest |
|
|
332
|
+
| `working-directory` | Directory to run in | `.` |
|
|
333
|
+
|
|
334
|
+
| Output | Description |
|
|
335
|
+
| --- | --- |
|
|
336
|
+
| `summary` | The upgrade summary printed by `uv-upsync` |
|
|
337
|
+
|
|
338
|
+
[PEP 691]: https://peps.python.org/pep-0691
|
|
339
|
+
[PEP 735]: https://peps.python.org/pep-0735
|
|
340
|
+
[`packaging`]: https://packaging.pypa.io
|
|
341
|
+
[pre-commit]: https://pre-commit.com
|