Skip to main content

upd logo

upd

Update dependencies across a polyglot repository—locally, safely, and in one command.

Python · Node.js · Rust · Go · Ruby · .NET · Docker · Terraform · GitHub Actions · pre-commit · Mise

crates.io PyPI CI License: MIT

upd gives you one reviewable update plan across mixed stacks. It preserves hand-written constraints, comments, and formatting; previews every proposed change before it writes; and runs without a hosted service or repository onboarding.

Try it now

From any directory inside a Git repository:

uvx upd

That first run is a dry run: it reports what would change and leaves every file untouched. No repository onboarding or hosted account is required. Review the plan, then apply it with uvx upd --apply.

upd previewing six dependency updates across four files without changing them

One preview across four file types. Nothing changes until you pass --apply.

Install another way · See every supported file · Compare dependency tools

Why upd

  • One command for a mixed stack. Check application dependencies, tool versions, container images, GitHub Actions, pre-commit hooks, and Terraform modules in the same run instead of assembling a different updater for each file type.
  • Safe on the first run. Dry-run is the default, constraints and formatting are preserved, and major updates are called out before you decide what to apply.
  • Local first, automation ready. Start with an interactive terminal review, then use the same CLI, configuration, stable exit codes, JSON/SARIF output, and rolling PR or MR workflows in CI.

upd is a local checker and editor, not a package manager or a hosted update bot. It can delegate lockfile refreshes to the package managers already in your project and can run inside your own GitHub or GitLab automation. See the decision guide and dated benchmarks for an exact comparison with adjacent tools.

Where upd fits

Choose by the job rather than treating every dependency tool as interchangeable:

If you need to… Start with…
Preview and edit versions across a mixed-language repository from one local command upd
Resolve, lock, install, and manage a Python environment uv or PDM
Continuously create update branches and PRs across the broadest manager catalog Renovate or Dependabot
Enforce immutable GitHub Actions references as a dedicated policy pinact or ratchet

Dated performance evidence

On the committed 18-reference fixture, the verified 2026-08-28 run recorded:

Workload upd mean Same-workload cohort
Check 12 Python constraints 134.7 ms uppd: 269.7 ms
Update 12 Python constraints 138.7 ms uppd: 250.8 ms
Check 6 GitHub Actions 758.7 ms taze: 1,019.0 ms; ratchet: 1,029.1 ms
Update 6 GitHub Actions 746.3 ms taze: 566.3 ms; ratchet: 1,066.8 ms

Lower is better. These are five-run live-network observations, not universal speed claims. Every timed update was first verified to change all intended references and leave a parseable result. See the full matrix, methodology, variance, commands, and raw JSON.

Features

  • Multi-ecosystem: Python, Node.js, Rust, Go, Ruby, .NET, Docker, Terraform, GitHub Actions, pre-commit, Mise/asdf
  • Dry-run by default: nothing is written without --apply
  • Fast: parallel registry requests, with a 24-hour version cache
  • Constraint-aware: respects >=2.0,<3 (Python), ~> 7.1 (Ruby), and ^2.0.0 / ~2.0.0 (npm, Cargo)
  • Format-preserving: keeps formatting, comments, and structure
  • Update filters: --only-bump, --max-bump, --package (exact names or quoted globs such as 'shiny*'), --lang, or approve one by one with -i
  • Major warnings: breaking changes are flagged with (MAJOR)
  • Pre-release aware: updates pre-releases to newer pre-releases
  • Cooldown: hold back releases younger than N days, against supply-chain attacks
  • Security auditing: OSV vulnerability scanning with auto-fix and SARIF output
  • Check mode: exit 1 if updates are available (for CI and pre-commit)
  • Gitignore-aware: honors .gitignore and prunes hidden directories, without missing the dotfiles it updates
  • Private registries: authentication for PyPI, npm, Cargo, Go, and GitHub
  • Config file: include or exclude paths, ignore packages, and pin versions via .updrc.toml

Installation

From crates.io

cargo install upd

# or with cargo-binstall (faster, pre-built binary)
cargo binstall upd

From PyPI

pip install upd
# or with uv
uv pip install upd

If you installed an earlier release under the old distribution name, migrate once with pip uninstall upd-cli && pip install upd. The upd-cli command remains available as a compatibility alias.

From source

git clone https://github.com/rvben/upd
cd upd
cargo install --path .

Usage

# Preview changes without modifying files (default when no --apply)
upd

# Apply updates to files
upd --apply

# Limit to specific files or directories
upd --apply requirements.txt pyproject.toml

# Approve updates one by one
upd -i

# Only the packages you name
upd -p requests,flask

# Select a package family (quote globs so your shell passes them to upd)
upd --package 'shiny*'

# Cap the bump level (allow patch + minor, skip major). Updates above the
# ceiling are reported as held back, never as up to date, and do not
# change the exit code.
upd --max-bump minor

# Restrict to exactly one level (repeatable, comma-separated)
upd --only-bump major

# One ecosystem at a time: python, node, rust, go, ruby, dot-net,
# docker, terraform, actions, pre-commit, mise, annotated
upd --lang python

# Exit 1 if anything is outdated (for CI and pre-commit)
upd --check

# Regenerate lockfiles after writing
upd --apply --lock

# Print the effective configuration and exit
upd --show-config

upd --help lists every flag; Stability documents the ones that are contractual, and upd schema emits the whole interface as JSON.

--package accepts exact names and case-sensitive globs using *, ?, and character classes such as [ab]. Wildcards can cross namespace separators, so patterns such as '@scope/*' and 'github.com/org/*' work as expected. Quote globs on the command line to prevent shell expansion. A glob that matches no package produces a warning without changing the exit code; use repeated or comma-separated values instead of brace alternation.

Dry-run by default: upd without --apply only previews changes. Pass --apply to write updates. --check, --dry-run, and --interactive do not require --apply.

VCS-root scoping: When no path argument is given, upd scans from the nearest .git ancestor directory rather than the current working directory. This prevents accidental rewrites when CWD is a subdirectory inside a repository.

Commands

upd --version      # Print version
upd self-update    # Check for upd updates
upd clean-cache    # Clear the version cache
upd align          # Align versions across files (--check exits 1 on misalignment)
upd audit          # Scan for known vulnerabilities (exit 6 if found)
upd schema         # Machine-readable interface description

For repositories replacing dependency bots, upd also provides separate reusable GitHub workflows for policy-constrained freshness updates and validated security-remediation pull requests. See GitHub dependency pull requests.

Example Output

.pre-commit-config.yaml:37: Would update pre-commit/pre-commit-hooks v4.6.0 → v6.0.0 (MAJOR)
.github/workflows/ci.yml:16: Would update actions/checkout v4 → v6 (MAJOR)
.github/workflows/ci.yml:18: Would update jdx/mise-action v2 → v4 (MAJOR)
.mise.toml:8: Would update rust 1.91.1 → 1.94.0
Cargo.toml:33: Would update clap 4.5.53 → 4.6.0
Cargo.toml:36: Would update tokio 1.48.0 → 1.50.0

Would update 6 package(s) (2 major, 3 minor, 1 patch) in 4 file(s), 8 up to date

Output includes clickable file:line: locations (recognized by VS Code, iTerm2, and modern terminals).

Version Constraints

upd respects version constraints in your dependency files:

Constraint Behavior
>=2.0,<3 Updates within 2.x range only
^2.0.0 Updates within 2.x range (npm/Cargo); never crosses the major bound
~2.0.0 Updates within 2.0.x range (npm); ~2.0.0 (Cargo) stays within 2.0.x
~> 7.1 Updates within 7.x range (Ruby pessimistic)
>=2.0 Updates to any version >= 2.0
==2.0.0 Updates the exact pin to the latest version (e.g. ==2.0.0==3.1.5). To freeze a package, use [pin] or ignore in .updrc.toml.

An update moves the lower bound and leaves every other clause where the author wrote it, so >=1.0, <2.0 becomes >=1.5.0, <2.0. A constraint is an unordered set of clauses, so the lower bound is found wherever it sits (<2.0, >=1.0 answers alike), and an upper bound is honored when picking the new version: the release chosen is the newest one the constraint already admits.

npm ranges keep the shape they were written in. A comparator range (">=1.0.0 <2.0.0") and a hyphen range ("4.17.0 - 4.18.0") each keep their ceiling. A wildcard or partial range takes its ceiling from its own floor, like a caret, so it follows the newest release and the whole shape moves with it: "4.3.x" becomes "4.4.x" and "^1.2" becomes "^3.1", never a fully written version. npm lets a comparator stand apart from the version it applies to, and that spacing is part of the shape: ">= 1.2.7 < 1.3.0" is read as the range it is and comes back spaced the same way. npm's tilde has two spellings and "~>1.2.3" means what "~1.2.3" does, ceiling included; each comes back spelled the way it was written.

An npm spec that names no published version is left alone and reported nowhere: "*", a dist-tag ("latest", "next", "beta"), and the workspace:, file:, link:, npm:, git+ssh: and github:owner/repo forms all resolve somewhere other than a release on the registry, so there is no version to compare and nothing an update could move.

Specifier shape changes are opt-in. A [normalize.pyproject] table in .updrc.toml can independently make project dependencies, optional dependencies, or standardized dependency groups use ==, >=, or <=; projects without that table retain the shape-preserving behavior above. See the configuration guide.

Bounds that are not floors

Only an inclusive lower bound names the version a project is on, so only that bound is raised. >1.2.3 names the one version its author refuses, <3 and <=3 are ceilings, != 1.5 is an exclusion, and an OR range ("^1 || ^2") has no single branch to edit. None of them is a floor, so none of them is moved. They are checked against the registry and reported anyway:

Outcome Reported as
The constraint admits the newest release Up to date
The newest release has outgrown it A warning naming the release and the constraint
The spec cannot be read at all An error, exit 2

The last row is the point of the other two: a dependency nothing looked at must not be counted as up to date, and a constraint that has quietly frozen a dependency should say so rather than pass under a green tick.

Annotated Version Pins

Files without a dependency-manifest format can carry a trailing annotation:

shinyhub_version: "0.11.16"  # upd: pypi shinyhub

Directory walks scan annotations in otherwise-unrecognized Makefile, makefile, GNUmakefile, justfile, Justfile, *.mk, *.sh, *.bash, *.yml, and *.yaml files. Any file passed explicitly is scanned as annotated. To add other files to normal repository discovery, use repository-relative globs in .updrc.toml:

include = ["deploy/*.env", "config/version.conf"]
exclude = ["**/archive/**"] # exclude wins over include

An include never changes a recognized manifest's parser: for example, a matching main.tf remains Terraform. Use --verbose to diagnose an upd: marker in an otherwise undiscovered UTF-8 text file up to 1 MiB.

A GitHub Actions workflow is the exception: it keeps its Actions updater and is scanned for annotations as well, so a tool version passed to an action through a with: input can be updated beside the uses: refs around it. See GitHub Actions.

Version Precision

By default, upd preserves version precision from the original file:

# Original file has 2-component versions
flask>=2.0        →  flask>=3.1        (not 3.1.5)
django>=4         →  django>=6         (not 6.0.0)

# Original file has 3-component versions
requests>=2.0.0   →  requests>=2.32.5

# GitHub Actions major-only tags
actions/checkout@v3  →  actions/checkout@v4  (not @v4.2.0)

Use --full-precision to always output full semver versions:

upd --full-precision
flask>=2.0        →  flask>=3.1.5
django>=4         →  django>=6.0.0
requests>=2.0.0   →  requests>=2.32.5

Version Alignment

In monorepos or projects with multiple dependency files, the same package might have different versions:

# requirements.txt
requests==2.28.0

# requirements-dev.txt
requests==2.31.0

# services/api/requirements.txt
requests==2.25.0

upd align updates every occurrence to the highest version found:

upd align              # Align all packages to highest version
upd align --dry-run    # Preview changes
upd align --check      # Exit 1 if misalignments (for CI)
upd align --lang python # Align only Python packages

It only aligns within one ecosystem, skips packages with upper bound constraints (e.g. >=2.0,<3.0) to avoid breaking them, and ignores pre-release versions when finding the highest version.

Pre-commit Integration

Add upd to your .pre-commit-config.yaml:

repos:
  - repo: https://github.com/rvben/upd-pre-commit
    rev: v0.0.24
    hooks:
      - id: upd-check
        # Optional: only check specific ecosystems
        # args: ['--lang', 'python']

Available hooks:

Hook ID Description
upd-check Fail if any dependencies are outdated
upd-check-major Fail only on major (breaking) updates

Both hooks run on pre-push by default. Uses language: python which installs upd from PyPI automatically, so no manual installation is needed.

Documentation

Everything you look up rather than read lives in docs/.

Releases

Vership workflow, publication guarantees, automated integration pins, and safe retry procedures. → docs/releases.md

Supported files

Every file upd discovers, per ecosystem, plus annotated version pins in files it does not otherwise understand. → docs/ecosystems.md

Comparison and benchmarks

A dated feature matrix for related dependency tools, plus workload-based, reproducible benchmarks that avoid ranking unlike operations. → docs/comparison.md

Security auditing

OSV vulnerability scanning, --fix-audit, SARIF output, and CI integration. → docs/audit.md

Security policy

Private vulnerability reporting, supported versions, trust boundaries, and release integrity. → SECURITY.md

Configuration file

.updrc.toml discovery order and every key it accepts. → docs/configuration.md

Cooldown (minimum release age)

Hold back versions published less than N days ago, per ecosystem. → docs/configuration.md#cooldown-minimum-release-age

Caching

Where the 24-hour version cache lives and how to clear or bypass it. → docs/configuration.md#caching

Environment variables

Every variable upd reads, in one table. → docs/configuration.md#environment-variables

Private repositories

Credential detection for PyPI, npm, Cargo, Go, and GitHub, including private indexes declared in pyproject.toml. → docs/private-registries.md

GitHub pull requests

Run any supported dependency updates as one rolling GitHub PR, with immutable Action SHA verification, validation, artifact reporting, and opt-in auto-merge. → docs/github-actions.md

GitLab merge requests

Run scheduled dependency updates as one rolling GitLab MR, with validation, lease-protected branch updates, and explicitly opt-in GitLab-native auto-merge. → docs/gitlab.md

Stability

The stable CLI surface, exit codes, --lock commands, and output guarantees. → docs/stability.md

Development

# Build
make build

# Run tests
make test

# Lint
make lint

# Format
make fmt

# All checks
make check

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

upd-0.10.3.tar.gz (766.4 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

upd-0.10.3-py3-none-win_amd64.whl (3.9 MB view details)

Uploaded Python 3Windows x86-64

upd-0.10.3-py3-none-musllinux_1_2_x86_64.whl (5.7 MB view details)

Uploaded Python 3musllinux: musl 1.2+ x86-64

upd-0.10.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (5.8 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ x86-64

upd-0.10.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl (5.9 MB view details)

Uploaded Python 3manylinux: glibc 2.17+ ARM64

upd-0.10.3-py3-none-macosx_11_0_arm64.whl (3.4 MB view details)

Uploaded Python 3macOS 11.0+ ARM64

upd-0.10.3-py3-none-macosx_10_12_x86_64.whl (3.6 MB view details)

Uploaded Python 3macOS 10.12+ x86-64

File details

Details for the file upd-0.10.3.tar.gz.

File metadata

  • Download URL: upd-0.10.3.tar.gz
  • Upload date:
  • Size: 766.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for upd-0.10.3.tar.gz
Algorithm Hash digest
SHA256 d68fd6199474e998274c09adecf4ca921a9bdce355e7816bd38b8e97b19eb6ce
MD5 3b903191b08fa83d2dc405cc3aacb0be
BLAKE2b-256 e341a4c2406d5229f005c333c97553c6929f94d8e2a19cac6d7ca8d72d250d49

See more details on using hashes here.

File details

Details for the file upd-0.10.3-py3-none-win_amd64.whl.

File metadata

  • Download URL: upd-0.10.3-py3-none-win_amd64.whl
  • Upload date:
  • Size: 3.9 MB
  • Tags: Python 3, Windows x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for upd-0.10.3-py3-none-win_amd64.whl
Algorithm Hash digest
SHA256 28a12c3786fc3e7fe80a31a37f72a42d48840181aa8279ecd5be243ab2742900
MD5 a5fb81b1ff118f533da56ab03f914aa0
BLAKE2b-256 980c35d257a43f5fd4d3d38e9629b7aaf763b8a7cca1307e682cef440e411295

See more details on using hashes here.

File details

Details for the file upd-0.10.3-py3-none-musllinux_1_2_x86_64.whl.

File metadata

  • Download URL: upd-0.10.3-py3-none-musllinux_1_2_x86_64.whl
  • Upload date:
  • Size: 5.7 MB
  • Tags: Python 3, musllinux: musl 1.2+ x86-64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for upd-0.10.3-py3-none-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 7eb8ed1d9da0e75d4a4cfb6e6a987b0c2a019b5d2f84e6c933dfed91b9b180a6
MD5 80dee2e3f7cdcd54a175f82d15ed307c
BLAKE2b-256 8b6ed8595985b8e5ba48b21c44b107325461c2e136e20a7fa262e5a35cabdc51

See more details on using hashes here.

File details

Details for the file upd-0.10.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.

File metadata

File hashes

Hashes for upd-0.10.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Algorithm Hash digest
SHA256 ecf3483b760a354b57bd571fe78ad52defe6d0d7ea0793f6baf0efb9c7383bba
MD5 295aa714122d94df0b759a470e6da79a
BLAKE2b-256 6b412635a9860d40c872ca203724152da65686087f98ec934dd889448bee016d

See more details on using hashes here.

File details

Details for the file upd-0.10.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.

File metadata

File hashes

Hashes for upd-0.10.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Algorithm Hash digest
SHA256 d7377ee6c1c713e29b80c76fab41dd7486eadaedbf6f3f817d0dbd6a26fc4499
MD5 dfd4c5c2bb54b49fe842d45b9d7fa411
BLAKE2b-256 12da8adfc150ee4f8727105c8dddf93976728936875e5df4356b0b5ffcc860ab

See more details on using hashes here.

File details

Details for the file upd-0.10.3-py3-none-macosx_11_0_arm64.whl.

File metadata

  • Download URL: upd-0.10.3-py3-none-macosx_11_0_arm64.whl
  • Upload date:
  • Size: 3.4 MB
  • Tags: Python 3, macOS 11.0+ ARM64
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.3

File hashes

Hashes for upd-0.10.3-py3-none-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 c24db5a5f31b00b2c670247bbb8ce3f6d85bbd2f6a40ad55dfbe206dce7374c8
MD5 74920dc64fa5c82bd95f789490be9a2a
BLAKE2b-256 c54224f1f85ebd30a94cf8d02677ab0d1185c5f1ab5e235bca1926f1415b6086

See more details on using hashes here.

File details

Details for the file upd-0.10.3-py3-none-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for upd-0.10.3-py3-none-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 3205fc5fe38b7f1c1638ebfbb7ddacd3ca10a7f90ad41d450509ce6c945961bf
MD5 59cea5334b08e0ddd99e78038ee992b0
BLAKE2b-256 9977950cbb829b901c381d47e1c0eb31db94a4ff7bac0109c42063a9ede95e1c

See more details on using hashes here.

Release history Release notifications | RSS feed

0.11.5

7 files

0.11.4

7 files

0.11.3

7 files

0.11.2

7 files

0.11.1

7 files

0.11.0

7 files

0.10.4

7 files

This release

0.10.3 This release

7 files

0.10.2

7 files

0.10.1

7 files

0.10.0

7 files

0.9.2

7 files

0.9.1

7 files

0.9.0

7 files

0.8.8

7 files

0.8.7

7 files

0.8.6

7 files

0.8.5

7 files

0.8.4

7 files

0.8.3

7 files

0.8.2

7 files

0.8.1

7 files

0.8.0

7 files

0.7.1

7 files

0.7.0

7 files

0.6.5

7 files

0.6.4

7 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page