mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 12:30:44 +00:00
fix(release): stabilize full validation lanes
This commit is contained in:
@@ -227,6 +227,25 @@ openclaw_package_dir="/npm-global/lib/node_modules/openclaw"
|
||||
# point those imports at the installed package without copying source into the test image.
|
||||
rm -rf /app/node_modules/openclaw
|
||||
ln -sfnT "$openclaw_package_dir" /app/node_modules/openclaw
|
||||
rm -rf /app/dist
|
||||
ln -sfnT "$openclaw_package_dir/dist" /app/dist
|
||||
cp "$openclaw_package_dir/package.json" /app/package.json
|
||||
node --input-type=module <<'NODE'
|
||||
import fs from "node:fs";
|
||||
|
||||
const packageJsonPath = "/app/package.json";
|
||||
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
||||
pkg.exports = pkg.exports && typeof pkg.exports === "object" ? pkg.exports : {};
|
||||
pkg.exports["./plugin-sdk/qa-channel"] = {
|
||||
types: "./extensions/qa-channel/api.ts",
|
||||
default: "./extensions/qa-channel/api.ts",
|
||||
};
|
||||
pkg.exports["./plugin-sdk/qa-channel-protocol"] = {
|
||||
types: "./extensions/qa-channel/src/protocol.ts",
|
||||
default: "./extensions/qa-channel/src/protocol.ts",
|
||||
};
|
||||
fs.writeFileSync(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`);
|
||||
NODE
|
||||
for deps_dir in "$openclaw_package_dir/node_modules" /npm-global/lib/node_modules; do
|
||||
[ -d "$deps_dir" ] || continue
|
||||
for dependency_dir in "$deps_dir"/*; do
|
||||
|
||||
@@ -60,6 +60,9 @@ const OMITTED_QA_EXTENSION_PREFIXES = [
|
||||
];
|
||||
export const CROSS_OS_DASHBOARD_SMOKE_TIMEOUT_MS = 120_000;
|
||||
export const CROSS_OS_DASHBOARD_FETCH_TIMEOUT_MS = 10_000;
|
||||
export const CROSS_OS_GATEWAY_STATUS_RPC_TIMEOUT_MS = 30_000;
|
||||
export const CROSS_OS_GATEWAY_READY_TIMEOUT_MS = 3 * 60_000;
|
||||
export const CROSS_OS_WINDOWS_GATEWAY_READY_TIMEOUT_MS = 5 * 60_000;
|
||||
|
||||
if (isMainModule()) {
|
||||
try {
|
||||
@@ -1629,7 +1632,14 @@ async function resolveInstalledGatewayStatusArgs(params) {
|
||||
requireRpc &&
|
||||
(help.stdout.includes("--require-rpc") || help.stderr.includes("--require-rpc"))
|
||||
) {
|
||||
return ["gateway", "status", "--deep", "--require-rpc", "--timeout", "5000"];
|
||||
return [
|
||||
"gateway",
|
||||
"status",
|
||||
"--deep",
|
||||
"--require-rpc",
|
||||
"--timeout",
|
||||
String(CROSS_OS_GATEWAY_STATUS_RPC_TIMEOUT_MS),
|
||||
];
|
||||
}
|
||||
return ["gateway", "status", "--deep"];
|
||||
}
|
||||
@@ -2370,7 +2380,9 @@ async function waitForGateway(params) {
|
||||
}
|
||||
|
||||
function gatewayReadyDeadlineMs() {
|
||||
return process.platform === "win32" ? 5 * 60 * 1000 : 90_000;
|
||||
return process.platform === "win32"
|
||||
? CROSS_OS_WINDOWS_GATEWAY_READY_TIMEOUT_MS
|
||||
: CROSS_OS_GATEWAY_READY_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
async function resolveGatewayStatusArgs(lane, env, logPath) {
|
||||
@@ -2383,7 +2395,14 @@ async function resolveGatewayStatusArgs(lane, env, logPath) {
|
||||
check: false,
|
||||
});
|
||||
if (help.stdout.includes("--require-rpc") || help.stderr.includes("--require-rpc")) {
|
||||
return ["gateway", "status", "--deep", "--require-rpc", "--timeout", "5000"];
|
||||
return [
|
||||
"gateway",
|
||||
"status",
|
||||
"--deep",
|
||||
"--require-rpc",
|
||||
"--timeout",
|
||||
String(CROSS_OS_GATEWAY_STATUS_RPC_TIMEOUT_MS),
|
||||
];
|
||||
}
|
||||
return ["gateway", "status", "--deep"];
|
||||
}
|
||||
|
||||
@@ -107,4 +107,56 @@ describe("prepareBundledPluginRuntimeRoot", () => {
|
||||
expect(fs.lstatSync(staleMirrorChunk).isSymbolicLink()).toBe(false);
|
||||
expect(fs.readFileSync(staleMirrorChunk, "utf8")).toContain("playwright-core");
|
||||
});
|
||||
|
||||
it("does not copy staged runtime mirror dist files onto themselves", () => {
|
||||
const stageDir = makeTempRoot();
|
||||
const installRoot = path.join(stageDir, "openclaw-2026.4.26-alpha");
|
||||
const pluginRoot = path.join(installRoot, "dist", "extensions", "qqbot");
|
||||
const distChunk = path.join(installRoot, "dist", "accounts-abc123.js");
|
||||
const env = { ...process.env, OPENCLAW_PLUGIN_STAGE_DIR: stageDir };
|
||||
fs.mkdirSync(pluginRoot, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(installRoot, "package.json"),
|
||||
JSON.stringify({ name: "openclaw", version: "2026.4.26", type: "module" }),
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(distChunk, "export const marker = 'same-root';\n", "utf8");
|
||||
fs.writeFileSync(
|
||||
path.join(pluginRoot, "index.js"),
|
||||
`import { marker } from "../../accounts-abc123.js"; export default { id: "qqbot", marker };\n`,
|
||||
"utf8",
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(pluginRoot, "package.json"),
|
||||
JSON.stringify(
|
||||
{
|
||||
name: "@openclaw/qqbot",
|
||||
version: "1.0.0",
|
||||
type: "module",
|
||||
dependencies: { "qqbot-runtime": "1.0.0" },
|
||||
openclaw: { extensions: ["./index.js"] },
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
"utf8",
|
||||
);
|
||||
fs.mkdirSync(path.join(installRoot, "node_modules", "qqbot-runtime"), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
path.join(installRoot, "node_modules", "qqbot-runtime", "package.json"),
|
||||
JSON.stringify({ name: "qqbot-runtime", version: "1.0.0", type: "module" }),
|
||||
"utf8",
|
||||
);
|
||||
|
||||
const prepared = prepareBundledPluginRuntimeRoot({
|
||||
pluginId: "qqbot",
|
||||
pluginRoot,
|
||||
modulePath: path.join(pluginRoot, "index.js"),
|
||||
env,
|
||||
});
|
||||
|
||||
expect(prepared.pluginRoot).toBe(pluginRoot);
|
||||
expect(prepared.modulePath).toBe(path.join(pluginRoot, "index.js"));
|
||||
expect(fs.readFileSync(distChunk, "utf8")).toContain("same-root");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -142,6 +142,9 @@ function prepareBundledPluginRuntimeDistMirror(params: {
|
||||
}
|
||||
const sourcePath = path.join(sourceDistRoot, entry.name);
|
||||
const targetPath = path.join(mirrorDistRoot, entry.name);
|
||||
if (path.resolve(sourcePath) === path.resolve(targetPath)) {
|
||||
continue;
|
||||
}
|
||||
if (entry.isFile() && shouldMaterializeBundledRuntimeMirrorDistFile(sourcePath)) {
|
||||
materializeBundledRuntimeMirrorDistFile(sourcePath, targetPath);
|
||||
continue;
|
||||
@@ -175,6 +178,9 @@ function ensureBundledRuntimeDistPackageJson(mirrorDistRoot: string): void {
|
||||
}
|
||||
|
||||
function copyBundledPluginRuntimeRoot(sourceRoot: string, targetRoot: string): void {
|
||||
if (path.resolve(sourceRoot) === path.resolve(targetRoot)) {
|
||||
return;
|
||||
}
|
||||
fs.mkdirSync(targetRoot, { recursive: true, mode: 0o755 });
|
||||
for (const entry of fs.readdirSync(sourceRoot, { withFileTypes: true })) {
|
||||
if (entry.name === "node_modules") {
|
||||
|
||||
@@ -791,6 +791,9 @@ function mirrorBundledRuntimeDistRootEntries(params: {
|
||||
}
|
||||
const sourcePath = path.join(params.sourceDistRoot, entry.name);
|
||||
const targetPath = path.join(params.mirrorDistRoot, entry.name);
|
||||
if (path.resolve(sourcePath) === path.resolve(targetPath)) {
|
||||
continue;
|
||||
}
|
||||
if (entry.isFile() && shouldMaterializeBundledRuntimeMirrorDistFile(sourcePath)) {
|
||||
materializeBundledRuntimeMirrorDistFile(sourcePath, targetPath);
|
||||
continue;
|
||||
@@ -860,6 +863,9 @@ function ensureBundledRuntimeDistPackageJson(mirrorDistRoot: string): void {
|
||||
}
|
||||
|
||||
function copyBundledPluginRuntimeRoot(sourceRoot: string, targetRoot: string): void {
|
||||
if (path.resolve(sourceRoot) === path.resolve(targetRoot)) {
|
||||
return;
|
||||
}
|
||||
fs.mkdirSync(targetRoot, { recursive: true, mode: 0o755 });
|
||||
for (const entry of fs.readdirSync(sourceRoot, { withFileTypes: true })) {
|
||||
if (entry.name === "node_modules") {
|
||||
|
||||
@@ -55,6 +55,17 @@ describe("package Telegram live Docker E2E", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps private QA harness imports local while using the installed package dist", () => {
|
||||
const script = readFileSync(DOCKER_SCRIPT_PATH, "utf8");
|
||||
|
||||
expect(script).toContain('ln -sfnT "$openclaw_package_dir/dist" /app/dist');
|
||||
expect(script).toContain('cp "$openclaw_package_dir/package.json" /app/package.json');
|
||||
expect(script).toContain('pkg.exports["./plugin-sdk/qa-channel"]');
|
||||
expect(script).toContain('"./extensions/qa-channel/api.ts"');
|
||||
expect(script).toContain('pkg.exports["./plugin-sdk/qa-channel-protocol"]');
|
||||
expect(script).toContain('"./extensions/qa-channel/src/protocol.ts"');
|
||||
});
|
||||
|
||||
it("lets npm-specific credential aliases override shared QA env", () => {
|
||||
expect(
|
||||
__testing.resolveCredentialSource({
|
||||
|
||||
@@ -12,6 +12,9 @@ import {
|
||||
canConnectToLoopbackPort,
|
||||
buildDiscordSmokeGuildsConfig,
|
||||
buildRealUpdateEnv,
|
||||
CROSS_OS_GATEWAY_READY_TIMEOUT_MS,
|
||||
CROSS_OS_GATEWAY_STATUS_RPC_TIMEOUT_MS,
|
||||
CROSS_OS_WINDOWS_GATEWAY_READY_TIMEOUT_MS,
|
||||
CROSS_OS_DASHBOARD_FETCH_TIMEOUT_MS,
|
||||
CROSS_OS_DASHBOARD_SMOKE_TIMEOUT_MS,
|
||||
isImmutableReleaseRef,
|
||||
@@ -46,6 +49,12 @@ describe("scripts/openclaw-cross-os-release-checks", () => {
|
||||
expect(CROSS_OS_DASHBOARD_FETCH_TIMEOUT_MS).toBeGreaterThanOrEqual(10_000);
|
||||
});
|
||||
|
||||
it("keeps gateway RPC status probes patient enough for live release startup", () => {
|
||||
expect(CROSS_OS_GATEWAY_STATUS_RPC_TIMEOUT_MS).toBeGreaterThanOrEqual(30_000);
|
||||
expect(CROSS_OS_GATEWAY_READY_TIMEOUT_MS).toBeGreaterThanOrEqual(180_000);
|
||||
expect(CROSS_OS_WINDOWS_GATEWAY_READY_TIMEOUT_MS).toBeGreaterThanOrEqual(300_000);
|
||||
});
|
||||
|
||||
it("accepts OK agent output from the captured log when stdout is empty", () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), "openclaw-cross-os-agent-output-"));
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user