dependency-update-metrics 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. dependency_update_metrics-0.1.0/LICENSE +21 -0
  2. dependency_update_metrics-0.1.0/MANIFEST.in +6 -0
  3. dependency_update_metrics-0.1.0/PKG-INFO +320 -0
  4. dependency_update_metrics-0.1.0/README.md +283 -0
  5. dependency_update_metrics-0.1.0/dependency_metrics/__init__.py +12 -0
  6. dependency_update_metrics-0.1.0/dependency_metrics/analyzer.py +1687 -0
  7. dependency_update_metrics-0.1.0/dependency_metrics/cache_config.py +69 -0
  8. dependency_update_metrics-0.1.0/dependency_metrics/cli.py +2060 -0
  9. dependency_update_metrics-0.1.0/dependency_metrics/depsdev_client.py +147 -0
  10. dependency_update_metrics-0.1.0/dependency_metrics/depsdev_resolver.py +445 -0
  11. dependency_update_metrics-0.1.0/dependency_metrics/interfaces.py +57 -0
  12. dependency_update_metrics-0.1.0/dependency_metrics/models.py +50 -0
  13. dependency_update_metrics-0.1.0/dependency_metrics/osv_builder.py +295 -0
  14. dependency_update_metrics-0.1.0/dependency_metrics/osv_service.py +251 -0
  15. dependency_update_metrics-0.1.0/dependency_metrics/pypi_resolver.py +153 -0
  16. dependency_update_metrics-0.1.0/dependency_metrics/reporting.py +227 -0
  17. dependency_update_metrics-0.1.0/dependency_metrics/resolvers.py +1366 -0
  18. dependency_update_metrics-0.1.0/dependency_metrics/time_utils.py +39 -0
  19. dependency_update_metrics-0.1.0/dependency_update_metrics.egg-info/PKG-INFO +320 -0
  20. dependency_update_metrics-0.1.0/dependency_update_metrics.egg-info/SOURCES.txt +45 -0
  21. dependency_update_metrics-0.1.0/dependency_update_metrics.egg-info/dependency_links.txt +1 -0
  22. dependency_update_metrics-0.1.0/dependency_update_metrics.egg-info/entry_points.txt +2 -0
  23. dependency_update_metrics-0.1.0/dependency_update_metrics.egg-info/requires.txt +15 -0
  24. dependency_update_metrics-0.1.0/dependency_update_metrics.egg-info/top_level.txt +1 -0
  25. dependency_update_metrics-0.1.0/pyproject.toml +73 -0
  26. dependency_update_metrics-0.1.0/setup.cfg +4 -0
  27. dependency_update_metrics-0.1.0/tests/test_basic.py +122 -0
  28. dependency_update_metrics-0.1.0/tests/test_bulk_csv.py +96 -0
  29. dependency_update_metrics-0.1.0/tests/test_cache_config.py +52 -0
  30. dependency_update_metrics-0.1.0/tests/test_cli_progress.py +391 -0
  31. dependency_update_metrics-0.1.0/tests/test_cli_resume.py +262 -0
  32. dependency_update_metrics-0.1.0/tests/test_crates_resolver.py +162 -0
  33. dependency_update_metrics-0.1.0/tests/test_dependency_first_use.py +169 -0
  34. dependency_update_metrics-0.1.0/tests/test_depsdev_client.py +244 -0
  35. dependency_update_metrics-0.1.0/tests/test_depsdev_resolver.py +949 -0
  36. dependency_update_metrics-0.1.0/tests/test_metamorphic.py +156 -0
  37. dependency_update_metrics-0.1.0/tests/test_models.py +50 -0
  38. dependency_update_metrics-0.1.0/tests/test_npm_resolver.py +326 -0
  39. dependency_update_metrics-0.1.0/tests/test_npm_semver.py +29 -0
  40. dependency_update_metrics-0.1.0/tests/test_osv.py +184 -0
  41. dependency_update_metrics-0.1.0/tests/test_per_release.py +541 -0
  42. dependency_update_metrics-0.1.0/tests/test_pypi_resolver.py +260 -0
  43. dependency_update_metrics-0.1.0/tests/test_pypi_version_resolution.py +121 -0
  44. dependency_update_metrics-0.1.0/tests/test_reporting.py +116 -0
  45. dependency_update_metrics-0.1.0/tests/test_resolver_cache.py +411 -0
  46. dependency_update_metrics-0.1.0/tests/test_severity_breakdown.py +340 -0
  47. dependency_update_metrics-0.1.0/tests/test_time_utils.py +49 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Imranur Rahman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,6 @@
1
+ include README.md
2
+ include LICENSE
3
+ include pyproject.toml
4
+ recursive-include dependency_metrics *.py
5
+ recursive-exclude * __pycache__
6
+ recursive-exclude * *.py[co]
@@ -0,0 +1,320 @@
1
+ Metadata-Version: 2.4
2
+ Name: dependency-update-metrics
3
+ Version: 0.1.0
4
+ Summary: A tool for analyzing time-to-update and time-to-remediate metrics for package dependencies
5
+ Author-email: Imranur Rahman <irahman3@ncsu.edu>, Mahzabina Tamanna <mtamman@ncsu.edu>, Laurie Williams <lawilli3@ncsu.edu>
6
+ License: MIT
7
+ Project-URL: Homepage, https://imranur-rahman.github.io/dependency-update-metrics/
8
+ Project-URL: Bug Reports, https://github.com/imranur-rahman/dependency-update-metrics/issues
9
+ Project-URL: Source, https://github.com/imranur-rahman/dependency-update-metrics
10
+ Keywords: dependencies,metrics,security,vulnerabilities,npm,pypi
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Topic :: Software Development :: Build Tools
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Requires-Dist: pandas>=1.3.0
23
+ Requires-Dist: requests>=2.25.0
24
+ Requires-Dist: tqdm>=4.60.0
25
+ Requires-Dist: packaging>=21.0
26
+ Requires-Dist: openpyxl>=3.0.0
27
+ Requires-Dist: pyarrow>=6.0.0
28
+ Requires-Dist: psutil>=5.8.0
29
+ Requires-Dist: semantic-version>=2.10.0
30
+ Provides-Extra: dev
31
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
32
+ Requires-Dist: pytest-cov>=3.0.0; extra == "dev"
33
+ Requires-Dist: black>=22.0.0; extra == "dev"
34
+ Requires-Dist: flake8>=4.0.0; extra == "dev"
35
+ Requires-Dist: mypy>=0.950; extra == "dev"
36
+ Dynamic: license-file
37
+
38
+ # Dependency Update Metrics
39
+
40
+ 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).
41
+
42
+ ## Features
43
+
44
+ - **Multi-ecosystem support**: Analyze packages from npm, PyPI, and Cargo/crates.io
45
+ - **Time-to-Update (TTU)**: Measure how long dependencies stay behind the latest version
46
+ - **Time-to-Remediate (TTR)**: Measure how long known vulnerabilities remain unpatched
47
+ - **Flexible weighting**: Support for linear, exponential, inverse, or no weighting
48
+ - **OSV integration**: Uses the Open Source Vulnerabilities database for security analysis
49
+ - **Export capabilities**: Export detailed worksheets and OSV data
50
+
51
+ ## Installation
52
+
53
+ ### From PyPI (when published)
54
+
55
+ ```bash
56
+ pip install dependency-update-metrics
57
+ ```
58
+
59
+ ### From source
60
+
61
+ ```bash
62
+ git clone https://github.com/imranur-rahman/dependency-update-metrics.git
63
+ cd dependency-update-metrics
64
+ pip install -e .
65
+ ```
66
+
67
+ ## Usage
68
+
69
+ ### As a command-line tool
70
+
71
+ ```bash
72
+ # Basic usage
73
+ dependency-metrics --ecosystem npm --package express
74
+
75
+ # With date range
76
+ dependency-metrics --ecosystem npm --package express \
77
+ --start-date 2020-01-01 \
78
+ --end-date 2023-12-31
79
+
80
+ # With exponential weighting
81
+ dependency-metrics --ecosystem npm --package express \
82
+ --weighting-type exponential \
83
+ --half-life 180
84
+
85
+ # Build OSV database first (one-time operation)
86
+ dependency-metrics --ecosystem npm --package express --build-osv
87
+
88
+ # Export detailed worksheets
89
+ dependency-metrics --ecosystem npm --package express --get-worksheets
90
+
91
+ # Get OSV vulnerability data
92
+ dependency-metrics --ecosystem npm --package express --get-osv
93
+
94
+ # Test a PyPI package
95
+ dependency-metrics --ecosystem pypi --package requests --start-date 2020-01-01 --end-date 2021-01-01 --get-worksheets
96
+
97
+ # Test a Cargo/crates.io package
98
+ dependency-metrics --ecosystem cargo --package serde --start-date 2020-01-01 --end-date 2024-01-01
99
+
100
+ # Bulk CSV input (parallel by default)
101
+ dependency-metrics --input-csv ./input.csv --workers 8 --output-dir ./output
102
+
103
+ # Resume an interrupted bulk run
104
+ dependency-metrics --input-csv ./input.csv --output-dir ./output --resume
105
+
106
+ # Per-release analysis: MTTU/MTTR at every release of the parent package
107
+ dependency-metrics --ecosystem pypi --package requests \
108
+ --start-date 2020-01-01 --end-date 2021-01-01 \
109
+ --per-release --get-worksheets
110
+
111
+ # Per-release in bulk CSV mode
112
+ dependency-metrics --input-csv ./input.csv --per-release --workers 4
113
+ ```
114
+
115
+ Input CSV columns: `ecosystem`, `package_name`, optional `start_date`, optional `end_date`.
116
+ Accepted ecosystem values are `npm`, `pypi`, `cargo`, and `crates.io`; `crates.io` is normalized to `cargo` in outputs.
117
+ If `end_date` is omitted, the CLI `--end-date` value is used; when `--end-date` is also omitted, it defaults to today.
118
+ Extra columns are allowed; duplicates are removed by `ecosystem, package_name, end_date`.
119
+ Bulk mode computes metrics per package using the latest dependency set.
120
+
121
+ ### As a Python module
122
+
123
+ ```python
124
+ from datetime import datetime
125
+ from pathlib import Path
126
+ from dependency_metrics.analyzer import DependencyAnalyzer
127
+
128
+ # Create analyzer
129
+ analyzer = DependencyAnalyzer(
130
+ ecosystem="npm",
131
+ package="express",
132
+ start_date=datetime(2020, 1, 1),
133
+ end_date=datetime(2023, 12, 31),
134
+ weighting_type="exponential",
135
+ half_life=180,
136
+ output_dir=Path("./output")
137
+ )
138
+
139
+ # Run analysis
140
+ results = analyzer.analyze()
141
+
142
+ print(f"Average TTU: {results['ttu']:.2f} days")
143
+ print(f"Average TTR: {results['ttr']:.2f} days")
144
+ ```
145
+
146
+ ## Command-line Arguments
147
+
148
+ - `--ecosystem`: Ecosystem to analyze (`npm`, `pypi`, `cargo`, or `crates.io`) [Required unless `--input-csv`]
149
+ - `--package`: Package name to analyze [Required unless `--input-csv`]
150
+ - `--input-csv`: CSV file with columns `ecosystem, package_name`, optional `start_date`, and optional `end_date`. If `end_date` is omitted, `--end-date` is used.
151
+ - `--start-date`: Start date for analysis (YYYY-MM-DD) [Default: 1900-01-01]
152
+ - `--end-date`: End date for analysis (YYYY-MM-DD) [Default: today]
153
+ - `--weighting-type`: Weighting method (`linear`, `exponential`, `inverse`, `disable`) [Default: disable]
154
+ - `--half-life`: Half-life in days (required for exponential weighting)
155
+ - `--build-osv`: Build/update the OSV vulnerability database
156
+ - `--get-osv`: Export OSV data for the package's dependencies
157
+ - `--get-worksheets`: Export detailed analysis worksheets to Excel
158
+ - `--output-dir`: Output directory for results [Default: ./output]
159
+ - `--workers`: Number of parallel workers for bulk CSV mode [Default: min(8, CPU count)]
160
+ - `--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]
161
+ - `--resume`: Resume bulk CSV runs by skipping completed rows and retrying errors
162
+ - `--per-release`: Compute MTTU/MTTR at every release of the parent package within the window (one output row per release)
163
+
164
+ ## Weighting Methods
165
+
166
+ ### Disable (default)
167
+ No weighting applied. All time periods are weighted equally.
168
+
169
+ ### Linear
170
+ Weight decreases linearly with age:
171
+ ```
172
+ weight = 1 - (age / max_age)
173
+ ```
174
+
175
+ ### Exponential
176
+ Weight decreases exponentially based on half-life:
177
+ ```
178
+ weight = exp(-λ * age)
179
+ where λ = ln(2) / half_life
180
+ ```
181
+
182
+ ### Inverse
183
+ Weight is inversely proportional to age:
184
+ ```
185
+ weight = 1 / (1 + age)
186
+ ```
187
+
188
+ ## Output
189
+
190
+ The tool generates several outputs in the specified output directory:
191
+
192
+ 1. **JSON results file**: Contains TTU, TTR, and metadata
193
+ 2. **OSV data** (with `--get-osv`): CSV file with vulnerability information
194
+ 3. **Excel worksheets** (with `--get-worksheets`): Detailed analysis for each dependency
195
+ 4. **Bulk CSV results** (with `--input-csv`): Summary CSV, plus an optional dependency details CSV with `--write-dependency-details`
196
+ 5. **Per-release results** (with `--per-release`): One row per package release with `package_version`, `package_release_date`, `window_start`, `window_end`, `mttu`, `mttr`
197
+
198
+ ### Example JSON output
199
+
200
+ ```json
201
+ {
202
+ "package": "express",
203
+ "ecosystem": "npm",
204
+ "version": "4.18.2",
205
+ "start_date": "2020-01-01T00:00:00",
206
+ "end_date": "2023-12-31T00:00:00",
207
+ "weighting_type": "exponential",
208
+ "half_life": 180,
209
+ "ttu": 45.32,
210
+ "ttr": 12.15,
211
+ "num_dependencies": 30
212
+ }
213
+ ```
214
+
215
+ ## How It Works
216
+
217
+ 1. **Package Analysis**: Fetches package metadata from the registry
218
+ 2. **Dependency Extraction**: Extracts dependencies from the package version closest to the end date
219
+ 3. **Timeline Construction**: Creates a timeline of version releases for each dependency
220
+ 4. **Version Resolution**: Resolves dependency versions at each time interval via ecosystem resolvers
221
+ 5. **Remediation Check**: Uses the OSV service to determine vulnerability remediation
222
+ 6. **Metric Calculation**:
223
+ - **TTU**: Measures time when dependency is not at the highest available version
224
+ - **TTR**: Measures time when dependency has known vulnerabilities
225
+ 7. **Weighting**: Applies optional time-based weighting to prioritize recent periods
226
+ 8. **Aggregation**: Averages metrics across all dependencies
227
+
228
+ ## Architecture Notes
229
+
230
+ - Ecosystem-specific resolution is encapsulated in `dependency_metrics/resolvers.py`.
231
+ - OSV remediation checks are handled by `dependency_metrics/osv_service.py`.
232
+ - Reporting/export helpers live in `dependency_metrics/reporting.py`.
233
+ - Bulk CSV mode groups rows by `(ecosystem, package_name)` and processes each group sequentially while running groups in parallel to maximize cache reuse.
234
+ - For bulk runs, a single package analysis computes metrics across multiple end dates using the latest dependency set.
235
+ - `--per-release` mode uses `analyze_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]`.
236
+ - **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()`'s `depsdev_package` namespace, and each resolver's `fetch_package_metadata()`/`_get_npm_time_data()` under the `metadata`/`npm_time` namespaces) cache the *entire*, mutable version list. Those lookups compare the requested `end_date` against 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-dir` won'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, fixed `end_date` values benefit from the cache staying warm across runs.
237
+
238
+ ## Requirements
239
+
240
+ - Python 3.10+
241
+ - git (for auto-installing the patched pip on first PyPI analysis)
242
+ - npm CLI (for npm ecosystem analysis) `sudo apt install nodejs npm`
243
+ - Internet connection (for fetching package metadata and OSV data)
244
+
245
+ > **Note:** PyPI analysis uses a patched pip fork that adds date-bounded version
246
+ > resolution. The tool clones it automatically to `~/.dependency_metrics/vendor/pip`
247
+ > on first use — no manual setup required.
248
+
249
+ ## Development
250
+
251
+ ### Setup development environment
252
+
253
+ ```bash
254
+ git clone https://github.com/imranur-rahman/dependency-update-metrics.git
255
+ cd dependency-update-metrics
256
+ pip install -e ".[dev]"
257
+ ```
258
+
259
+ ### Run tests
260
+
261
+ ```bash
262
+ pytest
263
+ ```
264
+
265
+ ### Code formatting
266
+
267
+ ```bash
268
+ black dependency_metrics/
269
+ ```
270
+
271
+ ## Publishing to PyPI
272
+
273
+ ### Prepare for publishing
274
+
275
+ ```bash
276
+ # Install build tools
277
+ pip install build twine
278
+
279
+ # Build distribution
280
+ python -m build
281
+
282
+ # Check distribution
283
+ twine check dist/*
284
+ ```
285
+
286
+ ### Upload to PyPI
287
+
288
+ ```bash
289
+ # Upload to Test PyPI first
290
+ twine upload --repository testpypi dist/*
291
+
292
+ # If all looks good, upload to PyPI
293
+ twine upload dist/*
294
+ ```
295
+
296
+ ## License
297
+
298
+ MIT License - see LICENSE file for details
299
+
300
+ ## Contributing
301
+
302
+ Contributions are welcome! Please feel free to submit a Pull Request.
303
+
304
+ ## Citation
305
+
306
+ If you use this tool in your research, please cite:
307
+
308
+ ```bibtex
309
+ @software{dependency_metrics,
310
+ author = {Rahman, Imranur},
311
+ title = {Dependency Update Metrics},
312
+ year = {2025},
313
+ url = {https://github.com/imranur-rahman/dependency-update-metrics}
314
+ }
315
+ ```
316
+
317
+ ## Acknowledgments
318
+
319
+ - Uses data from [Open Source Vulnerabilities (OSV)](https://osv.dev/)
320
+ - Package metadata from [npm Registry](https://registry.npmjs.org/) and [PyPI](https://pypi.org/)
@@ -0,0 +1,283 @@
1
+ # Dependency Update Metrics
2
+
3
+ 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).
4
+
5
+ ## Features
6
+
7
+ - **Multi-ecosystem support**: Analyze packages from npm, PyPI, and Cargo/crates.io
8
+ - **Time-to-Update (TTU)**: Measure how long dependencies stay behind the latest version
9
+ - **Time-to-Remediate (TTR)**: Measure how long known vulnerabilities remain unpatched
10
+ - **Flexible weighting**: Support for linear, exponential, inverse, or no weighting
11
+ - **OSV integration**: Uses the Open Source Vulnerabilities database for security analysis
12
+ - **Export capabilities**: Export detailed worksheets and OSV data
13
+
14
+ ## Installation
15
+
16
+ ### From PyPI (when published)
17
+
18
+ ```bash
19
+ pip install dependency-update-metrics
20
+ ```
21
+
22
+ ### From source
23
+
24
+ ```bash
25
+ git clone https://github.com/imranur-rahman/dependency-update-metrics.git
26
+ cd dependency-update-metrics
27
+ pip install -e .
28
+ ```
29
+
30
+ ## Usage
31
+
32
+ ### As a command-line tool
33
+
34
+ ```bash
35
+ # Basic usage
36
+ dependency-metrics --ecosystem npm --package express
37
+
38
+ # With date range
39
+ dependency-metrics --ecosystem npm --package express \
40
+ --start-date 2020-01-01 \
41
+ --end-date 2023-12-31
42
+
43
+ # With exponential weighting
44
+ dependency-metrics --ecosystem npm --package express \
45
+ --weighting-type exponential \
46
+ --half-life 180
47
+
48
+ # Build OSV database first (one-time operation)
49
+ dependency-metrics --ecosystem npm --package express --build-osv
50
+
51
+ # Export detailed worksheets
52
+ dependency-metrics --ecosystem npm --package express --get-worksheets
53
+
54
+ # Get OSV vulnerability data
55
+ dependency-metrics --ecosystem npm --package express --get-osv
56
+
57
+ # Test a PyPI package
58
+ dependency-metrics --ecosystem pypi --package requests --start-date 2020-01-01 --end-date 2021-01-01 --get-worksheets
59
+
60
+ # Test a Cargo/crates.io package
61
+ dependency-metrics --ecosystem cargo --package serde --start-date 2020-01-01 --end-date 2024-01-01
62
+
63
+ # Bulk CSV input (parallel by default)
64
+ dependency-metrics --input-csv ./input.csv --workers 8 --output-dir ./output
65
+
66
+ # Resume an interrupted bulk run
67
+ dependency-metrics --input-csv ./input.csv --output-dir ./output --resume
68
+
69
+ # Per-release analysis: MTTU/MTTR at every release of the parent package
70
+ dependency-metrics --ecosystem pypi --package requests \
71
+ --start-date 2020-01-01 --end-date 2021-01-01 \
72
+ --per-release --get-worksheets
73
+
74
+ # Per-release in bulk CSV mode
75
+ dependency-metrics --input-csv ./input.csv --per-release --workers 4
76
+ ```
77
+
78
+ Input CSV columns: `ecosystem`, `package_name`, optional `start_date`, optional `end_date`.
79
+ Accepted ecosystem values are `npm`, `pypi`, `cargo`, and `crates.io`; `crates.io` is normalized to `cargo` in outputs.
80
+ If `end_date` is omitted, the CLI `--end-date` value is used; when `--end-date` is also omitted, it defaults to today.
81
+ Extra columns are allowed; duplicates are removed by `ecosystem, package_name, end_date`.
82
+ Bulk mode computes metrics per package using the latest dependency set.
83
+
84
+ ### As a Python module
85
+
86
+ ```python
87
+ from datetime import datetime
88
+ from pathlib import Path
89
+ from dependency_metrics.analyzer import DependencyAnalyzer
90
+
91
+ # Create analyzer
92
+ analyzer = DependencyAnalyzer(
93
+ ecosystem="npm",
94
+ package="express",
95
+ start_date=datetime(2020, 1, 1),
96
+ end_date=datetime(2023, 12, 31),
97
+ weighting_type="exponential",
98
+ half_life=180,
99
+ output_dir=Path("./output")
100
+ )
101
+
102
+ # Run analysis
103
+ results = analyzer.analyze()
104
+
105
+ print(f"Average TTU: {results['ttu']:.2f} days")
106
+ print(f"Average TTR: {results['ttr']:.2f} days")
107
+ ```
108
+
109
+ ## Command-line Arguments
110
+
111
+ - `--ecosystem`: Ecosystem to analyze (`npm`, `pypi`, `cargo`, or `crates.io`) [Required unless `--input-csv`]
112
+ - `--package`: Package name to analyze [Required unless `--input-csv`]
113
+ - `--input-csv`: CSV file with columns `ecosystem, package_name`, optional `start_date`, and optional `end_date`. If `end_date` is omitted, `--end-date` is used.
114
+ - `--start-date`: Start date for analysis (YYYY-MM-DD) [Default: 1900-01-01]
115
+ - `--end-date`: End date for analysis (YYYY-MM-DD) [Default: today]
116
+ - `--weighting-type`: Weighting method (`linear`, `exponential`, `inverse`, `disable`) [Default: disable]
117
+ - `--half-life`: Half-life in days (required for exponential weighting)
118
+ - `--build-osv`: Build/update the OSV vulnerability database
119
+ - `--get-osv`: Export OSV data for the package's dependencies
120
+ - `--get-worksheets`: Export detailed analysis worksheets to Excel
121
+ - `--output-dir`: Output directory for results [Default: ./output]
122
+ - `--workers`: Number of parallel workers for bulk CSV mode [Default: min(8, CPU count)]
123
+ - `--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]
124
+ - `--resume`: Resume bulk CSV runs by skipping completed rows and retrying errors
125
+ - `--per-release`: Compute MTTU/MTTR at every release of the parent package within the window (one output row per release)
126
+
127
+ ## Weighting Methods
128
+
129
+ ### Disable (default)
130
+ No weighting applied. All time periods are weighted equally.
131
+
132
+ ### Linear
133
+ Weight decreases linearly with age:
134
+ ```
135
+ weight = 1 - (age / max_age)
136
+ ```
137
+
138
+ ### Exponential
139
+ Weight decreases exponentially based on half-life:
140
+ ```
141
+ weight = exp(-λ * age)
142
+ where λ = ln(2) / half_life
143
+ ```
144
+
145
+ ### Inverse
146
+ Weight is inversely proportional to age:
147
+ ```
148
+ weight = 1 / (1 + age)
149
+ ```
150
+
151
+ ## Output
152
+
153
+ The tool generates several outputs in the specified output directory:
154
+
155
+ 1. **JSON results file**: Contains TTU, TTR, and metadata
156
+ 2. **OSV data** (with `--get-osv`): CSV file with vulnerability information
157
+ 3. **Excel worksheets** (with `--get-worksheets`): Detailed analysis for each dependency
158
+ 4. **Bulk CSV results** (with `--input-csv`): Summary CSV, plus an optional dependency details CSV with `--write-dependency-details`
159
+ 5. **Per-release results** (with `--per-release`): One row per package release with `package_version`, `package_release_date`, `window_start`, `window_end`, `mttu`, `mttr`
160
+
161
+ ### Example JSON output
162
+
163
+ ```json
164
+ {
165
+ "package": "express",
166
+ "ecosystem": "npm",
167
+ "version": "4.18.2",
168
+ "start_date": "2020-01-01T00:00:00",
169
+ "end_date": "2023-12-31T00:00:00",
170
+ "weighting_type": "exponential",
171
+ "half_life": 180,
172
+ "ttu": 45.32,
173
+ "ttr": 12.15,
174
+ "num_dependencies": 30
175
+ }
176
+ ```
177
+
178
+ ## How It Works
179
+
180
+ 1. **Package Analysis**: Fetches package metadata from the registry
181
+ 2. **Dependency Extraction**: Extracts dependencies from the package version closest to the end date
182
+ 3. **Timeline Construction**: Creates a timeline of version releases for each dependency
183
+ 4. **Version Resolution**: Resolves dependency versions at each time interval via ecosystem resolvers
184
+ 5. **Remediation Check**: Uses the OSV service to determine vulnerability remediation
185
+ 6. **Metric Calculation**:
186
+ - **TTU**: Measures time when dependency is not at the highest available version
187
+ - **TTR**: Measures time when dependency has known vulnerabilities
188
+ 7. **Weighting**: Applies optional time-based weighting to prioritize recent periods
189
+ 8. **Aggregation**: Averages metrics across all dependencies
190
+
191
+ ## Architecture Notes
192
+
193
+ - Ecosystem-specific resolution is encapsulated in `dependency_metrics/resolvers.py`.
194
+ - OSV remediation checks are handled by `dependency_metrics/osv_service.py`.
195
+ - Reporting/export helpers live in `dependency_metrics/reporting.py`.
196
+ - Bulk CSV mode groups rows by `(ecosystem, package_name)` and processes each group sequentially while running groups in parallel to maximize cache reuse.
197
+ - For bulk runs, a single package analysis computes metrics across multiple end dates using the latest dependency set.
198
+ - `--per-release` mode uses `analyze_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]`.
199
+ - **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()`'s `depsdev_package` namespace, and each resolver's `fetch_package_metadata()`/`_get_npm_time_data()` under the `metadata`/`npm_time` namespaces) cache the *entire*, mutable version list. Those lookups compare the requested `end_date` against 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-dir` won'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, fixed `end_date` values benefit from the cache staying warm across runs.
200
+
201
+ ## Requirements
202
+
203
+ - Python 3.10+
204
+ - git (for auto-installing the patched pip on first PyPI analysis)
205
+ - npm CLI (for npm ecosystem analysis) `sudo apt install nodejs npm`
206
+ - Internet connection (for fetching package metadata and OSV data)
207
+
208
+ > **Note:** PyPI analysis uses a patched pip fork that adds date-bounded version
209
+ > resolution. The tool clones it automatically to `~/.dependency_metrics/vendor/pip`
210
+ > on first use — no manual setup required.
211
+
212
+ ## Development
213
+
214
+ ### Setup development environment
215
+
216
+ ```bash
217
+ git clone https://github.com/imranur-rahman/dependency-update-metrics.git
218
+ cd dependency-update-metrics
219
+ pip install -e ".[dev]"
220
+ ```
221
+
222
+ ### Run tests
223
+
224
+ ```bash
225
+ pytest
226
+ ```
227
+
228
+ ### Code formatting
229
+
230
+ ```bash
231
+ black dependency_metrics/
232
+ ```
233
+
234
+ ## Publishing to PyPI
235
+
236
+ ### Prepare for publishing
237
+
238
+ ```bash
239
+ # Install build tools
240
+ pip install build twine
241
+
242
+ # Build distribution
243
+ python -m build
244
+
245
+ # Check distribution
246
+ twine check dist/*
247
+ ```
248
+
249
+ ### Upload to PyPI
250
+
251
+ ```bash
252
+ # Upload to Test PyPI first
253
+ twine upload --repository testpypi dist/*
254
+
255
+ # If all looks good, upload to PyPI
256
+ twine upload dist/*
257
+ ```
258
+
259
+ ## License
260
+
261
+ MIT License - see LICENSE file for details
262
+
263
+ ## Contributing
264
+
265
+ Contributions are welcome! Please feel free to submit a Pull Request.
266
+
267
+ ## Citation
268
+
269
+ If you use this tool in your research, please cite:
270
+
271
+ ```bibtex
272
+ @software{dependency_metrics,
273
+ author = {Rahman, Imranur},
274
+ title = {Dependency Update Metrics},
275
+ year = {2025},
276
+ url = {https://github.com/imranur-rahman/dependency-update-metrics}
277
+ }
278
+ ```
279
+
280
+ ## Acknowledgments
281
+
282
+ - Uses data from [Open Source Vulnerabilities (OSV)](https://osv.dev/)
283
+ - Package metadata from [npm Registry](https://registry.npmjs.org/) and [PyPI](https://pypi.org/)
@@ -0,0 +1,12 @@
1
+ """
2
+ Dependency Update Metrics Tool
3
+
4
+ A tool for analyzing time-to-update and time-to-remediate metrics for package dependencies.
5
+ """
6
+
7
+ __version__ = "0.1.0"
8
+ __author__ = "Imranur Rahman"
9
+
10
+ from .cli import main
11
+
12
+ __all__ = ["main"]