greenbump 0.7.0 → 0.9.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/README.md +73 -15
- package/README.zh-CN.md +37 -4
- package/dist/action/github.test.js +37 -0
- package/dist/action/github.test.js.map +1 -0
- package/dist/action/main.js +19 -4
- package/dist/action/main.js.map +1 -1
- package/dist/agent/calllog.js +23 -0
- package/dist/agent/calllog.js.map +1 -0
- package/dist/agent/calllog.test.js +77 -0
- package/dist/agent/calllog.test.js.map +1 -0
- package/dist/agent/fixer.js +246 -29
- package/dist/agent/fixer.js.map +1 -1
- package/dist/agent/fixer.test.js +299 -0
- package/dist/agent/fixer.test.js.map +1 -1
- package/dist/agent/guard.js +37 -0
- package/dist/agent/guard.js.map +1 -0
- package/dist/agent/guard.test.js +48 -0
- package/dist/agent/guard.test.js.map +1 -0
- package/dist/agent/provider-anthropic.js +2 -2
- package/dist/agent/provider-anthropic.js.map +1 -1
- package/dist/agent/provider-openai.js +2 -2
- package/dist/agent/provider-openai.js.map +1 -1
- package/dist/benchmarks/run.js +0 -0
- package/dist/benchmarks/run.js.map +1 -0
- package/dist/cli/interactive.js +46 -11
- package/dist/cli/interactive.js.map +1 -1
- package/dist/cli.js +37 -7
- package/dist/cli.js.map +1 -1
- package/dist/engine/analyzers/changelog-parser.js +51 -2
- package/dist/engine/analyzers/changelog-parser.js.map +1 -1
- package/dist/engine/batch.js +54 -10
- package/dist/engine/batch.js.map +1 -1
- package/dist/engine/cache/manager.js +19 -1
- package/dist/engine/cache/manager.js.map +1 -1
- package/dist/engine/cache/manager.test.js +17 -0
- package/dist/engine/cache/manager.test.js.map +1 -1
- package/dist/engine/change-detector.js +52 -23
- package/dist/engine/change-detector.js.map +1 -1
- package/dist/engine/change-detector.test.js +72 -0
- package/dist/engine/change-detector.test.js.map +1 -0
- package/dist/engine/changelog.js +22 -2
- package/dist/engine/changelog.js.map +1 -1
- package/dist/engine/changelog.test.js +31 -0
- package/dist/engine/changelog.test.js.map +1 -0
- package/dist/engine/checks.js +10 -4
- package/dist/engine/checks.js.map +1 -1
- package/dist/engine/context/optimizer.js +14 -44
- package/dist/engine/context/optimizer.js.map +1 -1
- package/dist/engine/context/optimizer.test.js +23 -1
- package/dist/engine/context/optimizer.test.js.map +1 -1
- package/dist/engine/ecosystems/cargo.js +2 -1
- package/dist/engine/ecosystems/cargo.js.map +1 -1
- package/dist/engine/ecosystems/conan.js +2 -1
- package/dist/engine/ecosystems/conan.js.map +1 -1
- package/dist/engine/ecosystems/elm.js +2 -1
- package/dist/engine/ecosystems/elm.js.map +1 -1
- package/dist/engine/ecosystems/gradle.js +2 -1
- package/dist/engine/ecosystems/gradle.js.map +1 -1
- package/dist/engine/ecosystems/http.js +18 -0
- package/dist/engine/ecosystems/http.js.map +1 -0
- package/dist/engine/ecosystems/maven.js +2 -1
- package/dist/engine/ecosystems/maven.js.map +1 -1
- package/dist/engine/ecosystems/swiftpm.js +2 -1
- package/dist/engine/ecosystems/swiftpm.js.map +1 -1
- package/dist/engine/exec.js +37 -7
- package/dist/engine/exec.js.map +1 -1
- package/dist/engine/exec.test.js +36 -0
- package/dist/engine/exec.test.js.map +1 -0
- package/dist/engine/fixer/patterns.js +55 -18
- package/dist/engine/fixer/patterns.js.map +1 -1
- package/dist/engine/fixer/patterns.test.js +95 -1
- package/dist/engine/fixer/patterns.test.js.map +1 -1
- package/dist/engine/git.js +38 -16
- package/dist/engine/git.js.map +1 -1
- package/dist/engine/git.test.js +51 -1
- package/dist/engine/git.test.js.map +1 -1
- package/dist/engine/perf/metrics.js +44 -57
- package/dist/engine/perf/metrics.js.map +1 -1
- package/dist/engine/run.js +141 -15
- package/dist/engine/run.js.map +1 -1
- package/dist/engine/run.test.js +65 -1
- package/dist/engine/run.test.js.map +1 -1
- package/dist/engine/sandbox/docker-manager.js +52 -26
- package/dist/engine/sandbox/docker-manager.js.map +1 -1
- package/dist/engine/sandbox/template-generator.js +0 -4
- package/dist/engine/sandbox/template-generator.js.map +1 -1
- package/dist/engine/sandbox/template-generator.test.js +16 -0
- package/dist/engine/sandbox/template-generator.test.js.map +1 -0
- package/dist/engine/scan/benchmark-crosscheck.test.js +116 -0
- package/dist/engine/scan/benchmark-crosscheck.test.js.map +1 -0
- package/dist/engine/scan/detect.js +96 -0
- package/dist/engine/scan/detect.js.map +1 -0
- package/dist/engine/scan/impact.js +128 -0
- package/dist/engine/scan/impact.js.map +1 -0
- package/dist/engine/scan/impact.test.js +217 -0
- package/dist/engine/scan/impact.test.js.map +1 -0
- package/dist/engine/scan/indexer.js +129 -0
- package/dist/engine/scan/indexer.js.map +1 -0
- package/dist/engine/scan/indexer.test.js +149 -0
- package/dist/engine/scan/indexer.test.js.map +1 -0
- package/dist/engine/scan/probes.js +92 -0
- package/dist/engine/scan/probes.js.map +1 -0
- package/dist/engine/scan/probes.test.js +100 -0
- package/dist/engine/scan/probes.test.js.map +1 -0
- package/dist/engine/verify.js +41 -58
- package/dist/engine/verify.js.map +1 -1
- package/dist/engine/verify.test.js +49 -0
- package/dist/engine/verify.test.js.map +1 -0
- package/dist/format.js +96 -0
- package/dist/format.js.map +1 -1
- package/dist/format.test.js +53 -0
- package/dist/format.test.js.map +1 -0
- package/dist/pr.test.js +48 -0
- package/dist/pr.test.js.map +1 -0
- package/package.json +4 -4
- package/dist/engine/dep-graph.js +0 -101
- package/dist/engine/dep-graph.js.map +0 -1
- package/dist/engine/staged-fix.js +0 -108
- package/dist/engine/staged-fix.js.map +0 -1
- package/dist/engine/stages.js +0 -125
- package/dist/engine/stages.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template-generator.test.js","sourceRoot":"","sources":["../../../src/engine/sandbox/template-generator.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAE9D,IAAI,CAAC,8FAA8F,EAAE,KAAK,IAAI,EAAE;IAC9G,0EAA0E;IAC1E,2EAA2E;IAC3E,wDAAwD;IACxD,MAAM,IAAI,GAAG,MAAM,mBAAmB,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC;IACrD,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;IACrC,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;IACvC,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;AACjD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;IAC7E,MAAM,CAAC,KAAK,CAAC,MAAM,mBAAmB,CAAC,CAAC,kBAAkB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACpE,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { readFile, readdir, access, mkdtemp, rm } from "node:fs/promises";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { buildScanReport } from "./impact.js";
|
|
8
|
+
import { WORKSPACE_ROOT } from "../workspace.js";
|
|
9
|
+
import { resetCacheForTests } from "../cache/manager.js";
|
|
10
|
+
/**
|
|
11
|
+
* Cross-check: `--scan` must predict exactly the breakages the fix loop
|
|
12
|
+
* already knows how to fix.
|
|
13
|
+
*
|
|
14
|
+
* Each benchmark case under benchmarks/cases/ ships a real fixture repo plus
|
|
15
|
+
* an `expect.json` whose `forbiddenPatterns` describe the breakage present
|
|
16
|
+
* BEFORE the fix (and required absent after). If scan's deterministic
|
|
17
|
+
* prediction can't find those same patterns in the fixture, then either the
|
|
18
|
+
* codemod registry or the scan probe logic has drifted — the user would be
|
|
19
|
+
* told "no impact" for an upgrade greenbump would actually have to fix.
|
|
20
|
+
*/
|
|
21
|
+
const CASES_DIR = fileURLToPath(new URL("../../../benchmarks/cases", import.meta.url));
|
|
22
|
+
async function exists(p) {
|
|
23
|
+
try {
|
|
24
|
+
await access(p);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return false;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Every fixture that ships a case.json + expect.json with forbidden patterns. */
|
|
32
|
+
async function loadCases() {
|
|
33
|
+
const entries = await readdir(CASES_DIR, { withFileTypes: true });
|
|
34
|
+
const cases = [];
|
|
35
|
+
for (const e of entries) {
|
|
36
|
+
if (!e.isDirectory())
|
|
37
|
+
continue;
|
|
38
|
+
const dir = join(CASES_DIR, e.name, "repo");
|
|
39
|
+
if (!(await exists(dir)))
|
|
40
|
+
continue;
|
|
41
|
+
const meta = JSON.parse(await readFile(join(CASES_DIR, e.name, "case.json"), "utf8"));
|
|
42
|
+
const exp = JSON.parse(await readFile(join(CASES_DIR, e.name, "expect.json"), "utf8"));
|
|
43
|
+
if (!exp.forbiddenPatterns || exp.forbiddenPatterns.length === 0)
|
|
44
|
+
continue;
|
|
45
|
+
cases.push({ dir, meta, expect: exp });
|
|
46
|
+
}
|
|
47
|
+
return cases;
|
|
48
|
+
}
|
|
49
|
+
function asOutdated(meta) {
|
|
50
|
+
return {
|
|
51
|
+
name: meta.dep,
|
|
52
|
+
current: meta.from,
|
|
53
|
+
wanted: meta.from,
|
|
54
|
+
latest: meta.to,
|
|
55
|
+
workspacePath: WORKSPACE_ROOT,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Run `fn` with an empty, isolated cache dir so the codemod registry is the
|
|
60
|
+
* only possible source of scan results. Uses a real temp dir — pointing at a
|
|
61
|
+
* path under the repo would have the cache manager create it.
|
|
62
|
+
*/
|
|
63
|
+
async function withEmptyCache(fn) {
|
|
64
|
+
const cacheDir = await mkdtemp(join(tmpdir(), "greenbump-crosscheck-cache-"));
|
|
65
|
+
const prevCache = process.env.GREENBUMP_CACHE_DIR;
|
|
66
|
+
process.env.GREENBUMP_CACHE_DIR = cacheDir;
|
|
67
|
+
resetCacheForTests();
|
|
68
|
+
try {
|
|
69
|
+
await fn();
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
if (prevCache === undefined)
|
|
73
|
+
delete process.env.GREENBUMP_CACHE_DIR;
|
|
74
|
+
else
|
|
75
|
+
process.env.GREENBUMP_CACHE_DIR = prevCache;
|
|
76
|
+
resetCacheForTests();
|
|
77
|
+
await rm(cacheDir, { recursive: true, force: true });
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
test("scan cross-check: finds the benchmark fixtures", async () => {
|
|
81
|
+
const cases = await loadCases();
|
|
82
|
+
assert.ok(cases.length >= 5, `expected at least 5 fixtures, found ${cases.length}`);
|
|
83
|
+
});
|
|
84
|
+
test("scan cross-check: every tier-1 fixture's breakage is predicted from codemods alone", async () => {
|
|
85
|
+
const cases = await loadCases();
|
|
86
|
+
const tier1 = cases.filter((c) => c.meta.expectedTier === 1);
|
|
87
|
+
assert.ok(tier1.length > 0, "expected at least one tier-1 fixture");
|
|
88
|
+
await withEmptyCache(async () => {
|
|
89
|
+
for (const { dir, meta, expect } of tier1) {
|
|
90
|
+
const report = await buildScanReport(dir, [asOutdated(meta)]);
|
|
91
|
+
const det = report.detections[0];
|
|
92
|
+
assert.equal(det.status, "ok", `${meta.name}: scan did not recognise this upgrade`);
|
|
93
|
+
assert.ok(det.locationsMatched >= 1, `${meta.name}: scan found no impact, but this fixture is a known tier-1 breakage`);
|
|
94
|
+
// At least one predicted location must actually carry the breakage the
|
|
95
|
+
// benchmark says is present in the fixture.
|
|
96
|
+
const excerpts = det.changes.flatMap((c) => c.fileGroups.flatMap((g) => g.locations.map((l) => l.excerpt)));
|
|
97
|
+
const hit = expect.forbiddenPatterns.some((re) => excerpts.some((line) => new RegExp(re).test(line)));
|
|
98
|
+
assert.ok(hit, `${meta.name}: none of the ${expect.forbiddenPatterns.join(", ")} patterns appeared in scan output:\n` +
|
|
99
|
+
excerpts.map((e) => ` ${e}`).join("\n"));
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
test("scan cross-check: without a cache, an uncovered upgrade is reported unknown rather than guessed", async () => {
|
|
104
|
+
// A dependency with no built-in codemod and no cached digest must not
|
|
105
|
+
// invent an impact result — it reports "unknown" and nothing else.
|
|
106
|
+
await withEmptyCache(async () => {
|
|
107
|
+
const dir = join(CASES_DIR, "zod-3-to-4-issues", "repo");
|
|
108
|
+
const report = await buildScanReport(dir, [
|
|
109
|
+
{ name: "some-unmapped-package", current: "1.0.0", wanted: "1.0.0", latest: "2.0.0", workspacePath: WORKSPACE_ROOT },
|
|
110
|
+
]);
|
|
111
|
+
assert.equal(report.detections[0].status, "unknown");
|
|
112
|
+
assert.deepEqual(report.detections[0].changes, []);
|
|
113
|
+
assert.equal(report.summary.depsWithKnownBreaking, 0);
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
//# sourceMappingURL=benchmark-crosscheck.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"benchmark-crosscheck.test.js","sourceRoot":"","sources":["../../../src/engine/scan/benchmark-crosscheck.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { readDigestFromCache } from "../analyzers/changelog-parser.js";
|
|
2
|
+
import { codemodsForUpgrade, probesFromCodemod, probesFromChange } from "./probes.js";
|
|
3
|
+
/**
|
|
4
|
+
* Generate detection metadata for a single outdated dependency.
|
|
5
|
+
*
|
|
6
|
+
* Collects probes from two sources:
|
|
7
|
+
* - Tier A: built-in codemods (zero IO, always available)
|
|
8
|
+
* - Tier B: changelog digest cache (read-only, no LLM calls)
|
|
9
|
+
*/
|
|
10
|
+
export async function detectForOutdated(cwd, o, noCache) {
|
|
11
|
+
const codemods = codemodsForUpgrade(o.name, o.current, o.latest);
|
|
12
|
+
const digest = noCache ? null : await readDigestFromCache(o.name, o.current, o.latest);
|
|
13
|
+
const probes = [];
|
|
14
|
+
const changesMetadata = [];
|
|
15
|
+
const fileCreations = [];
|
|
16
|
+
let changeIndex = 0;
|
|
17
|
+
// Tier A: codemods
|
|
18
|
+
for (const codemod of codemods) {
|
|
19
|
+
if (codemod.transform) {
|
|
20
|
+
const codemodProbes = probesFromCodemod(codemod, o.name, changeIndex);
|
|
21
|
+
probes.push(...codemodProbes);
|
|
22
|
+
changesMetadata.push({
|
|
23
|
+
description: codemod.description,
|
|
24
|
+
oldApi: codemod.transform.find,
|
|
25
|
+
newApi: codemod.transform.replace,
|
|
26
|
+
severity: "high", // codemods are known breakages
|
|
27
|
+
source: "builtin-codemod",
|
|
28
|
+
rewrite: codemod.transform.replace,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
if (codemod.creates) {
|
|
32
|
+
for (const create of codemod.creates) {
|
|
33
|
+
fileCreations.push({
|
|
34
|
+
path: create.path,
|
|
35
|
+
note: `will be created by codemod: ${codemod.description}`,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (!codemod.transform && !codemod.creates) {
|
|
40
|
+
// Guidance-only codemod
|
|
41
|
+
changesMetadata.push({
|
|
42
|
+
description: codemod.description,
|
|
43
|
+
oldApi: "",
|
|
44
|
+
severity: "medium",
|
|
45
|
+
source: "builtin-codemod",
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
changeIndex++;
|
|
49
|
+
}
|
|
50
|
+
// Tier B: changelog digest
|
|
51
|
+
if (digest && digest.length > 0) {
|
|
52
|
+
for (const change of digest) {
|
|
53
|
+
const changeProbes = probesFromChange(change, o.name, changeIndex);
|
|
54
|
+
probes.push(...changeProbes);
|
|
55
|
+
changesMetadata.push({
|
|
56
|
+
description: change.description,
|
|
57
|
+
oldApi: change.oldApi,
|
|
58
|
+
newApi: change.newApi,
|
|
59
|
+
severity: change.severity,
|
|
60
|
+
source: "changelog-cache",
|
|
61
|
+
});
|
|
62
|
+
changeIndex++;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Determine status
|
|
66
|
+
let status;
|
|
67
|
+
let kind;
|
|
68
|
+
if (codemods.length > 0 && digest && digest.length > 0) {
|
|
69
|
+
kind = "both";
|
|
70
|
+
status = "ok";
|
|
71
|
+
}
|
|
72
|
+
else if (codemods.length > 0) {
|
|
73
|
+
kind = "codemod";
|
|
74
|
+
status = codemods.some((c) => c.transform || c.creates) ? "ok" : "info-only";
|
|
75
|
+
}
|
|
76
|
+
else if (digest && digest.length > 0) {
|
|
77
|
+
kind = "digest";
|
|
78
|
+
status = "ok";
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
kind = "codemod";
|
|
82
|
+
status = "unknown";
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
name: o.name,
|
|
86
|
+
current: o.current,
|
|
87
|
+
latest: o.latest,
|
|
88
|
+
workspacePath: o.workspacePath,
|
|
89
|
+
kind,
|
|
90
|
+
status,
|
|
91
|
+
probes,
|
|
92
|
+
changesMetadata,
|
|
93
|
+
fileCreations,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=detect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"detect.js","sourceRoot":"","sources":["../../../src/engine/scan/detect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { detectForOutdated } from "./detect.js";
|
|
2
|
+
import { scanOnce } from "./indexer.js";
|
|
3
|
+
export async function buildScanReport(cwd, outdated, opts = {}) {
|
|
4
|
+
const maxPerChange = opts.maxPerChange ?? 200;
|
|
5
|
+
// Phase 1: collect detections (probes) for all outdated deps
|
|
6
|
+
const pendingDetections = await Promise.all(outdated.map((o) => detectForOutdated(cwd, o, opts.noCache ?? false)));
|
|
7
|
+
// Phase 2: collect all probes
|
|
8
|
+
const allProbes = [];
|
|
9
|
+
for (const detection of pendingDetections) {
|
|
10
|
+
allProbes.push(...detection.probes);
|
|
11
|
+
}
|
|
12
|
+
// Phase 3: single-pass scan
|
|
13
|
+
const { hits, truncatedProbes } = await scanOnce(cwd, allProbes, { perProbeCap: maxPerChange });
|
|
14
|
+
// Phase 4: assemble report
|
|
15
|
+
const detections = pendingDetections.map((pending) => {
|
|
16
|
+
// Group hits by change index
|
|
17
|
+
const changeHits = new Map();
|
|
18
|
+
for (const hit of hits) {
|
|
19
|
+
const probe = pending.probes.find((p) => p.key === hit.probeKey);
|
|
20
|
+
if (!probe)
|
|
21
|
+
continue;
|
|
22
|
+
const changeIndex = probe.refs[0]?.changeIndex;
|
|
23
|
+
if (changeIndex === undefined)
|
|
24
|
+
continue;
|
|
25
|
+
if (!changeHits.has(changeIndex)) {
|
|
26
|
+
changeHits.set(changeIndex, []);
|
|
27
|
+
}
|
|
28
|
+
changeHits.get(changeIndex).push(hit);
|
|
29
|
+
}
|
|
30
|
+
// Build BreakingChangeImpact for each change
|
|
31
|
+
const changes = pending.changesMetadata.map((meta, idx) => {
|
|
32
|
+
const changeHitsList = changeHits.get(idx) ?? [];
|
|
33
|
+
const truncated = pending.probes
|
|
34
|
+
.filter((p) => p.refs[0]?.changeIndex === idx)
|
|
35
|
+
.some((p) => truncatedProbes.has(p.key));
|
|
36
|
+
// Group by file
|
|
37
|
+
const fileMap = new Map();
|
|
38
|
+
for (const hit of changeHitsList) {
|
|
39
|
+
if (!fileMap.has(hit.file)) {
|
|
40
|
+
fileMap.set(hit.file, []);
|
|
41
|
+
}
|
|
42
|
+
fileMap.get(hit.file).push(hit);
|
|
43
|
+
}
|
|
44
|
+
const fileGroups = Array.from(fileMap.entries()).map(([file, locations]) => ({
|
|
45
|
+
file,
|
|
46
|
+
locations: locations.map((l) => ({ file: l.file, line: l.line, excerpt: l.excerpt })),
|
|
47
|
+
shown: Math.min(locations.length, 20),
|
|
48
|
+
totalInFile: locations.length,
|
|
49
|
+
}));
|
|
50
|
+
const approx = pending.probes
|
|
51
|
+
.filter((p) => p.refs[0]?.changeIndex === idx)
|
|
52
|
+
.some((p) => p.approx);
|
|
53
|
+
return {
|
|
54
|
+
description: meta.description,
|
|
55
|
+
oldApi: meta.oldApi,
|
|
56
|
+
newApi: meta.newApi,
|
|
57
|
+
severity: meta.severity,
|
|
58
|
+
source: meta.source,
|
|
59
|
+
rewrite: meta.rewrite,
|
|
60
|
+
fileGroups,
|
|
61
|
+
truncated,
|
|
62
|
+
matches: changeHitsList.length,
|
|
63
|
+
...(approx ? { approx: true } : {}),
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
// Calculate detection-level stats
|
|
67
|
+
const allFiles = new Set();
|
|
68
|
+
let totalLocations = 0;
|
|
69
|
+
for (const change of changes) {
|
|
70
|
+
for (const fg of change.fileGroups) {
|
|
71
|
+
allFiles.add(fg.file);
|
|
72
|
+
totalLocations += fg.totalInFile;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const hasApprox = changes.some((c) => c.approx);
|
|
76
|
+
const status = pending.status === "unknown" || pending.status === "info-only"
|
|
77
|
+
? pending.status
|
|
78
|
+
: hasApprox
|
|
79
|
+
? "approx"
|
|
80
|
+
: "ok";
|
|
81
|
+
return {
|
|
82
|
+
name: pending.name,
|
|
83
|
+
current: pending.current,
|
|
84
|
+
latest: pending.latest,
|
|
85
|
+
workspacePath: pending.workspacePath,
|
|
86
|
+
kind: pending.kind,
|
|
87
|
+
status,
|
|
88
|
+
changes,
|
|
89
|
+
...(pending.fileCreations.length > 0 ? { fileCreations: pending.fileCreations } : {}),
|
|
90
|
+
filesMatched: allFiles.size,
|
|
91
|
+
locationsMatched: totalLocations,
|
|
92
|
+
displayedImpact: changes.length > 0 && totalLocations > 0,
|
|
93
|
+
truncated: pending.probes.some((p) => truncatedProbes.has(p.key)),
|
|
94
|
+
};
|
|
95
|
+
});
|
|
96
|
+
// Summary stats
|
|
97
|
+
const allAffectedFiles = new Set();
|
|
98
|
+
let totalLocationsAffected = 0;
|
|
99
|
+
let depsWithKnownBreaking = 0;
|
|
100
|
+
let anyTruncated = false;
|
|
101
|
+
for (const d of detections) {
|
|
102
|
+
if (d.status === "ok" || d.status === "approx") {
|
|
103
|
+
depsWithKnownBreaking++;
|
|
104
|
+
for (const change of d.changes) {
|
|
105
|
+
for (const fg of change.fileGroups) {
|
|
106
|
+
allAffectedFiles.add(fg.file);
|
|
107
|
+
totalLocationsAffected += fg.totalInFile;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (d.truncated)
|
|
112
|
+
anyTruncated = true;
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
schemaVersion: 1,
|
|
116
|
+
cwd,
|
|
117
|
+
packageManager: "", // filled by caller
|
|
118
|
+
summary: {
|
|
119
|
+
depsScanned: outdated.length,
|
|
120
|
+
depsWithKnownBreaking,
|
|
121
|
+
filesAffected: allAffectedFiles.size,
|
|
122
|
+
locationsAffected: totalLocationsAffected,
|
|
123
|
+
truncated: anyTruncated,
|
|
124
|
+
},
|
|
125
|
+
detections,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=impact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"impact.js","sourceRoot":"","sources":["../../../src/engine/scan/impact.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtemp, writeFile, rm, mkdir } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { join, dirname } from "node:path";
|
|
6
|
+
import { buildScanReport } from "./impact.js";
|
|
7
|
+
import { WORKSPACE_ROOT } from "../workspace.js";
|
|
8
|
+
import { getCache, resetCacheForTests } from "../cache/manager.js";
|
|
9
|
+
const DEFAULT_PKG = { name: "bench", private: true, dependencies: {} };
|
|
10
|
+
function outdated(overrides) {
|
|
11
|
+
return {
|
|
12
|
+
name: "zod",
|
|
13
|
+
current: "3.23.8",
|
|
14
|
+
wanted: "3.24.0",
|
|
15
|
+
latest: "4.0.5",
|
|
16
|
+
workspacePath: WORKSPACE_ROOT,
|
|
17
|
+
...overrides,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Build a temp project plus an isolated cache dir. Seeds the digest cache via
|
|
22
|
+
* the same `digest::pkg--from--to` key the real digest writer uses.
|
|
23
|
+
*/
|
|
24
|
+
async function withScanEnv(files, fn, seedDigest) {
|
|
25
|
+
const dir = await mkdtemp(join(tmpdir(), "greenbump-impact-"));
|
|
26
|
+
const cacheDir = await mkdtemp(join(tmpdir(), "greenbump-impact-cache-"));
|
|
27
|
+
const prevCache = process.env.GREENBUMP_CACHE_DIR;
|
|
28
|
+
process.env.GREENBUMP_CACHE_DIR = cacheDir;
|
|
29
|
+
resetCacheForTests();
|
|
30
|
+
for (const [path, content] of Object.entries(files)) {
|
|
31
|
+
await mkdir(dirname(join(dir, path)), { recursive: true });
|
|
32
|
+
await writeFile(join(dir, path), content, "utf8");
|
|
33
|
+
}
|
|
34
|
+
if (seedDigest) {
|
|
35
|
+
const cache = getCache();
|
|
36
|
+
await cache.init();
|
|
37
|
+
await cache.setLlmFix(`digest::${seedDigest.name}--${seedDigest.from}--${seedDigest.to}`, {
|
|
38
|
+
edits: { __digest__: JSON.stringify(seedDigest.changes) },
|
|
39
|
+
hits: 0,
|
|
40
|
+
model: "stub",
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
await fn(dir);
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
if (prevCache === undefined)
|
|
48
|
+
delete process.env.GREENBUMP_CACHE_DIR;
|
|
49
|
+
else
|
|
50
|
+
process.env.GREENBUMP_CACHE_DIR = prevCache;
|
|
51
|
+
resetCacheForTests();
|
|
52
|
+
await rm(cacheDir, { recursive: true, force: true });
|
|
53
|
+
await rm(dir, { recursive: true, force: true });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
test("buildScanReport: codemod tier reports file/line impact with no cache and no LLM", async () => {
|
|
57
|
+
await withScanEnv({
|
|
58
|
+
"package.json": JSON.stringify(DEFAULT_PKG),
|
|
59
|
+
"src/validate.js": 'const r = schema.safeParse(d);\nreturn r.error.errors[0].message;\n',
|
|
60
|
+
}, async (dir) => {
|
|
61
|
+
const report = await buildScanReport(dir, [outdated({})]);
|
|
62
|
+
assert.equal(report.schemaVersion, 1);
|
|
63
|
+
assert.equal(report.summary.depsScanned, 1);
|
|
64
|
+
const det = report.detections[0];
|
|
65
|
+
assert.equal(det.name, "zod");
|
|
66
|
+
assert.equal(det.kind, "codemod");
|
|
67
|
+
assert.equal(det.status, "ok");
|
|
68
|
+
const errors = det.changes.find((c) => c.description.includes("error.issues"));
|
|
69
|
+
assert.ok(errors, "expected the error.errors codemod to be reported");
|
|
70
|
+
assert.equal(errors.matches, 1);
|
|
71
|
+
assert.equal(errors.fileGroups.length, 1);
|
|
72
|
+
assert.equal(errors.fileGroups[0].file, join("src", "validate.js"));
|
|
73
|
+
assert.equal(errors.fileGroups[0].locations[0].line, 2);
|
|
74
|
+
assert.equal(det.filesMatched, 1);
|
|
75
|
+
assert.equal(det.locationsMatched, 1);
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
test("buildScanReport: wildcard codemods are not misattributed to an unrelated dep", async () => {
|
|
79
|
+
await withScanEnv({
|
|
80
|
+
"package.json": JSON.stringify(DEFAULT_PKG),
|
|
81
|
+
"test.js": 'assert.equal(a, b);\nconst r = z.safeParse(x);\nr.error.errors;\n',
|
|
82
|
+
}, async (dir) => {
|
|
83
|
+
const report = await buildScanReport(dir, [outdated({})]);
|
|
84
|
+
const det = report.detections[0];
|
|
85
|
+
// `assert.equal` is a `package: "*"` codemod — it must not appear as a zod change.
|
|
86
|
+
assert.ok(!det.changes.some((c) => c.description.includes("assert.equal")), "wildcard codemod leaked into the zod detection");
|
|
87
|
+
assert.ok(!det.changes.some((c) => c.description.includes("util.is")));
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
test("buildScanReport: an upgrade with no matching codemod and no cache is reported unknown", async () => {
|
|
91
|
+
await withScanEnv({ "package.json": JSON.stringify(DEFAULT_PKG), "a.js": "whatever();\n" }, async (dir) => {
|
|
92
|
+
const report = await buildScanReport(dir, [
|
|
93
|
+
outdated({ name: "left-pad", current: "1.0.0", latest: "2.0.0" }),
|
|
94
|
+
]);
|
|
95
|
+
const det = report.detections[0];
|
|
96
|
+
assert.equal(det.status, "unknown");
|
|
97
|
+
assert.equal(det.changes.length, 0);
|
|
98
|
+
assert.equal(det.displayedImpact, false);
|
|
99
|
+
assert.equal(report.summary.depsWithKnownBreaking, 0);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
test("buildScanReport: no cache hit means no filesystem-wide guessing (never calls an LLM)", async () => {
|
|
103
|
+
await withScanEnv({ "package.json": JSON.stringify(DEFAULT_PKG), "a.js": "err.error.errors;\n" }, async (dir) => {
|
|
104
|
+
// No cached digest for left-pad: the zod-only codemod must not fire for it,
|
|
105
|
+
// and nothing else may invent a result.
|
|
106
|
+
const report = await buildScanReport(dir, [
|
|
107
|
+
outdated({ name: "left-pad", current: "1.0.0", latest: "2.0.0" }),
|
|
108
|
+
]);
|
|
109
|
+
assert.equal(report.detections[0].status, "unknown");
|
|
110
|
+
assert.deepEqual(report.detections[0].changes, []);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
test("buildScanReport: reads a seeded digest from cache and maps it to project locations", async () => {
|
|
114
|
+
await withScanEnv({
|
|
115
|
+
"package.json": JSON.stringify(DEFAULT_PKG),
|
|
116
|
+
"src/api.js": "const r = client.oldMethod();\n",
|
|
117
|
+
}, async (dir) => {
|
|
118
|
+
const report = await buildScanReport(dir, [
|
|
119
|
+
outdated({ name: "some-lib", current: "1.0.0", latest: "2.0.0" }),
|
|
120
|
+
]);
|
|
121
|
+
const det = report.detections[0];
|
|
122
|
+
assert.equal(det.kind, "digest");
|
|
123
|
+
assert.equal(det.status, "ok");
|
|
124
|
+
const change = det.changes.find((c) => c.description.includes("oldMethod"));
|
|
125
|
+
assert.ok(change, "expected the cached digest change to be reported");
|
|
126
|
+
assert.equal(change.source, "changelog-cache");
|
|
127
|
+
assert.equal(change.matches, 1);
|
|
128
|
+
assert.equal(change.fileGroups[0].file, join("src", "api.js"));
|
|
129
|
+
assert.equal(change.fileGroups[0].locations[0].line, 1);
|
|
130
|
+
}, {
|
|
131
|
+
name: "some-lib",
|
|
132
|
+
from: "1.0.0",
|
|
133
|
+
to: "2.0.0",
|
|
134
|
+
changes: [
|
|
135
|
+
{
|
|
136
|
+
description: "oldMethod was replaced by newMethod",
|
|
137
|
+
oldApi: "client.oldMethod()",
|
|
138
|
+
newApi: "client.newMethod()",
|
|
139
|
+
severity: "high",
|
|
140
|
+
},
|
|
141
|
+
],
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
test("buildScanReport: --no-cache ignores a seeded digest and falls back to codemods", async () => {
|
|
145
|
+
await withScanEnv({
|
|
146
|
+
"package.json": JSON.stringify(DEFAULT_PKG),
|
|
147
|
+
"src/api.js": "const r = client.oldMethod();\n",
|
|
148
|
+
}, async (dir) => {
|
|
149
|
+
const report = await buildScanReport(dir, [outdated({ name: "some-lib", current: "1.0.0", latest: "2.0.0" })], { noCache: true });
|
|
150
|
+
const det = report.detections[0];
|
|
151
|
+
assert.equal(det.status, "unknown");
|
|
152
|
+
assert.deepEqual(det.changes, []);
|
|
153
|
+
}, {
|
|
154
|
+
name: "some-lib",
|
|
155
|
+
from: "1.0.0",
|
|
156
|
+
to: "2.0.0",
|
|
157
|
+
changes: [
|
|
158
|
+
{ description: "oldMethod gone", oldApi: "client.oldMethod()", severity: "high" },
|
|
159
|
+
],
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
test("buildScanReport: breaking changes that touch nothing report zero impact, not an error", async () => {
|
|
163
|
+
await withScanEnv({ "package.json": JSON.stringify(DEFAULT_PKG), "src/clean.js": "const x = 1;\n" }, async (dir) => {
|
|
164
|
+
const report = await buildScanReport(dir, [outdated({})]);
|
|
165
|
+
const det = report.detections[0];
|
|
166
|
+
// zod's codemods are known for this upgrade path...
|
|
167
|
+
assert.ok(det.changes.length > 0);
|
|
168
|
+
// ...but none of them appear in this codebase.
|
|
169
|
+
assert.equal(det.filesMatched, 0);
|
|
170
|
+
assert.equal(det.locationsMatched, 0);
|
|
171
|
+
assert.equal(det.displayedImpact, false);
|
|
172
|
+
// Status stays "ok": we *know* the breaking changes, they just don't apply.
|
|
173
|
+
assert.equal(det.status, "ok");
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
test("buildScanReport: aggregates a multi-dependency summary", async () => {
|
|
177
|
+
await withScanEnv({
|
|
178
|
+
"package.json": JSON.stringify(DEFAULT_PKG),
|
|
179
|
+
"src/a.js": "r.error.errors;\n",
|
|
180
|
+
"src/b.js": "<Switch>\n",
|
|
181
|
+
}, async (dir) => {
|
|
182
|
+
const report = await buildScanReport(dir, [
|
|
183
|
+
outdated({}),
|
|
184
|
+
outdated({ name: "react-router-dom", current: "5.3.4", latest: "6.30.0" }),
|
|
185
|
+
outdated({ name: "left-pad", current: "1.0.0", latest: "2.0.0" }),
|
|
186
|
+
]);
|
|
187
|
+
assert.equal(report.summary.depsScanned, 3);
|
|
188
|
+
assert.equal(report.summary.depsWithKnownBreaking, 2); // zod + react-router-dom
|
|
189
|
+
assert.equal(report.summary.filesAffected, 2);
|
|
190
|
+
assert.equal(report.summary.locationsAffected, 2);
|
|
191
|
+
assert.equal(report.detections.length, 3);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
test("buildScanReport: lists file creations for a scaffolding codemod", async () => {
|
|
195
|
+
await withScanEnv({ "package.json": JSON.stringify(DEFAULT_PKG), ".eslintrc.js": "module.exports = {};\n" }, async (dir) => {
|
|
196
|
+
const report = await buildScanReport(dir, [
|
|
197
|
+
outdated({ name: "eslint", current: "8.57.0", latest: "9.0.0" }),
|
|
198
|
+
]);
|
|
199
|
+
const det = report.detections[0];
|
|
200
|
+
assert.ok(det.fileCreations?.some((f) => f.path === "eslint.config.js"));
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
test("buildScanReport: workspace path is carried through to the detection", async () => {
|
|
204
|
+
await withScanEnv({ "package.json": JSON.stringify(DEFAULT_PKG), "src/a.js": "r.error.errors;\n" }, async (dir) => {
|
|
205
|
+
const report = await buildScanReport(dir, [outdated({ workspacePath: "packages/api" })]);
|
|
206
|
+
assert.equal(report.detections[0].workspacePath, "packages/api");
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
test("buildScanReport: empty outdated list produces an empty, well-formed report", async () => {
|
|
210
|
+
await withScanEnv({ "package.json": JSON.stringify(DEFAULT_PKG) }, async (dir) => {
|
|
211
|
+
const report = await buildScanReport(dir, []);
|
|
212
|
+
assert.equal(report.summary.depsScanned, 0);
|
|
213
|
+
assert.equal(report.summary.filesAffected, 0);
|
|
214
|
+
assert.deepEqual(report.detections, []);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
//# sourceMappingURL=impact.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"impact.test.js","sourceRoot":"","sources":["../../../src/engine/scan/impact.test.ts"],"names":[],"mappings":"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"}
|