@rainy-updates/cli 0.5.7 → 0.6.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 +134 -0
- package/README.md +90 -31
- package/dist/bin/cli.js +11 -126
- package/dist/bin/dispatch.js +35 -32
- package/dist/bin/help.js +79 -2
- package/dist/bin/main.d.ts +1 -0
- package/dist/bin/main.js +126 -0
- package/dist/cache/cache.js +13 -11
- package/dist/commands/audit/parser.js +38 -2
- package/dist/commands/audit/runner.js +41 -61
- package/dist/commands/audit/targets.js +13 -13
- package/dist/commands/bisect/oracle.js +31 -11
- package/dist/commands/bisect/parser.js +3 -3
- package/dist/commands/bisect/runner.js +16 -8
- package/dist/commands/changelog/fetcher.js +11 -5
- package/dist/commands/dashboard/parser.js +144 -1
- package/dist/commands/dashboard/runner.d.ts +2 -2
- package/dist/commands/dashboard/runner.js +67 -37
- package/dist/commands/doctor/parser.js +53 -4
- package/dist/commands/doctor/runner.js +2 -2
- package/dist/commands/ga/parser.js +43 -4
- package/dist/commands/ga/runner.js +22 -13
- package/dist/commands/health/parser.js +38 -2
- package/dist/commands/health/runner.js +5 -1
- package/dist/commands/hook/parser.d.ts +2 -0
- package/dist/commands/hook/parser.js +40 -0
- package/dist/commands/hook/runner.d.ts +2 -0
- package/dist/commands/hook/runner.js +174 -0
- package/dist/commands/licenses/parser.js +39 -0
- package/dist/commands/licenses/runner.js +9 -5
- package/dist/commands/resolve/graph/builder.js +5 -1
- package/dist/commands/resolve/parser.js +39 -0
- package/dist/commands/resolve/runner.js +14 -4
- package/dist/commands/review/parser.js +101 -4
- package/dist/commands/review/runner.js +31 -5
- package/dist/commands/snapshot/parser.js +39 -0
- package/dist/commands/snapshot/runner.js +21 -18
- package/dist/commands/snapshot/store.d.ts +0 -12
- package/dist/commands/snapshot/store.js +26 -38
- package/dist/commands/unused/parser.js +39 -0
- package/dist/commands/unused/runner.js +10 -8
- package/dist/commands/unused/scanner.d.ts +2 -1
- package/dist/commands/unused/scanner.js +65 -52
- 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/run-silenced.js +0 -1
- package/dist/core/artifacts.js +6 -5
- package/dist/core/baseline.js +3 -5
- package/dist/core/check.js +7 -3
- 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/result.js +8 -5
- package/dist/core/fix-pr-batch.js +38 -28
- package/dist/core/fix-pr.js +27 -24
- package/dist/core/init-ci.js +34 -28
- package/dist/core/options.d.ts +4 -1
- package/dist/core/options.js +152 -4
- package/dist/core/review-model.js +3 -0
- package/dist/core/summary.js +6 -0
- package/dist/core/upgrade.js +64 -2
- package/dist/core/verification.d.ts +2 -0
- package/dist/core/verification.js +108 -0
- package/dist/core/warm-cache.js +7 -3
- package/dist/generated/version.d.ts +1 -0
- package/dist/generated/version.js +2 -0
- package/dist/git/scope.d.ts +19 -0
- package/dist/git/scope.js +167 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/output/format.js +15 -0
- package/dist/output/github.js +6 -0
- package/dist/output/sarif.js +12 -18
- package/dist/parsers/package-json.js +2 -4
- package/dist/pm/detect.d.ts +40 -1
- package/dist/pm/detect.js +152 -9
- package/dist/pm/install.d.ts +3 -1
- package/dist/pm/install.js +18 -17
- package/dist/registry/npm.js +34 -76
- package/dist/rup +0 -0
- package/dist/types/index.d.ts +134 -5
- package/dist/ui/tui.d.ts +4 -1
- package/dist/ui/tui.js +156 -67
- 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.d.ts +7 -1
- package/dist/workspace/discover.js +67 -54
- package/package.json +24 -19
- 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
|
@@ -2,6 +2,11 @@ export function parseLicensesArgs(args) {
|
|
|
2
2
|
const options = {
|
|
3
3
|
cwd: process.cwd(),
|
|
4
4
|
workspace: false,
|
|
5
|
+
affected: false,
|
|
6
|
+
staged: false,
|
|
7
|
+
baseRef: undefined,
|
|
8
|
+
headRef: undefined,
|
|
9
|
+
sinceRef: undefined,
|
|
5
10
|
allow: undefined,
|
|
6
11
|
deny: undefined,
|
|
7
12
|
sbomFile: undefined,
|
|
@@ -25,6 +30,35 @@ export function parseLicensesArgs(args) {
|
|
|
25
30
|
options.workspace = true;
|
|
26
31
|
continue;
|
|
27
32
|
}
|
|
33
|
+
if (current === "--affected") {
|
|
34
|
+
options.affected = true;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (current === "--staged") {
|
|
38
|
+
options.staged = true;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (current === "--base" && next) {
|
|
42
|
+
options.baseRef = next;
|
|
43
|
+
i++;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (current === "--base")
|
|
47
|
+
throw new Error("Missing value for --base");
|
|
48
|
+
if (current === "--head" && next) {
|
|
49
|
+
options.headRef = next;
|
|
50
|
+
i++;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (current === "--head")
|
|
54
|
+
throw new Error("Missing value for --head");
|
|
55
|
+
if (current === "--since" && next) {
|
|
56
|
+
options.sinceRef = next;
|
|
57
|
+
i++;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (current === "--since")
|
|
61
|
+
throw new Error("Missing value for --since");
|
|
28
62
|
if (current === "--diff") {
|
|
29
63
|
options.diffMode = true;
|
|
30
64
|
continue;
|
|
@@ -105,6 +139,11 @@ Options:
|
|
|
105
139
|
--json-file <path> Write JSON report to file
|
|
106
140
|
--diff Show only packages with a different license than last scan
|
|
107
141
|
--workspace Scan all workspace packages
|
|
142
|
+
--affected Scan changed workspace packages and dependents
|
|
143
|
+
--staged Limit scanning to staged changes
|
|
144
|
+
--base <ref> Compare changes against a base git ref
|
|
145
|
+
--head <ref> Compare changes against a head git ref
|
|
146
|
+
--since <ref> Compare changes since a git ref
|
|
108
147
|
--timeout <ms> Registry request timeout (default: 10000)
|
|
109
148
|
--concurrency <n> Parallel registry requests (default: 12)
|
|
110
149
|
--cwd <path> Working directory (default: cwd)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import process from "node:process";
|
|
2
1
|
import { discoverPackageDirs } from "../../workspace/discover.js";
|
|
3
2
|
import { readManifest, collectDependencies, } from "../../parsers/package-json.js";
|
|
4
3
|
import { asyncPool } from "../../utils/async-pool.js";
|
|
5
4
|
import { stableStringify } from "../../utils/stable-json.js";
|
|
6
5
|
import { writeFileAtomic } from "../../utils/io.js";
|
|
6
|
+
import { writeStderr, writeStdout } from "../../utils/runtime.js";
|
|
7
7
|
import { generateSbom } from "./sbom.js";
|
|
8
8
|
/**
|
|
9
9
|
* Entry point for `rup licenses`. Lazy-loaded by cli.ts.
|
|
@@ -20,7 +20,11 @@ export async function runLicenses(options) {
|
|
|
20
20
|
errors: [],
|
|
21
21
|
warnings: [],
|
|
22
22
|
};
|
|
23
|
-
const packageDirs = await discoverPackageDirs(options.cwd, options.workspace
|
|
23
|
+
const packageDirs = await discoverPackageDirs(options.cwd, options.workspace, {
|
|
24
|
+
git: options,
|
|
25
|
+
includeKinds: ["dependencies", "devDependencies", "optionalDependencies"],
|
|
26
|
+
includeDependents: options.affected === true,
|
|
27
|
+
});
|
|
24
28
|
const allDeps = new Map(); // name → resolved version
|
|
25
29
|
for (const packageDir of packageDirs) {
|
|
26
30
|
let manifest;
|
|
@@ -63,17 +67,17 @@ export async function runLicenses(options) {
|
|
|
63
67
|
}
|
|
64
68
|
result.totalViolations = result.violations.length;
|
|
65
69
|
// Render
|
|
66
|
-
|
|
70
|
+
writeStdout(renderLicenseTable(result) + "\n");
|
|
67
71
|
// SBOM output
|
|
68
72
|
if (options.sbomFile) {
|
|
69
73
|
const sbom = generateSbom(result.packages, options.cwd);
|
|
70
74
|
await writeFileAtomic(options.sbomFile, stableStringify(sbom, 2) + "\n");
|
|
71
|
-
|
|
75
|
+
writeStderr(`[licenses] SBOM written to ${options.sbomFile}\n`);
|
|
72
76
|
}
|
|
73
77
|
// JSON output
|
|
74
78
|
if (options.jsonFile) {
|
|
75
79
|
await writeFileAtomic(options.jsonFile, stableStringify(result, 2) + "\n");
|
|
76
|
-
|
|
80
|
+
writeStderr(`[licenses] JSON report written to ${options.jsonFile}\n`);
|
|
77
81
|
}
|
|
78
82
|
return result;
|
|
79
83
|
}
|
|
@@ -22,7 +22,11 @@ export async function buildPeerGraph(options,
|
|
|
22
22
|
* to inject proposed upgrade versions before writing them to disk).
|
|
23
23
|
*/
|
|
24
24
|
resolvedVersionOverrides) {
|
|
25
|
-
const packageDirs = await discoverPackageDirs(options.cwd, options.workspace
|
|
25
|
+
const packageDirs = await discoverPackageDirs(options.cwd, options.workspace, {
|
|
26
|
+
git: options,
|
|
27
|
+
includeKinds: ["dependencies", "devDependencies", "optionalDependencies"],
|
|
28
|
+
includeDependents: options.affected === true,
|
|
29
|
+
});
|
|
26
30
|
const cache = await VersionCache.create();
|
|
27
31
|
const registry = new NpmRegistryClient(options.cwd, {
|
|
28
32
|
timeoutMs: options.registryTimeoutMs,
|
|
@@ -2,6 +2,11 @@ export function parseResolveArgs(args) {
|
|
|
2
2
|
const options = {
|
|
3
3
|
cwd: process.cwd(),
|
|
4
4
|
workspace: false,
|
|
5
|
+
affected: false,
|
|
6
|
+
staged: false,
|
|
7
|
+
baseRef: undefined,
|
|
8
|
+
headRef: undefined,
|
|
9
|
+
sinceRef: undefined,
|
|
5
10
|
afterUpdate: false,
|
|
6
11
|
safe: false,
|
|
7
12
|
jsonFile: undefined,
|
|
@@ -23,6 +28,35 @@ export function parseResolveArgs(args) {
|
|
|
23
28
|
options.workspace = true;
|
|
24
29
|
continue;
|
|
25
30
|
}
|
|
31
|
+
if (current === "--affected") {
|
|
32
|
+
options.affected = true;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (current === "--staged") {
|
|
36
|
+
options.staged = true;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
if (current === "--base" && next) {
|
|
40
|
+
options.baseRef = next;
|
|
41
|
+
i++;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (current === "--base")
|
|
45
|
+
throw new Error("Missing value for --base");
|
|
46
|
+
if (current === "--head" && next) {
|
|
47
|
+
options.headRef = next;
|
|
48
|
+
i++;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (current === "--head")
|
|
52
|
+
throw new Error("Missing value for --head");
|
|
53
|
+
if (current === "--since" && next) {
|
|
54
|
+
options.sinceRef = next;
|
|
55
|
+
i++;
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (current === "--since")
|
|
59
|
+
throw new Error("Missing value for --since");
|
|
26
60
|
if (current === "--after-update") {
|
|
27
61
|
options.afterUpdate = true;
|
|
28
62
|
continue;
|
|
@@ -77,6 +111,11 @@ Options:
|
|
|
77
111
|
--after-update Simulate conflicts after applying pending \`rup check\` updates
|
|
78
112
|
--safe Exit non-zero if any error-level conflicts exist
|
|
79
113
|
--workspace Scan all workspace packages
|
|
114
|
+
--affected Scan changed workspace packages and their dependents
|
|
115
|
+
--staged Limit scanning to staged changes
|
|
116
|
+
--base <ref> Compare changes against a base git ref
|
|
117
|
+
--head <ref> Compare changes against a head git ref
|
|
118
|
+
--since <ref> Compare changes since a git ref
|
|
80
119
|
--json-file <path> Write JSON conflict report to file
|
|
81
120
|
--timeout <ms> Registry request timeout in ms (default: 10000)
|
|
82
121
|
--concurrency <n> Parallel registry requests (default: 12)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import process from "node:process";
|
|
2
1
|
import { buildPeerGraph } from "./graph/builder.js";
|
|
3
2
|
import { resolvePeerConflicts } from "./graph/resolver.js";
|
|
4
3
|
import { stableStringify } from "../../utils/stable-json.js";
|
|
5
4
|
import { writeFileAtomic } from "../../utils/io.js";
|
|
5
|
+
import { writeStderr, writeStdout } from "../../utils/runtime.js";
|
|
6
6
|
/**
|
|
7
7
|
* Entry point for `rup resolve`. Lazy-loaded by cli.ts.
|
|
8
8
|
*
|
|
@@ -26,7 +26,7 @@ export async function runResolve(options) {
|
|
|
26
26
|
if (options.afterUpdate) {
|
|
27
27
|
versionOverrides = await fetchProposedVersions(options);
|
|
28
28
|
if (versionOverrides.size === 0 && !options.silent) {
|
|
29
|
-
|
|
29
|
+
writeStderr("[resolve] No pending updates found — checking current state.\n");
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
let graph;
|
|
@@ -42,12 +42,12 @@ export async function runResolve(options) {
|
|
|
42
42
|
result.errorConflicts = conflicts.filter((c) => c.severity === "error").length;
|
|
43
43
|
result.warningConflicts = conflicts.filter((c) => c.severity === "warning").length;
|
|
44
44
|
if (!options.silent) {
|
|
45
|
-
|
|
45
|
+
writeStdout(renderConflictsTable(result, options) + "\n");
|
|
46
46
|
}
|
|
47
47
|
if (options.jsonFile) {
|
|
48
48
|
await writeFileAtomic(options.jsonFile, stableStringify(result, 2) + "\n");
|
|
49
49
|
if (!options.silent) {
|
|
50
|
-
|
|
50
|
+
writeStderr(`[resolve] JSON report written to ${options.jsonFile}\n`);
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
return result;
|
|
@@ -95,11 +95,21 @@ async function fetchProposedVersions(options) {
|
|
|
95
95
|
cooldownDays: undefined,
|
|
96
96
|
prLimit: undefined,
|
|
97
97
|
onlyChanged: false,
|
|
98
|
+
affected: options.affected,
|
|
99
|
+
staged: options.staged,
|
|
100
|
+
baseRef: options.baseRef,
|
|
101
|
+
headRef: options.headRef,
|
|
102
|
+
sinceRef: options.sinceRef,
|
|
98
103
|
ciProfile: "minimal",
|
|
99
104
|
lockfileMode: "preserve",
|
|
100
105
|
interactive: false,
|
|
101
106
|
showImpact: false,
|
|
102
107
|
showHomepage: false,
|
|
108
|
+
decisionPlanFile: undefined,
|
|
109
|
+
verify: "none",
|
|
110
|
+
testCommand: undefined,
|
|
111
|
+
verificationReportFile: undefined,
|
|
112
|
+
ciGate: "check",
|
|
103
113
|
});
|
|
104
114
|
for (const update of checkResult.updates ?? []) {
|
|
105
115
|
overrides.set(update.name, update.toVersionResolved);
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import process from "node:process";
|
|
3
2
|
import { ensureRiskLevel } from "../../core/options.js";
|
|
3
|
+
import { exitProcess, getRuntimeCwd, writeStdout, } from "../../utils/runtime.js";
|
|
4
4
|
export function parseReviewArgs(args) {
|
|
5
5
|
const options = {
|
|
6
|
-
cwd:
|
|
6
|
+
cwd: getRuntimeCwd(),
|
|
7
7
|
target: "latest",
|
|
8
8
|
filter: undefined,
|
|
9
9
|
reject: undefined,
|
|
@@ -37,6 +37,11 @@ export function parseReviewArgs(args) {
|
|
|
37
37
|
cooldownDays: undefined,
|
|
38
38
|
prLimit: undefined,
|
|
39
39
|
onlyChanged: false,
|
|
40
|
+
affected: false,
|
|
41
|
+
staged: false,
|
|
42
|
+
baseRef: undefined,
|
|
43
|
+
headRef: undefined,
|
|
44
|
+
sinceRef: undefined,
|
|
40
45
|
ciProfile: "minimal",
|
|
41
46
|
lockfileMode: "preserve",
|
|
42
47
|
interactive: false,
|
|
@@ -47,6 +52,12 @@ export function parseReviewArgs(args) {
|
|
|
47
52
|
diff: undefined,
|
|
48
53
|
applySelected: false,
|
|
49
54
|
showChangelog: false,
|
|
55
|
+
decisionPlanFile: undefined,
|
|
56
|
+
queueFocus: "all",
|
|
57
|
+
verify: "none",
|
|
58
|
+
testCommand: undefined,
|
|
59
|
+
verificationReportFile: undefined,
|
|
60
|
+
ciGate: "check",
|
|
50
61
|
};
|
|
51
62
|
for (let i = 0; i < args.length; i += 1) {
|
|
52
63
|
const current = args[i];
|
|
@@ -62,6 +73,39 @@ export function parseReviewArgs(args) {
|
|
|
62
73
|
options.workspace = true;
|
|
63
74
|
continue;
|
|
64
75
|
}
|
|
76
|
+
if (current === "--only-changed") {
|
|
77
|
+
options.onlyChanged = true;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (current === "--affected") {
|
|
81
|
+
options.affected = true;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (current === "--staged") {
|
|
85
|
+
options.staged = true;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (current === "--base" && next) {
|
|
89
|
+
options.baseRef = next;
|
|
90
|
+
i += 1;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (current === "--base")
|
|
94
|
+
throw new Error("Missing value for --base");
|
|
95
|
+
if (current === "--head" && next) {
|
|
96
|
+
options.headRef = next;
|
|
97
|
+
i += 1;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (current === "--head")
|
|
101
|
+
throw new Error("Missing value for --head");
|
|
102
|
+
if (current === "--since" && next) {
|
|
103
|
+
options.sinceRef = next;
|
|
104
|
+
i += 1;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (current === "--since")
|
|
108
|
+
throw new Error("Missing value for --since");
|
|
65
109
|
if (current === "--interactive") {
|
|
66
110
|
options.interactive = true;
|
|
67
111
|
continue;
|
|
@@ -94,6 +138,41 @@ export function parseReviewArgs(args) {
|
|
|
94
138
|
options.applySelected = true;
|
|
95
139
|
continue;
|
|
96
140
|
}
|
|
141
|
+
if (current === "--plan-file" && next) {
|
|
142
|
+
options.decisionPlanFile = path.resolve(options.cwd, next);
|
|
143
|
+
i += 1;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
if (current === "--plan-file")
|
|
147
|
+
throw new Error("Missing value for --plan-file");
|
|
148
|
+
if (current === "--verify" && next) {
|
|
149
|
+
if (next === "none" ||
|
|
150
|
+
next === "install" ||
|
|
151
|
+
next === "test" ||
|
|
152
|
+
next === "install,test") {
|
|
153
|
+
options.verify = next;
|
|
154
|
+
i += 1;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
throw new Error("--verify must be none, install, test or install,test");
|
|
158
|
+
}
|
|
159
|
+
if (current === "--verify")
|
|
160
|
+
throw new Error("Missing value for --verify");
|
|
161
|
+
if (current === "--test-command" && next) {
|
|
162
|
+
options.testCommand = next;
|
|
163
|
+
i += 1;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (current === "--test-command")
|
|
167
|
+
throw new Error("Missing value for --test-command");
|
|
168
|
+
if (current === "--verification-report-file" && next) {
|
|
169
|
+
options.verificationReportFile = path.resolve(options.cwd, next);
|
|
170
|
+
i += 1;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (current === "--verification-report-file") {
|
|
174
|
+
throw new Error("Missing value for --verification-report-file");
|
|
175
|
+
}
|
|
97
176
|
if (current === "--show-changelog") {
|
|
98
177
|
options.showChangelog = true;
|
|
99
178
|
continue;
|
|
@@ -148,13 +227,22 @@ export function parseReviewArgs(args) {
|
|
|
148
227
|
throw new Error("Missing value for --registry-retries");
|
|
149
228
|
}
|
|
150
229
|
if (current === "--help" || current === "-h") {
|
|
151
|
-
|
|
152
|
-
|
|
230
|
+
writeStdout(REVIEW_HELP);
|
|
231
|
+
exitProcess(0);
|
|
153
232
|
}
|
|
154
233
|
if (current.startsWith("-"))
|
|
155
234
|
throw new Error(`Unknown review option: ${current}`);
|
|
156
235
|
throw new Error(`Unexpected review argument: ${current}`);
|
|
157
236
|
}
|
|
237
|
+
if (options.securityOnly) {
|
|
238
|
+
options.queueFocus = "security";
|
|
239
|
+
}
|
|
240
|
+
else if (options.risk === "critical" || options.risk === "high") {
|
|
241
|
+
options.queueFocus = "risk";
|
|
242
|
+
}
|
|
243
|
+
else if (options.diff === "major") {
|
|
244
|
+
options.queueFocus = "major";
|
|
245
|
+
}
|
|
158
246
|
return options;
|
|
159
247
|
}
|
|
160
248
|
const REVIEW_HELP = `
|
|
@@ -169,8 +257,17 @@ Options:
|
|
|
169
257
|
--risk <level> Minimum risk: critical, high, medium, low
|
|
170
258
|
--diff <level> Filter by patch, minor, major, latest
|
|
171
259
|
--apply-selected Apply all filtered updates after review
|
|
260
|
+
--plan-file <path> Write the selected decision set to a reusable plan file
|
|
261
|
+
--verify <mode> Run post-apply verification: none, install, test, install,test
|
|
262
|
+
--test-command <cmd> Override the command used for test verification
|
|
172
263
|
--show-changelog Fetch release notes summaries for review output
|
|
173
264
|
--workspace Scan all workspace packages
|
|
265
|
+
--only-changed Limit analysis to changed packages
|
|
266
|
+
--affected Include changed packages and their dependents
|
|
267
|
+
--staged Limit analysis to staged changes
|
|
268
|
+
--base <ref> Compare changes against a base git ref
|
|
269
|
+
--head <ref> Compare changes against a head git ref
|
|
270
|
+
--since <ref> Compare changes since a git ref
|
|
174
271
|
--policy-file <path> Load policy overrides
|
|
175
272
|
--json-file <path> Write JSON review report to file
|
|
176
273
|
--registry-timeout-ms <n>
|
|
@@ -1,16 +1,42 @@
|
|
|
1
|
-
import process from "node:process";
|
|
2
|
-
import { runTui } from "../../ui/tui.js";
|
|
3
1
|
import { buildReviewResult, renderReviewResult } from "../../core/review-model.js";
|
|
4
2
|
import { applySelectedUpdates } from "../../core/upgrade.js";
|
|
3
|
+
import { createDecisionPlan, writeDecisionPlan } from "../../core/decision-plan.js";
|
|
5
4
|
import { stableStringify } from "../../utils/stable-json.js";
|
|
6
5
|
import { writeFileAtomic } from "../../utils/io.js";
|
|
6
|
+
import { writeStdout } from "../../utils/runtime.js";
|
|
7
7
|
export async function runReview(options) {
|
|
8
8
|
const review = await buildReviewResult(options);
|
|
9
|
-
let selectedItems = review.items;
|
|
10
9
|
if (options.interactive && review.updates.length > 0) {
|
|
11
|
-
|
|
10
|
+
const { runDashboard } = await import("../dashboard/runner.js");
|
|
11
|
+
const dashboard = await runDashboard({
|
|
12
|
+
...options,
|
|
13
|
+
mode: options.applySelected ? "upgrade" : "review",
|
|
14
|
+
focus: options.queueFocus ?? "all",
|
|
15
|
+
applySelected: options.applySelected,
|
|
16
|
+
}, review);
|
|
17
|
+
review.summary.decisionPlan = dashboard.decisionPlanFile;
|
|
18
|
+
review.summary.interactiveSurface = "dashboard";
|
|
19
|
+
review.summary.queueFocus = options.queueFocus ?? "all";
|
|
20
|
+
if (options.jsonFile) {
|
|
21
|
+
await writeFileAtomic(options.jsonFile, stableStringify(review, 2) + "\n");
|
|
22
|
+
}
|
|
23
|
+
return review;
|
|
12
24
|
}
|
|
25
|
+
let selectedItems = review.items;
|
|
13
26
|
const selectedUpdates = selectedItems.map((item) => item.update);
|
|
27
|
+
if (options.decisionPlanFile) {
|
|
28
|
+
const decisionPlan = createDecisionPlan({
|
|
29
|
+
review,
|
|
30
|
+
selectedItems,
|
|
31
|
+
sourceCommand: "review",
|
|
32
|
+
mode: options.applySelected ? "upgrade" : "review",
|
|
33
|
+
focus: options.queueFocus ?? "all",
|
|
34
|
+
});
|
|
35
|
+
const decisionPlanFile = options.decisionPlanFile;
|
|
36
|
+
await writeDecisionPlan(decisionPlanFile, decisionPlan);
|
|
37
|
+
review.decisionPlan = decisionPlan;
|
|
38
|
+
review.summary.decisionPlan = decisionPlanFile;
|
|
39
|
+
}
|
|
14
40
|
if (options.applySelected && selectedUpdates.length > 0) {
|
|
15
41
|
await applySelectedUpdates({
|
|
16
42
|
...options,
|
|
@@ -19,7 +45,7 @@ export async function runReview(options) {
|
|
|
19
45
|
sync: false,
|
|
20
46
|
}, selectedUpdates);
|
|
21
47
|
}
|
|
22
|
-
|
|
48
|
+
writeStdout(renderReviewResult({
|
|
23
49
|
...review,
|
|
24
50
|
items: selectedItems,
|
|
25
51
|
updates: selectedUpdates,
|
|
@@ -3,6 +3,11 @@ export function parseSnapshotArgs(args) {
|
|
|
3
3
|
const options = {
|
|
4
4
|
cwd: process.cwd(),
|
|
5
5
|
workspace: false,
|
|
6
|
+
affected: false,
|
|
7
|
+
staged: false,
|
|
8
|
+
baseRef: undefined,
|
|
9
|
+
headRef: undefined,
|
|
10
|
+
sinceRef: undefined,
|
|
6
11
|
action: "list",
|
|
7
12
|
label: undefined,
|
|
8
13
|
snapshotId: undefined,
|
|
@@ -25,6 +30,35 @@ export function parseSnapshotArgs(args) {
|
|
|
25
30
|
options.workspace = true;
|
|
26
31
|
continue;
|
|
27
32
|
}
|
|
33
|
+
if (current === "--affected") {
|
|
34
|
+
options.affected = true;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (current === "--staged") {
|
|
38
|
+
options.staged = true;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (current === "--base" && next) {
|
|
42
|
+
options.baseRef = next;
|
|
43
|
+
i++;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (current === "--base")
|
|
47
|
+
throw new Error("Missing value for --base");
|
|
48
|
+
if (current === "--head" && next) {
|
|
49
|
+
options.headRef = next;
|
|
50
|
+
i++;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if (current === "--head")
|
|
54
|
+
throw new Error("Missing value for --head");
|
|
55
|
+
if (current === "--since" && next) {
|
|
56
|
+
options.sinceRef = next;
|
|
57
|
+
i++;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (current === "--since")
|
|
61
|
+
throw new Error("Missing value for --since");
|
|
28
62
|
if (current === "--label" && next) {
|
|
29
63
|
options.label = next;
|
|
30
64
|
i++;
|
|
@@ -75,6 +109,11 @@ Options:
|
|
|
75
109
|
--label <name> Human-readable label for the snapshot
|
|
76
110
|
--store <path> Custom snapshot store file (default: .rup-snapshots.json)
|
|
77
111
|
--workspace Include all workspace packages
|
|
112
|
+
--affected Include changed workspace packages and dependents
|
|
113
|
+
--staged Limit snapshot scope to staged changes
|
|
114
|
+
--base <ref> Compare changes against a base git ref
|
|
115
|
+
--head <ref> Compare changes against a head git ref
|
|
116
|
+
--since <ref> Compare changes since a git ref
|
|
78
117
|
--cwd <path> Working directory (default: cwd)
|
|
79
118
|
--help Show this help
|
|
80
119
|
`.trimStart();
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import process from "node:process";
|
|
2
1
|
import { discoverPackageDirs } from "../../workspace/discover.js";
|
|
3
2
|
import { SnapshotStore, captureState, restoreState, diffManifests, } from "./store.js";
|
|
3
|
+
import { writeStderr, writeStdout } from "../../utils/runtime.js";
|
|
4
4
|
/**
|
|
5
5
|
* Entry point for `rup snapshot`. Lazy-loaded by cli.ts.
|
|
6
6
|
*
|
|
@@ -16,7 +16,10 @@ export async function runSnapshot(options) {
|
|
|
16
16
|
errors: [],
|
|
17
17
|
warnings: [],
|
|
18
18
|
};
|
|
19
|
-
const packageDirs = await discoverPackageDirs(options.cwd, options.workspace
|
|
19
|
+
const packageDirs = await discoverPackageDirs(options.cwd, options.workspace, {
|
|
20
|
+
git: options,
|
|
21
|
+
includeDependents: options.affected === true,
|
|
22
|
+
});
|
|
20
23
|
const store = new SnapshotStore(options.cwd, options.storeFile);
|
|
21
24
|
switch (options.action) {
|
|
22
25
|
// ─ save ──────────────────────────────────────────────────────────────────
|
|
@@ -27,7 +30,7 @@ export async function runSnapshot(options) {
|
|
|
27
30
|
const entry = await store.saveSnapshot(manifests, lockfileHashes, label);
|
|
28
31
|
result.snapshotId = entry.id;
|
|
29
32
|
result.label = entry.label;
|
|
30
|
-
|
|
33
|
+
writeStdout(`✔ Snapshot saved: ${entry.label} (${entry.id})\n`);
|
|
31
34
|
break;
|
|
32
35
|
}
|
|
33
36
|
// ─ list ──────────────────────────────────────────────────────────────────
|
|
@@ -39,20 +42,20 @@ export async function runSnapshot(options) {
|
|
|
39
42
|
createdAt: new Date(e.createdAt).toISOString(),
|
|
40
43
|
}));
|
|
41
44
|
if (entries.length === 0) {
|
|
42
|
-
|
|
45
|
+
writeStdout("No snapshots saved yet. Use `rup snapshot save` to create one.\n");
|
|
43
46
|
}
|
|
44
47
|
else {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
writeStdout(`\n${entries.length} snapshot(s):\n\n`);
|
|
49
|
+
writeStdout(" " + "ID".padEnd(30) + "Label".padEnd(30) + "Created\n");
|
|
50
|
+
writeStdout(" " + "─".repeat(75) + "\n");
|
|
48
51
|
for (const e of entries) {
|
|
49
|
-
|
|
52
|
+
writeStdout(" " +
|
|
50
53
|
e.id.padEnd(30) +
|
|
51
54
|
e.label.padEnd(30) +
|
|
52
55
|
new Date(e.createdAt).toLocaleString() +
|
|
53
56
|
"\n");
|
|
54
57
|
}
|
|
55
|
-
|
|
58
|
+
writeStdout("\n");
|
|
56
59
|
}
|
|
57
60
|
break;
|
|
58
61
|
}
|
|
@@ -72,8 +75,8 @@ export async function runSnapshot(options) {
|
|
|
72
75
|
result.snapshotId = entry.id;
|
|
73
76
|
result.label = entry.label;
|
|
74
77
|
const count = Object.keys(entry.manifests).length;
|
|
75
|
-
|
|
76
|
-
|
|
78
|
+
writeStdout(`✔ Restored ${count} package.json file(s) from snapshot "${entry.label}" (${entry.id})\n`);
|
|
79
|
+
writeStdout(" Re-run your package manager install to apply.\n");
|
|
77
80
|
break;
|
|
78
81
|
}
|
|
79
82
|
// ─ diff ──────────────────────────────────────────────────────────────────
|
|
@@ -92,23 +95,23 @@ export async function runSnapshot(options) {
|
|
|
92
95
|
const changes = diffManifests(entry.manifests, currentManifests);
|
|
93
96
|
result.diff = changes;
|
|
94
97
|
if (changes.length === 0) {
|
|
95
|
-
|
|
98
|
+
writeStdout(`✔ No dependency changes since snapshot "${entry.label}"\n`);
|
|
96
99
|
}
|
|
97
100
|
else {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
+
writeStdout(`\nDependency changes since snapshot "${entry.label}":\n\n`);
|
|
102
|
+
writeStdout(" " + "Package".padEnd(35) + "Before".padEnd(20) + "After\n");
|
|
103
|
+
writeStdout(" " + "─".repeat(65) + "\n");
|
|
101
104
|
for (const c of changes) {
|
|
102
|
-
|
|
105
|
+
writeStdout(" " + c.name.padEnd(35) + c.from.padEnd(20) + c.to + "\n");
|
|
103
106
|
}
|
|
104
|
-
|
|
107
|
+
writeStdout("\n");
|
|
105
108
|
}
|
|
106
109
|
break;
|
|
107
110
|
}
|
|
108
111
|
}
|
|
109
112
|
if (result.errors.length > 0) {
|
|
110
113
|
for (const err of result.errors) {
|
|
111
|
-
|
|
114
|
+
writeStderr(`[snapshot] ✖ ${err}\n`);
|
|
112
115
|
}
|
|
113
116
|
}
|
|
114
117
|
return result;
|
|
@@ -1,13 +1,4 @@
|
|
|
1
1
|
import type { SnapshotEntry } from "../../types/index.js";
|
|
2
|
-
/**
|
|
3
|
-
* Lightweight SQLite-free snapshot store (uses a JSON file in the project root).
|
|
4
|
-
*
|
|
5
|
-
* Design goals:
|
|
6
|
-
* - No extra runtime dependencies (SQLite bindings vary by runtime)
|
|
7
|
-
* - Human-readable store file (git-committable if desired)
|
|
8
|
-
* - Atomic writes via tmp-rename to prevent corruption
|
|
9
|
-
* - Fast: entire store fits in memory for typical use (< 50 snapshots)
|
|
10
|
-
*/
|
|
11
2
|
export declare class SnapshotStore {
|
|
12
3
|
private readonly storePath;
|
|
13
4
|
private entries;
|
|
@@ -20,14 +11,11 @@ export declare class SnapshotStore {
|
|
|
20
11
|
findSnapshot(idOrLabel: string): Promise<SnapshotEntry | null>;
|
|
21
12
|
deleteSnapshot(idOrLabel: string): Promise<boolean>;
|
|
22
13
|
}
|
|
23
|
-
/** Captures current package.json and lockfile state for a set of directories. */
|
|
24
14
|
export declare function captureState(packageDirs: string[]): Promise<{
|
|
25
15
|
manifests: Record<string, string>;
|
|
26
16
|
lockfileHashes: Record<string, string>;
|
|
27
17
|
}>;
|
|
28
|
-
/** Restores package.json files from a snapshot's manifest map. */
|
|
29
18
|
export declare function restoreState(entry: SnapshotEntry): Promise<void>;
|
|
30
|
-
/** Computes a diff of dependency versions between two manifest snapshots. */
|
|
31
19
|
export declare function diffManifests(before: Record<string, string>, after: Record<string, string>): Array<{
|
|
32
20
|
name: string;
|
|
33
21
|
from: string;
|