@rainy-updates/cli 0.4.4 → 0.5.0-rc.2
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 +68 -0
- package/README.md +24 -1
- package/dist/bin/cli.js +132 -18
- package/dist/cache/cache.d.ts +1 -1
- package/dist/cache/cache.js +62 -9
- package/dist/config/loader.d.ts +8 -1
- package/dist/core/baseline.d.ts +23 -0
- package/dist/core/baseline.js +72 -0
- package/dist/core/check.js +25 -13
- package/dist/core/fix-pr.d.ts +7 -0
- package/dist/core/fix-pr.js +68 -0
- package/dist/core/init-ci.d.ts +1 -1
- package/dist/core/init-ci.js +9 -1
- package/dist/core/options.d.ts +6 -1
- package/dist/core/options.js +212 -9
- package/dist/core/warm-cache.js +5 -5
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/output/format.js +3 -0
- package/dist/output/github.js +3 -0
- package/dist/output/sarif.js +21 -1
- package/dist/registry/npm.d.ts +14 -3
- package/dist/registry/npm.js +117 -25
- package/dist/types/index.d.ts +19 -0
- package/dist/utils/semver.d.ts +1 -0
- package/dist/utils/semver.js +24 -0
- package/dist/workspace/discover.js +50 -18
- package/package.json +1 -1
package/dist/core/check.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { collectDependencies, readManifest } from "../parsers/package-json.js";
|
|
3
3
|
import { matchesPattern } from "../utils/pattern.js";
|
|
4
|
-
import { applyRangeStyle, classifyDiff, clampTarget,
|
|
4
|
+
import { applyRangeStyle, classifyDiff, clampTarget, pickTargetVersionFromAvailable } from "../utils/semver.js";
|
|
5
5
|
import { VersionCache } from "../cache/cache.js";
|
|
6
6
|
import { NpmRegistryClient } from "../registry/npm.js";
|
|
7
7
|
import { detectPackageManager } from "../pm/detect.js";
|
|
@@ -11,7 +11,7 @@ export async function check(options) {
|
|
|
11
11
|
const packageManager = await detectPackageManager(options.cwd);
|
|
12
12
|
const packageDirs = await discoverPackageDirs(options.cwd, options.workspace);
|
|
13
13
|
const cache = await VersionCache.create();
|
|
14
|
-
const registryClient = new NpmRegistryClient();
|
|
14
|
+
const registryClient = new NpmRegistryClient(options.cwd);
|
|
15
15
|
const policy = await loadPolicy(options.cwd, options.policyFile);
|
|
16
16
|
const updates = [];
|
|
17
17
|
const errors = [];
|
|
@@ -53,7 +53,10 @@ export async function check(options) {
|
|
|
53
53
|
for (const packageName of uniquePackageNames) {
|
|
54
54
|
const cached = await cache.getValid(packageName, options.target);
|
|
55
55
|
if (cached) {
|
|
56
|
-
resolvedVersions.set(packageName,
|
|
56
|
+
resolvedVersions.set(packageName, {
|
|
57
|
+
latestVersion: cached.latestVersion,
|
|
58
|
+
availableVersions: cached.availableVersions,
|
|
59
|
+
});
|
|
57
60
|
}
|
|
58
61
|
else {
|
|
59
62
|
unresolvedPackages.push(packageName);
|
|
@@ -64,7 +67,10 @@ export async function check(options) {
|
|
|
64
67
|
for (const packageName of unresolvedPackages) {
|
|
65
68
|
const stale = await cache.getAny(packageName, options.target);
|
|
66
69
|
if (stale) {
|
|
67
|
-
resolvedVersions.set(packageName,
|
|
70
|
+
resolvedVersions.set(packageName, {
|
|
71
|
+
latestVersion: stale.latestVersion,
|
|
72
|
+
availableVersions: stale.availableVersions,
|
|
73
|
+
});
|
|
68
74
|
warnings.push(`Using stale cache for ${packageName} because --offline is enabled.`);
|
|
69
75
|
}
|
|
70
76
|
else {
|
|
@@ -73,19 +79,25 @@ export async function check(options) {
|
|
|
73
79
|
}
|
|
74
80
|
}
|
|
75
81
|
else {
|
|
76
|
-
const fetched = await registryClient.
|
|
82
|
+
const fetched = await registryClient.resolveManyPackageMetadata(unresolvedPackages, {
|
|
77
83
|
concurrency: options.concurrency,
|
|
78
84
|
});
|
|
79
|
-
for (const [packageName,
|
|
80
|
-
resolvedVersions.set(packageName,
|
|
81
|
-
|
|
82
|
-
|
|
85
|
+
for (const [packageName, metadata] of fetched.metadata) {
|
|
86
|
+
resolvedVersions.set(packageName, {
|
|
87
|
+
latestVersion: metadata.latestVersion,
|
|
88
|
+
availableVersions: metadata.versions,
|
|
89
|
+
});
|
|
90
|
+
if (metadata.latestVersion) {
|
|
91
|
+
await cache.set(packageName, options.target, metadata.latestVersion, metadata.versions, options.cacheTtlSeconds);
|
|
83
92
|
}
|
|
84
93
|
}
|
|
85
94
|
for (const [packageName, error] of fetched.errors) {
|
|
86
95
|
const stale = await cache.getAny(packageName, options.target);
|
|
87
96
|
if (stale) {
|
|
88
|
-
resolvedVersions.set(packageName,
|
|
97
|
+
resolvedVersions.set(packageName, {
|
|
98
|
+
latestVersion: stale.latestVersion,
|
|
99
|
+
availableVersions: stale.availableVersions,
|
|
100
|
+
});
|
|
89
101
|
warnings.push(`Using stale cache for ${packageName} due to registry error: ${error}`);
|
|
90
102
|
}
|
|
91
103
|
else {
|
|
@@ -95,12 +107,12 @@ export async function check(options) {
|
|
|
95
107
|
}
|
|
96
108
|
}
|
|
97
109
|
for (const task of tasks) {
|
|
98
|
-
const
|
|
99
|
-
if (!latestVersion)
|
|
110
|
+
const metadata = resolvedVersions.get(task.dependency.name);
|
|
111
|
+
if (!metadata?.latestVersion)
|
|
100
112
|
continue;
|
|
101
113
|
const rule = policy.packageRules.get(task.dependency.name);
|
|
102
114
|
const effectiveTarget = clampTarget(options.target, rule?.maxTarget);
|
|
103
|
-
const picked =
|
|
115
|
+
const picked = pickTargetVersionFromAvailable(task.dependency.range, metadata.availableVersions, metadata.latestVersion, effectiveTarget);
|
|
104
116
|
if (!picked)
|
|
105
117
|
continue;
|
|
106
118
|
const nextRange = applyRangeStyle(task.dependency.range, picked);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { CheckResult, RunOptions } from "../types/index.js";
|
|
2
|
+
export interface FixPrResult {
|
|
3
|
+
applied: boolean;
|
|
4
|
+
branchName?: string;
|
|
5
|
+
commitSha?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function applyFixPr(options: RunOptions, result: CheckResult, extraFiles: string[]): Promise<FixPrResult>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
export async function applyFixPr(options, result, extraFiles) {
|
|
4
|
+
if (!options.fixPr)
|
|
5
|
+
return { applied: false };
|
|
6
|
+
if (result.updates.length === 0)
|
|
7
|
+
return { applied: false };
|
|
8
|
+
const status = await runGit(options.cwd, ["status", "--porcelain"]);
|
|
9
|
+
if (status.stdout.trim().length > 0) {
|
|
10
|
+
throw new Error("Cannot run --fix-pr with a dirty git working tree.");
|
|
11
|
+
}
|
|
12
|
+
const branch = options.fixBranch ?? "chore/rainy-updates";
|
|
13
|
+
const branchCheck = await runGit(options.cwd, ["rev-parse", "--verify", "--quiet", branch], true);
|
|
14
|
+
if (branchCheck.code === 0) {
|
|
15
|
+
await runGit(options.cwd, ["checkout", branch]);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
await runGit(options.cwd, ["checkout", "-b", branch]);
|
|
19
|
+
}
|
|
20
|
+
if (options.fixDryRun) {
|
|
21
|
+
return {
|
|
22
|
+
applied: false,
|
|
23
|
+
branchName: branch,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const manifestFiles = Array.from(new Set(result.packagePaths.map((pkgPath) => path.join(pkgPath, "package.json"))));
|
|
27
|
+
const filesToStage = Array.from(new Set([...manifestFiles, ...extraFiles]));
|
|
28
|
+
if (filesToStage.length > 0) {
|
|
29
|
+
await runGit(options.cwd, ["add", "--", ...filesToStage]);
|
|
30
|
+
}
|
|
31
|
+
const stagedCheck = await runGit(options.cwd, ["diff", "--cached", "--quiet"], true);
|
|
32
|
+
if (stagedCheck.code === 0) {
|
|
33
|
+
return {
|
|
34
|
+
applied: false,
|
|
35
|
+
branchName: branch,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const message = options.fixCommitMessage ?? `chore(deps): apply rainy-updates (${result.updates.length} updates)`;
|
|
39
|
+
await runGit(options.cwd, ["commit", "-m", message]);
|
|
40
|
+
const rev = await runGit(options.cwd, ["rev-parse", "HEAD"]);
|
|
41
|
+
return {
|
|
42
|
+
applied: true,
|
|
43
|
+
branchName: branch,
|
|
44
|
+
commitSha: rev.stdout.trim(),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
async function runGit(cwd, args, allowNonZero = false) {
|
|
48
|
+
return await new Promise((resolve, reject) => {
|
|
49
|
+
const child = spawn("git", args, { cwd, stdio: ["ignore", "pipe", "pipe"] });
|
|
50
|
+
let stdout = "";
|
|
51
|
+
let stderr = "";
|
|
52
|
+
child.stdout.on("data", (chunk) => {
|
|
53
|
+
stdout += typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
54
|
+
});
|
|
55
|
+
child.stderr.on("data", (chunk) => {
|
|
56
|
+
stderr += typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
57
|
+
});
|
|
58
|
+
child.on("error", reject);
|
|
59
|
+
child.on("exit", (code) => {
|
|
60
|
+
const normalized = code ?? 1;
|
|
61
|
+
if (normalized !== 0 && !allowNonZero) {
|
|
62
|
+
reject(new Error(`git ${args.join(" ")} failed (${normalized}): ${stderr.trim()}`));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
resolve({ code: normalized, stdout, stderr });
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
}
|
package/dist/core/init-ci.d.ts
CHANGED
package/dist/core/init-ci.js
CHANGED
|
@@ -15,7 +15,9 @@ export async function initCiWorkflow(cwd, force, options) {
|
|
|
15
15
|
const scheduleBlock = renderScheduleBlock(options.schedule);
|
|
16
16
|
const workflow = options.mode === "minimal"
|
|
17
17
|
? minimalWorkflowTemplate(scheduleBlock, packageManager)
|
|
18
|
-
:
|
|
18
|
+
: options.mode === "strict"
|
|
19
|
+
? strictWorkflowTemplate(scheduleBlock, packageManager)
|
|
20
|
+
: enterpriseWorkflowTemplate(scheduleBlock, packageManager);
|
|
19
21
|
await mkdir(path.dirname(workflowPath), { recursive: true });
|
|
20
22
|
await writeFile(workflowPath, workflow, "utf8");
|
|
21
23
|
return { path: workflowPath, created: true };
|
|
@@ -49,3 +51,9 @@ function minimalWorkflowTemplate(scheduleBlock, packageManager) {
|
|
|
49
51
|
function strictWorkflowTemplate(scheduleBlock, packageManager) {
|
|
50
52
|
return `name: Rainy Updates\n\non:\n${scheduleBlock}\n\npermissions:\n contents: read\n security-events: write\n\njobs:\n dependency-check:\n runs-on: ubuntu-latest\n steps:\n - name: Checkout\n uses: actions/checkout@v4\n\n - name: Setup Node\n uses: actions/setup-node@v4\n with:\n node-version: '20'\n\n${installStep(packageManager)}\n\n - name: Warm cache\n run: npx @rainy-updates/cli warm-cache --workspace --concurrency 32\n\n - name: Run strict dependency check\n run: |\n npx @rainy-updates/cli check \\\n --workspace \\\n --offline \\\n --ci \\\n --concurrency 32 \\\n --format github \\\n --json-file .artifacts/deps-report.json \\\n --pr-report-file .artifacts/deps-report.md \\\n --sarif-file .artifacts/deps-report.sarif \\\n --github-output $GITHUB_OUTPUT\n\n - name: Upload report artifacts\n uses: actions/upload-artifact@v4\n with:\n name: rainy-updates-report\n path: .artifacts/\n\n - name: Upload SARIF\n uses: github/codeql-action/upload-sarif@v3\n with:\n sarif_file: .artifacts/deps-report.sarif\n`;
|
|
51
53
|
}
|
|
54
|
+
function enterpriseWorkflowTemplate(scheduleBlock, packageManager) {
|
|
55
|
+
const detectedPmInstall = packageManager === "pnpm"
|
|
56
|
+
? "corepack enable && corepack prepare pnpm@9 --activate && pnpm install --frozen-lockfile"
|
|
57
|
+
: "npm ci";
|
|
58
|
+
return `name: Rainy Updates Enterprise\n\non:\n${scheduleBlock}\n\npermissions:\n contents: read\n security-events: write\n actions: read\n\nconcurrency:\n group: rainy-updates-\${{ github.ref }}\n cancel-in-progress: false\n\njobs:\n dependency-check:\n runs-on: ubuntu-latest\n strategy:\n fail-fast: false\n matrix:\n node: [20, 22]\n steps:\n - name: Checkout\n uses: actions/checkout@v4\n\n - name: Setup Node\n uses: actions/setup-node@v4\n with:\n node-version: \${{ matrix.node }}\n\n - name: Install dependencies\n run: ${detectedPmInstall}\n\n - name: Warm cache\n run: npx @rainy-updates/cli warm-cache --workspace --concurrency 32\n\n - name: Check updates with rollout controls\n run: |\n npx @rainy-updates/cli check \\\n --workspace \\\n --offline \\\n --concurrency 32 \\\n --format github \\\n --fail-on minor \\\n --max-updates 50 \\\n --json-file .artifacts/deps-report-node-\${{ matrix.node }}.json \\\n --pr-report-file .artifacts/deps-report-node-\${{ matrix.node }}.md \\\n --sarif-file .artifacts/deps-report-node-\${{ matrix.node }}.sarif \\\n --github-output $GITHUB_OUTPUT\n\n - name: Upload report artifacts\n uses: actions/upload-artifact@v4\n with:\n name: rainy-updates-report-node-\${{ matrix.node }}\n path: .artifacts/\n retention-days: 14\n\n - name: Upload SARIF\n uses: github/codeql-action/upload-sarif@v3\n with:\n sarif_file: .artifacts/deps-report-node-\${{ matrix.node }}.sarif\n`;
|
|
59
|
+
}
|
package/dist/core/options.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CheckOptions, UpgradeOptions } from "../types/index.js";
|
|
1
|
+
import type { BaselineOptions, CheckOptions, UpgradeOptions } from "../types/index.js";
|
|
2
2
|
import type { InitCiMode, InitCiSchedule } from "./init-ci.js";
|
|
3
3
|
export type ParsedCliArgs = {
|
|
4
4
|
command: "check";
|
|
@@ -17,5 +17,10 @@ export type ParsedCliArgs = {
|
|
|
17
17
|
mode: InitCiMode;
|
|
18
18
|
schedule: InitCiSchedule;
|
|
19
19
|
};
|
|
20
|
+
} | {
|
|
21
|
+
command: "baseline";
|
|
22
|
+
options: BaselineOptions & {
|
|
23
|
+
action: "save" | "check";
|
|
24
|
+
};
|
|
20
25
|
};
|
|
21
26
|
export declare function parseCliArgs(argv: string[]): Promise<ParsedCliArgs>;
|
package/dist/core/options.js
CHANGED
|
@@ -7,7 +7,7 @@ const DEFAULT_INCLUDE_KINDS = [
|
|
|
7
7
|
"optionalDependencies",
|
|
8
8
|
"peerDependencies",
|
|
9
9
|
];
|
|
10
|
-
const KNOWN_COMMANDS = ["check", "upgrade", "warm-cache", "init-ci"];
|
|
10
|
+
const KNOWN_COMMANDS = ["check", "upgrade", "warm-cache", "init-ci", "baseline"];
|
|
11
11
|
export async function parseCliArgs(argv) {
|
|
12
12
|
const firstArg = argv[0];
|
|
13
13
|
const isKnownCommand = KNOWN_COMMANDS.includes(firstArg);
|
|
@@ -34,10 +34,24 @@ export async function parseCliArgs(argv) {
|
|
|
34
34
|
offline: false,
|
|
35
35
|
policyFile: undefined,
|
|
36
36
|
prReportFile: undefined,
|
|
37
|
+
failOn: "none",
|
|
38
|
+
maxUpdates: undefined,
|
|
39
|
+
fixPr: false,
|
|
40
|
+
fixBranch: "chore/rainy-updates",
|
|
41
|
+
fixCommitMessage: undefined,
|
|
42
|
+
fixDryRun: false,
|
|
43
|
+
noPrReport: false,
|
|
37
44
|
};
|
|
38
45
|
let force = false;
|
|
39
|
-
let initCiMode = "
|
|
46
|
+
let initCiMode = "enterprise";
|
|
40
47
|
let initCiSchedule = "weekly";
|
|
48
|
+
let baselineAction = "check";
|
|
49
|
+
let baselineFilePath = path.resolve(base.cwd, ".rainy-updates-baseline.json");
|
|
50
|
+
let jsonFileRaw;
|
|
51
|
+
let githubOutputRaw;
|
|
52
|
+
let sarifFileRaw;
|
|
53
|
+
let policyFileRaw;
|
|
54
|
+
let prReportFileRaw;
|
|
41
55
|
let resolvedConfig = await loadConfig(base.cwd);
|
|
42
56
|
applyConfig(base, resolvedConfig);
|
|
43
57
|
for (let index = 0; index < args.length; index += 1) {
|
|
@@ -48,23 +62,36 @@ export async function parseCliArgs(argv) {
|
|
|
48
62
|
index += 1;
|
|
49
63
|
continue;
|
|
50
64
|
}
|
|
65
|
+
if (current === "--target") {
|
|
66
|
+
throw new Error("Missing value for --target");
|
|
67
|
+
}
|
|
51
68
|
if (current === "--filter" && next) {
|
|
52
69
|
base.filter = next;
|
|
53
70
|
index += 1;
|
|
54
71
|
continue;
|
|
55
72
|
}
|
|
73
|
+
if (current === "--filter") {
|
|
74
|
+
throw new Error("Missing value for --filter");
|
|
75
|
+
}
|
|
56
76
|
if (current === "--reject" && next) {
|
|
57
77
|
base.reject = next;
|
|
58
78
|
index += 1;
|
|
59
79
|
continue;
|
|
60
80
|
}
|
|
81
|
+
if (current === "--reject") {
|
|
82
|
+
throw new Error("Missing value for --reject");
|
|
83
|
+
}
|
|
61
84
|
if (current === "--cwd" && next) {
|
|
62
85
|
base.cwd = path.resolve(next);
|
|
63
86
|
resolvedConfig = await loadConfig(base.cwd);
|
|
64
87
|
applyConfig(base, resolvedConfig);
|
|
88
|
+
baselineFilePath = path.resolve(base.cwd, ".rainy-updates-baseline.json");
|
|
65
89
|
index += 1;
|
|
66
90
|
continue;
|
|
67
91
|
}
|
|
92
|
+
if (current === "--cwd") {
|
|
93
|
+
throw new Error("Missing value for --cwd");
|
|
94
|
+
}
|
|
68
95
|
if (current === "--cache-ttl" && next) {
|
|
69
96
|
const parsed = Number(next);
|
|
70
97
|
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
@@ -74,11 +101,17 @@ export async function parseCliArgs(argv) {
|
|
|
74
101
|
index += 1;
|
|
75
102
|
continue;
|
|
76
103
|
}
|
|
104
|
+
if (current === "--cache-ttl") {
|
|
105
|
+
throw new Error("Missing value for --cache-ttl");
|
|
106
|
+
}
|
|
77
107
|
if (current === "--format" && next) {
|
|
78
108
|
base.format = ensureFormat(next);
|
|
79
109
|
index += 1;
|
|
80
110
|
continue;
|
|
81
111
|
}
|
|
112
|
+
if (current === "--format") {
|
|
113
|
+
throw new Error("Missing value for --format");
|
|
114
|
+
}
|
|
82
115
|
if (current === "--ci") {
|
|
83
116
|
base.ci = true;
|
|
84
117
|
continue;
|
|
@@ -88,20 +121,29 @@ export async function parseCliArgs(argv) {
|
|
|
88
121
|
continue;
|
|
89
122
|
}
|
|
90
123
|
if (current === "--json-file" && next) {
|
|
91
|
-
|
|
124
|
+
jsonFileRaw = next;
|
|
92
125
|
index += 1;
|
|
93
126
|
continue;
|
|
94
127
|
}
|
|
128
|
+
if (current === "--json-file") {
|
|
129
|
+
throw new Error("Missing value for --json-file");
|
|
130
|
+
}
|
|
95
131
|
if (current === "--github-output" && next) {
|
|
96
|
-
|
|
132
|
+
githubOutputRaw = next;
|
|
97
133
|
index += 1;
|
|
98
134
|
continue;
|
|
99
135
|
}
|
|
136
|
+
if (current === "--github-output") {
|
|
137
|
+
throw new Error("Missing value for --github-output");
|
|
138
|
+
}
|
|
100
139
|
if (current === "--sarif-file" && next) {
|
|
101
|
-
|
|
140
|
+
sarifFileRaw = next;
|
|
102
141
|
index += 1;
|
|
103
142
|
continue;
|
|
104
143
|
}
|
|
144
|
+
if (current === "--sarif-file") {
|
|
145
|
+
throw new Error("Missing value for --sarif-file");
|
|
146
|
+
}
|
|
105
147
|
if (current === "--concurrency" && next) {
|
|
106
148
|
const parsed = Number(next);
|
|
107
149
|
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
@@ -111,39 +153,160 @@ export async function parseCliArgs(argv) {
|
|
|
111
153
|
index += 1;
|
|
112
154
|
continue;
|
|
113
155
|
}
|
|
156
|
+
if (current === "--concurrency") {
|
|
157
|
+
throw new Error("Missing value for --concurrency");
|
|
158
|
+
}
|
|
114
159
|
if (current === "--offline") {
|
|
115
160
|
base.offline = true;
|
|
116
161
|
continue;
|
|
117
162
|
}
|
|
118
163
|
if (current === "--policy-file" && next) {
|
|
119
|
-
|
|
164
|
+
policyFileRaw = next;
|
|
120
165
|
index += 1;
|
|
121
166
|
continue;
|
|
122
167
|
}
|
|
168
|
+
if (current === "--policy-file") {
|
|
169
|
+
throw new Error("Missing value for --policy-file");
|
|
170
|
+
}
|
|
123
171
|
if (current === "--pr-report-file" && next) {
|
|
124
|
-
|
|
172
|
+
prReportFileRaw = next;
|
|
125
173
|
index += 1;
|
|
126
174
|
continue;
|
|
127
175
|
}
|
|
176
|
+
if (current === "--pr-report-file") {
|
|
177
|
+
throw new Error("Missing value for --pr-report-file");
|
|
178
|
+
}
|
|
128
179
|
if (current === "--force") {
|
|
129
180
|
force = true;
|
|
130
181
|
continue;
|
|
131
182
|
}
|
|
183
|
+
if (current === "--fix-pr") {
|
|
184
|
+
base.fixPr = true;
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (current === "--fix-branch" && next) {
|
|
188
|
+
base.fixBranch = next;
|
|
189
|
+
index += 1;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (current === "--fix-branch") {
|
|
193
|
+
throw new Error("Missing value for --fix-branch");
|
|
194
|
+
}
|
|
195
|
+
if (current === "--fix-commit-message" && next) {
|
|
196
|
+
base.fixCommitMessage = next;
|
|
197
|
+
index += 1;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (current === "--fix-commit-message") {
|
|
201
|
+
throw new Error("Missing value for --fix-commit-message");
|
|
202
|
+
}
|
|
203
|
+
if (current === "--fix-dry-run") {
|
|
204
|
+
base.fixDryRun = true;
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (current === "--no-pr-report") {
|
|
208
|
+
base.noPrReport = true;
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
if (current === "--install" && command === "upgrade") {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (current === "--sync" && command === "upgrade") {
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (current === "--pm" && next && command === "upgrade") {
|
|
218
|
+
parsePackageManager(args);
|
|
219
|
+
index += 1;
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (current === "--pm" && command === "upgrade") {
|
|
223
|
+
throw new Error("Missing value for --pm");
|
|
224
|
+
}
|
|
132
225
|
if (current === "--mode" && next) {
|
|
133
226
|
initCiMode = ensureInitCiMode(next);
|
|
134
227
|
index += 1;
|
|
135
228
|
continue;
|
|
136
229
|
}
|
|
230
|
+
if (current === "--mode") {
|
|
231
|
+
throw new Error("Missing value for --mode");
|
|
232
|
+
}
|
|
137
233
|
if (current === "--schedule" && next) {
|
|
138
234
|
initCiSchedule = ensureInitCiSchedule(next);
|
|
139
235
|
index += 1;
|
|
140
236
|
continue;
|
|
141
237
|
}
|
|
238
|
+
if (current === "--schedule") {
|
|
239
|
+
throw new Error("Missing value for --schedule");
|
|
240
|
+
}
|
|
142
241
|
if (current === "--dep-kinds" && next) {
|
|
143
242
|
base.includeKinds = parseDependencyKinds(next);
|
|
144
243
|
index += 1;
|
|
145
244
|
continue;
|
|
146
245
|
}
|
|
246
|
+
if (current === "--dep-kinds") {
|
|
247
|
+
throw new Error("Missing value for --dep-kinds");
|
|
248
|
+
}
|
|
249
|
+
if (current === "--fail-on" && next) {
|
|
250
|
+
base.failOn = ensureFailOn(next);
|
|
251
|
+
index += 1;
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (current === "--fail-on") {
|
|
255
|
+
throw new Error("Missing value for --fail-on");
|
|
256
|
+
}
|
|
257
|
+
if (current === "--max-updates" && next) {
|
|
258
|
+
const parsed = Number(next);
|
|
259
|
+
if (!Number.isInteger(parsed) || parsed < 0) {
|
|
260
|
+
throw new Error("--max-updates must be a non-negative integer");
|
|
261
|
+
}
|
|
262
|
+
base.maxUpdates = parsed;
|
|
263
|
+
index += 1;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (current === "--max-updates") {
|
|
267
|
+
throw new Error("Missing value for --max-updates");
|
|
268
|
+
}
|
|
269
|
+
if (current === "--save") {
|
|
270
|
+
baselineAction = "save";
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (current === "--check") {
|
|
274
|
+
baselineAction = "check";
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (current === "--file" && next) {
|
|
278
|
+
baselineFilePath = path.resolve(base.cwd, next);
|
|
279
|
+
index += 1;
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
if (current === "--file") {
|
|
283
|
+
throw new Error("Missing value for --file");
|
|
284
|
+
}
|
|
285
|
+
if (current.startsWith("-")) {
|
|
286
|
+
throw new Error(`Unknown option: ${current}`);
|
|
287
|
+
}
|
|
288
|
+
throw new Error(`Unexpected argument: ${current}`);
|
|
289
|
+
}
|
|
290
|
+
if (jsonFileRaw) {
|
|
291
|
+
base.jsonFile = path.resolve(base.cwd, jsonFileRaw);
|
|
292
|
+
}
|
|
293
|
+
if (githubOutputRaw) {
|
|
294
|
+
base.githubOutputFile = path.resolve(base.cwd, githubOutputRaw);
|
|
295
|
+
}
|
|
296
|
+
if (sarifFileRaw) {
|
|
297
|
+
base.sarifFile = path.resolve(base.cwd, sarifFileRaw);
|
|
298
|
+
}
|
|
299
|
+
if (policyFileRaw) {
|
|
300
|
+
base.policyFile = path.resolve(base.cwd, policyFileRaw);
|
|
301
|
+
}
|
|
302
|
+
if (prReportFileRaw) {
|
|
303
|
+
base.prReportFile = path.resolve(base.cwd, prReportFileRaw);
|
|
304
|
+
}
|
|
305
|
+
if (base.noPrReport) {
|
|
306
|
+
base.prReportFile = undefined;
|
|
307
|
+
}
|
|
308
|
+
else if (base.fixPr && !base.prReportFile) {
|
|
309
|
+
base.prReportFile = path.resolve(base.cwd, ".artifacts/deps-report.md");
|
|
147
310
|
}
|
|
148
311
|
if (command === "upgrade") {
|
|
149
312
|
const configPm = resolvedConfig.packageManager;
|
|
@@ -170,6 +333,19 @@ export async function parseCliArgs(argv) {
|
|
|
170
333
|
},
|
|
171
334
|
};
|
|
172
335
|
}
|
|
336
|
+
if (command === "baseline") {
|
|
337
|
+
return {
|
|
338
|
+
command,
|
|
339
|
+
options: {
|
|
340
|
+
action: baselineAction,
|
|
341
|
+
cwd: base.cwd,
|
|
342
|
+
workspace: base.workspace,
|
|
343
|
+
includeKinds: base.includeKinds,
|
|
344
|
+
filePath: baselineFilePath,
|
|
345
|
+
ci: base.ci,
|
|
346
|
+
},
|
|
347
|
+
};
|
|
348
|
+
}
|
|
173
349
|
return {
|
|
174
350
|
command: "check",
|
|
175
351
|
options: base,
|
|
@@ -212,6 +388,27 @@ function applyConfig(base, config) {
|
|
|
212
388
|
if (typeof config.prReportFile === "string") {
|
|
213
389
|
base.prReportFile = path.resolve(base.cwd, config.prReportFile);
|
|
214
390
|
}
|
|
391
|
+
if (typeof config.failOn === "string") {
|
|
392
|
+
base.failOn = ensureFailOn(config.failOn);
|
|
393
|
+
}
|
|
394
|
+
if (typeof config.maxUpdates === "number" && Number.isInteger(config.maxUpdates) && config.maxUpdates >= 0) {
|
|
395
|
+
base.maxUpdates = config.maxUpdates;
|
|
396
|
+
}
|
|
397
|
+
if (typeof config.fixPr === "boolean") {
|
|
398
|
+
base.fixPr = config.fixPr;
|
|
399
|
+
}
|
|
400
|
+
if (typeof config.fixBranch === "string" && config.fixBranch.length > 0) {
|
|
401
|
+
base.fixBranch = config.fixBranch;
|
|
402
|
+
}
|
|
403
|
+
if (typeof config.fixCommitMessage === "string" && config.fixCommitMessage.length > 0) {
|
|
404
|
+
base.fixCommitMessage = config.fixCommitMessage;
|
|
405
|
+
}
|
|
406
|
+
if (typeof config.fixDryRun === "boolean") {
|
|
407
|
+
base.fixDryRun = config.fixDryRun;
|
|
408
|
+
}
|
|
409
|
+
if (typeof config.noPrReport === "boolean") {
|
|
410
|
+
base.noPrReport = config.noPrReport;
|
|
411
|
+
}
|
|
215
412
|
}
|
|
216
413
|
function parsePackageManager(args) {
|
|
217
414
|
const index = args.indexOf("--pm");
|
|
@@ -257,10 +454,10 @@ function parseDependencyKinds(value) {
|
|
|
257
454
|
return Array.from(new Set(mapped));
|
|
258
455
|
}
|
|
259
456
|
function ensureInitCiMode(value) {
|
|
260
|
-
if (value === "minimal" || value === "strict") {
|
|
457
|
+
if (value === "minimal" || value === "strict" || value === "enterprise") {
|
|
261
458
|
return value;
|
|
262
459
|
}
|
|
263
|
-
throw new Error("--mode must be minimal or
|
|
460
|
+
throw new Error("--mode must be minimal, strict or enterprise");
|
|
264
461
|
}
|
|
265
462
|
function ensureInitCiSchedule(value) {
|
|
266
463
|
if (value === "weekly" || value === "daily" || value === "off") {
|
|
@@ -268,3 +465,9 @@ function ensureInitCiSchedule(value) {
|
|
|
268
465
|
}
|
|
269
466
|
throw new Error("--schedule must be weekly, daily or off");
|
|
270
467
|
}
|
|
468
|
+
function ensureFailOn(value) {
|
|
469
|
+
if (value === "none" || value === "patch" || value === "minor" || value === "major" || value === "any") {
|
|
470
|
+
return value;
|
|
471
|
+
}
|
|
472
|
+
throw new Error("--fail-on must be none, patch, minor, major or any");
|
|
473
|
+
}
|
package/dist/core/warm-cache.js
CHANGED
|
@@ -8,7 +8,7 @@ export async function warmCache(options) {
|
|
|
8
8
|
const packageManager = await detectPackageManager(options.cwd);
|
|
9
9
|
const packageDirs = await discoverPackageDirs(options.cwd, options.workspace);
|
|
10
10
|
const cache = await VersionCache.create();
|
|
11
|
-
const registryClient = new NpmRegistryClient();
|
|
11
|
+
const registryClient = new NpmRegistryClient(options.cwd);
|
|
12
12
|
const errors = [];
|
|
13
13
|
const warnings = [];
|
|
14
14
|
let totalDependencies = 0;
|
|
@@ -52,12 +52,12 @@ export async function warmCache(options) {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
else {
|
|
55
|
-
const fetched = await registryClient.
|
|
55
|
+
const fetched = await registryClient.resolveManyPackageMetadata(needsFetch, {
|
|
56
56
|
concurrency: options.concurrency,
|
|
57
57
|
});
|
|
58
|
-
for (const [pkg,
|
|
59
|
-
if (
|
|
60
|
-
await cache.set(pkg, options.target,
|
|
58
|
+
for (const [pkg, metadata] of fetched.metadata) {
|
|
59
|
+
if (metadata.latestVersion) {
|
|
60
|
+
await cache.set(pkg, options.target, metadata.latestVersion, metadata.versions, options.cacheTtlSeconds);
|
|
61
61
|
warmed += 1;
|
|
62
62
|
}
|
|
63
63
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,8 @@ export { check } from "./core/check.js";
|
|
|
2
2
|
export { upgrade } from "./core/upgrade.js";
|
|
3
3
|
export { warmCache } from "./core/warm-cache.js";
|
|
4
4
|
export { initCiWorkflow } from "./core/init-ci.js";
|
|
5
|
+
export { saveBaseline, diffBaseline } from "./core/baseline.js";
|
|
5
6
|
export { createSarifReport } from "./output/sarif.js";
|
|
6
7
|
export { writeGitHubOutput, renderGitHubAnnotations } from "./output/github.js";
|
|
7
8
|
export { renderPrReport } from "./output/pr-report.js";
|
|
8
|
-
export type { CheckOptions, CheckResult, DependencyKind, OutputFormat, PackageUpdate, RunOptions, TargetLevel, UpgradeOptions, UpgradeResult, } from "./types/index.js";
|
|
9
|
+
export type { CheckOptions, CheckResult, DependencyKind, FailOnLevel, OutputFormat, PackageUpdate, RunOptions, TargetLevel, UpgradeOptions, UpgradeResult, } from "./types/index.js";
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ export { check } from "./core/check.js";
|
|
|
2
2
|
export { upgrade } from "./core/upgrade.js";
|
|
3
3
|
export { warmCache } from "./core/warm-cache.js";
|
|
4
4
|
export { initCiWorkflow } from "./core/init-ci.js";
|
|
5
|
+
export { saveBaseline, diffBaseline } from "./core/baseline.js";
|
|
5
6
|
export { createSarifReport } from "./output/sarif.js";
|
|
6
7
|
export { writeGitHubOutput, renderGitHubAnnotations } from "./output/github.js";
|
|
7
8
|
export { renderPrReport } from "./output/pr-report.js";
|
package/dist/output/format.js
CHANGED
|
@@ -52,5 +52,8 @@ export function renderResult(result, format) {
|
|
|
52
52
|
}
|
|
53
53
|
lines.push("");
|
|
54
54
|
lines.push(`Summary: ${result.summary.updatesFound} updates, ${result.summary.checkedDependencies}/${result.summary.totalDependencies} checked, ${result.summary.warmedPackages} warmed`);
|
|
55
|
+
if (result.summary.fixPrApplied) {
|
|
56
|
+
lines.push(`Fix PR: applied on branch ${result.summary.fixBranchName ?? "unknown"} (${result.summary.fixCommitSha ?? "no-commit"})`);
|
|
57
|
+
}
|
|
55
58
|
return lines.join("\n");
|
|
56
59
|
}
|
package/dist/output/github.js
CHANGED
|
@@ -8,6 +8,9 @@ export async function writeGitHubOutput(filePath, result) {
|
|
|
8
8
|
`checked_dependencies=${result.summary.checkedDependencies}`,
|
|
9
9
|
`scanned_packages=${result.summary.scannedPackages}`,
|
|
10
10
|
`warmed_packages=${result.summary.warmedPackages}`,
|
|
11
|
+
`fix_pr_applied=${result.summary.fixPrApplied === true ? "1" : "0"}`,
|
|
12
|
+
`fix_pr_branch=${result.summary.fixBranchName ?? ""}`,
|
|
13
|
+
`fix_pr_commit=${result.summary.fixCommitSha ?? ""}`,
|
|
11
14
|
];
|
|
12
15
|
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
13
16
|
await fs.writeFile(filePath, lines.join("\n") + "\n", "utf8");
|