@rainy-updates/cli 0.5.6 → 0.6.0
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 +133 -0
- package/README.md +90 -31
- package/dist/bin/cli.js +24 -482
- package/dist/bin/dispatch.d.ts +16 -0
- package/dist/bin/dispatch.js +147 -0
- package/dist/bin/help.d.ts +1 -0
- package/dist/bin/help.js +314 -0
- package/dist/cache/cache.js +13 -11
- package/dist/commands/audit/parser.js +2 -2
- package/dist/commands/audit/runner.js +27 -46
- package/dist/commands/audit/targets.js +13 -13
- package/dist/commands/bisect/oracle.js +28 -11
- package/dist/commands/bisect/parser.js +3 -3
- package/dist/commands/bisect/runner.js +15 -8
- package/dist/commands/changelog/fetcher.js +11 -5
- package/dist/commands/dashboard/parser.js +103 -1
- package/dist/commands/dashboard/runner.d.ts +2 -2
- package/dist/commands/dashboard/runner.js +67 -37
- package/dist/commands/doctor/parser.js +15 -4
- package/dist/commands/doctor/runner.js +6 -3
- package/dist/commands/ga/parser.js +4 -4
- package/dist/commands/ga/runner.js +13 -7
- package/dist/commands/health/parser.js +2 -2
- package/dist/commands/licenses/runner.js +4 -4
- package/dist/commands/resolve/runner.js +9 -4
- package/dist/commands/review/parser.js +57 -4
- package/dist/commands/review/runner.js +31 -5
- package/dist/commands/snapshot/runner.js +17 -17
- package/dist/commands/snapshot/store.d.ts +0 -12
- package/dist/commands/snapshot/store.js +26 -38
- package/dist/commands/unused/runner.js +6 -7
- package/dist/commands/unused/scanner.js +17 -20
- package/dist/config/loader.d.ts +2 -2
- package/dist/config/loader.js +2 -5
- package/dist/config/policy.js +20 -11
- package/dist/core/analysis/options.d.ts +6 -0
- package/dist/core/analysis/options.js +69 -0
- package/dist/core/analysis/review-items.d.ts +4 -0
- package/dist/core/analysis/review-items.js +128 -0
- package/dist/core/analysis/run-silenced.d.ts +1 -0
- package/dist/core/analysis/run-silenced.js +13 -0
- package/dist/core/analysis-bundle.js +3 -211
- package/dist/core/artifacts.js +6 -5
- package/dist/core/baseline.js +3 -5
- package/dist/core/check.js +2 -2
- package/dist/core/ci.js +52 -1
- package/dist/core/decision-plan.d.ts +14 -0
- package/dist/core/decision-plan.js +107 -0
- package/dist/core/doctor/findings.d.ts +2 -0
- package/dist/core/doctor/findings.js +166 -0
- package/dist/core/doctor/render.d.ts +3 -0
- package/dist/core/doctor/render.js +44 -0
- package/dist/core/doctor/result.d.ts +2 -0
- package/dist/core/doctor/result.js +58 -0
- package/dist/core/doctor/score.d.ts +5 -0
- package/dist/core/doctor/score.js +28 -0
- package/dist/core/fix-pr-batch.js +38 -28
- package/dist/core/fix-pr.js +27 -24
- package/dist/core/init-ci.js +25 -21
- package/dist/core/options.js +95 -4
- package/dist/core/review-model.d.ts +3 -3
- package/dist/core/review-model.js +6 -67
- package/dist/core/review-verdict.d.ts +2 -0
- package/dist/core/review-verdict.js +14 -0
- package/dist/core/summary.js +12 -0
- package/dist/core/upgrade.js +64 -2
- package/dist/core/verification.d.ts +2 -0
- package/dist/core/verification.js +106 -0
- package/dist/core/warm-cache.js +2 -2
- package/dist/output/format.js +22 -0
- package/dist/output/github.js +10 -0
- package/dist/output/sarif.js +16 -12
- package/dist/parsers/package-json.js +2 -4
- package/dist/pm/detect.d.ts +3 -1
- package/dist/pm/detect.js +24 -12
- package/dist/pm/install.d.ts +2 -1
- package/dist/pm/install.js +15 -16
- package/dist/registry/npm.js +34 -76
- package/dist/rup +0 -0
- package/dist/types/index.d.ts +104 -5
- package/dist/ui/tui.d.ts +4 -1
- package/dist/ui/tui.js +5 -4
- package/dist/utils/io.js +5 -6
- package/dist/utils/lockfile.js +24 -19
- package/dist/utils/runtime-paths.d.ts +4 -0
- package/dist/utils/runtime-paths.js +35 -0
- package/dist/utils/runtime.d.ts +7 -0
- package/dist/utils/runtime.js +32 -0
- package/dist/workspace/discover.js +55 -51
- package/package.json +16 -16
- package/dist/ui/dashboard/DashboardTUI.d.ts +0 -6
- package/dist/ui/dashboard/DashboardTUI.js +0 -34
- package/dist/ui/dashboard/components/DetailPanel.d.ts +0 -4
- package/dist/ui/dashboard/components/DetailPanel.js +0 -30
- package/dist/ui/dashboard/components/Footer.d.ts +0 -4
- package/dist/ui/dashboard/components/Footer.js +0 -9
- package/dist/ui/dashboard/components/Header.d.ts +0 -4
- package/dist/ui/dashboard/components/Header.js +0 -12
- package/dist/ui/dashboard/components/Sidebar.d.ts +0 -4
- package/dist/ui/dashboard/components/Sidebar.js +0 -23
- package/dist/ui/dashboard/store.d.ts +0 -34
- package/dist/ui/dashboard/store.js +0 -148
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { check } from "../core/check.js";
|
|
2
|
+
import { upgrade } from "../core/upgrade.js";
|
|
3
|
+
import { warmCache } from "../core/warm-cache.js";
|
|
4
|
+
import { runCi } from "../core/ci.js";
|
|
5
|
+
import { initCiWorkflow } from "../core/init-ci.js";
|
|
6
|
+
import { diffBaseline, saveBaseline } from "../core/baseline.js";
|
|
7
|
+
import { setRuntimeExitCode, writeStdout, } from "../utils/runtime.js";
|
|
8
|
+
export async function handleDirectCommand(parsed) {
|
|
9
|
+
if (parsed.command === "init-ci") {
|
|
10
|
+
const workflow = await initCiWorkflow(parsed.options.cwd, parsed.options.force, {
|
|
11
|
+
mode: parsed.options.mode,
|
|
12
|
+
schedule: parsed.options.schedule,
|
|
13
|
+
});
|
|
14
|
+
writeStdout(workflow.created
|
|
15
|
+
? `Created CI workflow at ${workflow.path}\n`
|
|
16
|
+
: `CI workflow already exists at ${workflow.path}. Use --force to overwrite.\n`);
|
|
17
|
+
return true;
|
|
18
|
+
}
|
|
19
|
+
if (parsed.command === "baseline") {
|
|
20
|
+
if (parsed.options.action === "save") {
|
|
21
|
+
const saved = await saveBaseline(parsed.options);
|
|
22
|
+
writeStdout(`Saved baseline at ${saved.filePath} (${saved.entries} entries)\n`);
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
const diff = await diffBaseline(parsed.options);
|
|
26
|
+
const changes = diff.added.length + diff.removed.length + diff.changed.length;
|
|
27
|
+
if (changes === 0) {
|
|
28
|
+
writeStdout(`No baseline drift detected (${diff.filePath}).\n`);
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
writeStdout(`Baseline drift detected (${diff.filePath}).\n`);
|
|
32
|
+
if (diff.added.length > 0)
|
|
33
|
+
writeStdout(`Added: ${diff.added.length}\n`);
|
|
34
|
+
if (diff.removed.length > 0)
|
|
35
|
+
writeStdout(`Removed: ${diff.removed.length}\n`);
|
|
36
|
+
if (diff.changed.length > 0)
|
|
37
|
+
writeStdout(`Changed: ${diff.changed.length}\n`);
|
|
38
|
+
setRuntimeExitCode(1);
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
if (parsed.command === "bisect") {
|
|
42
|
+
const { runBisect } = await import("../commands/bisect/runner.js");
|
|
43
|
+
const result = await runBisect(parsed.options);
|
|
44
|
+
setRuntimeExitCode(result.breakingVersion ? 1 : 0);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (parsed.command === "audit") {
|
|
48
|
+
const { runAudit } = await import("../commands/audit/runner.js");
|
|
49
|
+
const result = await runAudit(parsed.options);
|
|
50
|
+
setRuntimeExitCode(result.advisories.length > 0 ? 1 : 0);
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
if (parsed.command === "health") {
|
|
54
|
+
const { runHealth } = await import("../commands/health/runner.js");
|
|
55
|
+
const result = await runHealth(parsed.options);
|
|
56
|
+
setRuntimeExitCode(result.totalFlagged > 0 ? 1 : 0);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
if (parsed.command === "unused") {
|
|
60
|
+
const { runUnused } = await import("../commands/unused/runner.js");
|
|
61
|
+
const result = await runUnused(parsed.options);
|
|
62
|
+
setRuntimeExitCode(result.totalUnused > 0 || result.totalMissing > 0 ? 1 : 0);
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
if (parsed.command === "resolve") {
|
|
66
|
+
const { runResolve } = await import("../commands/resolve/runner.js");
|
|
67
|
+
const result = await runResolve(parsed.options);
|
|
68
|
+
setRuntimeExitCode(result.errorConflicts > 0 ? 1 : 0);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
if (parsed.command === "licenses") {
|
|
72
|
+
const { runLicenses } = await import("../commands/licenses/runner.js");
|
|
73
|
+
const result = await runLicenses(parsed.options);
|
|
74
|
+
setRuntimeExitCode(result.totalViolations > 0 ? 1 : 0);
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
if (parsed.command === "snapshot") {
|
|
78
|
+
const { runSnapshot } = await import("../commands/snapshot/runner.js");
|
|
79
|
+
const result = await runSnapshot(parsed.options);
|
|
80
|
+
setRuntimeExitCode(result.errors.length > 0 ? 1 : 0);
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
if (parsed.command === "review") {
|
|
84
|
+
const { runReview } = await import("../commands/review/runner.js");
|
|
85
|
+
const result = await runReview(parsed.options);
|
|
86
|
+
setRuntimeExitCode(result.summary.verdict === "blocked" ||
|
|
87
|
+
result.summary.verdict === "actionable" ||
|
|
88
|
+
result.summary.verdict === "review"
|
|
89
|
+
? 1
|
|
90
|
+
: 0);
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
if (parsed.command === "doctor") {
|
|
94
|
+
const { runDoctor } = await import("../commands/doctor/runner.js");
|
|
95
|
+
const result = await runDoctor(parsed.options);
|
|
96
|
+
setRuntimeExitCode(result.verdict === "safe" ? 0 : 1);
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
if (parsed.command === "dashboard") {
|
|
100
|
+
const { runDashboard } = await import("../commands/dashboard/runner.js");
|
|
101
|
+
const result = await runDashboard(parsed.options);
|
|
102
|
+
setRuntimeExitCode(result.errors.length > 0 ? 1 : 0);
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
if (parsed.command === "ga") {
|
|
106
|
+
const { runGa } = await import("../commands/ga/runner.js");
|
|
107
|
+
const result = await runGa(parsed.options);
|
|
108
|
+
setRuntimeExitCode(result.ready ? 0 : 1);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
if (parsed.options.interactive &&
|
|
112
|
+
(parsed.command === "check" ||
|
|
113
|
+
parsed.command === "upgrade" ||
|
|
114
|
+
parsed.command === "ci")) {
|
|
115
|
+
const { runDashboard } = await import("../commands/dashboard/runner.js");
|
|
116
|
+
const result = await runDashboard({
|
|
117
|
+
...parsed.options,
|
|
118
|
+
mode: parsed.command === "upgrade" ? "upgrade" : "review",
|
|
119
|
+
focus: "all",
|
|
120
|
+
applySelected: parsed.command === "upgrade",
|
|
121
|
+
});
|
|
122
|
+
setRuntimeExitCode(result.errors.length > 0 ? 1 : 0);
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
export async function runPrimaryCommand(parsed) {
|
|
128
|
+
if (parsed.command === "upgrade") {
|
|
129
|
+
return upgrade(parsed.options);
|
|
130
|
+
}
|
|
131
|
+
if (parsed.command === "warm-cache") {
|
|
132
|
+
return warmCache(parsed.options);
|
|
133
|
+
}
|
|
134
|
+
if (parsed.command === "ci") {
|
|
135
|
+
return runCi(parsed.options);
|
|
136
|
+
}
|
|
137
|
+
if (parsed.options.fixPr) {
|
|
138
|
+
const upgradeOptions = {
|
|
139
|
+
...parsed.options,
|
|
140
|
+
install: false,
|
|
141
|
+
packageManager: "auto",
|
|
142
|
+
sync: false,
|
|
143
|
+
};
|
|
144
|
+
return upgrade(upgradeOptions);
|
|
145
|
+
}
|
|
146
|
+
return check(parsed.options);
|
|
147
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function renderHelp(command?: string): string;
|
package/dist/bin/help.js
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
export function renderHelp(command) {
|
|
2
|
+
const isCommand = command && !command.startsWith("-");
|
|
3
|
+
if (isCommand && command === "check") {
|
|
4
|
+
return `rainy-updates check [options]
|
|
5
|
+
|
|
6
|
+
Detect candidate dependency updates. This is the first step in the flow:
|
|
7
|
+
check detects
|
|
8
|
+
doctor summarizes
|
|
9
|
+
review decides
|
|
10
|
+
upgrade applies
|
|
11
|
+
|
|
12
|
+
Options:
|
|
13
|
+
--workspace
|
|
14
|
+
--target patch|minor|major|latest
|
|
15
|
+
--filter <pattern>
|
|
16
|
+
--reject <pattern>
|
|
17
|
+
--dep-kinds deps,dev,optional,peer
|
|
18
|
+
--concurrency <n>
|
|
19
|
+
--registry-timeout-ms <n>
|
|
20
|
+
--registry-retries <n>
|
|
21
|
+
--cache-ttl <seconds>
|
|
22
|
+
--stream
|
|
23
|
+
--policy-file <path>
|
|
24
|
+
--offline
|
|
25
|
+
--fix-pr
|
|
26
|
+
--fix-branch <name>
|
|
27
|
+
--fix-commit-message <text>
|
|
28
|
+
--fix-dry-run
|
|
29
|
+
--fix-pr-no-checkout
|
|
30
|
+
--fix-pr-batch-size <n>
|
|
31
|
+
--no-pr-report
|
|
32
|
+
--json-file <path>
|
|
33
|
+
--github-output <path>
|
|
34
|
+
--sarif-file <path>
|
|
35
|
+
--pr-report-file <path>
|
|
36
|
+
--fail-on none|patch|minor|major|any
|
|
37
|
+
--max-updates <n>
|
|
38
|
+
--group-by none|name|scope|kind|risk
|
|
39
|
+
--group-max <n>
|
|
40
|
+
--cooldown-days <n>
|
|
41
|
+
--pr-limit <n>
|
|
42
|
+
--only-changed
|
|
43
|
+
--interactive
|
|
44
|
+
--plan-file <path>
|
|
45
|
+
--verify none|install|test|install,test
|
|
46
|
+
--test-command <cmd>
|
|
47
|
+
--verification-report-file <path>
|
|
48
|
+
--show-impact
|
|
49
|
+
--show-links
|
|
50
|
+
--show-homepage
|
|
51
|
+
--lockfile-mode preserve|update|error
|
|
52
|
+
--log-level error|warn|info|debug
|
|
53
|
+
--ci`;
|
|
54
|
+
}
|
|
55
|
+
if (isCommand && command === "warm-cache") {
|
|
56
|
+
return `rainy-updates warm-cache [options]
|
|
57
|
+
|
|
58
|
+
Pre-warm local metadata cache for faster CI checks.
|
|
59
|
+
|
|
60
|
+
Options:
|
|
61
|
+
--workspace
|
|
62
|
+
--target patch|minor|major|latest
|
|
63
|
+
--filter <pattern>
|
|
64
|
+
--reject <pattern>
|
|
65
|
+
--dep-kinds deps,dev,optional,peer
|
|
66
|
+
--concurrency <n>
|
|
67
|
+
--registry-timeout-ms <n>
|
|
68
|
+
--registry-retries <n>
|
|
69
|
+
--cache-ttl <seconds>
|
|
70
|
+
--offline
|
|
71
|
+
--stream
|
|
72
|
+
--json-file <path>
|
|
73
|
+
--github-output <path>
|
|
74
|
+
--sarif-file <path>
|
|
75
|
+
--pr-report-file <path>`;
|
|
76
|
+
}
|
|
77
|
+
if (isCommand && command === "upgrade") {
|
|
78
|
+
return `rainy-updates upgrade [options]
|
|
79
|
+
|
|
80
|
+
Apply an approved change set to package.json manifests.
|
|
81
|
+
|
|
82
|
+
Options:
|
|
83
|
+
--workspace
|
|
84
|
+
--sync
|
|
85
|
+
--install
|
|
86
|
+
--pm auto|bun|npm|pnpm|yarn
|
|
87
|
+
--target patch|minor|major|latest
|
|
88
|
+
--policy-file <path>
|
|
89
|
+
--concurrency <n>
|
|
90
|
+
--registry-timeout-ms <n>
|
|
91
|
+
--registry-retries <n>
|
|
92
|
+
--fix-pr
|
|
93
|
+
--fix-branch <name>
|
|
94
|
+
--fix-commit-message <text>
|
|
95
|
+
--fix-dry-run
|
|
96
|
+
--fix-pr-no-checkout
|
|
97
|
+
--fix-pr-batch-size <n>
|
|
98
|
+
--interactive
|
|
99
|
+
--from-plan <path>
|
|
100
|
+
--verify none|install|test|install,test
|
|
101
|
+
--test-command <cmd>
|
|
102
|
+
--verification-report-file <path>
|
|
103
|
+
--lockfile-mode preserve|update|error
|
|
104
|
+
--no-pr-report
|
|
105
|
+
--json-file <path>
|
|
106
|
+
--pr-report-file <path>`;
|
|
107
|
+
}
|
|
108
|
+
if (isCommand && command === "ci") {
|
|
109
|
+
return `rainy-updates ci [options]
|
|
110
|
+
|
|
111
|
+
Run CI-oriented automation around the same lifecycle:
|
|
112
|
+
check detects
|
|
113
|
+
doctor summarizes
|
|
114
|
+
review decides
|
|
115
|
+
upgrade applies
|
|
116
|
+
|
|
117
|
+
Options:
|
|
118
|
+
--workspace
|
|
119
|
+
--mode minimal|strict|enterprise
|
|
120
|
+
--gate check|doctor|review|upgrade
|
|
121
|
+
--group-by none|name|scope|kind|risk
|
|
122
|
+
--group-max <n>
|
|
123
|
+
--cooldown-days <n>
|
|
124
|
+
--pr-limit <n>
|
|
125
|
+
--only-changed
|
|
126
|
+
--offline
|
|
127
|
+
--concurrency <n>
|
|
128
|
+
--registry-timeout-ms <n>
|
|
129
|
+
--registry-retries <n>
|
|
130
|
+
--stream
|
|
131
|
+
--fix-pr
|
|
132
|
+
--fix-branch <name>
|
|
133
|
+
--fix-commit-message <text>
|
|
134
|
+
--fix-dry-run
|
|
135
|
+
--fix-pr-no-checkout
|
|
136
|
+
--fix-pr-batch-size <n>
|
|
137
|
+
--no-pr-report
|
|
138
|
+
--plan-file <path>
|
|
139
|
+
--verify none|install|test|install,test
|
|
140
|
+
--test-command <cmd>
|
|
141
|
+
--verification-report-file <path>
|
|
142
|
+
--json-file <path>
|
|
143
|
+
--github-output <path>
|
|
144
|
+
--sarif-file <path>
|
|
145
|
+
--pr-report-file <path>
|
|
146
|
+
--fail-on none|patch|minor|major|any
|
|
147
|
+
--max-updates <n>
|
|
148
|
+
--lockfile-mode preserve|update|error
|
|
149
|
+
--log-level error|warn|info|debug
|
|
150
|
+
--ci`;
|
|
151
|
+
}
|
|
152
|
+
if (isCommand && command === "init-ci") {
|
|
153
|
+
return `rainy-updates init-ci [options]
|
|
154
|
+
|
|
155
|
+
Create a GitHub Actions workflow template at:
|
|
156
|
+
.github/workflows/rainy-updates.yml
|
|
157
|
+
|
|
158
|
+
Options:
|
|
159
|
+
--force
|
|
160
|
+
--mode minimal|strict|enterprise
|
|
161
|
+
--schedule weekly|daily|off`;
|
|
162
|
+
}
|
|
163
|
+
if (isCommand && command === "baseline") {
|
|
164
|
+
return `rainy-updates baseline [options]
|
|
165
|
+
|
|
166
|
+
Save or compare dependency baseline snapshots.
|
|
167
|
+
|
|
168
|
+
Options:
|
|
169
|
+
--save
|
|
170
|
+
--check
|
|
171
|
+
--file <path>
|
|
172
|
+
--workspace
|
|
173
|
+
--dep-kinds deps,dev,optional,peer
|
|
174
|
+
--ci`;
|
|
175
|
+
}
|
|
176
|
+
if (isCommand && command === "audit") {
|
|
177
|
+
return `rainy-updates audit [options]
|
|
178
|
+
|
|
179
|
+
Scan dependencies for CVEs using OSV.dev and GitHub Advisory Database.
|
|
180
|
+
|
|
181
|
+
Options:
|
|
182
|
+
--workspace
|
|
183
|
+
--severity critical|high|medium|low
|
|
184
|
+
--summary
|
|
185
|
+
--report table|summary|json
|
|
186
|
+
--source auto|osv|github|all
|
|
187
|
+
--fix
|
|
188
|
+
--dry-run
|
|
189
|
+
--commit
|
|
190
|
+
--pm auto|npm|pnpm|bun|yarn
|
|
191
|
+
--json-file <path>
|
|
192
|
+
--concurrency <n>
|
|
193
|
+
--registry-timeout-ms <n>`;
|
|
194
|
+
}
|
|
195
|
+
if (isCommand && command === "review") {
|
|
196
|
+
return `rainy-updates review [options]
|
|
197
|
+
|
|
198
|
+
Review is the decision center of Rainy Updates.
|
|
199
|
+
Use it to inspect risk, security, peer, license, and policy context before applying changes.
|
|
200
|
+
|
|
201
|
+
Options:
|
|
202
|
+
--workspace
|
|
203
|
+
--interactive
|
|
204
|
+
--security-only
|
|
205
|
+
--risk critical|high|medium|low
|
|
206
|
+
--diff patch|minor|major|latest
|
|
207
|
+
--apply-selected
|
|
208
|
+
--plan-file <path>
|
|
209
|
+
--show-changelog
|
|
210
|
+
--policy-file <path>
|
|
211
|
+
--json-file <path>
|
|
212
|
+
--concurrency <n>
|
|
213
|
+
--registry-timeout-ms <n>
|
|
214
|
+
--registry-retries <n>`;
|
|
215
|
+
}
|
|
216
|
+
if (isCommand && command === "doctor") {
|
|
217
|
+
return `rainy-updates doctor [options]
|
|
218
|
+
|
|
219
|
+
Produce a fast summary verdict and point the operator to review when action is needed.
|
|
220
|
+
|
|
221
|
+
Options:
|
|
222
|
+
--workspace
|
|
223
|
+
--verdict-only
|
|
224
|
+
--include-changelog
|
|
225
|
+
--json-file <path>`;
|
|
226
|
+
}
|
|
227
|
+
if (isCommand && command === "dashboard") {
|
|
228
|
+
return `rainy-updates dashboard [options]
|
|
229
|
+
|
|
230
|
+
Open the primary interactive dependency operations console.
|
|
231
|
+
|
|
232
|
+
Options:
|
|
233
|
+
--workspace
|
|
234
|
+
--mode check|review|upgrade
|
|
235
|
+
--focus all|security|risk|major|blocked|workspace
|
|
236
|
+
--apply-selected
|
|
237
|
+
--plan-file <path>
|
|
238
|
+
--verify none|install|test|install,test
|
|
239
|
+
--test-command <cmd>
|
|
240
|
+
--verification-report-file <path>
|
|
241
|
+
--cwd <path>`;
|
|
242
|
+
}
|
|
243
|
+
if (isCommand && command === "ga") {
|
|
244
|
+
return `rainy-updates ga [options]
|
|
245
|
+
|
|
246
|
+
Audit release and CI readiness for Rainy Updates.
|
|
247
|
+
|
|
248
|
+
Options:
|
|
249
|
+
--workspace
|
|
250
|
+
--json-file <path>
|
|
251
|
+
--cwd <path>`;
|
|
252
|
+
}
|
|
253
|
+
return `rainy-updates (rup / rainy-up) <command> [options]
|
|
254
|
+
|
|
255
|
+
Commands:
|
|
256
|
+
check Detect candidate updates
|
|
257
|
+
doctor Summarize what matters
|
|
258
|
+
review Decide what to do
|
|
259
|
+
upgrade Apply the approved change set
|
|
260
|
+
dashboard Open the primary interactive dependency dashboard
|
|
261
|
+
ci Run CI-focused orchestration
|
|
262
|
+
warm-cache Warm local cache for fast/offline checks
|
|
263
|
+
init-ci Scaffold GitHub Actions workflow
|
|
264
|
+
baseline Save/check dependency baseline snapshots
|
|
265
|
+
audit Scan dependencies for CVEs (OSV.dev + GitHub)
|
|
266
|
+
health Detect stale/deprecated/unmaintained packages
|
|
267
|
+
bisect Find which version of a dep introduced a failure
|
|
268
|
+
unused Detect unused or missing npm dependencies
|
|
269
|
+
resolve Check peer dependency conflicts (pure-TS, no subprocess)
|
|
270
|
+
licenses Scan dependency licenses and generate SPDX SBOM
|
|
271
|
+
snapshot Save, list, restore, and diff dependency state snapshots
|
|
272
|
+
ga Audit GA and CI readiness for this checkout
|
|
273
|
+
|
|
274
|
+
Global options:
|
|
275
|
+
--cwd <path>
|
|
276
|
+
--workspace
|
|
277
|
+
--target patch|minor|major|latest
|
|
278
|
+
--format table|json|minimal|github|metrics
|
|
279
|
+
--json-file <path>
|
|
280
|
+
--github-output <path>
|
|
281
|
+
--sarif-file <path>
|
|
282
|
+
--pr-report-file <path>
|
|
283
|
+
--policy-file <path>
|
|
284
|
+
--fail-on none|patch|minor|major|any
|
|
285
|
+
--max-updates <n>
|
|
286
|
+
--group-by none|name|scope|kind|risk
|
|
287
|
+
--group-max <n>
|
|
288
|
+
--cooldown-days <n>
|
|
289
|
+
--pr-limit <n>
|
|
290
|
+
--only-changed
|
|
291
|
+
--interactive
|
|
292
|
+
--show-impact
|
|
293
|
+
--show-links
|
|
294
|
+
--show-homepage
|
|
295
|
+
--mode minimal|strict|enterprise
|
|
296
|
+
--fix-pr
|
|
297
|
+
--fix-branch <name>
|
|
298
|
+
--fix-commit-message <text>
|
|
299
|
+
--fix-dry-run
|
|
300
|
+
--fix-pr-no-checkout
|
|
301
|
+
--fix-pr-batch-size <n>
|
|
302
|
+
--no-pr-report
|
|
303
|
+
--log-level error|warn|info|debug
|
|
304
|
+
--concurrency <n>
|
|
305
|
+
--registry-timeout-ms <n>
|
|
306
|
+
--registry-retries <n>
|
|
307
|
+
--cache-ttl <seconds>
|
|
308
|
+
--offline
|
|
309
|
+
--stream
|
|
310
|
+
--lockfile-mode preserve|update|error
|
|
311
|
+
--ci
|
|
312
|
+
--help, -h
|
|
313
|
+
--version, -v`;
|
|
314
|
+
}
|
package/dist/cache/cache.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { promises as fs } from "node:fs";
|
|
2
|
-
import os from "node:os";
|
|
3
1
|
import path from "node:path";
|
|
4
|
-
import
|
|
2
|
+
import { writeFileAtomic } from "../utils/io.js";
|
|
3
|
+
import { getCacheDir } from "../utils/runtime-paths.js";
|
|
4
|
+
import { readEnv } from "../utils/runtime.js";
|
|
5
5
|
class FileCacheStore {
|
|
6
6
|
filePath;
|
|
7
7
|
constructor(filePath) {
|
|
@@ -15,19 +15,19 @@ class FileCacheStore {
|
|
|
15
15
|
return null;
|
|
16
16
|
return {
|
|
17
17
|
...entry,
|
|
18
|
-
availableVersions: Array.isArray(entry.availableVersions)
|
|
18
|
+
availableVersions: Array.isArray(entry.availableVersions)
|
|
19
|
+
? entry.availableVersions
|
|
20
|
+
: [entry.latestVersion],
|
|
19
21
|
};
|
|
20
22
|
}
|
|
21
23
|
async set(entry) {
|
|
22
24
|
const entries = await this.readEntries();
|
|
23
25
|
entries[this.getKey(entry.packageName, entry.target)] = entry;
|
|
24
|
-
await
|
|
25
|
-
await fs.writeFile(this.filePath, JSON.stringify(entries), "utf8");
|
|
26
|
+
await writeFileAtomic(this.filePath, JSON.stringify(entries));
|
|
26
27
|
}
|
|
27
28
|
async readEntries() {
|
|
28
29
|
try {
|
|
29
|
-
|
|
30
|
-
return JSON.parse(content);
|
|
30
|
+
return (await Bun.file(this.filePath).json());
|
|
31
31
|
}
|
|
32
32
|
catch {
|
|
33
33
|
return {};
|
|
@@ -93,7 +93,9 @@ class SqliteCacheStore {
|
|
|
93
93
|
}
|
|
94
94
|
ensureSchema() {
|
|
95
95
|
try {
|
|
96
|
-
const columns = this.db
|
|
96
|
+
const columns = this.db
|
|
97
|
+
.prepare("PRAGMA table_info(versions);")
|
|
98
|
+
.all();
|
|
97
99
|
const hasAvailableVersions = columns.some((column) => column.name === "available_versions");
|
|
98
100
|
if (!hasAvailableVersions) {
|
|
99
101
|
this.db.exec("ALTER TABLE versions ADD COLUMN available_versions TEXT;");
|
|
@@ -117,8 +119,8 @@ export class VersionCache {
|
|
|
117
119
|
this.fallbackReason = fallbackReason;
|
|
118
120
|
}
|
|
119
121
|
static async create(customPath) {
|
|
120
|
-
const basePath = customPath ??
|
|
121
|
-
if (
|
|
122
|
+
const basePath = customPath ?? getCacheDir();
|
|
123
|
+
if (readEnv("RAINY_UPDATES_CACHE_BACKEND") === "file") {
|
|
122
124
|
const jsonPath = path.join(basePath, "cache.json");
|
|
123
125
|
return new VersionCache(new FileCacheStore(jsonPath), "file", true, "forced via RAINY_UPDATES_CACHE_BACKEND=file");
|
|
124
126
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import
|
|
2
|
+
import { getRuntimeCwd } from "../../utils/runtime.js";
|
|
3
3
|
const SEVERITY_LEVELS = ["critical", "high", "medium", "low"];
|
|
4
4
|
const SOURCE_MODES = ["auto", "osv", "github", "all"];
|
|
5
5
|
export function parseSeverity(value) {
|
|
@@ -10,7 +10,7 @@ export function parseSeverity(value) {
|
|
|
10
10
|
}
|
|
11
11
|
export function parseAuditArgs(args) {
|
|
12
12
|
const options = {
|
|
13
|
-
cwd:
|
|
13
|
+
cwd: getRuntimeCwd(),
|
|
14
14
|
workspace: false,
|
|
15
15
|
severity: undefined,
|
|
16
16
|
fix: false,
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { spawn } from "node:child_process";
|
|
2
|
-
import { promises as fs } from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
1
|
import { collectDependencies, readManifest, } from "../../parsers/package-json.js";
|
|
2
|
+
import { detectPackageManager as detectProjectPackageManager, resolvePackageManager, } from "../../pm/detect.js";
|
|
5
3
|
import { discoverPackageDirs } from "../../workspace/discover.js";
|
|
6
4
|
import { writeFileAtomic } from "../../utils/io.js";
|
|
7
5
|
import { stableStringify } from "../../utils/stable-json.js";
|
|
6
|
+
import { writeStderr, writeStdout } from "../../utils/runtime.js";
|
|
8
7
|
import { fetchAdvisories } from "./fetcher.js";
|
|
9
8
|
import { resolveAuditTargets } from "./targets.js";
|
|
10
9
|
import { filterBySeverity, buildPatchMap, renderAuditSourceHealth, renderAuditSummary, renderAuditTable, summarizeAdvisories, } from "./mapper.js";
|
|
@@ -55,7 +54,7 @@ export async function runAudit(options) {
|
|
|
55
54
|
return result;
|
|
56
55
|
}
|
|
57
56
|
if (!options.silent) {
|
|
58
|
-
|
|
57
|
+
writeStderr(`[audit] Querying ${describeSourceMode(options.sourceMode)} for ${targetResolution.targets.length} dependency version${targetResolution.targets.length === 1 ? "" : "s"}...\n`);
|
|
59
58
|
}
|
|
60
59
|
const fetched = await fetchAdvisories(targetResolution.targets, {
|
|
61
60
|
concurrency: options.concurrency,
|
|
@@ -81,12 +80,12 @@ export async function runAudit(options) {
|
|
|
81
80
|
result.autoFixable = advisories.filter((a) => a.patchedVersion !== null).length;
|
|
82
81
|
if (!options.silent) {
|
|
83
82
|
if (options.reportFormat === "summary") {
|
|
84
|
-
|
|
83
|
+
writeStdout(renderAuditSummary(result.packages) +
|
|
85
84
|
renderAuditSourceHealth(result.sourceHealth) +
|
|
86
85
|
"\n");
|
|
87
86
|
}
|
|
88
87
|
else if (options.reportFormat === "table" || !options.jsonFile) {
|
|
89
|
-
|
|
88
|
+
writeStdout(renderAuditTable(advisories) +
|
|
90
89
|
renderAuditSourceHealth(result.sourceHealth) +
|
|
91
90
|
"\n");
|
|
92
91
|
}
|
|
@@ -102,7 +101,7 @@ export async function runAudit(options) {
|
|
|
102
101
|
warnings: result.warnings,
|
|
103
102
|
}, 2) + "\n");
|
|
104
103
|
if (!options.silent) {
|
|
105
|
-
|
|
104
|
+
writeStderr(`[audit] JSON report written to ${options.jsonFile}\n`);
|
|
106
105
|
}
|
|
107
106
|
}
|
|
108
107
|
if (options.fix && result.autoFixable > 0) {
|
|
@@ -122,40 +121,41 @@ async function applyFix(advisories, options) {
|
|
|
122
121
|
const patchMap = buildPatchMap(advisories);
|
|
123
122
|
if (patchMap.size === 0)
|
|
124
123
|
return;
|
|
125
|
-
const
|
|
124
|
+
const detected = await detectProjectPackageManager(options.cwd);
|
|
125
|
+
const pm = resolvePackageManager(options.packageManager, detected);
|
|
126
126
|
const installArgs = buildInstallArgs(pm, patchMap);
|
|
127
127
|
const installCmd = `${pm} ${installArgs.join(" ")}`;
|
|
128
128
|
if (options.dryRun) {
|
|
129
129
|
if (!options.silent) {
|
|
130
|
-
|
|
130
|
+
writeStderr(`[audit] --dry-run: would execute:\n ${installCmd}\n`);
|
|
131
131
|
if (options.commit) {
|
|
132
132
|
const msg = buildCommitMessage(patchMap);
|
|
133
|
-
|
|
133
|
+
writeStderr(`[audit] --dry-run: would commit:\n git commit -m "${msg}"\n`);
|
|
134
134
|
}
|
|
135
135
|
}
|
|
136
136
|
return;
|
|
137
137
|
}
|
|
138
138
|
if (!options.silent) {
|
|
139
|
-
|
|
140
|
-
|
|
139
|
+
writeStderr(`[audit] Applying ${patchMap.size} fix(es)...\n`);
|
|
140
|
+
writeStderr(` → ${installCmd}\n`);
|
|
141
141
|
}
|
|
142
142
|
try {
|
|
143
143
|
await runCommand(pm, installArgs, options.cwd);
|
|
144
144
|
}
|
|
145
145
|
catch (err) {
|
|
146
146
|
if (!options.silent) {
|
|
147
|
-
|
|
147
|
+
writeStderr(`[audit] Install failed: ${String(err)}\n`);
|
|
148
148
|
}
|
|
149
149
|
return;
|
|
150
150
|
}
|
|
151
151
|
if (!options.silent) {
|
|
152
|
-
|
|
152
|
+
writeStderr(`[audit] ✔ Patches applied successfully.\n`);
|
|
153
153
|
}
|
|
154
154
|
if (options.commit) {
|
|
155
155
|
await commitFix(patchMap, options.cwd, options.silent);
|
|
156
156
|
}
|
|
157
157
|
else if (!options.silent) {
|
|
158
|
-
|
|
158
|
+
writeStderr(`[audit] Tip: run with --commit to automatically commit the changes.\n`);
|
|
159
159
|
}
|
|
160
160
|
}
|
|
161
161
|
function buildInstallArgs(pm, patchMap) {
|
|
@@ -205,48 +205,29 @@ function buildCommitMessage(patchMap) {
|
|
|
205
205
|
const names = items.map(([n]) => n).join(", ");
|
|
206
206
|
return `fix(security): patch ${items.length} vulnerabilities — ${names} (rup audit)`;
|
|
207
207
|
}
|
|
208
|
-
/** Detects the package manager in use by checking for lockfiles. */
|
|
209
|
-
async function detectPackageManager(cwd, explicit) {
|
|
210
|
-
if (explicit !== "auto")
|
|
211
|
-
return explicit;
|
|
212
|
-
const checks = [
|
|
213
|
-
["bun.lock", "bun"],
|
|
214
|
-
["bun.lockb", "bun"],
|
|
215
|
-
["pnpm-lock.yaml", "pnpm"],
|
|
216
|
-
["yarn.lock", "yarn"],
|
|
217
|
-
];
|
|
218
|
-
for (const [lockfile, pm] of checks) {
|
|
219
|
-
try {
|
|
220
|
-
await fs.access(path.join(cwd, lockfile));
|
|
221
|
-
return pm;
|
|
222
|
-
}
|
|
223
|
-
catch {
|
|
224
|
-
// not found, try next
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
return "npm"; // default
|
|
228
|
-
}
|
|
229
208
|
/** Spawns a subprocess, pipes stdio live to the terminal. */
|
|
230
209
|
function runCommand(cmd, args, cwd, ignoreErrors = false) {
|
|
231
|
-
return new Promise((resolve, reject) => {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
210
|
+
return new Promise(async (resolve, reject) => {
|
|
211
|
+
try {
|
|
212
|
+
const proc = Bun.spawn([cmd, ...args], {
|
|
213
|
+
cwd,
|
|
214
|
+
stdin: "inherit",
|
|
215
|
+
stdout: "inherit",
|
|
216
|
+
stderr: "inherit",
|
|
217
|
+
});
|
|
218
|
+
const code = await proc.exited;
|
|
238
219
|
if (code === 0 || ignoreErrors) {
|
|
239
220
|
resolve();
|
|
240
221
|
}
|
|
241
222
|
else {
|
|
242
223
|
reject(new Error(`${cmd} exited with code ${code}`));
|
|
243
224
|
}
|
|
244
|
-
}
|
|
245
|
-
|
|
225
|
+
}
|
|
226
|
+
catch (err) {
|
|
246
227
|
if (ignoreErrors)
|
|
247
228
|
resolve();
|
|
248
229
|
else
|
|
249
230
|
reject(err);
|
|
250
|
-
}
|
|
231
|
+
}
|
|
251
232
|
});
|
|
252
233
|
}
|