<p align="center">
<img src="assets/logo-wide.svg" alt="upd logo" width="400">
</p>
# upd
A fast dependency updater for Python, Node.js, Rust, and Go projects,
written in Rust.
## Quick Start
```bash
# 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, and Go dependencies
- **Fast**: Parallel registry requests for all dependencies
- **Constraint-aware**: Respects version constraints like `>=2.0,<3`
- **Smart caching**: 24-hour version cache for faster subsequent runs
- **Update filters**: Filter by `--major`, `--minor`, or `--patch` updates
- **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 `.gitignore` when discovering files
- **Version alignment**: Align package versions across multiple files
- **Security auditing**: Check dependencies for known vulnerabilities via OSV
## Installation
### From crates.io
```bash
cargo install upd
# or with cargo-binstall (faster, pre-built binary)
cargo binstall upd
```
### From PyPI
```bash
pip install upd-cli
# or with uv
uv pip install upd-cli
```
### From source
```bash
git clone https://github.com/rvben/upd
cd upd
cargo install --path .
```
## Usage
```bash
# 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
# 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
```
### Commands
```bash
# 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-*.txt`
- `requirements.in`, `requirements-dev.in`, `requirements-*.in`
- `dev-requirements.txt`, `*-requirements.txt`, `*_requirements.txt`
- `pyproject.toml` (PEP 621 and Poetry formats)
### Node.js
- `package.json` (`dependencies` and `devDependencies`)
### Rust
- `Cargo.toml` (`[dependencies]`, `[dev-dependencies]`, `[build-dependencies]`)
### Go
- `go.mod` (`require` blocks)
## Example Output
```text
pyproject.toml:12: Would update requests 2.28.0 → 2.31.0
pyproject.toml:13: Would update flask 2.2.0 → 3.0.0 (MAJOR)
Cargo.toml:8: Would update serde 1.0.180 → 1.0.200
Cargo.toml:9: Would update tokio 1.28.0 → 1.35.0
Would update 4 package(s) in 2 file(s), 15 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:
```text
# 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
```
Use `--full-precision` to always output full semver versions:
```text
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:
```text
# 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:
```bash
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)](https://osv.dev/) database:
```bash
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:**
```text
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
```
**Features:**
- Queries the OSV API (free, no API key required)
- Supports all ecosystems: PyPI, npm, crates.io, Go
- Deduplicates packages across files
- Shows severity scores (CVSS), descriptions, and fixed versions
- Batch queries for efficiency (up to 1000 packages per request)
**CI/CD Integration:**
```yaml
# GitHub Actions example
- name: Check for vulnerabilities
run: upd audit --check
```
```yaml
# Pre-commit hook (.pre-commit-config.yaml)
repos:
- repo: local
hooks:
- id: upd-audit
name: security audit
entry: upd audit --check
language: system
pass_filenames: false
stages: [pre-push]
```
## Version Constraints
`upd` respects version constraints in your dependency files:
| `>=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) |
| `>=2.0` | Updates to any version >= 2.0 |
| `==2.0.0` | No updates (pinned) |
## 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
```bash
# 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: 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
```
**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
```bash
# 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
//npm.mycompany.com/:_authToken=your-auth-token
# Or for environment variable reference:
//npm.mycompany.com/:_authToken=${NPM_TOKEN}
```
### Cargo / Private Registry
```bash
# 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"
```
### Go / Private Module Proxy
```bash
# Option 1: Environment variables
export GOPROXY=https://proxy.mycompany.com
export GOPROXY_USERNAME=myuser
export GOPROXY_PASSWORD=mypassword
# Option 2: ~/.netrc file (commonly used with go modules)
# machine proxy.mycompany.com
# login myuser
# password mypassword
```
Use `--verbose` to see when authenticated access is being used:
```bash
upd --verbose
# Output: Using authenticated PyPI access
# Output: Using authenticated npm access
```
## Environment Variables
| `UV_INDEX_URL` | Custom PyPI index URL |
| `PIP_INDEX_URL` | Custom PyPI index URL (fallback) |
| `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 |
| `UPD_CACHE_DIR` | Custom cache directory |
## Development
```bash
# Build
make build
# Run tests
make test
# Lint
make lint
# Format
make fmt
# All checks
make check
```
## License
MIT