Dependency Update Metrics
A Python tool for analyzing time-to-update (TTU) and time-to-remediate (TTR) metrics for package dependencies across different ecosystems (npm, PyPI, Cargo/crates.io).
Features
- Multi-ecosystem support: Analyze packages from npm, PyPI, and Cargo/crates.io
- Time-to-Update (TTU): Measure how long dependencies stay behind the latest version
- Time-to-Remediate (TTR): Measure how long known vulnerabilities remain unpatched
- Flexible weighting: Support for linear, exponential, inverse, or no weighting
- OSV integration: Uses the Open Source Vulnerabilities database for security analysis
- Export capabilities: Export detailed worksheets and OSV data
Installation
From PyPI
pip install dependency-update-metrics
From source
git clone https://github.com/imranur-rahman/dependency-update-metrics.git
cd dependency-update-metrics
pip install -e .
With Docker (exact reruns)
The container image ghcr.io/imranur-rahman/mttx holds the Tool with pinned dependencies and the patched pip, so a rerun years later uses the same environment:
docker run --rm -v "$PWD:/data" ghcr.io/imranur-rahman/mttx:<version>-osv-<YYYY-MM-DD> \
--package express --ecosystem npm --start-date 2024-01-01 --end-date 2025-01-01
mttx:<version>(andlatest) has no OSV index. MTTR needs--build-osvonce, as with pip.mttx:<version>-osv-<YYYY-MM-DD>holds the OSV index built on that date, so MTTR is the same in each rerun.
The results and the cache go to /data. For exact reruns, cite the image digest (ghcr.io/imranur-rahman/mttx@sha256:…), not the tag. See image/README.md.
Usage
As a command-line tool
# Basic usage
dependency-metrics --ecosystem npm --package express
# With date range
dependency-metrics --ecosystem npm --package express \
--start-date 2020-01-01 \
--end-date 2023-12-31
# With exponential weighting
dependency-metrics --ecosystem npm --package express \
--weighting-type exponential \
--half-life 180
# Build OSV database first (one-time operation)
dependency-metrics --ecosystem npm --package express --build-osv
# Export detailed worksheets
dependency-metrics --ecosystem npm --package express --get-worksheets
# Get OSV vulnerability data
dependency-metrics --ecosystem npm --package express --get-osv
# Test a PyPI package
dependency-metrics --ecosystem pypi --package requests --start-date 2020-01-01 --end-date 2021-01-01 --get-worksheets
# Test a Cargo/crates.io package
dependency-metrics --ecosystem cargo --package serde --start-date 2020-01-01 --end-date 2024-01-01
# Bulk CSV input (parallel by default)
dependency-metrics --input-csv ./input.csv --workers 8 --output-dir ./output
# Resume an interrupted bulk run
dependency-metrics --input-csv ./input.csv --output-dir ./output --resume
# Per-release analysis: MTTU/MTTR at every release of the parent package
dependency-metrics --ecosystem pypi --package requests \
--start-date 2020-01-01 --end-date 2021-01-01 \
--per-release --get-worksheets
# Per-release in bulk CSV mode
dependency-metrics --input-csv ./input.csv --per-release --workers 4
Input CSV columns: ecosystem, package_name, optional start_date, optional end_date.
Accepted ecosystem values are npm, pypi, cargo, and crates.io; crates.io is normalized to cargo in outputs.
If end_date is omitted, the CLI --end-date value is used; when --end-date is also omitted, it defaults to today.
Extra columns are allowed; duplicates are removed by ecosystem, package_name, end_date.
Bulk mode computes metrics per package using the latest dependency set.
In GitHub Actions
The MTTX Action runs the Tool image in a workflow. It reports the MTTU and MTTR of chosen packages (with badges and thresholds), or comments on a pull request with the MTTU and MTTR of the dependencies that it adds. See action/README.md.
- uses: imranur-rahman/mttx-action@v1
with:
packages: express, koa
ecosystem: npm
max-mttr-days: 30
As a Python module
from datetime import datetime
from pathlib import Path
from dependency_metrics.analyzer import DependencyAnalyzer
# Create analyzer
analyzer = DependencyAnalyzer(
ecosystem="npm",
package="express",
start_date=datetime(2020, 1, 1),
end_date=datetime(2023, 12, 31),
weighting_type="exponential",
half_life=180,
output_dir=Path("./output")
)
# Run analysis
results = analyzer.analyze()
print(f"Average TTU: {results['ttu']:.2f} days")
print(f"Average TTR: {results['ttr']:.2f} days")
Command-line Arguments
--ecosystem: Ecosystem to analyze (npm,pypi,cargo, orcrates.io) [Required unless--input-csv]--package: Package name to analyze [Required unless--input-csv]--input-csv: CSV file with columnsecosystem, package_name, optionalstart_date, and optionalend_date. Ifend_dateis omitted,--end-dateis used.--start-date: Start date for analysis (YYYY-MM-DD) [Default: 1900-01-01]--end-date: End date for analysis (YYYY-MM-DD) [Default: today]--weighting-type: Weighting method (linear,exponential,inverse,disable) [Default: disable]--half-life: Half-life in days (required for exponential weighting)--build-osv: Build or rebuild the OSV vulnerability database. It downloads the npm, PyPI, and crates.io OSV zips (about 260 MB in total). Without--package, it only rebuilds the database.--osv-index-date: Print the build date of the OSV database as JSON (nullwhen there is no database), then exit--cache-stats: Show the rows and the size of each namespace in the resolver cache (<output-dir>/cache/cache.db), then exit--cache-prune-gb N: Remove the least recently used cache rows until the cache holds less than N GB of data, then exit. The prune does not runVACUUM, so the file does not shrink; SQLite reuses the freed pages--cache-clear: Remove all cache rows, then exit--cache-namespace X: With--cache-clear, remove only the rows of namespace X--get-osv: Export OSV data for the package's dependencies--get-worksheets: Export detailed analysis worksheets to Excel--output-dir: Output directory for results [Default: ./output]--workers: Number of parallel workers for bulk CSV mode [Default: min(8, CPU count)]--package-timeout: Per-package analysis timeout in seconds (bulk CSV mode only). Packages that exceed this time receive an error row and are skipped [Default: disabled]--resume: Resume bulk CSV runs by skipping completed rows and retrying errors--per-release: Compute MTTU/MTTR at every release of the parent package within the window (one output row per release)--json: Print the result as one JSON object on stdout, in the result shape of the Host API (needs--package; seeUSAGE.md)
Weighting Methods
Disable (default)
No weighting applied. All time periods are weighted equally.
Linear
Weight decreases linearly with age:
weight = 1 - (age / max_age)
Exponential
Weight decreases exponentially based on half-life:
weight = exp(-λ * age)
where λ = ln(2) / half_life
Inverse
Weight is inversely proportional to age:
weight = 1 / (1 + age)
Output
The tool generates several outputs in the specified output directory:
- JSON results file: Contains TTU, TTR, and metadata
- OSV data (with
--get-osv): CSV file with vulnerability information - Excel worksheets (with
--get-worksheets): Detailed analysis for each dependency - Bulk CSV results (with
--input-csv): Summary CSV, plus an optional dependency details CSV with--write-dependency-details - Per-release results (with
--per-release): One row per package release withpackage_version,package_release_date,window_start,window_end,mttu,mttr
Example JSON output
{
"package": "express",
"ecosystem": "npm",
"version": "4.18.2",
"start_date": "2020-01-01T00:00:00",
"end_date": "2023-12-31T00:00:00",
"weighting_type": "exponential",
"half_life": 180,
"ttu": 45.32,
"ttr": 12.15,
"num_dependencies": 30
}
How It Works
- Package Analysis: Fetches package metadata from the registry
- Dependency Extraction: Extracts dependencies from the package version closest to the end date
- Timeline Construction: Creates a timeline of version releases for each dependency
- Version Resolution: Resolves dependency versions at each time interval via ecosystem resolvers
- Remediation Check: Uses the OSV service to determine vulnerability remediation
- Metric Calculation:
- TTU: Measures time when dependency is not at the highest available version
- TTR: Measures time when dependency has known vulnerabilities
- Weighting: Applies optional time-based weighting to prioritize recent periods
- Aggregation: Averages metrics across all dependencies
Architecture Notes
- Ecosystem-specific resolution is encapsulated in
dependency_metrics/resolvers.py. - OSV remediation checks are handled by
dependency_metrics/osv_service.py. - Reporting/export helpers live in
dependency_metrics/reporting.py. - Bulk CSV mode groups rows by
(ecosystem, package_name)and processes each group sequentially while running groups in parallel to maximize cache reuse. - For bulk runs, a single package analysis computes metrics across multiple end dates using the latest dependency set.
--per-releasemode usesanalyze_at_release_points()which fetches per-version dependency sets and builds a shared dep-cache covering the full window; it slices that cache for each release sub-window[start_date, release_date].- Cache invalidation:
ResolverCache(dependency_metrics/resolvers.py) persists all HTTP responses to a SQLite file at<output-dir>/cache/cache.db, keyed only by(namespace, key)— there is no TTL. This is fine for per-version data (a specific version's dependencies/metadata never changes), but package-level version-list lookups (DepsDevClient.get_package()'sdepsdev_packagenamespace, and each resolver'sfetch_package_metadata()/_get_npm_time_data()under themetadata/npm_timenamespaces) cache the entire, mutable version list. Those lookups compare the requestedend_dateagainst the newest publish date already in the cached listing and transparently re-fetch whenever the cache can't yet rule out a newer release — so re-running against the same--output-dirwon't silently miss versions published since the last run. The tradeoff: analyzing "as of today" re-fetches each package's version list on every run (since "today" is always newer than the last known release), which is the correct behavior — only truly historical, fixedend_datevalues benefit from the cache staying warm across runs.
Requirements
- Python 3.10+
- git (for auto-installing the patched pip on first PyPI analysis)
- npm CLI (for npm ecosystem analysis)
sudo apt install nodejs npm - Internet connection (for fetching package metadata and OSV data)
Note: PyPI analysis uses a patched pip fork that adds date-bounded version resolution. The tool clones it automatically to
~/.dependency_metrics/vendor/pipon first use — no manual setup required.
Development
Setup development environment
git clone https://github.com/imranur-rahman/dependency-update-metrics.git
cd dependency-update-metrics
pip install -e ".[dev]"
Run tests
pytest
Code formatting
black dependency_metrics/
Publishing to PyPI
Prepare for publishing
# Install build tools
pip install build twine
# Build distribution
python -m build
# Check distribution
twine check dist/*
Upload to PyPI
# Upload to Test PyPI first
twine upload --repository testpypi dist/*
# If all looks good, upload to PyPI
twine upload dist/*
License
MIT License - see LICENSE file for details
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Citation
If you use this tool in your research, please cite:
@software{dependency_metrics,
author = {Rahman, Imranur},
title = {Dependency Update Metrics},
year = {2025},
url = {https://github.com/imranur-rahman/dependency-update-metrics}
}
Acknowledgments
- Uses data from Open Source Vulnerabilities (OSV)
- Package metadata from npm Registry and PyPI
Metadata
Release files for dependency-update-metrics 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| dependency_update_metrics-0.2.1.tar.gz | 121.4 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dependency_update_metrics-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 199.4 kB
Release files / dependency_update_metrics-0.2.1.tar.gz
| Download URL | dependency_update_metrics-0.2.1.tar.gz |
|---|---|
| Size | 121.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
4332bf3a5389fff357a37e2e40e27d4462350ffb3438035cae94e505058578b8
|
|
BLAKE2b-256 checksum How to use checksums |
f179146436a3b9b3df060fcd70d5248bfd421931acb7aff05e50490362340cca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logRelease files / dependency_update_metrics-0.2.1-py3-none-any.whl
| Download URL | dependency_update_metrics-0.2.1-py3-none-any.whl |
|---|---|
| Size | 78.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
206a8b7b3b5bee2966b40660cc7ae7d65b7230bfeaf541f4dd0a0450ba65b740
|
|
BLAKE2b-256 checksum How to use checksums |
a7ec8643108fe9406fab45f2725fbae2c36dac6c67d84d624e805c72d11b42b3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logSecurity for dependency-update-metrics 0.2.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for dependency_update_metrics-0.2.1.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.