mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 12:40:43 +00:00
fix: preserve nested runtime test packages
This commit is contained in:
@@ -13,6 +13,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Matrix/E2EE: keep startup bootstrap conservative for passwordless token-auth bots, still attempt the guarded repair pass without requiring `channels.matrix.password`, and document the remaining password-UIA limitation. (#66228) Thanks @SARAMALI15792.
|
||||
- Cron/announce delivery: suppress mixed-content isolated cron announce replies that end with `NO_REPLY` so trailing silent sentinels no longer leak summary text to the target channel. (#65004) thanks @neo1027144-creator.
|
||||
- Plugins/bundled channels: partition bundled channel lazy caches by active bundled root so `OPENCLAW_BUNDLED_PLUGINS_DIR` flips stop reusing stale plugin, setup, secrets, and runtime state. (#67200) Thanks @gumadeiras.
|
||||
- Packaging/plugins: keep packaged runtime-dependency pruning and npm release validation from deleting or rejecting legitimate nested packages named `test`/`tests` under `node_modules`. (#67275) thanks @gumadeiras.
|
||||
|
||||
## 2026.4.15-beta.1
|
||||
|
||||
|
||||
@@ -118,7 +118,6 @@ const skipPackValidationEnv = "OPENCLAW_NPM_RELEASE_SKIP_PACK_CHECK";
|
||||
function normalizePackedPath(packedPath: string): string {
|
||||
return packedPath.replace(/\\/g, "/");
|
||||
}
|
||||
|
||||
function isNodeModulesPackageRoot(segments: string[], index: number): boolean {
|
||||
const parent = segments[index - 1];
|
||||
if (parent === "node_modules") {
|
||||
@@ -521,11 +520,8 @@ function collectPackedTarballErrors(): string[] {
|
||||
return [
|
||||
...collectControlUiPackErrors(packedPaths),
|
||||
...collectForbiddenPackedPathErrors(packedPaths),
|
||||
<<<<<<< HEAD
|
||||
...collectForbiddenPackedContentErrors(packedPaths),
|
||||
=======
|
||||
...collectPackedTestCargoErrors(packedPaths),
|
||||
>>>>>>> caafdea0bb (Build: prune packaged runtime test cargo)
|
||||
];
|
||||
}
|
||||
|
||||
@@ -591,11 +587,8 @@ export function collectPackedTestCargoErrors(paths: Iterable<string>): string[]
|
||||
return errors.toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
<<<<<<< HEAD
|
||||
async function main(): Promise<number> {
|
||||
=======
|
||||
function main(): number {
|
||||
>>>>>>> caafdea0bb (Build: prune packaged runtime test cargo)
|
||||
async function main(): Promise<number> {
|
||||
const pkg = loadPackageJson();
|
||||
const now = new Date();
|
||||
const skipPackValidation = shouldSkipPackedTarballValidation();
|
||||
|
||||
@@ -504,6 +504,18 @@ function pruneDependencyFilesBySuffixes(depRoot, suffixes) {
|
||||
});
|
||||
}
|
||||
|
||||
function relativePathSegments(rootDir, fullPath) {
|
||||
return path.relative(rootDir, fullPath).split(path.sep).filter(Boolean);
|
||||
}
|
||||
|
||||
function isNodeModulesPackageRoot(segments, index) {
|
||||
const parent = segments[index - 1];
|
||||
if (parent === "node_modules") {
|
||||
return true;
|
||||
}
|
||||
return parent?.startsWith("@") === true && segments[index - 2] === "node_modules";
|
||||
}
|
||||
|
||||
function pruneDependencyDirectoriesByBasename(depRoot, basenames) {
|
||||
if (!basenames || basenames.length === 0 || !fs.existsSync(depRoot)) {
|
||||
return;
|
||||
@@ -517,7 +529,8 @@ function pruneDependencyDirectoriesByBasename(depRoot, basenames) {
|
||||
continue;
|
||||
}
|
||||
const fullPath = path.join(currentDir, entry.name);
|
||||
if (basenameSet.has(entry.name)) {
|
||||
const segments = relativePathSegments(depRoot, fullPath);
|
||||
if (basenameSet.has(entry.name) && !isNodeModulesPackageRoot(segments, segments.length - 1)) {
|
||||
removePathIfExists(fullPath);
|
||||
continue;
|
||||
}
|
||||
@@ -531,7 +544,7 @@ function pruneDependencyFilesByPatterns(depRoot, patterns) {
|
||||
return;
|
||||
}
|
||||
walkFiles(depRoot, (fullPath) => {
|
||||
const relativePath = path.relative(depRoot, fullPath).replace(/\\/g, "/");
|
||||
const relativePath = relativePathSegments(depRoot, fullPath).join("/");
|
||||
if (patterns.some((pattern) => pattern.test(relativePath))) {
|
||||
removePathIfExists(fullPath);
|
||||
}
|
||||
|
||||
@@ -406,6 +406,15 @@ describe("collectPackedTestCargoErrors", () => {
|
||||
]),
|
||||
).toEqual([]);
|
||||
});
|
||||
|
||||
it("allows legitimate package roots named test under node_modules", () => {
|
||||
expect(
|
||||
collectPackedTestCargoErrors([
|
||||
"dist/extensions/fixture-plugin/node_modules/direct/node_modules/test/index.js",
|
||||
"dist/extensions/fixture-plugin/node_modules/direct/node_modules/@scope/tests/index.js",
|
||||
]),
|
||||
).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("collectReleaseTagErrors", () => {
|
||||
|
||||
@@ -415,6 +415,67 @@ describe("stageBundledPluginRuntimeDeps", () => {
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("preserves nested runtime dependencies named test or tests", () => {
|
||||
const { pluginDir, repoRoot } = createBundledPluginFixture({
|
||||
packageJson: {
|
||||
name: "@openclaw/fixture-plugin",
|
||||
version: "1.0.0",
|
||||
dependencies: { direct: "1.0.0" },
|
||||
openclaw: { bundle: { stageRuntimeDependencies: true } },
|
||||
},
|
||||
});
|
||||
const directDir = path.join(repoRoot, "node_modules", "direct");
|
||||
const nestedTestDir = path.join(directDir, "node_modules", "test");
|
||||
const scopedTestsDir = path.join(directDir, "node_modules", "@scope", "tests");
|
||||
fs.mkdirSync(nestedTestDir, { recursive: true });
|
||||
fs.mkdirSync(scopedTestsDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(directDir, "package.json"),
|
||||
'{ "name": "direct", "version": "1.0.0", "dependencies": { "test": "^1.0.0", "@scope/tests": "^1.0.0" } }\n',
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(path.join(directDir, "index.js"), "module.exports = 'direct';\n", "utf8");
|
||||
fs.writeFileSync(
|
||||
path.join(nestedTestDir, "package.json"),
|
||||
'{ "name": "test", "version": "1.0.0" }\n',
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(path.join(nestedTestDir, "index.js"), "module.exports = 'test';\n", "utf8");
|
||||
fs.writeFileSync(
|
||||
path.join(scopedTestsDir, "package.json"),
|
||||
'{ "name": "@scope/tests", "version": "1.0.0" }\n',
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(scopedTestsDir, "index.js"),
|
||||
"module.exports = 'scoped-tests';\n",
|
||||
"utf8",
|
||||
);
|
||||
|
||||
stageBundledPluginRuntimeDeps({ cwd: repoRoot });
|
||||
|
||||
expect(
|
||||
fs.readFileSync(
|
||||
path.join(pluginDir, "node_modules", "direct", "node_modules", "test", "index.js"),
|
||||
"utf8",
|
||||
),
|
||||
).toBe("module.exports = 'test';\n");
|
||||
expect(
|
||||
fs.readFileSync(
|
||||
path.join(
|
||||
pluginDir,
|
||||
"node_modules",
|
||||
"direct",
|
||||
"node_modules",
|
||||
"@scope",
|
||||
"tests",
|
||||
"index.js",
|
||||
),
|
||||
"utf8",
|
||||
),
|
||||
).toBe("module.exports = 'scoped-tests';\n");
|
||||
});
|
||||
|
||||
it("stages hoisted transitive runtime deps from the root node_modules", () => {
|
||||
const { pluginDir, repoRoot } = createBundledPluginFixture({
|
||||
packageJson: {
|
||||
|
||||
Reference in New Issue
Block a user