mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 12:20:44 +00:00
ci(deps): gate extension-owned root dependencies
This commit is contained in:
@@ -1,9 +1,34 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import {
|
||||
classifyRootDependencyOwnership,
|
||||
collectRootDependencyOwnershipAudit,
|
||||
collectRootDependencyOwnershipCheckErrors,
|
||||
collectModuleSpecifiers,
|
||||
} from "../../scripts/root-dependency-ownership-audit.mjs";
|
||||
|
||||
const tempDirs: string[] = [];
|
||||
|
||||
afterEach(() => {
|
||||
for (const dir of tempDirs.splice(0)) {
|
||||
rmSync(dir, { force: true, recursive: true });
|
||||
}
|
||||
});
|
||||
|
||||
function makeTempRepo() {
|
||||
const dir = mkdtempSync(path.join(tmpdir(), "openclaw-root-deps-audit-"));
|
||||
tempDirs.push(dir);
|
||||
return dir;
|
||||
}
|
||||
|
||||
function writeRepoFile(repoRoot: string, relativePath: string, value: string) {
|
||||
const filePath = path.join(repoRoot, relativePath);
|
||||
mkdirSync(path.dirname(filePath), { recursive: true });
|
||||
writeFileSync(filePath, value, "utf8");
|
||||
}
|
||||
|
||||
describe("collectModuleSpecifiers", () => {
|
||||
it("captures require.resolve package lookups used by runtime shims and bundled plugins", () => {
|
||||
expect([
|
||||
@@ -80,3 +105,54 @@ describe("classifyRootDependencyOwnership", () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("collectRootDependencyOwnershipCheckErrors", () => {
|
||||
it("catches dependencies mirrored at root but only imported by one extension", () => {
|
||||
const repoRoot = makeTempRepo();
|
||||
writeRepoFile(
|
||||
repoRoot,
|
||||
"package.json",
|
||||
JSON.stringify({ dependencies: { "vendor-sdk": "^1.0.0" } }),
|
||||
);
|
||||
writeRepoFile(
|
||||
repoRoot,
|
||||
"extensions/qqbot/package.json",
|
||||
JSON.stringify({ dependencies: { "vendor-sdk": "^1.0.0" } }),
|
||||
);
|
||||
writeRepoFile(
|
||||
repoRoot,
|
||||
"extensions/qqbot/src/setup.ts",
|
||||
'const sdk = await import("vendor-sdk");\n',
|
||||
);
|
||||
|
||||
const records = collectRootDependencyOwnershipAudit({ repoRoot, scanRoots: ["extensions"] });
|
||||
|
||||
expect(collectRootDependencyOwnershipCheckErrors(records)).toEqual([
|
||||
"root dependency 'vendor-sdk' is extension-owned (remove from root package.json and rely on owning extension manifests plus doctor --fix); extension declarations: qqbot:dependencies; sample imports: extensions/qqbot/src/setup.ts",
|
||||
]);
|
||||
});
|
||||
|
||||
it("fails only extension-owned root dependencies", () => {
|
||||
expect(
|
||||
collectRootDependencyOwnershipCheckErrors([
|
||||
{
|
||||
category: "extension_only_localizable",
|
||||
declaredInExtensions: ["qqbot:dependencies"],
|
||||
depName: "@tencent-connect/qqbot-connector",
|
||||
recommendation:
|
||||
"remove from root package.json and rely on owning extension manifests plus doctor --fix",
|
||||
sampleFiles: ["extensions/qqbot/src/bridge/setup/finalize.ts"],
|
||||
},
|
||||
{
|
||||
category: "unreferenced",
|
||||
declaredInExtensions: [],
|
||||
depName: "@mozilla/readability",
|
||||
recommendation: "investigate removal; no direct source imports found in scanned files",
|
||||
sampleFiles: [],
|
||||
},
|
||||
]),
|
||||
).toEqual([
|
||||
"root dependency '@tencent-connect/qqbot-connector' is extension-owned (remove from root package.json and rely on owning extension manifests plus doctor --fix); extension declarations: qqbot:dependencies; sample imports: extensions/qqbot/src/bridge/setup/finalize.ts",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user