@rainy-updates/cli 0.5.1-rc.3 → 0.5.2-rc.1
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.
- package/CHANGELOG.md +145 -2
- package/README.md +92 -26
- package/dist/bin/cli.js +87 -7
- package/dist/commands/audit/fetcher.d.ts +6 -0
- package/dist/commands/audit/fetcher.js +79 -0
- package/dist/commands/audit/mapper.d.ts +16 -0
- package/dist/commands/audit/mapper.js +61 -0
- package/dist/commands/audit/parser.d.ts +3 -0
- package/dist/commands/audit/parser.js +87 -0
- package/dist/commands/audit/runner.d.ts +7 -0
- package/dist/commands/audit/runner.js +64 -0
- package/dist/commands/bisect/engine.d.ts +12 -0
- package/dist/commands/bisect/engine.js +89 -0
- package/dist/commands/bisect/oracle.d.ts +7 -0
- package/dist/commands/bisect/oracle.js +36 -0
- package/dist/commands/bisect/parser.d.ts +2 -0
- package/dist/commands/bisect/parser.js +73 -0
- package/dist/commands/bisect/runner.d.ts +6 -0
- package/dist/commands/bisect/runner.js +27 -0
- package/dist/commands/health/parser.d.ts +2 -0
- package/dist/commands/health/parser.js +90 -0
- package/dist/commands/health/runner.d.ts +7 -0
- package/dist/commands/health/runner.js +130 -0
- package/dist/commands/licenses/parser.d.ts +2 -0
- package/dist/commands/licenses/parser.js +116 -0
- package/dist/commands/licenses/runner.d.ts +9 -0
- package/dist/commands/licenses/runner.js +163 -0
- package/dist/commands/licenses/sbom.d.ts +10 -0
- package/dist/commands/licenses/sbom.js +70 -0
- package/dist/commands/resolve/graph/builder.d.ts +20 -0
- package/dist/commands/resolve/graph/builder.js +183 -0
- package/dist/commands/resolve/graph/conflict.d.ts +20 -0
- package/dist/commands/resolve/graph/conflict.js +52 -0
- package/dist/commands/resolve/graph/resolver.d.ts +17 -0
- package/dist/commands/resolve/graph/resolver.js +71 -0
- package/dist/commands/resolve/parser.d.ts +2 -0
- package/dist/commands/resolve/parser.js +89 -0
- package/dist/commands/resolve/runner.d.ts +13 -0
- package/dist/commands/resolve/runner.js +136 -0
- package/dist/commands/snapshot/parser.d.ts +2 -0
- package/dist/commands/snapshot/parser.js +80 -0
- package/dist/commands/snapshot/runner.d.ts +11 -0
- package/dist/commands/snapshot/runner.js +115 -0
- package/dist/commands/snapshot/store.d.ts +35 -0
- package/dist/commands/snapshot/store.js +158 -0
- package/dist/commands/unused/matcher.d.ts +22 -0
- package/dist/commands/unused/matcher.js +95 -0
- package/dist/commands/unused/parser.d.ts +2 -0
- package/dist/commands/unused/parser.js +95 -0
- package/dist/commands/unused/runner.d.ts +11 -0
- package/dist/commands/unused/runner.js +113 -0
- package/dist/commands/unused/scanner.d.ts +18 -0
- package/dist/commands/unused/scanner.js +129 -0
- package/dist/config/loader.d.ts +5 -1
- package/dist/config/policy.d.ts +4 -0
- package/dist/config/policy.js +2 -0
- package/dist/core/check.js +56 -3
- package/dist/core/fix-pr-batch.js +3 -2
- package/dist/core/fix-pr.js +19 -4
- package/dist/core/impact.d.ts +36 -0
- package/dist/core/impact.js +82 -0
- package/dist/core/init-ci.js +3 -3
- package/dist/core/options.d.ts +22 -1
- package/dist/core/options.js +151 -13
- package/dist/core/summary.d.ts +1 -0
- package/dist/core/summary.js +11 -1
- package/dist/core/upgrade.js +10 -0
- package/dist/core/warm-cache.js +19 -1
- package/dist/output/format.js +4 -0
- package/dist/output/github.js +3 -0
- package/dist/registry/npm.d.ts +9 -2
- package/dist/registry/npm.js +87 -17
- package/dist/types/index.d.ts +236 -0
- package/dist/utils/lockfile.d.ts +5 -0
- package/dist/utils/lockfile.js +44 -0
- package/dist/utils/semver.d.ts +18 -0
- package/dist/utils/semver.js +88 -3
- package/package.json +13 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,151 @@
|
|
|
1
|
-
#
|
|
1
|
+
# CHANGELOG
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.5.2] - 2026-02-27
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **New `unused` command**: Detect unused and missing npm dependencies by statically scanning source files.
|
|
10
|
+
- Walks `src/` (configurable via `--src`) and extracts all import/require specifiers (ESM static, ESM dynamic, CJS, re-exports).
|
|
11
|
+
- Cross-references against `package.json` `dependencies`, `devDependencies`, and `optionalDependencies`.
|
|
12
|
+
- Reports two problem classes: `declared-not-imported` (unused bloat) and `imported-not-declared` (missing declarations).
|
|
13
|
+
- `--fix` — removes unused entries from `package.json` atomically (with `--dry-run` preview).
|
|
14
|
+
- `--no-dev` — skip `devDependencies` from the unused scan.
|
|
15
|
+
- `--json-file <path>` — write structured JSON report for CI pipelines.
|
|
16
|
+
- Exit code `1` when unused or missing dependencies are found.
|
|
17
|
+
|
|
18
|
+
- **New `resolve` command**: Pure-TS in-memory peer dependency conflict detector — **no `npm install` subprocess spawned**.
|
|
19
|
+
- Builds a `PeerGraph` from declared dependencies, enriched with `peerDependencies` fetched in parallel from the registry (cache-first — instant on warm cache, offline-capable).
|
|
20
|
+
- Performs a single-pass O(n × peers) BFS traversal using the new `satisfies()` semver util.
|
|
21
|
+
- Classifies conflicts as `error` (ERESOLVE-level, different major) or `warning` (soft peer incompatibility).
|
|
22
|
+
- Generates human-readable fix suggestions per conflict.
|
|
23
|
+
- `--after-update` — simulates proposed `rup check` updates in-memory _before_ writing anything, showing you peer conflicts before they happen.
|
|
24
|
+
- `--safe` — exits non-zero on any error-level conflict.
|
|
25
|
+
- `--json-file <path>` — write structured JSON conflict report.
|
|
26
|
+
- Exit code `1` when error-level conflicts are detected.
|
|
27
|
+
|
|
28
|
+
- **New `licenses` command**: SPDX license compliance scanning with SBOM generation.
|
|
29
|
+
- Fetches the `license` field from each dependency's npm packument in parallel.
|
|
30
|
+
- Normalizes raw license strings to SPDX 2.x identifiers.
|
|
31
|
+
- `--allow <spdx,...>` — allowlist mode: flag any package not in the list.
|
|
32
|
+
- `--deny <spdx,...>` — denylist mode: flag any package matching these identifiers.
|
|
33
|
+
- `--sbom <path>` — generate a standards-compliant **SPDX 2.3 JSON SBOM** document (`DESCRIBES` + `DEPENDS_ON` relationship graph, required by CISA/EU CRA mandates).
|
|
34
|
+
- `--json-file <path>` — write full license report as JSON.
|
|
35
|
+
- Exit code `1` when license violations are detected.
|
|
36
|
+
|
|
37
|
+
- **New `snapshot` command**: Save, list, restore, and diff dependency state snapshots.
|
|
38
|
+
- `rup snapshot save [--label <name>]` — captures `package.json` contents and lockfile hashes for all workspace packages into a lightweight JSON store (`.rup-snapshots.json`).
|
|
39
|
+
- `rup snapshot list` — shows all saved snapshots with timestamp and label.
|
|
40
|
+
- `rup snapshot restore <id|label>` — writes back captured `package.json` files atomically; prompts to re-run the package manager install.
|
|
41
|
+
- `rup snapshot diff <id|label>` — shows dependency version changes since the snapshot.
|
|
42
|
+
- JSON-file store (no SQLite dependency), human-readable and git-committable.
|
|
43
|
+
- `--store <path>` — custom store file location.
|
|
44
|
+
|
|
45
|
+
- **Impact Score engine** (`src/core/impact.ts`): Per-update risk assessment.
|
|
46
|
+
- Computes a 0–100 composite score: `diffTypeWeight` (patch=10, minor=25, major=55) + CVE presence bonus (+35) + workspace spread (up to +20).
|
|
47
|
+
- Ranks each update as `critical`, `high`, `medium`, or `low`.
|
|
48
|
+
- `applyImpactScores()` batch helper for the check/upgrade pipeline.
|
|
49
|
+
- ANSI `impactBadge()` for terminal table rendering (wired to `--show-impact` flag, coming in a follow-up).
|
|
50
|
+
|
|
51
|
+
- **`satisfies(version, range)` utility** (`src/utils/semver.ts`): Pure-TS semver range checker.
|
|
52
|
+
- Handles `^`, `~`, `>=`, `<=`, `>`, `<`, exact, `*`/empty (always true).
|
|
53
|
+
- Supports compound AND ranges (`>=1.0.0 <2.0.0`) and OR union ranges (`^16 || ^18`).
|
|
54
|
+
- Falls through gracefully on non-semver inputs (e.g., `workspace:*`, `latest`) — no false-positive conflicts.
|
|
55
|
+
- Used by `rup resolve` peer graph resolver.
|
|
56
|
+
|
|
57
|
+
### Architecture
|
|
58
|
+
|
|
59
|
+
- `unused`, `resolve`, `licenses`, and `snapshot` are fully isolated modules under `src/commands/`. All are lazy-loaded (dynamic `import()`) on first invocation — zero startup cost penalty.
|
|
60
|
+
- `src/core/options.ts` dispatches all 4 new commands to their isolated sub-parsers. `KNOWN_COMMANDS` now contains **13 entries**.
|
|
61
|
+
- `ParsedCliArgs` union extended with 4 new command variants.
|
|
62
|
+
- `src/types/index.ts` extended with: `ImpactScore`, `PeerNode`, `PeerGraph`, `PeerConflict`, `PeerConflictSeverity`, `UnusedKind`, `UnusedDependency`, `UnusedOptions`, `UnusedResult`, `PackageLicense`, `SbomDocument`, `SbomPackage`, `SbomRelationship`, `LicenseOptions`, `LicenseResult`, `SnapshotEntry`, `SnapshotAction`, `SnapshotOptions`, `SnapshotResult`, `ResolveOptions`, `ResolveResult`.
|
|
63
|
+
- `PackageUpdate` extended with optional `impactScore?: ImpactScore` and `homepage?: string` fields.
|
|
64
|
+
|
|
65
|
+
### Changed
|
|
66
|
+
|
|
67
|
+
- CLI global help updated to list all **13 commands** including `unused`, `resolve`, `licenses`, and `snapshot`.
|
|
68
|
+
- `src/bin/cli.ts` exit codes: `unused` exits `1` on any unused/missing dep; `resolve` exits `1` on error-level peer conflicts; `licenses` exits `1` on violations; `snapshot` exits `1` on store errors.
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## [0.5.1] - 2026-02-27
|
|
73
|
+
|
|
74
|
+
### Added
|
|
75
|
+
|
|
76
|
+
- **New `audit` command**: Scan dependencies for known CVEs using [OSV.dev](https://osv.dev) (Google's open vulnerability database). Runs queries in parallel for all installed packages.
|
|
77
|
+
- `--severity critical|high|medium|low` — Filter by minimum severity level
|
|
78
|
+
- `--fix` — Print the minimum-secure-version `npm install` command to patch advisories
|
|
79
|
+
- `--dry-run` — Preview without side effects
|
|
80
|
+
- `--report json` — Machine-readable JSON output
|
|
81
|
+
- `--json-file <path>` — Write JSON report to file for CI pipelines
|
|
82
|
+
- Exit code `1` when vulnerabilities are found; `0` when clean.
|
|
83
|
+
|
|
84
|
+
- **New `health` command**: Surface stale, deprecated, and unmaintained packages before they become liabilities.
|
|
85
|
+
- `--stale 12m|180d|365` — Flag packages with no release in the given period (supports months and days)
|
|
86
|
+
- `--deprecated` / `--no-deprecated` — Control deprecated package detection
|
|
87
|
+
- `--alternatives` — Suggest active alternatives for deprecated packages
|
|
88
|
+
- `--report json` — Machine-readable JSON output
|
|
89
|
+
- Exit code `1` when flagged packages are found.
|
|
90
|
+
|
|
91
|
+
- **New `bisect` command**: Binary search across semver versions to find the exact version that introduced a failing test or breaking change.
|
|
92
|
+
- `rup bisect <package> --cmd "<test command>"` — Specify test oracle command
|
|
93
|
+
- `--range <start>..<end>` — Narrow the search to a specific version range
|
|
94
|
+
- `--dry-run` — Simulate without installing anything
|
|
95
|
+
- Exit code `1` when a breaking version is identified.
|
|
96
|
+
|
|
97
|
+
- **New CLI binary aliases for developer ergonomics**:
|
|
98
|
+
- `rup` — Ultra-short power-user alias (e.g., `rup ci`, `rup audit`)
|
|
99
|
+
- `rainy-up` — Human-friendly alias (e.g., `rainy-up check`)
|
|
100
|
+
- `rainy-updates` retained for backwards compatibility with CI scripts.
|
|
101
|
+
|
|
102
|
+
### Architecture
|
|
103
|
+
|
|
104
|
+
- `bisect`, `audit`, and `health` are fully isolated modules under `src/commands/`. They are lazy-loaded (dynamic `import()`) only when their command is invoked — zero startup cost penalty.
|
|
105
|
+
- `src/core/options.ts` now dispatches `bisect`, `audit`, and `health` to their isolated sub-parsers, keeping the command router clean and extensible.
|
|
106
|
+
- New type definitions: `AuditOptions`, `AuditResult`, `CveAdvisory`, `BisectOptions`, `BisectResult`, `HealthOptions`, `HealthResult`, `PackageHealthMetric`.
|
|
107
|
+
|
|
108
|
+
### Changed
|
|
109
|
+
|
|
110
|
+
- CLI global help updated to list all 9 commands.
|
|
111
|
+
- Error messages now include `(rup)` in the binary identifier.
|
|
112
|
+
- `package.json` description updated to reflect DevOps-first positioning.
|
|
113
|
+
|
|
114
|
+
## [0.5.1-rc.4] - 2026-02-27
|
|
115
|
+
|
|
116
|
+
### Added
|
|
117
|
+
|
|
118
|
+
- New registry and stream controls:
|
|
119
|
+
- `--registry-timeout-ms <n>`
|
|
120
|
+
- `--registry-retries <n>`
|
|
121
|
+
- `--stream`
|
|
122
|
+
- New lockfile execution control:
|
|
123
|
+
- `--lockfile-mode preserve|update|error`
|
|
124
|
+
- Policy extensions:
|
|
125
|
+
- package rule `target` override
|
|
126
|
+
- package rule `autofix` control for fix-PR flows
|
|
127
|
+
- New additive summary/output metadata:
|
|
128
|
+
- `streamedEvents`
|
|
129
|
+
- `policyOverridesApplied`
|
|
130
|
+
- `registryAuthFailure`
|
|
131
|
+
- `streamed_events` GitHub output key
|
|
132
|
+
- `policy_overrides_applied` GitHub output key
|
|
133
|
+
- `registry_auth_failures` GitHub output key
|
|
134
|
+
|
|
135
|
+
### Changed
|
|
136
|
+
|
|
137
|
+
- Registry client now supports configurable retry count and timeout defaults.
|
|
138
|
+
- Registry resolution now supports `.npmrc` auth token/basic auth parsing for scoped/private registries.
|
|
139
|
+
- Fix-PR automation now excludes updates with `autofix: false`.
|
|
140
|
+
- CI workflow templates generated by `init-ci` now include stream mode and registry control flags.
|
|
141
|
+
- Upgrade flow now enforces explicit lockfile policy semantics via `--lockfile-mode`.
|
|
142
|
+
|
|
143
|
+
### Tests
|
|
144
|
+
|
|
145
|
+
- Extended options parsing tests for registry/stream/lockfile flags.
|
|
146
|
+
- Extended policy tests for `target` and `autofix` rule behavior.
|
|
147
|
+
- Updated output and summary tests for additive metadata fields.
|
|
148
|
+
|
|
5
149
|
## [0.5.1-rc.3] - 2026-02-27
|
|
6
150
|
|
|
7
151
|
### Fixed
|
|
@@ -182,7 +326,6 @@ All notable changes to this project are documented in this file.
|
|
|
182
326
|
- `--schedule weekly|daily|off`
|
|
183
327
|
- package-manager-aware install step generation (npm/pnpm)
|
|
184
328
|
|
|
185
|
-
|
|
186
329
|
## [0.4.0] - 2026-02-27
|
|
187
330
|
|
|
188
331
|
### Added
|
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# @rainy-updates/cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The fastest DevOps-first dependency CLI. Checks, audits, upgrades, bisects, and automates npm/pnpm dependencies in CI.
|
|
4
4
|
|
|
5
|
-
`@rainy-updates/cli` is built for teams that need fast dependency intelligence, policy-aware upgrades, and automation-ready output for CI/CD and pull request workflows.
|
|
5
|
+
`@rainy-updates/cli` is built for teams that need fast dependency intelligence, security auditing, policy-aware upgrades, and automation-ready output for CI/CD and pull request workflows.
|
|
6
6
|
|
|
7
7
|
## Why this package
|
|
8
8
|
|
|
@@ -15,47 +15,104 @@ Agentic CLI to detect, control, and apply dependency updates across npm/pnpm pro
|
|
|
15
15
|
## Install
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
|
|
18
|
+
# As a project dev dependency (recommended for teams)
|
|
19
|
+
npm install --save-dev @rainy-updates/cli
|
|
19
20
|
# or
|
|
20
21
|
pnpm add -D @rainy-updates/cli
|
|
21
22
|
```
|
|
22
23
|
|
|
23
|
-
|
|
24
|
+
Once installed, three binary aliases are available in your `node_modules/.bin/`:
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
| Alias | Use case |
|
|
27
|
+
| --------------- | ------------------------------------------- |
|
|
28
|
+
| `rup` | Power-user shortcut — `rup ci`, `rup audit` |
|
|
29
|
+
| `rainy-up` | Human-friendly — `rainy-up check` |
|
|
30
|
+
| `rainy-updates` | Backwards-compatible (safe in CI scripts) |
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# All three are identical — use whichever you prefer:
|
|
34
|
+
rup check
|
|
35
|
+
rainy-up check
|
|
36
|
+
rainy-updates check
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### One-off usage with npx (no install required)
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Always works without installing:
|
|
43
|
+
npx @rainy-updates/cli check
|
|
44
|
+
npx @rainy-updates/cli audit --severity high
|
|
45
|
+
npx @rainy-updates/cli ci --workspace --mode strict
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
> **Note:** The short aliases (`rup`, `rainy-up`) only work after installing the package. For one-off `npx` runs, use `npx @rainy-updates/cli <command>`.
|
|
49
|
+
|
|
50
|
+
## Commands
|
|
51
|
+
|
|
52
|
+
### Dependency management
|
|
53
|
+
|
|
54
|
+
- `check` — analyze dependencies and report available updates
|
|
55
|
+
- `upgrade` — rewrite dependency ranges in manifests, optionally install lockfile updates
|
|
56
|
+
- `ci` — run CI-focused dependency automation (warm cache, check/upgrade, policy gates)
|
|
57
|
+
- `warm-cache` — prefetch package metadata for fast and offline checks
|
|
58
|
+
- `baseline` — save and compare dependency baseline snapshots
|
|
59
|
+
|
|
60
|
+
### Security & health (_new in v0.5.1_)
|
|
61
|
+
|
|
62
|
+
- `audit` — scan dependencies for CVEs using [OSV.dev](https://osv.dev) (Google's open vulnerability database)
|
|
63
|
+
- `health` — detect stale, deprecated, and unmaintained packages before they become liabilities
|
|
64
|
+
- `bisect` — binary-search across semver versions to find the exact version that broke your tests
|
|
30
65
|
|
|
31
66
|
## Quick usage
|
|
32
67
|
|
|
68
|
+
> Commands work with `npx` (no install) **or** with the `rup` / `rainy-up` shortcut if the package is installed.
|
|
69
|
+
|
|
33
70
|
```bash
|
|
34
71
|
# 1) Detect updates
|
|
35
72
|
npx @rainy-updates/cli check --format table
|
|
73
|
+
rup check --format table # if installed
|
|
36
74
|
|
|
37
75
|
# 2) Strict CI mode (non-zero when updates exist)
|
|
38
76
|
npx @rainy-updates/cli check --workspace --ci --format json --json-file .artifacts/updates.json
|
|
77
|
+
rup check --workspace --ci --format json --json-file .artifacts/updates.json
|
|
39
78
|
|
|
40
|
-
#
|
|
41
|
-
npx @rainy-updates/cli ci --workspace --mode strict --format github
|
|
79
|
+
# 3) CI orchestration with policy gates
|
|
80
|
+
npx @rainy-updates/cli ci --workspace --mode strict --format github
|
|
81
|
+
rup ci --workspace --mode strict --format github
|
|
42
82
|
|
|
43
|
-
#
|
|
83
|
+
# 4) Batch fix branches by scope (enterprise)
|
|
44
84
|
npx @rainy-updates/cli ci --workspace --mode enterprise --group-by scope --fix-pr --fix-pr-batch-size 2
|
|
85
|
+
rup ci --workspace --mode enterprise --group-by scope --fix-pr --fix-pr-batch-size 2
|
|
45
86
|
|
|
46
|
-
#
|
|
87
|
+
# 5) Apply upgrades with workspace sync
|
|
47
88
|
npx @rainy-updates/cli upgrade --target latest --workspace --sync --install
|
|
89
|
+
rup upgrade --target latest --workspace --sync --install
|
|
48
90
|
|
|
49
|
-
#
|
|
50
|
-
npx @rainy-updates/cli check --workspace --fix-pr --fix-branch chore/rainy-updates
|
|
51
|
-
|
|
52
|
-
# 4) Warm cache for deterministic offline checks
|
|
91
|
+
# 6) Warm cache → deterministic offline CI check
|
|
53
92
|
npx @rainy-updates/cli warm-cache --workspace --concurrency 32
|
|
54
93
|
npx @rainy-updates/cli check --workspace --offline --ci
|
|
55
94
|
|
|
56
|
-
#
|
|
95
|
+
# 7) Save and compare baseline drift
|
|
57
96
|
npx @rainy-updates/cli baseline --save --file .artifacts/deps-baseline.json --workspace
|
|
58
97
|
npx @rainy-updates/cli baseline --check --file .artifacts/deps-baseline.json --workspace --ci
|
|
98
|
+
|
|
99
|
+
# 8) Scan for known CVEs ── NEW in v0.5.1
|
|
100
|
+
npx @rainy-updates/cli audit
|
|
101
|
+
npx @rainy-updates/cli audit --severity high
|
|
102
|
+
npx @rainy-updates/cli audit --fix # prints the patching npm install command
|
|
103
|
+
rup audit --severity high # if installed
|
|
104
|
+
|
|
105
|
+
# 9) Check dependency maintenance health ── NEW in v0.5.1
|
|
106
|
+
npx @rainy-updates/cli health
|
|
107
|
+
npx @rainy-updates/cli health --stale 6m # flag packages with no release in 6 months
|
|
108
|
+
npx @rainy-updates/cli health --stale 180d # same but in days
|
|
109
|
+
rup health --stale 6m # if installed
|
|
110
|
+
|
|
111
|
+
# 10) Find which version introduced a breaking change ── NEW in v0.5.1
|
|
112
|
+
npx @rainy-updates/cli bisect axios --cmd "bun test"
|
|
113
|
+
npx @rainy-updates/cli bisect react --range "18.0.0..19.0.0" --cmd "npm test"
|
|
114
|
+
npx @rainy-updates/cli bisect lodash --cmd "npm run test:unit" --dry-run
|
|
115
|
+
rup bisect axios --cmd "bun test" # if installed
|
|
59
116
|
```
|
|
60
117
|
|
|
61
118
|
## What it does in production
|
|
@@ -65,7 +122,9 @@ npx @rainy-updates/cli baseline --check --file .artifacts/deps-baseline.json --w
|
|
|
65
122
|
- Scans dependency groups: `dependencies`, `devDependencies`, `optionalDependencies`, `peerDependencies`.
|
|
66
123
|
- Resolves versions per unique package to reduce duplicate network requests.
|
|
67
124
|
- Uses network concurrency controls and resilient retries.
|
|
125
|
+
- Supports explicit registry retry/timeout tuning (`--registry-retries`, `--registry-timeout-ms`).
|
|
68
126
|
- Supports stale-cache fallback when registry calls fail.
|
|
127
|
+
- Supports streamed progress output for long CI runs (`--stream`).
|
|
69
128
|
|
|
70
129
|
### Workspace support
|
|
71
130
|
|
|
@@ -80,6 +139,7 @@ npx @rainy-updates/cli baseline --check --file .artifacts/deps-baseline.json --w
|
|
|
80
139
|
- Apply global ignore patterns.
|
|
81
140
|
- Apply package-specific rules.
|
|
82
141
|
- Enforce max upgrade target per package (for safer rollout).
|
|
142
|
+
- Support per-package target override and fix-pr inclusion (`target`, `autofix`).
|
|
83
143
|
|
|
84
144
|
Example policy file:
|
|
85
145
|
|
|
@@ -87,7 +147,7 @@ Example policy file:
|
|
|
87
147
|
{
|
|
88
148
|
"ignore": ["@types/*", "eslint*"],
|
|
89
149
|
"packageRules": {
|
|
90
|
-
"react": { "maxTarget": "minor" },
|
|
150
|
+
"react": { "maxTarget": "minor", "target": "patch", "autofix": false },
|
|
91
151
|
"typescript": { "ignore": true }
|
|
92
152
|
}
|
|
93
153
|
}
|
|
@@ -116,17 +176,16 @@ npx @rainy-updates/cli check --policy-file .rainyupdates-policy.json
|
|
|
116
176
|
|
|
117
177
|
These outputs are designed for CI pipelines, security tooling, and PR review automation.
|
|
118
178
|
|
|
119
|
-
|
|
120
179
|
## Automatic CI bootstrap
|
|
121
180
|
|
|
122
181
|
Generate a workflow in the target project automatically:
|
|
123
182
|
|
|
124
183
|
```bash
|
|
125
|
-
#
|
|
126
|
-
|
|
184
|
+
# enterprise mode (recommended)
|
|
185
|
+
rup init-ci --mode enterprise --schedule weekly
|
|
127
186
|
|
|
128
187
|
# lightweight mode
|
|
129
|
-
|
|
188
|
+
rup init-ci --mode minimal --schedule daily
|
|
130
189
|
```
|
|
131
190
|
|
|
132
191
|
Generated file:
|
|
@@ -155,7 +214,10 @@ Schedule:
|
|
|
155
214
|
- `--dep-kinds deps,dev,optional,peer`
|
|
156
215
|
- `--concurrency <n>`
|
|
157
216
|
- `--cache-ttl <seconds>`
|
|
217
|
+
- `--registry-timeout-ms <n>`
|
|
218
|
+
- `--registry-retries <n>`
|
|
158
219
|
- `--offline`
|
|
220
|
+
- `--stream`
|
|
159
221
|
- `--fail-on none|patch|minor|major|any`
|
|
160
222
|
- `--max-updates <n>`
|
|
161
223
|
- `--group-by none|name|scope|kind|risk`
|
|
@@ -175,6 +237,7 @@ Schedule:
|
|
|
175
237
|
- `--fix-branch <name>`
|
|
176
238
|
- `--fix-commit-message <text>`
|
|
177
239
|
- `--fix-dry-run`
|
|
240
|
+
- `--lockfile-mode preserve|update|error`
|
|
178
241
|
- `--no-pr-report`
|
|
179
242
|
- `--ci`
|
|
180
243
|
|
|
@@ -201,9 +264,13 @@ Configuration can be loaded from:
|
|
|
201
264
|
## CLI help
|
|
202
265
|
|
|
203
266
|
```bash
|
|
267
|
+
rup --help
|
|
268
|
+
rup <command> --help
|
|
269
|
+
rup --version
|
|
270
|
+
|
|
271
|
+
# or with the full name:
|
|
204
272
|
rainy-updates --help
|
|
205
|
-
rainy-updates
|
|
206
|
-
rainy-updates --version
|
|
273
|
+
npx @rainy-updates/cli --help
|
|
207
274
|
```
|
|
208
275
|
|
|
209
276
|
## Reliability characteristics
|
|
@@ -223,7 +290,6 @@ This package ships with production CI/CD pipelines in the repository:
|
|
|
223
290
|
- Tag-driven release pipeline for npm publishing with provenance.
|
|
224
291
|
- Release preflight validation for npm auth/scope checks before publishing.
|
|
225
292
|
|
|
226
|
-
|
|
227
293
|
## Product roadmap
|
|
228
294
|
|
|
229
295
|
The long-term roadmap is maintained in [`ROADMAP.md`](./ROADMAP.md).
|
package/dist/bin/cli.js
CHANGED
|
@@ -66,16 +66,68 @@ async function main() {
|
|
|
66
66
|
process.exitCode = 1;
|
|
67
67
|
return;
|
|
68
68
|
}
|
|
69
|
+
// ─── v0.5.1 commands: lazy-loaded, isolated from check pipeline ──────────
|
|
70
|
+
if (parsed.command === "bisect") {
|
|
71
|
+
const { runBisect } = await import("../commands/bisect/runner.js");
|
|
72
|
+
const result = await runBisect(parsed.options);
|
|
73
|
+
process.exitCode = result.breakingVersion ? 1 : 0;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (parsed.command === "audit") {
|
|
77
|
+
const { runAudit } = await import("../commands/audit/runner.js");
|
|
78
|
+
const result = await runAudit(parsed.options);
|
|
79
|
+
process.exitCode = result.advisories.length > 0 ? 1 : 0;
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (parsed.command === "health") {
|
|
83
|
+
const { runHealth } = await import("../commands/health/runner.js");
|
|
84
|
+
const result = await runHealth(parsed.options);
|
|
85
|
+
process.exitCode = result.totalFlagged > 0 ? 1 : 0;
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
// ─── v0.5.2 commands ─────────────────────────────────────────────────────
|
|
89
|
+
if (parsed.command === "unused") {
|
|
90
|
+
const { runUnused } = await import("../commands/unused/runner.js");
|
|
91
|
+
const result = await runUnused(parsed.options);
|
|
92
|
+
process.exitCode =
|
|
93
|
+
result.totalUnused > 0 || result.totalMissing > 0 ? 1 : 0;
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (parsed.command === "resolve") {
|
|
97
|
+
const { runResolve } = await import("../commands/resolve/runner.js");
|
|
98
|
+
const result = await runResolve(parsed.options);
|
|
99
|
+
process.exitCode = result.errorConflicts > 0 ? 1 : 0;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (parsed.command === "licenses") {
|
|
103
|
+
const { runLicenses } = await import("../commands/licenses/runner.js");
|
|
104
|
+
const result = await runLicenses(parsed.options);
|
|
105
|
+
process.exitCode = result.totalViolations > 0 ? 1 : 0;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (parsed.command === "snapshot") {
|
|
109
|
+
const { runSnapshot } = await import("../commands/snapshot/runner.js");
|
|
110
|
+
const result = await runSnapshot(parsed.options);
|
|
111
|
+
process.exitCode = result.errors.length > 0 ? 1 : 0;
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
69
114
|
const result = await runCommand(parsed);
|
|
70
|
-
if (parsed.options.fixPr &&
|
|
115
|
+
if (parsed.options.fixPr &&
|
|
116
|
+
(parsed.command === "check" ||
|
|
117
|
+
parsed.command === "upgrade" ||
|
|
118
|
+
parsed.command === "ci")) {
|
|
71
119
|
result.summary.fixPrApplied = false;
|
|
72
|
-
result.summary.fixBranchName =
|
|
120
|
+
result.summary.fixBranchName =
|
|
121
|
+
parsed.options.fixBranch ?? "chore/rainy-updates";
|
|
73
122
|
result.summary.fixCommitSha = "";
|
|
74
123
|
result.summary.fixPrBranchesCreated = 0;
|
|
75
124
|
if (parsed.command === "ci") {
|
|
76
125
|
const batched = await applyFixPrBatches(parsed.options, result);
|
|
77
126
|
result.summary.fixPrApplied = batched.applied;
|
|
78
|
-
result.summary.fixBranchName =
|
|
127
|
+
result.summary.fixBranchName =
|
|
128
|
+
batched.branches[0] ??
|
|
129
|
+
parsed.options.fixBranch ??
|
|
130
|
+
"chore/rainy-updates";
|
|
79
131
|
result.summary.fixCommitSha = batched.commits[0] ?? "";
|
|
80
132
|
result.summary.fixPrBranchesCreated = batched.branches.length;
|
|
81
133
|
if (batched.branches.length > 1) {
|
|
@@ -98,14 +150,17 @@ async function main() {
|
|
|
98
150
|
const renderStartedAt = Date.now();
|
|
99
151
|
let rendered = renderResult(result, parsed.options.format);
|
|
100
152
|
result.summary.durationMs.render = Math.max(0, Date.now() - renderStartedAt);
|
|
101
|
-
if (parsed.options.format === "json" ||
|
|
153
|
+
if (parsed.options.format === "json" ||
|
|
154
|
+
parsed.options.format === "metrics") {
|
|
102
155
|
rendered = renderResult(result, parsed.options.format);
|
|
103
156
|
}
|
|
104
157
|
if (parsed.options.onlyChanged &&
|
|
105
158
|
result.updates.length === 0 &&
|
|
106
159
|
result.errors.length === 0 &&
|
|
107
160
|
result.warnings.length === 0 &&
|
|
108
|
-
(parsed.options.format === "table" ||
|
|
161
|
+
(parsed.options.format === "table" ||
|
|
162
|
+
parsed.options.format === "minimal" ||
|
|
163
|
+
parsed.options.format === "github")) {
|
|
109
164
|
rendered = "";
|
|
110
165
|
}
|
|
111
166
|
if (parsed.options.jsonFile) {
|
|
@@ -122,7 +177,7 @@ async function main() {
|
|
|
122
177
|
process.exitCode = resolveExitCode(result, result.summary.failReason);
|
|
123
178
|
}
|
|
124
179
|
catch (error) {
|
|
125
|
-
process.stderr.write(`rainy-updates: ${String(error)}\n`);
|
|
180
|
+
process.stderr.write(`rainy-updates (rup): ${String(error)}\n`);
|
|
126
181
|
process.exitCode = 2;
|
|
127
182
|
}
|
|
128
183
|
}
|
|
@@ -141,7 +196,10 @@ Options:
|
|
|
141
196
|
--reject <pattern>
|
|
142
197
|
--dep-kinds deps,dev,optional,peer
|
|
143
198
|
--concurrency <n>
|
|
199
|
+
--registry-timeout-ms <n>
|
|
200
|
+
--registry-retries <n>
|
|
144
201
|
--cache-ttl <seconds>
|
|
202
|
+
--stream
|
|
145
203
|
--policy-file <path>
|
|
146
204
|
--offline
|
|
147
205
|
--fix-pr
|
|
@@ -162,6 +220,7 @@ Options:
|
|
|
162
220
|
--cooldown-days <n>
|
|
163
221
|
--pr-limit <n>
|
|
164
222
|
--only-changed
|
|
223
|
+
--lockfile-mode preserve|update|error
|
|
165
224
|
--log-level error|warn|info|debug
|
|
166
225
|
--ci`;
|
|
167
226
|
}
|
|
@@ -177,8 +236,11 @@ Options:
|
|
|
177
236
|
--reject <pattern>
|
|
178
237
|
--dep-kinds deps,dev,optional,peer
|
|
179
238
|
--concurrency <n>
|
|
239
|
+
--registry-timeout-ms <n>
|
|
240
|
+
--registry-retries <n>
|
|
180
241
|
--cache-ttl <seconds>
|
|
181
242
|
--offline
|
|
243
|
+
--stream
|
|
182
244
|
--json-file <path>
|
|
183
245
|
--github-output <path>
|
|
184
246
|
--sarif-file <path>
|
|
@@ -197,12 +259,15 @@ Options:
|
|
|
197
259
|
--target patch|minor|major|latest
|
|
198
260
|
--policy-file <path>
|
|
199
261
|
--concurrency <n>
|
|
262
|
+
--registry-timeout-ms <n>
|
|
263
|
+
--registry-retries <n>
|
|
200
264
|
--fix-pr
|
|
201
265
|
--fix-branch <name>
|
|
202
266
|
--fix-commit-message <text>
|
|
203
267
|
--fix-dry-run
|
|
204
268
|
--fix-pr-no-checkout
|
|
205
269
|
--fix-pr-batch-size <n>
|
|
270
|
+
--lockfile-mode preserve|update|error
|
|
206
271
|
--no-pr-report
|
|
207
272
|
--json-file <path>
|
|
208
273
|
--pr-report-file <path>`;
|
|
@@ -222,6 +287,9 @@ Options:
|
|
|
222
287
|
--only-changed
|
|
223
288
|
--offline
|
|
224
289
|
--concurrency <n>
|
|
290
|
+
--registry-timeout-ms <n>
|
|
291
|
+
--registry-retries <n>
|
|
292
|
+
--stream
|
|
225
293
|
--fix-pr
|
|
226
294
|
--fix-branch <name>
|
|
227
295
|
--fix-commit-message <text>
|
|
@@ -235,6 +303,7 @@ Options:
|
|
|
235
303
|
--pr-report-file <path>
|
|
236
304
|
--fail-on none|patch|minor|major|any
|
|
237
305
|
--max-updates <n>
|
|
306
|
+
--lockfile-mode preserve|update|error
|
|
238
307
|
--log-level error|warn|info|debug
|
|
239
308
|
--ci`;
|
|
240
309
|
}
|
|
@@ -262,7 +331,7 @@ Options:
|
|
|
262
331
|
--dep-kinds deps,dev,optional,peer
|
|
263
332
|
--ci`;
|
|
264
333
|
}
|
|
265
|
-
return `rainy-updates <command> [options]
|
|
334
|
+
return `rainy-updates (rup / rainy-up) <command> [options]
|
|
266
335
|
|
|
267
336
|
Commands:
|
|
268
337
|
check Detect available updates
|
|
@@ -271,6 +340,13 @@ Commands:
|
|
|
271
340
|
warm-cache Warm local cache for fast/offline checks
|
|
272
341
|
init-ci Scaffold GitHub Actions workflow
|
|
273
342
|
baseline Save/check dependency baseline snapshots
|
|
343
|
+
audit Scan dependencies for CVEs (OSV.dev)
|
|
344
|
+
health Detect stale/deprecated/unmaintained packages
|
|
345
|
+
bisect Find which version of a dep introduced a failure
|
|
346
|
+
unused Detect unused or missing npm dependencies
|
|
347
|
+
resolve Check peer dependency conflicts (pure-TS, no subprocess)
|
|
348
|
+
licenses Scan dependency licenses and generate SPDX SBOM
|
|
349
|
+
snapshot Save, list, restore, and diff dependency state snapshots
|
|
274
350
|
|
|
275
351
|
Global options:
|
|
276
352
|
--cwd <path>
|
|
@@ -299,8 +375,12 @@ Global options:
|
|
|
299
375
|
--no-pr-report
|
|
300
376
|
--log-level error|warn|info|debug
|
|
301
377
|
--concurrency <n>
|
|
378
|
+
--registry-timeout-ms <n>
|
|
379
|
+
--registry-retries <n>
|
|
302
380
|
--cache-ttl <seconds>
|
|
303
381
|
--offline
|
|
382
|
+
--stream
|
|
383
|
+
--lockfile-mode preserve|update|error
|
|
304
384
|
--ci
|
|
305
385
|
--help, -h
|
|
306
386
|
--version, -v`;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { CveAdvisory, AuditOptions } from "../../types/index.js";
|
|
2
|
+
/**
|
|
3
|
+
* Fetches CVE advisories for all given package names in parallel.
|
|
4
|
+
* Uses OSV.dev as primary source.
|
|
5
|
+
*/
|
|
6
|
+
export declare function fetchAdvisories(packageNames: string[], options: Pick<AuditOptions, "concurrency" | "registryTimeoutMs">): Promise<CveAdvisory[]>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { asyncPool } from "../../utils/async-pool.js";
|
|
2
|
+
const OSV_API = "https://api.osv.dev/v1/query";
|
|
3
|
+
const GITHUB_ADVISORY_API = "https://api.github.com/advisories";
|
|
4
|
+
/**
|
|
5
|
+
* Queries OSV.dev for advisories for a single npm package.
|
|
6
|
+
*/
|
|
7
|
+
async function queryOsv(packageName, timeoutMs) {
|
|
8
|
+
const body = JSON.stringify({
|
|
9
|
+
package: { name: packageName, ecosystem: "npm" },
|
|
10
|
+
});
|
|
11
|
+
let response;
|
|
12
|
+
try {
|
|
13
|
+
const controller = new AbortController();
|
|
14
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
15
|
+
response = await fetch(OSV_API, {
|
|
16
|
+
method: "POST",
|
|
17
|
+
headers: { "Content-Type": "application/json" },
|
|
18
|
+
body,
|
|
19
|
+
signal: controller.signal,
|
|
20
|
+
});
|
|
21
|
+
clearTimeout(timer);
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
if (!response.ok)
|
|
27
|
+
return [];
|
|
28
|
+
const data = (await response.json());
|
|
29
|
+
const advisories = [];
|
|
30
|
+
for (const vuln of data.vulns ?? []) {
|
|
31
|
+
const cveId = vuln.id ?? "UNKNOWN";
|
|
32
|
+
const rawSeverity = (vuln.database_specific?.severity ?? "medium").toLowerCase();
|
|
33
|
+
const severity = (["critical", "high", "medium", "low"].includes(rawSeverity)
|
|
34
|
+
? rawSeverity
|
|
35
|
+
: "medium");
|
|
36
|
+
let patchedVersion = null;
|
|
37
|
+
let vulnerableRange = "*";
|
|
38
|
+
for (const affected of vuln.affected ?? []) {
|
|
39
|
+
if (affected.package?.name !== packageName)
|
|
40
|
+
continue;
|
|
41
|
+
for (const range of affected.ranges ?? []) {
|
|
42
|
+
const fixedEvent = range.events?.find((e) => e.fixed);
|
|
43
|
+
if (fixedEvent?.fixed) {
|
|
44
|
+
patchedVersion = fixedEvent.fixed;
|
|
45
|
+
const introducedEvent = range.events?.find((e) => e.introduced);
|
|
46
|
+
vulnerableRange = introducedEvent?.introduced
|
|
47
|
+
? `>=${introducedEvent.introduced} <${patchedVersion}`
|
|
48
|
+
: `<${patchedVersion}`;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
advisories.push({
|
|
53
|
+
cveId,
|
|
54
|
+
packageName,
|
|
55
|
+
severity,
|
|
56
|
+
vulnerableRange,
|
|
57
|
+
patchedVersion,
|
|
58
|
+
title: vuln.summary ?? cveId,
|
|
59
|
+
url: vuln.references?.[0]?.url ?? `https://osv.dev/vulnerability/${cveId}`,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return advisories;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Fetches CVE advisories for all given package names in parallel.
|
|
66
|
+
* Uses OSV.dev as primary source.
|
|
67
|
+
*/
|
|
68
|
+
export async function fetchAdvisories(packageNames, options) {
|
|
69
|
+
const tasks = packageNames.map((name) => () => queryOsv(name, options.registryTimeoutMs));
|
|
70
|
+
const results = await asyncPool(options.concurrency, tasks);
|
|
71
|
+
const advisories = [];
|
|
72
|
+
for (const r of results) {
|
|
73
|
+
if (!(r instanceof Error)) {
|
|
74
|
+
for (const adv of r)
|
|
75
|
+
advisories.push(adv);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return advisories;
|
|
79
|
+
}
|