fix(update): skip unrelated enclosing git roots (#123197)

* fix(update): skip unrelated enclosing git roots

* docs(changelog): note linked updater fix

* chore: remove changelog entry
This commit is contained in:
Dallin Romney
2026-08-14 18:12:24 +08:00
committed by GitHub
parent 7c73f80281
commit b28ff3e0a2
3 changed files with 42 additions and 10 deletions
+5 -5
View File
@@ -64,13 +64,16 @@ export async function resolveGitRoot(
runCommand: CommandRunner,
candidates: string[],
timeoutMs: number,
packageRoot?: string | null,
): Promise<string | null> {
for (const dir of candidates) {
const result = await runCommand(["git", "-C", dir, "rev-parse", "--show-toplevel"], {
timeoutMs,
}).catch(() => null);
const root = result?.code === 0 ? result.stdout.trim() : "";
if (root) {
// A launcher may live inside an unrelated checkout (for example nvm).
// Keep probing until the Git root owns the discovered OpenClaw package.
if (root && (!packageRoot || updateInstallRootsMatch(root, packageRoot))) {
return root;
}
}
@@ -117,10 +120,7 @@ export async function resolveUpdateInstallSurface(
const candidates = buildStartDirs(opts);
const packageRoot = await findPackageRoot(candidates);
let gitRoot = await resolveGitRoot(runCommand, candidates, timeoutMs);
if (gitRoot && packageRoot && !updateInstallRootsMatch(gitRoot, packageRoot)) {
gitRoot = null;
}
const gitRoot = await resolveGitRoot(runCommand, candidates, timeoutMs, packageRoot);
if (gitRoot && !packageRoot) {
return { kind: "missing", mode: "unknown", root: resolveUpdateInstallRoot(gitRoot) };
}
+35
View File
@@ -302,6 +302,41 @@ describe("runGatewayUpdate", () => {
},
);
it("skips an unrelated enclosing git root before the OpenClaw checkout", async () => {
const versionManagerRoot = path.join(tempDir, "version-manager");
const binDir = path.join(versionManagerRoot, "bin");
const sourceRoot = path.join(tempDir, "source");
await Promise.all([
fs.mkdir(binDir, { recursive: true }),
fs.mkdir(sourceRoot, { recursive: true }),
]);
await fs.writeFile(
path.join(sourceRoot, "package.json"),
JSON.stringify({ name: "openclaw", version: "1.0.0" }),
"utf8",
);
const { runner, calls } = createRunner({
[`git -C ${binDir} rev-parse --show-toplevel`]: { stdout: versionManagerRoot },
[`git -C ${sourceRoot} rev-parse --show-toplevel`]: { stdout: sourceRoot },
});
await expect(
resolveUpdateInstallSurface({
argv1: path.join(binDir, "openclaw"),
cwd: sourceRoot,
timeoutMs: 1000,
runCommand: runner,
}),
).resolves.toMatchObject({
kind: "git",
mode: "git",
root: sourceRoot,
packageRoot: sourceRoot,
});
expect(calls).toContain(`git -C ${sourceRoot} rev-parse --show-toplevel`);
});
async function setupUiIndex() {
const uiIndexPath = path.join(tempDir, "dist", "control-ui", "index.html");
await fs.mkdir(path.dirname(uiIndexPath), { recursive: true });
+2 -5
View File
@@ -32,7 +32,7 @@ export async function runGatewayUpdate(opts: UpdateRunnerOptions = {}): Promise<
const candidates = buildStartDirs(opts);
const pkgRoot = await findPackageRoot(candidates);
let gitRoot = await resolveGitRoot(runCommand, candidates, timeoutMs);
let gitRoot = await resolveGitRoot(runCommand, candidates, timeoutMs, pkgRoot);
if (!gitRoot && pkgRoot) {
const cwdRoot = normalizeDir(opts.cwd);
if (
@@ -43,9 +43,6 @@ export async function runGatewayUpdate(opts: UpdateRunnerOptions = {}): Promise<
gitRoot = resolveUpdateInstallRoot(cwdRoot);
}
}
if (gitRoot && pkgRoot && !updateInstallRootsMatch(gitRoot, pkgRoot)) {
gitRoot = null;
}
if (gitRoot && !pkgRoot) {
return {
status: "error",
@@ -56,7 +53,7 @@ export async function runGatewayUpdate(opts: UpdateRunnerOptions = {}): Promise<
durationMs: Date.now() - startedAt,
};
}
if (gitRoot && pkgRoot && updateInstallRootsMatch(gitRoot, pkgRoot)) {
if (gitRoot && pkgRoot) {
return await updateGitCheckout({
opts,
gitRoot,