A fast dependency updater for Python, Node.js, Rust, Go, Ruby, Terraform, GitHub Actions, pre-commit, and Mise projects
Project description
upd
A fast dependency updater for Python, Node.js, Rust, Go, Ruby, .NET, Terraform, GitHub Actions, pre-commit, and Mise projects, written in Rust.
Quick Start
# Run without installing (using uv)
uvx --from upd-cli upd
# Or with pipx
pipx run --spec upd-cli upd
# Preview changes without modifying files
uvx --from upd-cli upd -n
Features
- Multi-ecosystem: Python, Node.js, Rust, Go, Ruby, .NET, Terraform, GitHub Actions, pre-commit, Mise/asdf
- Fast: Parallel registry requests for all dependencies
- Constraint-aware: Respects version constraints like
>=2.0,<3and~> 7.1 - Smart caching: 24-hour version cache for faster subsequent runs
- Update filters: Filter by
--major,--minor, or--patchupdates - Interactive mode: Approve updates individually with
-i - Check mode: Exit with code 1 if updates available (for CI/pre-commit)
- Major warnings: Highlights breaking changes with
(MAJOR) - Format-preserving: Keeps formatting, comments, and structure
- Pre-release aware: Updates pre-releases to newer pre-releases
- Gitignore-aware: Respects
.gitignorewhen discovering files - Version alignment: Align package versions across multiple files
- Security auditing: Check dependencies for known vulnerabilities via OSV
- Config file support: Ignore or pin packages via
.updrc.toml - Private registries: Authentication for PyPI, npm, Cargo, Go, and GitHub
Installation
From crates.io
cargo install upd
# or with cargo-binstall (faster, pre-built binary)
cargo binstall upd
From PyPI
pip install upd-cli
# or with uv
uv pip install upd-cli
From source
git clone https://github.com/rvben/upd
cd upd
cargo install --path .
Usage
# Update all dependency files in current directory
upd
# Update specific files or directories
upd requirements.txt pyproject.toml
# Dry-run mode (preview changes without writing)
upd -n
upd --dry-run
# Verbose output
upd -v
upd --verbose
# Disable colored output
upd --no-color
# Disable caching (force fresh lookups)
upd --no-cache
# Filter by update type
upd --major # Show only major (breaking) updates
upd --minor # Show only minor updates
upd --patch # Show only patch updates
# Combine filters
upd --major --minor # Show major and minor updates only
# Interactive mode - approve updates one by one
upd -i
upd --interactive
# Filter by language/ecosystem
upd --lang python # Update only Python dependencies
upd -l rust # Short form
upd --lang python --lang go # Update Python and Go only
upd --lang actions # Update only GitHub Actions
upd --lang pre-commit # Update only pre-commit hooks
upd --lang ruby # Update only Ruby gems
upd --lang dot-net # Update only .NET NuGet packages
upd --lang terraform # Update only Terraform providers/modules
upd --lang mise # Update only Mise/asdf tools
# Version precision
upd --full-precision # Output full versions (e.g., 3.1.5 instead of 3.1)
# Check mode - exit with code 1 if updates available (for CI/pre-commit)
upd --check
upd -c
upd --check --lang python # Check only Python dependencies
# Use a specific config file
upd --config /path/to/config.toml
upd --config .updrc.toml
Commands
# Show version
upd version
# Check for upd updates
upd self-update
# Clear version cache
upd clean-cache
# Align versions across files (use highest version found)
upd align
upd align --check # Exit 1 if misalignments found (for CI)
# Check for security vulnerabilities
upd audit
upd audit --check # Exit 1 if vulnerabilities found (for CI)
Supported Files
Python
requirements.txt,requirements-dev.txt,requirements-*.txtrequirements.in,requirements-dev.in,requirements-*.indev-requirements.txt,*-requirements.txt,*_requirements.txtpyproject.toml(PEP 621 and Poetry formats)
Node.js
package.json(dependenciesanddevDependencies)
Rust
Cargo.toml([dependencies],[dev-dependencies],[build-dependencies])
Go
go.mod(requireblocks)
Ruby
Gemfile(gem declarations with version constraints)
.NET / NuGet
.csprojfiles (PackageReferenceelements)Directory.Packages.propsandDirectory.Build.props(PackageVersionelements)- Supports both inline
Versionattributes and child<Version>elements - Queries the NuGet v3 API (
api.nuget.org) - Skips range version constraints (
[1.0, 2.0))
Terraform / OpenTofu
.tffiles (HCL format)- Updates
required_providersversion constraints andmoduleversion declarations - Queries the Terraform Registry API (
registry.terraform.io) - Skips local modules (
./,../) and git sources - Supports pessimistic constraints (
~> 5.0)
GitHub Actions
.github/workflows/*.ymland.github/workflows/*.yaml- Updates
uses:version references (e.g.,actions/checkout@v3→actions/checkout@v4) - Skips SHA-pinned actions, branch refs, local actions, and Docker references
- Authenticates via
GITHUB_TOKENorGH_TOKENfor higher API rate limits
Pre-commit
.pre-commit-config.yaml- Updates
rev:fields for GitHub-hosted hook repositories - Skips local hooks, meta hooks, and non-GitHub repositories
Mise / asdf
.mise.toml([tools]section).tool-versions(space-delimited format)- Supports 24+ common dev tools: node, python, go, rust, zig, deno, bun, uv, ruff, terraform, kubectl, helm, and more
- Skips
latestversions andcargo:*tools
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 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
Use upd align to update all occurrences 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
Behavior:
- Only aligns packages within the same ecosystem (Python with Python, etc.)
- Skips packages with upper bound constraints (e.g.,
>=2.0,<3.0) to avoid breaking them - Ignores pre-release versions when finding the highest version
Security Auditing
Check your dependencies for known security vulnerabilities using the OSV (Open Source Vulnerabilities) database:
upd audit # Scan all dependency files
upd audit --dry-run # Same as audit (read-only operation)
upd audit --check # Exit 1 if vulnerabilities found (for CI)
upd audit --lang python # Audit only Python packages
upd audit ./services # Audit specific directory
Example output:
Checking 42 unique package(s) for vulnerabilities...
⚠ Found 3 vulnerability/ies in 2 package(s):
● requests@2.19.0 (PyPI)
├── GHSA-j8r2-6x86-q33q [CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:N/A:N] Unintended leak of Proxy-Authorization header
│ Fixed in: 2.31.0
│ https://github.com/psf/requests/security/advisories/GHSA-j8r2-6x86-q33q
● flask@0.12.2 (PyPI)
├── GHSA-562c-5r94-xh97 [CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H] Denial of Service vulnerability
│ Fixed in: 0.12.3
│ https://nvd.nist.gov/vuln/detail/CVE-2018-1000656
├── GHSA-m2qf-hxjv-5gpq [CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N] Session cookie disclosure
│ Fixed in: 2.3.2
│ https://github.com/pallets/flask/security/advisories/GHSA-m2qf-hxjv-5gpq
Summary: 2 vulnerable package(s), 3 total vulnerability/ies
Supported ecosystems for auditing: PyPI, npm, crates.io, Go, RubyGems, NuGet
CI/CD Integration:
# GitHub Actions example
- name: Check for vulnerabilities
run: upd audit --check
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) |
~2.0.0 |
Updates within 2.0.x range (npm) |
~> 7.1 |
Updates within 7.x range (Ruby pessimistic) |
>=2.0 |
Updates to any version >= 2.0 |
==2.0.0 |
No updates (pinned) |
Configuration File
upd supports configuration files to customize update behavior on a per-project basis.
File Discovery
upd searches for configuration files in the following order (first found wins):
.updrc.toml- Recommended, explicit config fileupd.toml- Alternative name.updrc- Minimal name (TOML format)
The search starts from the target directory and walks up to parent directories, allowing you to place a config file at the repository root.
Configuration Options
# .updrc.toml
# Packages to ignore during updates (never updated)
ignore = [
"legacy-package",
"internal-tool",
"actions/checkout", # GitHub Actions use owner/repo
"pre-commit/pre-commit-hooks", # Pre-commit hooks too
]
# Pin packages to specific versions (bypasses registry lookup)
[pin]
flask = "2.3.0"
django = "4.2.0"
"actions/setup-node" = "v4" # Pin GitHub Actions
"psf/black" = "24.0.0" # Pin pre-commit hooks
Options
| Option | Type | Description |
|---|---|---|
ignore |
string[] |
List of package names to skip during updates |
pin |
table |
Map of package names to pinned versions |
Verbose Output
Use --verbose to see which packages are ignored or pinned:
upd --verbose
# Output:
# Using config from: .updrc.toml
# Ignoring 2 package(s)
# Pinning 3 package(s)
# pyproject.toml:12: Pinned flask 2.2.0 → 3.0.0 (pinned)
# pyproject.toml:13: Skipped internal-utils 1.0.0 (ignored)
Caching
Version lookups are cached for 24 hours in:
- macOS:
~/Library/Caches/upd/versions.json - Linux:
~/.cache/upd/versions.json - Windows:
%LOCALAPPDATA%\upd\versions.json
Use upd clean-cache to clear the cache, or upd --no-cache to bypass it.
Private Repositories
upd supports private package registries for all ecosystems. Credentials are automatically detected from environment variables and configuration files.
PyPI / Private Python Index
# Option 1: Environment variables
export UV_INDEX_URL=https://my-private-pypi.com/simple
export UV_INDEX_USERNAME=myuser
export UV_INDEX_PASSWORD=mypassword
# Option 2: PIP-style environment variables
export PIP_INDEX_URL=https://my-private-pypi.com/simple
export PIP_INDEX_USERNAME=myuser
export PIP_INDEX_PASSWORD=mypassword
# Option 3: ~/.netrc file
# machine my-private-pypi.com
# login myuser
# password mypassword
# Option 4: pip.conf / pip.ini
# ~/.config/pip/pip.conf (Linux/macOS)
# %APPDATA%\pip\pip.ini (Windows)
[global]
index-url = https://my-private-pypi.com/simple
extra-index-url = https://pypi.org/simple
# Option 5: Inline in requirements.txt (with credentials)
# --index-url https://user:pass@my-private-pypi.com/simple
# or just the URL (credentials from netrc):
# --index-url https://my-private-pypi.com/simple
pip.conf locations (searched in order):
$PIP_CONFIG_FILEenvironment variable$VIRTUAL_ENV/pip.conf(if in a virtual environment)$XDG_CONFIG_HOME/pip/pip.confor~/.config/pip/pip.conf~/.pip/pip.conf/etc/pip.conf(system-wide)
Inline index URLs: When a requirements.txt file contains --index-url or -i,
upd automatically uses that index instead of the default PyPI. Credentials can be
embedded in the URL (https://user:pass@host/simple) or looked up from ~/.netrc.
npm / Private Registry
# Option 1: Environment variables
export NPM_REGISTRY=https://npm.mycompany.com
export NPM_TOKEN=your-auth-token
# Option 2: NODE_AUTH_TOKEN (GitHub Actions)
export NODE_AUTH_TOKEN=your-auth-token
# Option 3: ~/.npmrc file (global registry)
registry=https://npm.mycompany.com
//npm.mycompany.com/:_authToken=your-auth-token
# Or for environment variable reference:
//npm.mycompany.com/:_authToken=${NPM_TOKEN}
# Option 4: ~/.npmrc file (scoped registries)
@mycompany:registry=https://npm.mycompany.com
//npm.mycompany.com/:_authToken=your-auth-token
@another-scope:registry=https://another.registry.com
Scoped registries: Packages with scopes (e.g., @mycompany/package) will use the
registry configured for that scope in .npmrc. This allows mixing public and private
packages in the same project.
Cargo / Private Registry
# Option 1: Environment variables
export CARGO_REGISTRY_TOKEN=your-token # For crates.io default
export CARGO_REGISTRIES_MY_REGISTRY_TOKEN=your-token # For named registry
# Option 2: ~/.cargo/credentials.toml
[registry]
token = "your-crates-io-token"
[registries.my-private-registry]
token = "your-private-token"
# Option 3: ~/.cargo/config.toml (registry URLs)
[registries.my-private-registry]
index = "https://my-registry.com/git/index"
# or sparse registry:
index = "sparse+https://my-registry.com/index/"
Custom registries: upd reads ~/.cargo/config.toml to discover custom registry
URLs. Combine with credentials.toml for authenticated access.
Go / Private Module Proxy
# Option 1: Environment variables
export GOPROXY=https://proxy.mycompany.com
export GOPROXY_USERNAME=myuser
export GOPROXY_PASSWORD=mypassword
# Option 2: Private module patterns
export GOPRIVATE=github.com/mycompany/*,gitlab.mycompany.com/*
export GONOPROXY=github.com/mycompany/*
export GONOSUMDB=github.com/mycompany/*
# Option 3: ~/.netrc file (commonly used with go modules)
# machine github.com
# login myuser
# password mytoken
Private modules: Set GOPRIVATE to specify module patterns that should bypass
the public proxy. upd respects these patterns and will attempt direct access
for matching modules.
GitHub (Actions & Pre-commit)
# Option 1: GITHUB_TOKEN (automatically available in GitHub Actions)
export GITHUB_TOKEN=ghp_your-token-here
# Option 2: GH_TOKEN (used by the gh CLI)
export GH_TOKEN=ghp_your-token-here
Without a token, the GitHub API rate limit is 60 requests/hour. With a token, it's 5,000 requests/hour.
Use --verbose to see when authenticated access is being used:
upd --verbose
# Output: Using authenticated PyPI access
# Output: Using authenticated npm access
# Output: Using authenticated GitHub access
Environment Variables
| Variable | Description |
|---|---|
UV_INDEX_URL |
Custom PyPI index URL |
PIP_INDEX_URL |
Custom PyPI index URL (fallback) |
PIP_CONFIG_FILE |
Path to pip configuration file |
UV_INDEX_USERNAME |
PyPI username (with UV_INDEX_URL) |
UV_INDEX_PASSWORD |
PyPI password (with UV_INDEX_URL) |
PIP_INDEX_USERNAME |
PyPI username (with PIP_INDEX_URL) |
PIP_INDEX_PASSWORD |
PyPI password (with PIP_INDEX_URL) |
NPM_REGISTRY |
Custom npm registry URL |
NPM_TOKEN |
npm authentication token |
NODE_AUTH_TOKEN |
npm token (GitHub Actions compatible) |
CARGO_REGISTRY_TOKEN |
crates.io authentication token |
CARGO_REGISTRIES_<NAME>_TOKEN |
Named registry token |
GOPROXY |
Custom Go module proxy URL |
GOPROXY_USERNAME |
Go proxy username |
GOPROXY_PASSWORD |
Go proxy password |
GOPRIVATE |
Comma-separated private module patterns |
GONOPROXY |
Modules to exclude from proxy |
GONOSUMDB |
Modules to exclude from checksum DB |
GITHUB_TOKEN |
GitHub API token (for Actions and pre-commit) |
GH_TOKEN |
GitHub API token (gh CLI compatible) |
UPD_CACHE_DIR |
Custom cache directory |
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-cli from PyPI automatically — no manual installation needed.
Development
# Build
make build
# Run tests
make test
# Lint
make lint
# Format
make fmt
# All checks
make check
License
MIT
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 Distributions
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 upd_cli-0.0.27.tar.gz.
File metadata
- Download URL: upd_cli-0.0.27.tar.gz
- Upload date:
- Size: 176.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
73955886891aa8df09cded40ad80fce22afd378c8cc2af659e7a0aa2fb2c65e3
|
|
| MD5 |
a26e5f51e4a96374832333c3e38bcf29
|
|
| BLAKE2b-256 |
9890a78fa4ee25d2e321d0565c4d906ab32ad444ec198c1f790f66f720a5a682
|
File details
Details for the file upd_cli-0.0.27-py3-none-win_amd64.whl.
File metadata
- Download URL: upd_cli-0.0.27-py3-none-win_amd64.whl
- Upload date:
- Size: 3.3 MB
- Tags: Python 3, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
52aacd3586dc54aaef211415654c6076f4794bdf79d9e12d02cd5159c0fabb16
|
|
| MD5 |
795a442b283e1c3b094fde01261ab844
|
|
| BLAKE2b-256 |
f3c685dc134f8e26c2a846a0391c93395273e6e85983e1432eecbf6160b52c25
|
File details
Details for the file upd_cli-0.0.27-py3-none-musllinux_1_2_x86_64.whl.
File metadata
- Download URL: upd_cli-0.0.27-py3-none-musllinux_1_2_x86_64.whl
- Upload date:
- Size: 3.3 MB
- Tags: Python 3, musllinux: musl 1.2+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ed8f6c26813eae2a8353d52444dce76b57d7f7adc2c480b663c9e6ffa495339b
|
|
| MD5 |
82db92a14f5d9a9c3ed825dae05b4f68
|
|
| BLAKE2b-256 |
8c87af134aedb72c89c13b26dd00abca5330aa5d4d7a57e946fa1358a7526459
|
File details
Details for the file upd_cli-0.0.27-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.
File metadata
- Download URL: upd_cli-0.0.27-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
- Upload date:
- Size: 3.3 MB
- Tags: Python 3, manylinux: glibc 2.17+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6a8346445ffbdfe096443ebab1ba1b4adcb028261b8758014d31231a0947015f
|
|
| MD5 |
8150ae17e222f6478858063502f064c8
|
|
| BLAKE2b-256 |
1f5806813ccf061aae61ac76295785adc4b0419a8053a86e8c94e75833374a99
|
File details
Details for the file upd_cli-0.0.27-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl.
File metadata
- Download URL: upd_cli-0.0.27-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
- Upload date:
- Size: 3.1 MB
- Tags: Python 3, manylinux: glibc 2.17+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ae17cd67e1f601191826e96da18012e0486a7308829223b57b91284bd45ce134
|
|
| MD5 |
8cad8e6cea94cb407ff79a07947ac0c6
|
|
| BLAKE2b-256 |
99c434f19bf77448e4cacc2bc9f0302977e0122cdd0e18c2d47314ef37df96b3
|
File details
Details for the file upd_cli-0.0.27-py3-none-macosx_11_0_arm64.whl.
File metadata
- Download URL: upd_cli-0.0.27-py3-none-macosx_11_0_arm64.whl
- Upload date:
- Size: 3.0 MB
- Tags: Python 3, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8ebc9e4075ef02487b29ca5724f9570eb8fb161b4b6b67bc27605d8eb9073e95
|
|
| MD5 |
68a1c5dfa4c4510546f89be2005d7225
|
|
| BLAKE2b-256 |
ec73681f854978bf1d62f346ae650443a7fa168f10f5ad0b554c1f3412d93f08
|
File details
Details for the file upd_cli-0.0.27-py3-none-macosx_10_12_x86_64.whl.
File metadata
- Download URL: upd_cli-0.0.27-py3-none-macosx_10_12_x86_64.whl
- Upload date:
- Size: 3.2 MB
- Tags: Python 3, macOS 10.12+ x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
05885a1b9bbc01eacaa63a2e58ef1dbb1a9c832f4c61db70c4096a88d0a868e6
|
|
| MD5 |
64d1e858e92b7aed4b528d89b88d26f6
|
|
| BLAKE2b-256 |
1298f1221bb47ef831ac59e1707b795126f06bc681553604100f1c2f547e07bf
|