Multi-language dependency updater CLI for Node.js, Python, Rust, Go, Ruby, PHP, Java, Swift and mise, with a release age filter and OSV vulnerability checks
depup finds the manifests in a project (package.json, pyproject.toml, Cargo.toml, go.mod, Gemfile, composer.json, Gradle build files, Package.swift, and mise config files), looks up newer versions in each registry, and rewrites the version specifiers in place, for every language in one run.
It is conservative by default: a new version becomes a candidate only after it has been public for a week, versions with known vulnerabilities on OSV.dev are avoided, pinned versions stay as they are, and ranges keep their operators and upper bounds.
Add --install to let each project's package manager refresh its lock file after the update.
- Multi-Language Support: Node.js, Python, Rust, Go, Ruby, PHP, Java, and Swift dependencies, plus mise tool versions in
mise.toml/.tool-versions, through the same workflow - Manifest Updates: Directly updates version specifications in manifest files (
package.json,Cargo.toml, and so on) - Smart Version Handling: Preserves version range formats (
^,~,>=) while keeping upper bounds intact - Pinned Version Detection: Skips intentionally pinned versions by default
- Age Filter: Only updates to versions that have been public for at least N days or weeks (1 week by default)
- Project Age Policies: Inherits pnpm, Bun, and mise policies per directory and preserves stricter uv and mise native cutoffs
- Vulnerability Check: Looks up the chosen version on OSV.dev and avoids versions with known vulnerabilities (enabled by default)
- Package Manager Install:
--installruns each project's package manager after the update and passes the age filter on to pnpm, uv, and mise - Cargo Lock Age Audit: Checks changed direct and transitive crates after install; unresolved violations or unverified entries exit with code
2 - Bun Catalogs: Updates Bun
catalog/catalogsdefinitions inpackage.json - Monorepo Support:
.depup, Cargo/pnpm/Go workspaces, Gradle multi-project builds, nested package installs, and Tauri projects - Multiple Output Formats: Colored text with the release date of each new version, JSON, and diff
- mise: At runtime, only needed to update mise tool versions (version lists come from
mise ls-remote). Without it, depup ignores mise config files, warns once, and continues with the other languages - Package managers:
--installruns the package manager of each project (npm, pnpm, uv, Cargo, Bundler, Composer, Gradle, and so on), so it must be installed; a package manager that is not installed counts as a failed install - Network access: Versions are looked up in the public registries, and the vulnerability check queries the OSV.dev API
brew install owayo/depup/depupwinget install owayo.depupRequires Rust 1.98 or later.
cargo install --git https://github.com/owayo/depup --lockedDownload the archive for your platform from Releases, extract it, and put depup on your PATH. Each release also includes SHA256SUMS for checking the downloads.
| Platform | Archive |
|---|---|
| Linux (x86_64) | depup-x86_64-unknown-linux-gnu.tar.gz |
| Linux (ARM64) | depup-aarch64-unknown-linux-gnu.tar.gz |
| macOS (Intel) | depup-x86_64-apple-darwin.tar.gz |
| macOS (Apple Silicon) | depup-aarch64-apple-darwin.tar.gz |
| Windows (x86_64) | depup-x86_64-pc-windows-msvc.zip |
On macOS, if you downloaded the archive with a browser, remove the quarantine attribute before running it: xattr -d com.apple.quarantine depup.
Requires mise (the Rust toolchain is pinned in mise.toml).
git clone https://github.com/owayo/depup.git
cd depup
make installmake install installs to /usr/local/bin. Set INSTALL_PATH to change it (for example make install INSTALL_PATH="$HOME/.local/bin").
After a winget install, open a new terminal so that the updated PATH takes effect. To remove a build installed from source, run make uninstall with the same INSTALL_PATH.
depup [OPTIONS] [PATH]PATH is the directory to process (defaults to the current directory). depup updates every supported language it finds there unless you limit it with a language flag such as --node or --python.
# Preview all updates (dry run)
depup -n
# Update Node.js dependencies only
depup --node
# Update only lodash and typescript
depup --only lodash --only typescript
# Exclude react from updates
depup --exclude react
# Only update to versions at least 2 weeks old (the default is 1 week)
depup --age 2w
# Update and show diff
depup --diff
# Update, then run the package manager install (npm install, etc.)
depup --node --install
# JSON output for CI/CD
depup --jsonOutput for a Python project and for a Tauri project:
Python (pyproject.toml)
|
Tauri (package.json + Cargo.toml)
|
Unless a dependency is pinned, depup looks up its published versions, picks the newest one that passes the checks below, and rewrites the manifest in place. By default:
- An exact version such as
"1.2.3"inpackage.jsonis treated as an intentional pin and is not updated unless you pass--include-pinned. Go modules and mise tools are exceptions (Pinned Versions and--include-pinned). - Only versions that have been public for at least one week are candidates (Age Filter).
- Versions with known vulnerabilities are avoided (Vulnerability Check).
- Prereleases are not proposed while the current version is stable (Candidate Ordering and Prereleases).
- Ranges keep their shape and upper bound; only the lower bound moves (Upper and Lower Bounds of Ranges).
- Major bumps are allowed unless
--max-changecaps them (Limiting Bumps).
- Filtering Candidates: the age filter, project age policies, the OSV.dev check, and
--max-change - Version Specifiers and Rewriting: which specifiers count as pinned, how ranges and formats are preserved, and which constraints are left unchanged
- Running the Package Manager: the command
--installruns for each package manager, and how far the age filter reaches into transitive dependencies - When a Dependency Is Not Updated: the skip reasons that
--verboselists - Command-Line Reference: all options, the text / JSON / diff output, and the exit codes
- Ecosystems and Monorepos: workspaces and Tauri projects, and the exact declarations depup reads in each ecosystem
depup reads its defaults, monorepo directories, and release-age policies from the sources below. A command-line flag overrides depup's global defaults for that run. Explicit project age policies take precedence over --age; stricter uv and mise native constraints are also preserved.
| Setting | Location | Purpose |
|---|---|---|
| Global defaults | ~/.config/depup/config.toml, created on the first run |
Default age, osv, and max_change, plus verified publisher exceptions (age_exempt) |
| Monorepo directories | .depup at the project root |
Additional directories to process |
| Project age policy | minimumReleaseAge in the pnpm or Bun settings, minimum_release_age in the mise settings |
Minimum release age of that project |
| Native age constraints | uv's exclude-newer and mise's global, local, and per-tool age settings |
Preserve stricter cutoffs during candidate selection and install |
Each manifest inherits age settings from its ancestors. Shared installs and Cargo workspace locks use the strictest applicable member policy, including members with no updates. See Age Filter for sources, precedence, and limits.
For example, to wait two weeks instead of one and to rule out major bumps in every project:
# ~/.config/depup/config.toml
age = "2w"
max_change = "minor"All keys, the .depup format, and how invalid values are handled: docs/configuration.md
Requires mise. Tool versions are pinned in mise.toml.
make setup # Install the toolchain (mise) and dependencies
make ci # Run the same checks as CI (no changes)| Command | Description |
|---|---|
make setup |
Install the toolchain (mise) and dependencies |
make build |
Build a debug binary |
make release |
Build a release binary |
make run |
Run the debug binary (arguments via ARGS="...") |
make test |
Run the tests |
make lint |
Run clippy with warnings as errors |
make fmt |
Format the code (rewrites files) |
make fmt-check |
Check the formatting (no changes) |
make check |
Run fmt-check and lint (no changes) |
make ci |
Run the same checks as CI (no changes) |
make install |
Install the release binary to INSTALL_PATH (default /usr/local/bin) |
make uninstall |
Remove the binary from INSTALL_PATH |
make clean |
Remove build artifacts |
Run make to list every target. Releases are published from GitHub Actions (Actions → Release → Run workflow).
The additional test targets, what CI runs on each OS, and the release steps are described in docs/development.md.


