refactor(core): dedupe shared config and runtime helpers

This commit is contained in:
Peter Steinberger
2026-02-16 14:52:03 +00:00
parent 544ffbcf7b
commit 04892ee230
68 changed files with 1966 additions and 2018 deletions

View File

@@ -105,13 +105,28 @@ describe("runGatewayUpdate", () => {
};
}
it("skips git update when worktree is dirty", async () => {
async function setupGitCheckout(options?: { packageManager?: string }) {
await fs.mkdir(path.join(tempDir, ".git"));
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "openclaw", version: "1.0.0" }),
"utf-8",
);
const pkg: Record<string, string> = { name: "openclaw", version: "1.0.0" };
if (options?.packageManager) {
pkg.packageManager = options.packageManager;
}
await fs.writeFile(path.join(tempDir, "package.json"), JSON.stringify(pkg), "utf-8");
}
async function setupUiIndex() {
const uiIndexPath = path.join(tempDir, "dist", "control-ui", "index.html");
await fs.mkdir(path.dirname(uiIndexPath), { recursive: true });
await fs.writeFile(uiIndexPath, "<html></html>", "utf-8");
return uiIndexPath;
}
async function removeControlUiAssets() {
await fs.rm(path.join(tempDir, "dist", "control-ui"), { recursive: true, force: true });
}
it("skips git update when worktree is dirty", async () => {
await setupGitCheckout();
const { runner, calls } = createRunner({
[`git -C ${tempDir} rev-parse --show-toplevel`]: { stdout: tempDir },
[`git -C ${tempDir} rev-parse HEAD`]: { stdout: "abc123" },
@@ -131,12 +146,7 @@ describe("runGatewayUpdate", () => {
});
it("aborts rebase on failure", async () => {
await fs.mkdir(path.join(tempDir, ".git"));
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "openclaw", version: "1.0.0" }),
"utf-8",
);
await setupGitCheckout();
const { runner, calls } = createRunner({
[`git -C ${tempDir} rev-parse --show-toplevel`]: { stdout: tempDir },
[`git -C ${tempDir} rev-parse HEAD`]: { stdout: "abc123" },
@@ -164,15 +174,8 @@ describe("runGatewayUpdate", () => {
});
it("uses stable tag when beta tag is older than release", async () => {
await fs.mkdir(path.join(tempDir, ".git"));
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "openclaw", version: "1.0.0", packageManager: "pnpm@8.0.0" }),
"utf-8",
);
const uiIndexPath = path.join(tempDir, "dist", "control-ui", "index.html");
await fs.mkdir(path.dirname(uiIndexPath), { recursive: true });
await fs.writeFile(uiIndexPath, "<html></html>", "utf-8");
await setupGitCheckout({ packageManager: "pnpm@8.0.0" });
await setupUiIndex();
const stableTag = "v1.0.1-1";
const betaTag = "v1.0.0-beta.2";
const { runner, calls } = createRunner({
@@ -243,29 +246,18 @@ describe("runGatewayUpdate", () => {
"utf-8",
);
const calls: string[] = [];
const runCommand = async (argv: string[]) => {
const key = argv.join(" ");
calls.push(key);
if (key === `git -C ${pkgRoot} rev-parse --show-toplevel`) {
return { stdout: "", stderr: "not a git repository", code: 128 };
}
if (key === "npm root -g") {
return { stdout: nodeModules, stderr: "", code: 0 };
}
if (key === params.expectedInstallCommand) {
const { calls, runCommand } = createGlobalInstallHarness({
pkgRoot,
npmRootOutput: nodeModules,
installCommand: params.expectedInstallCommand,
onInstall: async () => {
await fs.writeFile(
path.join(pkgRoot, "package.json"),
JSON.stringify({ name: "openclaw", version: "2.0.0" }),
"utf-8",
);
return { stdout: "ok", stderr: "", code: 0 };
}
if (key === "pnpm root -g") {
return { stdout: "", stderr: "", code: 1 };
}
return { stdout: "", stderr: "", code: 0 };
};
},
});
const result = await runGatewayUpdate({
cwd: pkgRoot,
@@ -278,6 +270,37 @@ describe("runGatewayUpdate", () => {
return { calls, result };
}
const createGlobalInstallHarness = (params: {
pkgRoot: string;
npmRootOutput?: string;
installCommand: string;
onInstall?: () => Promise<void>;
}) => {
const calls: string[] = [];
const runCommand = async (argv: string[]) => {
const key = argv.join(" ");
calls.push(key);
if (key === `git -C ${params.pkgRoot} rev-parse --show-toplevel`) {
return { stdout: "", stderr: "not a git repository", code: 128 };
}
if (key === "npm root -g") {
if (params.npmRootOutput) {
return { stdout: params.npmRootOutput, stderr: "", code: 0 };
}
return { stdout: "", stderr: "", code: 1 };
}
if (key === "pnpm root -g") {
return { stdout: "", stderr: "", code: 1 };
}
if (key === params.installCommand) {
await params.onInstall?.();
return { stdout: "ok", stderr: "", code: 0 };
}
return { stdout: "", stderr: "", code: 0 };
};
return { calls, runCommand };
};
it.each([
{
title: "updates global npm installs when detected",
@@ -364,29 +387,17 @@ describe("runGatewayUpdate", () => {
"utf-8",
);
const calls: string[] = [];
const runCommand = async (argv: string[]) => {
const key = argv.join(" ");
calls.push(key);
if (key === `git -C ${pkgRoot} rev-parse --show-toplevel`) {
return { stdout: "", stderr: "not a git repository", code: 128 };
}
if (key === "npm root -g") {
return { stdout: "", stderr: "", code: 1 };
}
if (key === "pnpm root -g") {
return { stdout: "", stderr: "", code: 1 };
}
if (key === "bun add -g openclaw@latest") {
const { calls, runCommand } = createGlobalInstallHarness({
pkgRoot,
installCommand: "bun add -g openclaw@latest",
onInstall: async () => {
await fs.writeFile(
path.join(pkgRoot, "package.json"),
JSON.stringify({ name: "openclaw", version: "2.0.0" }),
"utf-8",
);
return { stdout: "ok", stderr: "", code: 0 };
}
return { stdout: "", stderr: "", code: 0 };
};
},
});
const result = await runGatewayUpdate({
cwd: pkgRoot,
@@ -429,12 +440,7 @@ describe("runGatewayUpdate", () => {
});
it("fails with a clear reason when openclaw.mjs is missing", async () => {
await fs.mkdir(path.join(tempDir, ".git"));
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "openclaw", version: "1.0.0", packageManager: "pnpm@8.0.0" }),
"utf-8",
);
await setupGitCheckout({ packageManager: "pnpm@8.0.0" });
await fs.rm(path.join(tempDir, "openclaw.mjs"), { force: true });
const stableTag = "v1.0.1-1";
@@ -463,15 +469,8 @@ describe("runGatewayUpdate", () => {
});
it("repairs UI assets when doctor run removes control-ui files", async () => {
await fs.mkdir(path.join(tempDir, ".git"));
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "openclaw", version: "1.0.0", packageManager: "pnpm@8.0.0" }),
"utf-8",
);
const uiIndexPath = path.join(tempDir, "dist", "control-ui", "index.html");
await fs.mkdir(path.dirname(uiIndexPath), { recursive: true });
await fs.writeFile(uiIndexPath, "<html></html>", "utf-8");
await setupGitCheckout({ packageManager: "pnpm@8.0.0" });
const uiIndexPath = await setupUiIndex();
const stableTag = "v1.0.1-1";
const { runCommand, calls, doctorKey, getUiBuildCount } = createStableTagRunner({
@@ -481,9 +480,7 @@ describe("runGatewayUpdate", () => {
await fs.mkdir(path.dirname(uiIndexPath), { recursive: true });
await fs.writeFile(uiIndexPath, `<html>${count}</html>`, "utf-8");
},
onDoctor: async () => {
await fs.rm(path.join(tempDir, "dist", "control-ui"), { recursive: true, force: true });
},
onDoctor: removeControlUiAssets,
});
const result = await runGatewayUpdate({
@@ -500,15 +497,8 @@ describe("runGatewayUpdate", () => {
});
it("fails when UI assets are still missing after post-doctor repair", async () => {
await fs.mkdir(path.join(tempDir, ".git"));
await fs.writeFile(
path.join(tempDir, "package.json"),
JSON.stringify({ name: "openclaw", version: "1.0.0", packageManager: "pnpm@8.0.0" }),
"utf-8",
);
const uiIndexPath = path.join(tempDir, "dist", "control-ui", "index.html");
await fs.mkdir(path.dirname(uiIndexPath), { recursive: true });
await fs.writeFile(uiIndexPath, "<html></html>", "utf-8");
await setupGitCheckout({ packageManager: "pnpm@8.0.0" });
const uiIndexPath = await setupUiIndex();
const stableTag = "v1.0.1-1";
const { runCommand } = createStableTagRunner({
@@ -520,9 +510,7 @@ describe("runGatewayUpdate", () => {
await fs.writeFile(uiIndexPath, "<html>built</html>", "utf-8");
}
},
onDoctor: async () => {
await fs.rm(path.join(tempDir, "dist", "control-ui"), { recursive: true, force: true });
},
onDoctor: removeControlUiAssets,
});
const result = await runGatewayUpdate({