mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(gateway): verify all runtime postbuild outputs
This commit is contained in:
committed by
Peter Steinberger
parent
795dd2d02e
commit
8a9f142942
+11
-2
@@ -32,7 +32,10 @@ import {
|
||||
runNodeSourceRoots,
|
||||
runNodeWatchedPaths,
|
||||
} from "./run-node-watch-paths.mjs";
|
||||
import { runRuntimePostBuild } from "./runtime-postbuild.mjs";
|
||||
import {
|
||||
listCoreRuntimePostBuildOutputs,
|
||||
runRuntimePostBuild,
|
||||
} from "./runtime-postbuild.mjs";
|
||||
|
||||
export { isBuildRelevantRunNodePath, isRestartRelevantRunNodePath, runNodeWatchedPaths };
|
||||
|
||||
@@ -427,9 +430,15 @@ const listRequiredStaticExtensionAssetOutputs = (deps) => {
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
};
|
||||
|
||||
const listRequiredRuntimePostBuildOutputs = (deps) => {
|
||||
const listRequiredCoreRuntimePostBuildOutputs = (deps) =>
|
||||
listCoreRuntimePostBuildOutputs({ rootDir: deps.cwd, fs: deps.fs }).map((relativePath) =>
|
||||
path.join(deps.cwd, normalizePath(relativePath)),
|
||||
);
|
||||
|
||||
export const listRequiredRuntimePostBuildOutputs = (deps) => {
|
||||
const builtPluginEntries = listBuiltBundledPluginEntries(deps);
|
||||
return [
|
||||
...listRequiredCoreRuntimePostBuildOutputs(deps),
|
||||
...listRequiredOpenClawExtensionAliasOutputs(deps),
|
||||
...listRequiredStaticExtensionAssetOutputs(deps),
|
||||
...listRequiredBundledPluginMetadataOutputs(builtPluginEntries, deps),
|
||||
|
||||
@@ -20,6 +20,8 @@ const ROOT_STABLE_RUNTIME_ALIAS_PATTERN = /^.+\.(?:runtime|contract)\.js$/u;
|
||||
const ROOT_RUNTIME_IMPORT_SPECIFIER_PATTERN =
|
||||
/(["'])\.\/([^"']+\.(?:runtime|contract)-[A-Za-z0-9_-]+\.js)\1/gu;
|
||||
const escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
|
||||
const PLUGIN_SDK_ROOT_ALIAS_OUTPUT = "dist/plugin-sdk/root-alias.cjs";
|
||||
const OFFICIAL_CHANNEL_CATALOG_OUTPUT = "dist/channel-catalog.json";
|
||||
const LEGACY_ROOT_RUNTIME_COMPAT_ALIASES = [
|
||||
// v2026.4.29 dispatch lazy chunks. Package updates used to replace the
|
||||
// dist tree before the live gateway had restarted, so an already-loaded old
|
||||
@@ -117,7 +119,7 @@ const LEGACY_PLUGIN_INSTALL_RUNTIME_COMPAT_ALIASES = [
|
||||
aliasFileName: PLUGIN_INSTALL_RUNTIME_ALIAS.aliasFileName,
|
||||
sourceIncludes: LEGACY_PLUGIN_INSTALL_RUNTIME_MARKERS,
|
||||
}));
|
||||
const LEGACY_CLI_EXIT_COMPAT_CHUNKS = [
|
||||
export const LEGACY_CLI_EXIT_COMPAT_CHUNKS = [
|
||||
{
|
||||
dest: "dist/memory-state-CcqRgDZU.js",
|
||||
contents: "export function hasMemoryRuntime() {\n return false;\n}\n",
|
||||
@@ -128,6 +130,48 @@ const LEGACY_CLI_EXIT_COMPAT_CHUNKS = [
|
||||
},
|
||||
];
|
||||
|
||||
export function listPluginSdkRootAliasOutputs() {
|
||||
return [PLUGIN_SDK_ROOT_ALIAS_OUTPUT];
|
||||
}
|
||||
|
||||
export function listOfficialChannelCatalogOutputs() {
|
||||
return [OFFICIAL_CHANNEL_CATALOG_OUTPUT];
|
||||
}
|
||||
|
||||
export function listStableRootRuntimeAliasOutputs(params = {}) {
|
||||
const rootDir = params.rootDir ?? ROOT;
|
||||
const distDir = path.join(rootDir, "dist");
|
||||
const fsImpl = params.fs ?? fs;
|
||||
let entries = [];
|
||||
try {
|
||||
entries = fsImpl.readdirSync(distDir, { withFileTypes: true });
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
|
||||
return entries
|
||||
.filter((entry) => entry.isFile())
|
||||
.map((entry) => entry.name.match(ROOT_RUNTIME_ALIAS_PATTERN)?.groups?.base)
|
||||
.filter((base) => typeof base === "string" && base.length > 0)
|
||||
.map((base) => `dist/${base}.js`)
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
export function listLegacyCliExitCompatOutputs(params = {}) {
|
||||
const chunks = params.chunks ?? LEGACY_CLI_EXIT_COMPAT_CHUNKS;
|
||||
return chunks
|
||||
.map(({ dest }) => dest.replace(/\\/g, "/"))
|
||||
.toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
|
||||
export function listCoreRuntimePostBuildOutputs(params = {}) {
|
||||
return [
|
||||
...listPluginSdkRootAliasOutputs(),
|
||||
...listOfficialChannelCatalogOutputs(),
|
||||
...listStableRootRuntimeAliasOutputs(params),
|
||||
...listLegacyCliExitCompatOutputs(params),
|
||||
].toSorted((left, right) => left.localeCompare(right));
|
||||
}
|
||||
export function writeStableRootRuntimeAliases(params = {}) {
|
||||
const rootDir = params.rootDir ?? ROOT;
|
||||
const distDir = path.join(rootDir, "dist");
|
||||
|
||||
@@ -30,6 +30,12 @@ const DIST_ENTRY = "dist/entry.js";
|
||||
const BUILD_STAMP = `dist/${BUILD_STAMP_FILE}`;
|
||||
const RUNTIME_POSTBUILD_STAMP = `dist/${RUNTIME_POSTBUILD_STAMP_FILE}`;
|
||||
const DIST_PLUGIN_SDK_INDEX = "dist/plugin-sdk/index.js";
|
||||
const DIST_PLUGIN_SDK_ROOT_ALIAS = "dist/plugin-sdk/root-alias.cjs";
|
||||
const DIST_CHANNEL_CATALOG = "dist/channel-catalog.json";
|
||||
const DIST_LEGACY_CLI_EXIT_COMPAT = "dist/memory-state-CcqRgDZU.js";
|
||||
const DIST_LEGACY_CLI_EXIT_COMPAT_ALT = "dist/memory-state-DwGdReW4.js";
|
||||
const DIST_STABLE_ROOT_RUNTIME_SOURCE = "dist/model-catalog.runtime-AbCd1234.js";
|
||||
const DIST_STABLE_ROOT_RUNTIME_ALIAS = "dist/model-catalog.runtime.js";
|
||||
const QA_LAB_PLUGIN_SDK_ENTRY = "dist/plugin-sdk/qa-lab.js";
|
||||
const QA_RUNTIME_PLUGIN_SDK_ENTRY = "dist/plugin-sdk/qa-runtime.js";
|
||||
const EXTENSION_INDEX = bundledPluginFile("demo", "index.ts");
|
||||
@@ -126,6 +132,25 @@ async function writeRuntimePostBuildScaffold(tmp: string): Promise<void> {
|
||||
await fs.mkdir(path.join(tmp, "extensions"), { recursive: true });
|
||||
await fs.writeFile(pluginSdkAliasPath, "module.exports = {};\n", "utf-8");
|
||||
await fs.utimes(pluginSdkAliasPath, BUILD_TIME, BUILD_TIME);
|
||||
await writeProjectFiles(tmp, {
|
||||
[DIST_PLUGIN_SDK_ROOT_ALIAS]: "module.exports = {};\n",
|
||||
[DIST_CHANNEL_CATALOG]: '{"entries":[]}\n',
|
||||
[DIST_LEGACY_CLI_EXIT_COMPAT]: "export function hasMemoryRuntime() { return false; }\n",
|
||||
[DIST_LEGACY_CLI_EXIT_COMPAT_ALT]: "export function hasMemoryRuntime() { return false; }\n",
|
||||
[DIST_OPENCLAW_ALIAS_PACKAGE]:
|
||||
'{"name":"openclaw","type":"module","exports":{"./plugin-sdk":"./plugin-sdk/index.js"}}\n',
|
||||
});
|
||||
await touchProjectFiles(
|
||||
tmp,
|
||||
[
|
||||
DIST_PLUGIN_SDK_ROOT_ALIAS,
|
||||
DIST_CHANNEL_CATALOG,
|
||||
DIST_LEGACY_CLI_EXIT_COMPAT,
|
||||
DIST_LEGACY_CLI_EXIT_COMPAT_ALT,
|
||||
DIST_OPENCLAW_ALIAS_PACKAGE,
|
||||
],
|
||||
BUILD_TIME,
|
||||
);
|
||||
}
|
||||
|
||||
function expectedBuildSpawn() {
|
||||
@@ -1787,6 +1812,48 @@ describe("run-node script", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("reports missing core runtime postbuild outputs when runtime stamps match HEAD", async () => {
|
||||
for (const missingPath of [
|
||||
DIST_PLUGIN_SDK_ROOT_ALIAS,
|
||||
DIST_CHANNEL_CATALOG,
|
||||
DIST_LEGACY_CLI_EXIT_COMPAT,
|
||||
DIST_STABLE_ROOT_RUNTIME_ALIAS,
|
||||
]) {
|
||||
await withTempDir({ prefix: "openclaw-run-node-" }, async (tmp) => {
|
||||
await setupTrackedProject(tmp, {
|
||||
files: {
|
||||
[ROOT_SRC]: "export const value = 1;\n",
|
||||
[DIST_STABLE_ROOT_RUNTIME_SOURCE]: "export const value = 1;\n",
|
||||
[DIST_STABLE_ROOT_RUNTIME_ALIAS]:
|
||||
"export * from './model-catalog.runtime-AbCd1234.js';\n",
|
||||
[RUNTIME_POSTBUILD_STAMP]: '{"head":"abc123"}\n',
|
||||
},
|
||||
buildPaths: [
|
||||
ROOT_SRC,
|
||||
DIST_ENTRY,
|
||||
DIST_STABLE_ROOT_RUNTIME_SOURCE,
|
||||
DIST_STABLE_ROOT_RUNTIME_ALIAS,
|
||||
BUILD_STAMP,
|
||||
RUNTIME_POSTBUILD_STAMP,
|
||||
],
|
||||
});
|
||||
await fs.rm(resolvePath(tmp, missingPath));
|
||||
|
||||
const requirement = resolveRuntimePostBuildRequirement(
|
||||
createBuildRequirementDeps(tmp, {
|
||||
gitHead: "abc123\n",
|
||||
gitStatus: "",
|
||||
}),
|
||||
);
|
||||
|
||||
expect(requirement).toEqual({
|
||||
shouldSync: true,
|
||||
reason: "missing_runtime_postbuild_output",
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
it("reports missing runtime skill outputs even when stamps match HEAD", async () => {
|
||||
await withTempDir({ prefix: "openclaw-run-node-" }, async (tmp) => {
|
||||
await setupTrackedProject(tmp, {
|
||||
|
||||
Reference in New Issue
Block a user