@rainy-updates/cli 0.5.2-rc.2 → 0.5.3
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 +107 -0
- package/README.md +105 -22
- package/dist/bin/cli.js +124 -9
- package/dist/cache/cache.d.ts +1 -0
- package/dist/cache/cache.js +9 -2
- package/dist/commands/audit/runner.js +8 -1
- package/dist/commands/audit/sources/index.js +8 -1
- package/dist/commands/doctor/parser.d.ts +2 -0
- package/dist/commands/doctor/parser.js +92 -0
- package/dist/commands/doctor/runner.d.ts +2 -0
- package/dist/commands/doctor/runner.js +13 -0
- package/dist/commands/resolve/runner.js +3 -0
- package/dist/commands/review/parser.d.ts +2 -0
- package/dist/commands/review/parser.js +174 -0
- package/dist/commands/review/runner.d.ts +2 -0
- package/dist/commands/review/runner.js +30 -0
- package/dist/config/loader.d.ts +3 -0
- package/dist/core/check.js +64 -5
- package/dist/core/errors.d.ts +11 -0
- package/dist/core/errors.js +6 -0
- package/dist/core/options.d.ts +8 -1
- package/dist/core/options.js +43 -0
- package/dist/core/review-model.d.ts +5 -0
- package/dist/core/review-model.js +372 -0
- package/dist/core/summary.js +11 -2
- package/dist/core/upgrade.d.ts +1 -0
- package/dist/core/upgrade.js +27 -21
- package/dist/core/warm-cache.js +28 -4
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/output/format.d.ts +4 -1
- package/dist/output/format.js +41 -3
- package/dist/output/github.js +6 -1
- package/dist/output/sarif.js +14 -0
- package/dist/registry/npm.d.ts +22 -0
- package/dist/registry/npm.js +33 -4
- package/dist/risk/index.d.ts +3 -0
- package/dist/risk/index.js +24 -0
- package/dist/risk/scorer.d.ts +3 -0
- package/dist/risk/scorer.js +114 -0
- package/dist/risk/signals/fresh-package.d.ts +3 -0
- package/dist/risk/signals/fresh-package.js +22 -0
- package/dist/risk/signals/install-scripts.d.ts +3 -0
- package/dist/risk/signals/install-scripts.js +10 -0
- package/dist/risk/signals/maintainer-churn.d.ts +3 -0
- package/dist/risk/signals/maintainer-churn.js +11 -0
- package/dist/risk/signals/metadata.d.ts +3 -0
- package/dist/risk/signals/metadata.js +18 -0
- package/dist/risk/signals/mutable-source.d.ts +3 -0
- package/dist/risk/signals/mutable-source.js +24 -0
- package/dist/risk/signals/typosquat.d.ts +3 -0
- package/dist/risk/signals/typosquat.js +70 -0
- package/dist/risk/types.d.ts +15 -0
- package/dist/risk/types.js +1 -0
- package/dist/types/index.d.ts +85 -0
- package/dist/ui/tui.d.ts +0 -4
- package/dist/ui/tui.js +103 -21
- package/package.json +10 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,113 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.5.3] - 2026-03-01
|
|
6
|
+
|
|
7
|
+
GA stabilization and review-centered workflow refinement.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **Dedicated risk engine layer** under `src/risk/`:
|
|
12
|
+
- formal risk scoring with `riskScore`, `riskLevel`, `riskReasons`, `riskCategories`, and `recommendedAction`,
|
|
13
|
+
- deterministic scoring for:
|
|
14
|
+
- known vulnerabilities,
|
|
15
|
+
- install lifecycle scripts,
|
|
16
|
+
- typosquatting heuristic,
|
|
17
|
+
- newly published packages,
|
|
18
|
+
- suspicious metadata,
|
|
19
|
+
- mutable git/http dependencies,
|
|
20
|
+
- maintainer stability heuristic,
|
|
21
|
+
- peer conflicts,
|
|
22
|
+
- license violations,
|
|
23
|
+
- stale/deprecated health signals,
|
|
24
|
+
- major version jumps.
|
|
25
|
+
- **Benchmark tooling and methodology**:
|
|
26
|
+
- `scripts/generate-benchmark-fixtures.mjs`,
|
|
27
|
+
- `scripts/benchmark.mjs`,
|
|
28
|
+
- generated fixtures under `benchmarks/fixtures/`,
|
|
29
|
+
- benchmark methodology doc: `docs/benchmarks.md`.
|
|
30
|
+
- **New workflow docs**:
|
|
31
|
+
- `docs/command-model.md`
|
|
32
|
+
- `docs/review-workflow.md`
|
|
33
|
+
- `docs/risk-engine.md`
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- `review` is now the explicit product center:
|
|
38
|
+
- `check` detects,
|
|
39
|
+
- `doctor` summarizes,
|
|
40
|
+
- `review` decides,
|
|
41
|
+
- `upgrade` applies.
|
|
42
|
+
- CLI help, README, and docs now reflect the review-centered workflow instead of a flat command surface.
|
|
43
|
+
- Review outputs now carry formal risk engine results instead of ad hoc composite signals.
|
|
44
|
+
- TUI language has been upgraded to decision-oriented semantics:
|
|
45
|
+
- review queue,
|
|
46
|
+
- decision panel,
|
|
47
|
+
- explicit state labels,
|
|
48
|
+
- recommended action per candidate.
|
|
49
|
+
- GitHub annotations, SARIF, and human-readable output now expose formal risk score/action metadata additively.
|
|
50
|
+
|
|
51
|
+
### Benchmarks
|
|
52
|
+
|
|
53
|
+
- Added package scripts for reproducible benchmark runs:
|
|
54
|
+
- `bench:fixtures`
|
|
55
|
+
- `bench:check`
|
|
56
|
+
- `bench:review`
|
|
57
|
+
- `bench:resolve`
|
|
58
|
+
- `bench:ci`
|
|
59
|
+
|
|
60
|
+
## [0.5.2] - 2026-03-01
|
|
61
|
+
|
|
62
|
+
### Added
|
|
63
|
+
|
|
64
|
+
- **New `review` command**: Aggregates pending updates with security, peer-conflict, license, health, and unused-dependency signals for guided dependency review.
|
|
65
|
+
- `--interactive` launches the upgraded Ink review TUI.
|
|
66
|
+
- `--security-only`, `--risk <level>`, and `--diff <level>` filter the review set.
|
|
67
|
+
- `--apply-selected` can apply the filtered/selected updates after review.
|
|
68
|
+
- **New `doctor` command**: Produces a fast dependency verdict for local triage and CI summaries.
|
|
69
|
+
- Verdict classes: `safe`, `review`, `blocked`, `actionable`.
|
|
70
|
+
- `--verdict-only` prints a one-line CI-friendly summary.
|
|
71
|
+
- **Interactive review TUI overhaul**:
|
|
72
|
+
- multi-pane layout with filters, selection state, detail panel, and status bar,
|
|
73
|
+
- risk/security/peer/license context inline per package,
|
|
74
|
+
- explicit selection controls for interactive upgrade review.
|
|
75
|
+
- **Additive output contract metadata**:
|
|
76
|
+
- summary fields: `verdict`, `riskPackages`, `securityPackages`, `peerConflictPackages`, `licenseViolationPackages`, `interactiveSession`,
|
|
77
|
+
- GitHub outputs: `verdict`, `risk_packages`, `security_packages`, `peer_conflict_packages`, `license_violation_packages`,
|
|
78
|
+
- SARIF result properties for impact/risk/advisory/license context.
|
|
79
|
+
- **New display controls**:
|
|
80
|
+
- `--interactive`
|
|
81
|
+
- `--show-impact`
|
|
82
|
+
- `--show-homepage`
|
|
83
|
+
- **RC3 hardening layer on top of GA surfaces**:
|
|
84
|
+
- centralized classified error taxonomy in `src/core/errors.ts`,
|
|
85
|
+
- compatibility coverage for scoped private registries and cache backend fallback,
|
|
86
|
+
- explicit cache fallback reason reporting for SQLite → file cache degradation,
|
|
87
|
+
- dedicated performance scenarios for `check`, `resolve`, and `ci`,
|
|
88
|
+
- new comparison document: `docs/why-rainy-vs-dependabot-renovate.md`.
|
|
89
|
+
|
|
90
|
+
### Changed
|
|
91
|
+
|
|
92
|
+
- `check` now computes impact scores in the core pipeline and carries homepage metadata when available.
|
|
93
|
+
- `upgrade --interactive` now routes through the guided review flow before applying selected updates.
|
|
94
|
+
- CLI help and package exports now cover the new review/verdict surfaces.
|
|
95
|
+
- `doctor` output now follows the RC3 quick-verdict shape:
|
|
96
|
+
- `State`
|
|
97
|
+
- `PrimaryRisk`
|
|
98
|
+
- `NextAction`
|
|
99
|
+
- `check`, `warm-cache`, and `audit` now emit RC3-style classified warnings/errors for:
|
|
100
|
+
- registry failures,
|
|
101
|
+
- auth failures,
|
|
102
|
+
- advisory-source degradation/outage,
|
|
103
|
+
- cache backend fallback.
|
|
104
|
+
|
|
105
|
+
### Tests
|
|
106
|
+
|
|
107
|
+
- Added compatibility tests for:
|
|
108
|
+
- scoped `.npmrc` private registry resolution,
|
|
109
|
+
- forced cache backend fallback behavior.
|
|
110
|
+
- Updated audit coverage to assert the new classified advisory degradation warning format.
|
|
111
|
+
|
|
5
112
|
## [0.5.2-rc.2] - 2026-02-27
|
|
6
113
|
|
|
7
114
|
### Added
|
package/README.md
CHANGED
|
@@ -1,16 +1,65 @@
|
|
|
1
1
|
# @rainy-updates/cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Rainy Updates is a deterministic dependency review and upgrade operator for Node monorepos and CI.
|
|
4
4
|
|
|
5
|
-
`@rainy-updates/cli` is built for teams that need fast dependency
|
|
5
|
+
`@rainy-updates/cli` is built for teams that need fast dependency detection, trustworthy review, controlled upgrades, and automation-ready outputs for CI/CD.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Comparison:
|
|
8
|
+
[Why Rainy vs Dependabot and Renovate](./docs/why-rainy-vs-dependabot-renovate.md)
|
|
9
|
+
|
|
10
|
+
Command model:
|
|
11
|
+
[Check → Doctor → Review → Upgrade](./docs/command-model.md)
|
|
12
|
+
|
|
13
|
+
Review workflow:
|
|
14
|
+
[Review workflow guide](./docs/review-workflow.md)
|
|
15
|
+
|
|
16
|
+
Risk engine:
|
|
17
|
+
[Risk engine guide](./docs/risk-engine.md)
|
|
18
|
+
|
|
19
|
+
Benchmarks:
|
|
20
|
+
[Benchmark methodology](./docs/benchmarks.md)
|
|
21
|
+
|
|
22
|
+
## What it is
|
|
23
|
+
|
|
24
|
+
Rainy Updates gives teams one dependency lifecycle:
|
|
25
|
+
|
|
26
|
+
- `check` detects candidate updates.
|
|
27
|
+
- `doctor` summarizes the current situation.
|
|
28
|
+
- `review` decides what should happen.
|
|
29
|
+
- `upgrade` applies the approved change set.
|
|
30
|
+
|
|
31
|
+
Everything else supports that lifecycle: CI orchestration, advisory lookup, peer resolution, licenses, snapshots, baselines, and fix-PR automation.
|
|
32
|
+
|
|
33
|
+
## Who it is for
|
|
34
|
+
|
|
35
|
+
- Node monorepo teams that want deterministic CI artifacts.
|
|
36
|
+
- Engineers who want to review dependency risk locally before applying changes.
|
|
37
|
+
- Teams that need fewer, better upgrade decisions instead of noisy automated PR churn.
|
|
38
|
+
|
|
39
|
+
## 60-second workflow
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# 1) Detect what changed
|
|
43
|
+
npx @rainy-updates/cli check --workspace --show-impact
|
|
44
|
+
|
|
45
|
+
# 2) Summarize what matters
|
|
46
|
+
npx @rainy-updates/cli doctor --workspace
|
|
47
|
+
|
|
48
|
+
# 3) Decide in the review surface
|
|
49
|
+
npx @rainy-updates/cli review --interactive
|
|
50
|
+
|
|
51
|
+
# 4) Apply the approved set
|
|
52
|
+
npx @rainy-updates/cli upgrade --interactive
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Why teams use it
|
|
8
56
|
|
|
9
57
|
- Detects updates quickly across single-package repos and workspaces.
|
|
58
|
+
- Centralizes security, peer, license, health, and behavioral risk review.
|
|
10
59
|
- Applies updates safely with configurable targets (`patch`, `minor`, `major`, `latest`).
|
|
11
|
-
- Enforces policy rules per package
|
|
60
|
+
- Enforces policy rules per package.
|
|
12
61
|
- Supports offline and cache-warmed execution for deterministic CI runs.
|
|
13
|
-
- Produces machine-readable artifacts
|
|
62
|
+
- Produces machine-readable artifacts: JSON, SARIF, GitHub outputs, and PR reports.
|
|
14
63
|
|
|
15
64
|
## Install
|
|
16
65
|
|
|
@@ -49,10 +98,15 @@ npx @rainy-updates/cli ci --workspace --mode strict
|
|
|
49
98
|
|
|
50
99
|
## Commands
|
|
51
100
|
|
|
52
|
-
###
|
|
101
|
+
### Primary workflow
|
|
102
|
+
|
|
103
|
+
- `check` — detect candidate dependency updates
|
|
104
|
+
- `doctor` — summarize the current dependency situation
|
|
105
|
+
- `review` — decide what to do with security, risk, peer, and policy context
|
|
106
|
+
- `upgrade` — apply the approved change set
|
|
107
|
+
|
|
108
|
+
### Supporting workflow
|
|
53
109
|
|
|
54
|
-
- `check` — analyze dependencies and report available updates
|
|
55
|
-
- `upgrade` — rewrite dependency ranges in manifests, optionally install lockfile updates
|
|
56
110
|
- `ci` — run CI-focused dependency automation (warm cache, check/upgrade, policy gates)
|
|
57
111
|
- `warm-cache` — prefetch package metadata for fast and offline checks
|
|
58
112
|
- `baseline` — save and compare dependency baseline snapshots
|
|
@@ -72,31 +126,35 @@ npx @rainy-updates/cli ci --workspace --mode strict
|
|
|
72
126
|
npx @rainy-updates/cli check --format table
|
|
73
127
|
rup check --format table # if installed
|
|
74
128
|
|
|
75
|
-
# 2)
|
|
76
|
-
npx @rainy-updates/cli
|
|
77
|
-
rup
|
|
129
|
+
# 2) Summarize the state
|
|
130
|
+
npx @rainy-updates/cli doctor --workspace
|
|
131
|
+
rup doctor --workspace
|
|
78
132
|
|
|
79
|
-
# 3)
|
|
133
|
+
# 3) Review and decide
|
|
134
|
+
npx @rainy-updates/cli review --security-only
|
|
135
|
+
rup review --interactive
|
|
136
|
+
|
|
137
|
+
# 4) Apply upgrades with workspace sync
|
|
138
|
+
npx @rainy-updates/cli upgrade --target latest --workspace --sync --install
|
|
139
|
+
rup upgrade --target latest --workspace --sync --install
|
|
140
|
+
|
|
141
|
+
# 5) CI orchestration with policy gates
|
|
80
142
|
npx @rainy-updates/cli ci --workspace --mode strict --format github
|
|
81
143
|
rup ci --workspace --mode strict --format github
|
|
82
144
|
|
|
83
|
-
#
|
|
145
|
+
# 6) Batch fix branches by scope (enterprise)
|
|
84
146
|
npx @rainy-updates/cli ci --workspace --mode enterprise --group-by scope --fix-pr --fix-pr-batch-size 2
|
|
85
147
|
rup ci --workspace --mode enterprise --group-by scope --fix-pr --fix-pr-batch-size 2
|
|
86
148
|
|
|
87
|
-
#
|
|
88
|
-
npx @rainy-updates/cli upgrade --target latest --workspace --sync --install
|
|
89
|
-
rup upgrade --target latest --workspace --sync --install
|
|
90
|
-
|
|
91
|
-
# 6) Warm cache → deterministic offline CI check
|
|
149
|
+
# 7) Warm cache → deterministic offline CI check
|
|
92
150
|
npx @rainy-updates/cli warm-cache --workspace --concurrency 32
|
|
93
151
|
npx @rainy-updates/cli check --workspace --offline --ci
|
|
94
152
|
|
|
95
|
-
#
|
|
153
|
+
# 8) Save and compare baseline drift
|
|
96
154
|
npx @rainy-updates/cli baseline --save --file .artifacts/deps-baseline.json --workspace
|
|
97
155
|
npx @rainy-updates/cli baseline --check --file .artifacts/deps-baseline.json --workspace --ci
|
|
98
156
|
|
|
99
|
-
#
|
|
157
|
+
# 9) Scan for known CVEs
|
|
100
158
|
npx @rainy-updates/cli audit
|
|
101
159
|
npx @rainy-updates/cli audit --severity high
|
|
102
160
|
npx @rainy-updates/cli audit --summary
|
|
@@ -106,17 +164,20 @@ rup audit --severity high # if installed
|
|
|
106
164
|
|
|
107
165
|
`audit` prefers npm/pnpm lockfiles today for exact installed-version inference, and now also reads simple `bun.lock` workspace entries when available. It reports source-health warnings when OSV or GitHub returns only partial coverage.
|
|
108
166
|
|
|
109
|
-
#
|
|
167
|
+
# 10) Check dependency maintenance health
|
|
110
168
|
npx @rainy-updates/cli health
|
|
111
169
|
npx @rainy-updates/cli health --stale 6m # flag packages with no release in 6 months
|
|
112
170
|
npx @rainy-updates/cli health --stale 180d # same but in days
|
|
113
171
|
rup health --stale 6m # if installed
|
|
114
172
|
|
|
115
|
-
#
|
|
173
|
+
# 11) Find which version introduced a breaking change
|
|
116
174
|
npx @rainy-updates/cli bisect axios --cmd "bun test"
|
|
117
175
|
npx @rainy-updates/cli bisect react --range "18.0.0..19.0.0" --cmd "npm test"
|
|
118
176
|
npx @rainy-updates/cli bisect lodash --cmd "npm run test:unit" --dry-run
|
|
119
177
|
rup bisect axios --cmd "bun test" # if installed
|
|
178
|
+
|
|
179
|
+
# 12) Focus review on high-risk changes
|
|
180
|
+
rup review --risk high --diff major
|
|
120
181
|
```
|
|
121
182
|
|
|
122
183
|
## What it does in production
|
|
@@ -129,6 +190,7 @@ rup bisect axios --cmd "bun test" # if installed
|
|
|
129
190
|
- Supports explicit registry retry/timeout tuning (`--registry-retries`, `--registry-timeout-ms`).
|
|
130
191
|
- Supports stale-cache fallback when registry calls fail.
|
|
131
192
|
- Supports streamed progress output for long CI runs (`--stream`).
|
|
193
|
+
- Exposes impact/risk metadata and homepage context in update output (`--show-impact`, `--show-homepage`).
|
|
132
194
|
|
|
133
195
|
### Workspace support
|
|
134
196
|
|
|
@@ -170,6 +232,13 @@ npx @rainy-updates/cli check --policy-file .rainyupdates-policy.json
|
|
|
170
232
|
- `--format table`
|
|
171
233
|
- `--format minimal`
|
|
172
234
|
|
|
235
|
+
Review-centered outputs:
|
|
236
|
+
|
|
237
|
+
- `check` is optimized for detection.
|
|
238
|
+
- `doctor` is optimized for summary.
|
|
239
|
+
- `review` is optimized for decision-making.
|
|
240
|
+
- `upgrade` is optimized for safe application.
|
|
241
|
+
|
|
173
242
|
### Automation output
|
|
174
243
|
|
|
175
244
|
- `--format json`
|
|
@@ -229,6 +298,9 @@ Schedule:
|
|
|
229
298
|
- `--cooldown-days <n>`
|
|
230
299
|
- `--pr-limit <n>`
|
|
231
300
|
- `--only-changed`
|
|
301
|
+
- `--interactive`
|
|
302
|
+
- `--show-impact`
|
|
303
|
+
- `--show-homepage`
|
|
232
304
|
- `--mode minimal|strict|enterprise` (for `ci`)
|
|
233
305
|
- `--fix-pr-batch-size <n>` (for batched fix branches in `ci`)
|
|
234
306
|
- `--policy-file <path>`
|
|
@@ -251,6 +323,17 @@ Schedule:
|
|
|
251
323
|
- `--pm auto|npm|pnpm`
|
|
252
324
|
- `--sync`
|
|
253
325
|
|
|
326
|
+
### Review-only
|
|
327
|
+
|
|
328
|
+
- `--security-only`
|
|
329
|
+
- `--risk critical|high|medium|low`
|
|
330
|
+
- `--diff patch|minor|major|latest`
|
|
331
|
+
- `--apply-selected`
|
|
332
|
+
|
|
333
|
+
### Doctor-only
|
|
334
|
+
|
|
335
|
+
- `--verdict-only`
|
|
336
|
+
|
|
254
337
|
### Baseline-only
|
|
255
338
|
|
|
256
339
|
- `--save`
|
package/dist/bin/cli.js
CHANGED
|
@@ -85,7 +85,7 @@ async function main() {
|
|
|
85
85
|
process.exitCode = result.totalFlagged > 0 ? 1 : 0;
|
|
86
86
|
return;
|
|
87
87
|
}
|
|
88
|
-
// ─── v0.5.
|
|
88
|
+
// ─── v0.5.3 commands ─────────────────────────────────────────────────────
|
|
89
89
|
if (parsed.command === "unused") {
|
|
90
90
|
const { runUnused } = await import("../commands/unused/runner.js");
|
|
91
91
|
const result = await runUnused(parsed.options);
|
|
@@ -111,6 +111,39 @@ async function main() {
|
|
|
111
111
|
process.exitCode = result.errors.length > 0 ? 1 : 0;
|
|
112
112
|
return;
|
|
113
113
|
}
|
|
114
|
+
if (parsed.command === "review") {
|
|
115
|
+
const { runReview } = await import("../commands/review/runner.js");
|
|
116
|
+
const result = await runReview(parsed.options);
|
|
117
|
+
process.exitCode =
|
|
118
|
+
result.summary.verdict === "blocked" ||
|
|
119
|
+
result.summary.verdict === "actionable" ||
|
|
120
|
+
result.summary.verdict === "review"
|
|
121
|
+
? 1
|
|
122
|
+
: 0;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (parsed.command === "doctor") {
|
|
126
|
+
const { runDoctor } = await import("../commands/doctor/runner.js");
|
|
127
|
+
const result = await runDoctor(parsed.options);
|
|
128
|
+
process.exitCode = result.verdict === "safe" ? 0 : 1;
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (parsed.options.interactive &&
|
|
132
|
+
(parsed.command === "check" ||
|
|
133
|
+
parsed.command === "upgrade" ||
|
|
134
|
+
parsed.command === "ci")) {
|
|
135
|
+
const { runReview } = await import("../commands/review/runner.js");
|
|
136
|
+
const result = await runReview({
|
|
137
|
+
...parsed.options,
|
|
138
|
+
securityOnly: false,
|
|
139
|
+
risk: undefined,
|
|
140
|
+
diff: undefined,
|
|
141
|
+
applySelected: parsed.command === "upgrade",
|
|
142
|
+
});
|
|
143
|
+
process.exitCode =
|
|
144
|
+
result.summary.verdict === "safe" && result.updates.length === 0 ? 0 : 1;
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
114
147
|
const result = await runCommand(parsed);
|
|
115
148
|
if (parsed.options.fixPr &&
|
|
116
149
|
(parsed.command === "check" ||
|
|
@@ -148,11 +181,17 @@ async function main() {
|
|
|
148
181
|
}
|
|
149
182
|
result.summary.failReason = resolveFailReason(result.updates, result.errors, parsed.options.failOn, parsed.options.maxUpdates, parsed.options.ci);
|
|
150
183
|
const renderStartedAt = Date.now();
|
|
151
|
-
let rendered = renderResult(result, parsed.options.format
|
|
184
|
+
let rendered = renderResult(result, parsed.options.format, {
|
|
185
|
+
showImpact: parsed.options.showImpact,
|
|
186
|
+
showHomepage: parsed.options.showHomepage,
|
|
187
|
+
});
|
|
152
188
|
result.summary.durationMs.render = Math.max(0, Date.now() - renderStartedAt);
|
|
153
189
|
if (parsed.options.format === "json" ||
|
|
154
190
|
parsed.options.format === "metrics") {
|
|
155
|
-
rendered = renderResult(result, parsed.options.format
|
|
191
|
+
rendered = renderResult(result, parsed.options.format, {
|
|
192
|
+
showImpact: parsed.options.showImpact,
|
|
193
|
+
showHomepage: parsed.options.showHomepage,
|
|
194
|
+
});
|
|
156
195
|
}
|
|
157
196
|
if (parsed.options.onlyChanged &&
|
|
158
197
|
result.updates.length === 0 &&
|
|
@@ -187,7 +226,11 @@ function renderHelp(command) {
|
|
|
187
226
|
if (isCommand && command === "check") {
|
|
188
227
|
return `rainy-updates check [options]
|
|
189
228
|
|
|
190
|
-
Detect
|
|
229
|
+
Detect candidate dependency updates. This is the first step in the flow:
|
|
230
|
+
check detects
|
|
231
|
+
doctor summarizes
|
|
232
|
+
review decides
|
|
233
|
+
upgrade applies
|
|
191
234
|
|
|
192
235
|
Options:
|
|
193
236
|
--workspace
|
|
@@ -220,6 +263,9 @@ Options:
|
|
|
220
263
|
--cooldown-days <n>
|
|
221
264
|
--pr-limit <n>
|
|
222
265
|
--only-changed
|
|
266
|
+
--interactive
|
|
267
|
+
--show-impact
|
|
268
|
+
--show-homepage
|
|
223
269
|
--lockfile-mode preserve|update|error
|
|
224
270
|
--log-level error|warn|info|debug
|
|
225
271
|
--ci`;
|
|
@@ -249,7 +295,7 @@ Options:
|
|
|
249
295
|
if (isCommand && command === "upgrade") {
|
|
250
296
|
return `rainy-updates upgrade [options]
|
|
251
297
|
|
|
252
|
-
Apply
|
|
298
|
+
Apply an approved change set to package.json manifests.
|
|
253
299
|
|
|
254
300
|
Options:
|
|
255
301
|
--workspace
|
|
@@ -267,6 +313,7 @@ Options:
|
|
|
267
313
|
--fix-dry-run
|
|
268
314
|
--fix-pr-no-checkout
|
|
269
315
|
--fix-pr-batch-size <n>
|
|
316
|
+
--interactive
|
|
270
317
|
--lockfile-mode preserve|update|error
|
|
271
318
|
--no-pr-report
|
|
272
319
|
--json-file <path>
|
|
@@ -275,7 +322,11 @@ Options:
|
|
|
275
322
|
if (isCommand && command === "ci") {
|
|
276
323
|
return `rainy-updates ci [options]
|
|
277
324
|
|
|
278
|
-
Run CI-oriented
|
|
325
|
+
Run CI-oriented automation around the same lifecycle:
|
|
326
|
+
check detects
|
|
327
|
+
doctor summarizes
|
|
328
|
+
review decides
|
|
329
|
+
upgrade applies
|
|
279
330
|
|
|
280
331
|
Options:
|
|
281
332
|
--workspace
|
|
@@ -349,13 +400,44 @@ Options:
|
|
|
349
400
|
--json-file <path>
|
|
350
401
|
--concurrency <n>
|
|
351
402
|
--registry-timeout-ms <n>`;
|
|
403
|
+
}
|
|
404
|
+
if (isCommand && command === "review") {
|
|
405
|
+
return `rainy-updates review [options]
|
|
406
|
+
|
|
407
|
+
Review is the decision center of Rainy Updates.
|
|
408
|
+
Use it to inspect risk, security, peer, license, and policy context before applying changes.
|
|
409
|
+
|
|
410
|
+
Options:
|
|
411
|
+
--workspace
|
|
412
|
+
--interactive
|
|
413
|
+
--security-only
|
|
414
|
+
--risk critical|high|medium|low
|
|
415
|
+
--diff patch|minor|major|latest
|
|
416
|
+
--apply-selected
|
|
417
|
+
--policy-file <path>
|
|
418
|
+
--json-file <path>
|
|
419
|
+
--concurrency <n>
|
|
420
|
+
--registry-timeout-ms <n>
|
|
421
|
+
--registry-retries <n>`;
|
|
422
|
+
}
|
|
423
|
+
if (isCommand && command === "doctor") {
|
|
424
|
+
return `rainy-updates doctor [options]
|
|
425
|
+
|
|
426
|
+
Produce a fast summary verdict and point the operator to review when action is needed.
|
|
427
|
+
|
|
428
|
+
Options:
|
|
429
|
+
--workspace
|
|
430
|
+
--verdict-only
|
|
431
|
+
--json-file <path>`;
|
|
352
432
|
}
|
|
353
433
|
return `rainy-updates (rup / rainy-up) <command> [options]
|
|
354
434
|
|
|
355
435
|
Commands:
|
|
356
|
-
check Detect
|
|
357
|
-
|
|
358
|
-
|
|
436
|
+
check Detect candidate updates
|
|
437
|
+
doctor Summarize what matters
|
|
438
|
+
review Decide what to do
|
|
439
|
+
upgrade Apply the approved change set
|
|
440
|
+
ci Run CI-focused orchestration
|
|
359
441
|
warm-cache Warm local cache for fast/offline checks
|
|
360
442
|
init-ci Scaffold GitHub Actions workflow
|
|
361
443
|
baseline Save/check dependency baseline snapshots
|
|
@@ -384,6 +466,9 @@ Global options:
|
|
|
384
466
|
--cooldown-days <n>
|
|
385
467
|
--pr-limit <n>
|
|
386
468
|
--only-changed
|
|
469
|
+
--interactive
|
|
470
|
+
--show-impact
|
|
471
|
+
--show-homepage
|
|
387
472
|
--mode minimal|strict|enterprise
|
|
388
473
|
--fix-pr
|
|
389
474
|
--fix-branch <name>
|
|
@@ -405,6 +490,36 @@ Global options:
|
|
|
405
490
|
--version, -v`;
|
|
406
491
|
}
|
|
407
492
|
async function runCommand(parsed) {
|
|
493
|
+
if (parsed.command === "review") {
|
|
494
|
+
const { runReview } = await import("../commands/review/runner.js");
|
|
495
|
+
const result = await runReview(parsed.options);
|
|
496
|
+
return {
|
|
497
|
+
projectPath: result.projectPath,
|
|
498
|
+
packagePaths: result.items.map((item) => item.update.packagePath),
|
|
499
|
+
packageManager: "unknown",
|
|
500
|
+
target: result.target,
|
|
501
|
+
timestamp: new Date().toISOString(),
|
|
502
|
+
summary: result.summary,
|
|
503
|
+
updates: result.updates,
|
|
504
|
+
errors: result.errors,
|
|
505
|
+
warnings: result.warnings,
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
if (parsed.command === "doctor") {
|
|
509
|
+
const { runDoctor } = await import("../commands/doctor/runner.js");
|
|
510
|
+
const result = await runDoctor(parsed.options);
|
|
511
|
+
return {
|
|
512
|
+
projectPath: result.review.projectPath,
|
|
513
|
+
packagePaths: result.review.items.map((item) => item.update.packagePath),
|
|
514
|
+
packageManager: "unknown",
|
|
515
|
+
target: result.review.target,
|
|
516
|
+
timestamp: new Date().toISOString(),
|
|
517
|
+
summary: result.summary,
|
|
518
|
+
updates: result.review.updates,
|
|
519
|
+
errors: result.review.errors,
|
|
520
|
+
warnings: result.review.warnings,
|
|
521
|
+
};
|
|
522
|
+
}
|
|
408
523
|
if (parsed.command === "upgrade") {
|
|
409
524
|
return await upgrade(parsed.options);
|
|
410
525
|
}
|
package/dist/cache/cache.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export declare class VersionCache {
|
|
|
3
3
|
private readonly store;
|
|
4
4
|
readonly backend: "sqlite" | "file";
|
|
5
5
|
readonly degraded: boolean;
|
|
6
|
+
readonly fallbackReason?: string;
|
|
6
7
|
private constructor();
|
|
7
8
|
static create(customPath?: string): Promise<VersionCache>;
|
|
8
9
|
getValid(packageName: string, target: TargetLevel): Promise<CachedVersion | null>;
|
package/dist/cache/cache.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { promises as fs } from "node:fs";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import process from "node:process";
|
|
4
5
|
class FileCacheStore {
|
|
5
6
|
filePath;
|
|
6
7
|
constructor(filePath) {
|
|
@@ -108,20 +109,26 @@ export class VersionCache {
|
|
|
108
109
|
store;
|
|
109
110
|
backend;
|
|
110
111
|
degraded;
|
|
111
|
-
|
|
112
|
+
fallbackReason;
|
|
113
|
+
constructor(store, backend, degraded, fallbackReason) {
|
|
112
114
|
this.store = store;
|
|
113
115
|
this.backend = backend;
|
|
114
116
|
this.degraded = degraded;
|
|
117
|
+
this.fallbackReason = fallbackReason;
|
|
115
118
|
}
|
|
116
119
|
static async create(customPath) {
|
|
117
120
|
const basePath = customPath ?? path.join(os.homedir(), ".cache", "rainy-updates");
|
|
121
|
+
if (process.env.RAINY_UPDATES_CACHE_BACKEND === "file") {
|
|
122
|
+
const jsonPath = path.join(basePath, "cache.json");
|
|
123
|
+
return new VersionCache(new FileCacheStore(jsonPath), "file", true, "forced via RAINY_UPDATES_CACHE_BACKEND=file");
|
|
124
|
+
}
|
|
118
125
|
const sqlitePath = path.join(basePath, "cache.db");
|
|
119
126
|
const sqliteStore = await tryCreateSqliteStore(sqlitePath);
|
|
120
127
|
if (sqliteStore)
|
|
121
128
|
return new VersionCache(sqliteStore, "sqlite", false);
|
|
122
129
|
const jsonPath = path.join(basePath, "cache.json");
|
|
123
130
|
const degraded = typeof Bun !== "undefined";
|
|
124
|
-
return new VersionCache(new FileCacheStore(jsonPath), "file", degraded);
|
|
131
|
+
return new VersionCache(new FileCacheStore(jsonPath), "file", degraded, degraded ? "bun:sqlite unavailable; using file cache backend" : undefined);
|
|
125
132
|
}
|
|
126
133
|
async getValid(packageName, target) {
|
|
127
134
|
const entry = await this.store.get(packageName, target);
|
|
@@ -8,6 +8,7 @@ import { stableStringify } from "../../utils/stable-json.js";
|
|
|
8
8
|
import { fetchAdvisories } from "./fetcher.js";
|
|
9
9
|
import { resolveAuditTargets } from "./targets.js";
|
|
10
10
|
import { filterBySeverity, buildPatchMap, renderAuditSourceHealth, renderAuditSummary, renderAuditTable, summarizeAdvisories, } from "./mapper.js";
|
|
11
|
+
import { formatClassifiedMessage } from "../../core/errors.js";
|
|
11
12
|
/**
|
|
12
13
|
* Entry point for `rup audit`. Lazy-loaded by cli.ts.
|
|
13
14
|
* Discovers packages, fetches CVE advisories, filters by severity, and
|
|
@@ -63,7 +64,13 @@ export async function runAudit(options) {
|
|
|
63
64
|
result.sourceHealth = fetched.sourceHealth;
|
|
64
65
|
result.warnings.push(...fetched.warnings);
|
|
65
66
|
if (fetched.sourceHealth.every((item) => item.status === "failed")) {
|
|
66
|
-
result.errors.push(
|
|
67
|
+
result.errors.push(formatClassifiedMessage({
|
|
68
|
+
code: "ADVISORY_SOURCE_DOWN",
|
|
69
|
+
whatFailed: "All advisory sources failed.",
|
|
70
|
+
intact: "Dependency target resolution completed, but no advisory coverage was returned.",
|
|
71
|
+
validity: "invalid",
|
|
72
|
+
next: "Retry `rup audit` later or select a single healthy source with --source.",
|
|
73
|
+
}));
|
|
67
74
|
}
|
|
68
75
|
let advisories = fetched.advisories;
|
|
69
76
|
advisories = filterBySeverity(advisories, options.severity);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { compareVersions, parseVersion } from "../../../utils/semver.js";
|
|
2
2
|
import { githubAuditSource } from "./github.js";
|
|
3
3
|
import { osvAuditSource } from "./osv.js";
|
|
4
|
+
import { formatClassifiedMessage } from "../../../core/errors.js";
|
|
4
5
|
const SOURCE_MAP = {
|
|
5
6
|
osv: osvAuditSource,
|
|
6
7
|
github: githubAuditSource,
|
|
@@ -62,7 +63,13 @@ function normalizeSourceWarnings(warnings, sourceHealth) {
|
|
|
62
63
|
const successfulNames = successful
|
|
63
64
|
.map((item) => formatSourceName(item.source))
|
|
64
65
|
.join(", ");
|
|
65
|
-
normalized.push(
|
|
66
|
+
normalized.push(formatClassifiedMessage({
|
|
67
|
+
code: "ADVISORY_SOURCE_DEGRADED",
|
|
68
|
+
whatFailed: `${failedNames} advisory source(s) failed during the audit query.`,
|
|
69
|
+
intact: `${successfulNames} still returned advisory results.`,
|
|
70
|
+
validity: "partial",
|
|
71
|
+
next: "Retry `rup audit` later or pin `--source` to a healthy backend.",
|
|
72
|
+
}));
|
|
66
73
|
}
|
|
67
74
|
return normalized;
|
|
68
75
|
}
|