uv-upsync 2.3.1__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.
Files changed (65) hide show
  1. uv_upsync-2.4.0/.github/workflows/ci.yaml +45 -0
  2. uv_upsync-2.4.0/.github/workflows/demo.yaml +38 -0
  3. uv_upsync-2.4.0/.github/workflows/release.yaml +55 -0
  4. uv_upsync-2.3.1/.github/workflows/labels.yaml → uv_upsync-2.4.0/.github/workflows/update-labels.yaml +5 -3
  5. uv_upsync-2.4.0/.pre-commit-hooks.yaml +17 -0
  6. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/CHANGELOG.md +92 -0
  7. uv_upsync-2.4.0/CLAUDE.md +58 -0
  8. uv_upsync-2.4.0/PKG-INFO +341 -0
  9. uv_upsync-2.4.0/README.md +305 -0
  10. uv_upsync-2.4.0/action.yml +46 -0
  11. uv_upsync-2.4.0/assets/demo/pyproject.toml +10 -0
  12. uv_upsync-2.4.0/assets/demo.tape +19 -0
  13. uv_upsync-2.4.0/justfile +19 -0
  14. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/pyproject.toml +13 -41
  15. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/src/uv_upsync/__init__.py +1 -1
  16. uv_upsync-2.4.0/src/uv_upsync/__main__.py +519 -0
  17. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/src/uv_upsync/commands.py +5 -11
  18. uv_upsync-2.4.0/src/uv_upsync/config.py +102 -0
  19. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/src/uv_upsync/exceptions.py +1 -18
  20. uv_upsync-2.4.0/src/uv_upsync/logging.py +73 -0
  21. uv_upsync-2.4.0/src/uv_upsync/parsers.py +380 -0
  22. uv_upsync-2.4.0/src/uv_upsync/pypi.py +133 -0
  23. uv_upsync-2.4.0/src/uv_upsync/report.py +67 -0
  24. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/src/uv_upsync/uv.py +15 -4
  25. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/tests/conftest.py +2 -2
  26. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/tests/test_commands.py +8 -30
  27. uv_upsync-2.4.0/tests/test_config.py +78 -0
  28. uv_upsync-2.4.0/tests/test_exceptions.py +55 -0
  29. uv_upsync-2.4.0/tests/test_logging.py +152 -0
  30. uv_upsync-2.4.0/tests/test_parsers.py +378 -0
  31. uv_upsync-2.4.0/tests/test_pypi.py +149 -0
  32. uv_upsync-2.4.0/tests/test_report.py +44 -0
  33. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/tests/test_uv.py +24 -19
  34. uv_upsync-2.4.0/uv.lock +1167 -0
  35. uv_upsync-2.3.1/.github/FUNDING.yaml +0 -1
  36. uv_upsync-2.3.1/.github/dependabot.yaml +0 -20
  37. uv_upsync-2.3.1/.github/workflows/linters.yaml +0 -42
  38. uv_upsync-2.3.1/.github/workflows/release.yaml +0 -46
  39. uv_upsync-2.3.1/.github/workflows/tests.yaml +0 -54
  40. uv_upsync-2.3.1/PKG-INFO +0 -238
  41. uv_upsync-2.3.1/README.md +0 -203
  42. uv_upsync-2.3.1/src/uv_upsync/__main__.py +0 -155
  43. uv_upsync-2.3.1/src/uv_upsync/logging.py +0 -34
  44. uv_upsync-2.3.1/src/uv_upsync/parsers.py +0 -155
  45. uv_upsync-2.3.1/src/uv_upsync/pypi.py +0 -67
  46. uv_upsync-2.3.1/tests/test_exceptions.py +0 -109
  47. uv_upsync-2.3.1/tests/test_logging.py +0 -120
  48. uv_upsync-2.3.1/tests/test_parsers.py +0 -269
  49. uv_upsync-2.3.1/tests/test_pypi.py +0 -99
  50. uv_upsync-2.3.1/uv.lock +0 -1347
  51. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/.editorconfig +0 -0
  52. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/.github/.codecov.yaml +0 -0
  53. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/.github/ISSUE_TEMPLATE/---bug-report.yaml +0 -0
  54. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/.github/ISSUE_TEMPLATE/---documentation.yaml +0 -0
  55. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/.github/ISSUE_TEMPLATE/---feature-request.yaml +0 -0
  56. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/.github/ISSUE_TEMPLATE/config.yaml +0 -0
  57. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  58. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/.github/labels.yaml +0 -0
  59. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/.gitignore +0 -0
  60. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/CODE_OF_CONDUCT.md +0 -0
  61. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/CONTRIBUTING.md +0 -0
  62. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/LICENSE +0 -0
  63. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/SECURITY.md +0 -0
  64. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/assets/logo.svg +0 -0
  65. {uv_upsync-2.3.1 → uv_upsync-2.4.0}/tests/__init__.py +0 -0
@@ -0,0 +1,45 @@
1
+ # Requires GitHub secrets
2
+ # - CODECOV_TOKEN
3
+
4
+ name: CI
5
+
6
+ on:
7
+ push:
8
+ branches: ["main"]
9
+ pull_request:
10
+ workflow_dispatch:
11
+
12
+ jobs:
13
+ ci:
14
+ name: CI
15
+ runs-on: ubuntu-latest
16
+ steps:
17
+ - name: Checkout repository
18
+ uses: actions/checkout@v6
19
+ with:
20
+ fetch-depth: 0
21
+
22
+ - name: Install just
23
+ uses: extractions/setup-just@v3
24
+
25
+ - name: Install uv
26
+ uses: astral-sh/setup-uv@v6
27
+ with:
28
+ python-version: "3.13"
29
+
30
+ - name: Install dependencies
31
+ run: uv sync --all-groups --all-extras
32
+
33
+ - name: Run linters
34
+ run: just lint
35
+
36
+ - name: Run tests
37
+ run: just test
38
+
39
+ - name: Upload coverage to Codecov
40
+ uses: codecov/codecov-action@v5
41
+ with:
42
+ token: ${{ secrets.CODECOV_TOKEN }}
43
+
44
+ - name: Run audit
45
+ run: uv audit
@@ -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,55 @@
1
+ # Requires GitHub secrets
2
+ # - GH_TOKEN
3
+ # - PYPI_TOKEN
4
+
5
+ name: Release
6
+
7
+ on:
8
+ workflow_dispatch:
9
+ inputs:
10
+ version:
11
+ description: "Version override (leave empty to auto-detect from commits)"
12
+ required: false
13
+
14
+ permissions:
15
+ contents: write
16
+
17
+ jobs:
18
+ release:
19
+ name: Release
20
+ runs-on: ubuntu-latest
21
+ concurrency: release
22
+ permissions:
23
+ id-token: write
24
+ contents: write
25
+ steps:
26
+ - name: Checkout repository
27
+ uses: actions/checkout@v6
28
+ with:
29
+ fetch-depth: 0
30
+
31
+ - name: Install uv
32
+ uses: astral-sh/setup-uv@v6
33
+ with:
34
+ python-version: "3.13"
35
+
36
+ - name: Install dependencies
37
+ run: uv sync --group ci
38
+
39
+ - name: Semantic release (auto)
40
+ if: ${{ !inputs.version }}
41
+ uses: python-semantic-release/python-semantic-release@v10.5.3
42
+ with:
43
+ github_token: ${{ secrets.GH_TOKEN }}
44
+
45
+ - name: Semantic release (version override)
46
+ if: ${{ inputs.version }}
47
+ env:
48
+ GH_TOKEN: ${{ secrets.GH_TOKEN }}
49
+ run: uv run semantic-release version --define "version=${{ inputs.version }}"
50
+
51
+ - name: Build package
52
+ run: uv build
53
+
54
+ - name: Publish package
55
+ run: uv publish --token ${{ secrets.PYPI_TOKEN }}
@@ -1,17 +1,19 @@
1
1
  # Requires GitHub secrets
2
2
  # - GH_TOKEN
3
3
 
4
- name: Update GitHub Labels
4
+ name: Update Labels
5
5
 
6
6
  on:
7
7
  push:
8
8
  branches: ["main"]
9
9
  paths:
10
10
  - ".github/labels.yaml"
11
- - ".github/workflows/labels.yaml"
11
+ - ".github/workflows/update-labels.yaml"
12
+ workflow_dispatch:
12
13
 
13
14
  jobs:
14
- labels:
15
+ update-labels:
16
+ name: Update Labels
15
17
  runs-on: ubuntu-latest
16
18
  steps:
17
19
  - name: Checkout repository
@@ -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,98 @@
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
+
84
+ ## v2.3.2 (2026-03-29)
85
+
86
+ ### Chores
87
+
88
+ - Remove deprecated GitHub workflows and files
89
+ ([`9f2f579`](https://github.com/pivoshenko/uv-upsync/commit/9f2f5795469ac2535bdaf12c938950588184a174))
90
+
91
+ ### Documentation
92
+
93
+ - Remove TOC
94
+ ([`91548dc`](https://github.com/pivoshenko/uv-upsync/commit/91548dc7d012087d2640ea6f7c6f68fcc294a160))
95
+
96
+
5
97
  ## v2.3.1 (2026-03-29)
6
98
 
7
99
  ### Build System
@@ -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).
@@ -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