fix: prune stale plugin runtime symlinks

This commit is contained in:
Peter Steinberger
2026-05-03 21:39:45 +01:00
parent 1ace6a0d6a
commit 797d02497e
7 changed files with 366 additions and 5 deletions

View File

@@ -252,7 +252,7 @@ legacy_runtime_deps_symlink_plugin() {
legacy_runtime_deps_symlink_target() {
local plugin="$1"
printf '%s/dist/extensions/%s/node_modules\n' "$(package_root)" "$plugin"
printf '%s/@openclaw-upgrade-survivor/%s-runtime-dep\n' "$(dirname "$(package_root)")" "$plugin"
}
legacy_runtime_deps_symlink_source() {
@@ -431,6 +431,7 @@ seed_legacy_runtime_deps_symlink() {
source_dir="$(legacy_runtime_deps_symlink_source "$plugin")"
target_dir="$(legacy_runtime_deps_symlink_target "$plugin")"
mkdir -p "$source_dir"
mkdir -p "$(dirname "$target_dir")"
printf '{"name":"openclaw-upgrade-survivor-legacy-runtime-deps","version":"0.0.0"}\n' \
>"$source_dir/package.json"
rm -rf "$target_dir"

View File

@@ -11,6 +11,7 @@ import {
openSync,
readdirSync,
readFileSync,
readlinkSync,
realpathSync,
renameSync,
rmdirSync,
@@ -366,13 +367,101 @@ export function collectLegacyPluginRuntimeDepsStateRoots(params = {}) {
);
}
function isPathInsideRoot(candidate, root) {
const relativePath = relative(root, candidate);
return relativePath === "" || (!relativePath.startsWith("..") && !isAbsolute(relativePath));
}
function collectLegacyPluginRuntimeDepsSymlinkPaths(roots, params = {}) {
const packageRoot = params.packageRoot ?? DEFAULT_PACKAGE_ROOT;
const readDir = params.readdirSync ?? readdirSync;
const pathLstat = params.lstatSync ?? lstatSync;
const readLink = params.readlinkSync ?? readlinkSync;
const pathExists = params.existsSync ?? existsSync;
const containingNodeModules = dirname(packageRoot);
if (basename(containingNodeModules) !== "node_modules") {
return [];
}
const normalizedRoots = roots.map((root) => pathResolve(root));
const candidates = [];
function addCandidate(linkPath) {
let linkStat;
try {
linkStat = pathLstat(linkPath);
} catch {
return;
}
if (!linkStat.isSymbolicLink()) {
return;
}
let target;
try {
target = readLink(linkPath);
} catch {
return;
}
if (!target.includes(LEGACY_PLUGIN_RUNTIME_DEPS_DIR)) {
return;
}
const resolvedTarget = pathResolve(dirname(linkPath), target);
const pointsIntoPrunedRoot = normalizedRoots.some((root) =>
isPathInsideRoot(resolvedTarget, root),
);
if (pointsIntoPrunedRoot || !pathExists(resolvedTarget)) {
candidates.push(linkPath);
}
}
let entries;
try {
entries = readDir(containingNodeModules, { withFileTypes: true });
} catch {
return [];
}
for (const entry of entries) {
if (entry.isDirectory() && entry.name.startsWith("@")) {
const scopeDir = join(containingNodeModules, entry.name);
let scopeEntries;
try {
scopeEntries = readDir(scopeDir, { withFileTypes: true });
} catch {
continue;
}
for (const scopeEntry of scopeEntries) {
addCandidate(join(scopeDir, scopeEntry.name));
}
continue;
}
if (entry.isSymbolicLink()) {
addCandidate(join(containingNodeModules, entry.name));
}
}
return [...new Set(candidates.map((entry) => pathResolve(entry)))].toSorted((left, right) =>
left.localeCompare(right),
);
}
export function pruneLegacyPluginRuntimeDepsState(params = {}) {
const pathExists = params.existsSync ?? existsSync;
const removePath = params.rmSync ?? rmSync;
const log = params.log ?? console;
const removed = [];
const removedSymlinks = [];
const roots = collectLegacyPluginRuntimeDepsStateRoots(params);
for (const root of collectLegacyPluginRuntimeDepsStateRoots(params)) {
for (const linkPath of collectLegacyPluginRuntimeDepsSymlinkPaths(roots, params)) {
try {
removePath(linkPath, { force: true });
removedSymlinks.push(linkPath);
} catch (error) {
log.warn?.(
`[postinstall] could not prune legacy plugin runtime deps symlink ${linkPath}: ${String(error)}`,
);
}
}
for (const root of roots) {
if (!pathExists(root)) {
continue;
}
@@ -389,6 +478,11 @@ export function pruneLegacyPluginRuntimeDepsState(params = {}) {
if (removed.length > 0) {
log.log?.(`[postinstall] pruned legacy plugin runtime deps: ${removed.join(", ")}`);
}
if (removedSymlinks.length > 0) {
log.log?.(
`[postinstall] pruned legacy plugin runtime deps symlinks: ${removedSymlinks.join(", ")}`,
);
}
return removed;
}