fix(installer): resolve relative CLI install paths (#122626)

This commit is contained in:
Peter Steinberger
2026-08-12 06:32:15 -07:00
committed by GitHub
parent 1e5e0d786c
commit cc99d99f24
2 changed files with 108 additions and 24 deletions
+20 -24
View File
@@ -41,27 +41,27 @@ cleanup_tmpfiles() {
}
trap cleanup_tmpfiles EXIT
resolve_openclaw_effective_home() {
local openclaw_home="${OPENCLAW_HOME:-}"
if [[ -z "$openclaw_home" ]]; then
echo "$HOME"
return 0
fi
case "$openclaw_home" in
\~)
echo "$HOME"
;;
\~/*)
echo "${HOME}/${openclaw_home#~/}"
;;
*)
echo "$openclaw_home"
;;
resolve_home_path() {
local input="$1"
case "$input" in
\~) echo "$HOME" ;;
\~/*) echo "${HOME}${input:1}" ;;
*) echo "$input" ;;
esac
}
OPENCLAW_EFFECTIVE_HOME="$(resolve_openclaw_effective_home)"
INSTALLER_CWD="$(pwd -P)"
resolve_installer_path() {
local input
input="$(resolve_home_path "$1")"
case "$input" in
"") echo "" ;;
/*) echo "$input" ;;
*) echo "${INSTALLER_CWD}/${input}" ;;
esac
}
OPENCLAW_EFFECTIVE_HOME="$(resolve_home_path "${OPENCLAW_HOME:-$HOME}")"
PREFIX="${OPENCLAW_PREFIX:-${HOME}/.openclaw}"
OPENCLAW_VERSION="${OPENCLAW_VERSION:-latest}"
REQUIRED_COMPATIBLE_VERSION=""
@@ -209,9 +209,6 @@ preflight_fresh_git_disk_space() {
local available_kib
local available_gib
if [[ "$repo_dir" != /* ]]; then
repo_dir="$(pwd)/$repo_dir"
fi
if [[ -d "$repo_dir/.git" ]]; then
return 0
fi
@@ -1356,9 +1353,6 @@ install_openclaw_from_git() {
if [[ -z "$repo_dir" ]]; then
fail "Git install dir cannot be empty"
fi
if [[ "$repo_dir" != /* ]]; then
repo_dir="$(pwd)/$repo_dir"
fi
mkdir -p "$(dirname "$repo_dir")"
repo_dir="$(cd "$(dirname "$repo_dir")" && pwd)/$(basename "$repo_dir")"
@@ -1515,6 +1509,8 @@ refresh_gateway_service_if_loaded() {
main() {
parse_args "$@"
PREFIX="$(resolve_installer_path "$PREFIX")"
GIT_DIR="$(resolve_installer_path "$GIT_DIR")"
if [[ "${OPENCLAW_NO_ONBOARD:-0}" == "1" ]]; then
RUN_ONBOARD=0
+88
View File
@@ -509,6 +509,94 @@ describe("install-cli.sh", () => {
expect(output).toContain(`git=${join(openclawHome, "openclaw")}`);
});
it.each([
{ input: "arguments", method: "npm" },
{ input: "environment", method: "npm" },
{ input: "literal tilde", method: "npm" },
{ input: "arguments", method: "git" },
{ input: "environment", method: "git" },
{ input: "literal tilde", method: "git" },
] as const)(
"keeps a generated $method launcher working after $input supplied paths change cwd",
({ input, method }) => {
const tmp = mkdtempSync(join(tmpdir(), `openclaw-install-cli-relative-${method}-`));
const installRoot = join(tmp, "install-root");
const otherRoot = join(tmp, "other-root");
const home = join(tmp, "home");
const prefixInput = input === "literal tilde" ? "~/openclaw-local" : "openclaw-local";
const prefix = join(input === "literal tilde" ? home : installRoot, "openclaw-local");
const nodeDir = join(prefix, "tools", "node-v24.15.0");
const repoInput = input === "literal tilde" ? "~/openclaw-source" : "openclaw-source";
const repo = join(input === "literal tilde" ? home : installRoot, "openclaw-source");
mkdirSync(installRoot, { recursive: true });
mkdirSync(join(nodeDir, "bin"), { recursive: true });
mkdirSync(join(nodeDir, "lib", "node_modules", "openclaw", "dist"), { recursive: true });
mkdirSync(join(repo, ".git"), { recursive: true });
mkdirSync(join(repo, "dist"), { recursive: true });
mkdirSync(otherRoot, { recursive: true });
symlinkSync(process.execPath, join(nodeDir, "bin", "node"));
symlinkSync("node-v24.15.0", join(prefix, "tools", "node"));
writeFileSync(
join(nodeDir, "bin", "npm"),
'#!/bin/bash\nif [[ "$1" == "config" ]]; then printf "null\\n"; fi\n',
);
chmodSync(join(nodeDir, "bin", "npm"), 0o755);
for (const entry of [
join(nodeDir, "lib", "node_modules", "openclaw", "dist", "entry.js"),
join(repo, "dist", "entry.js"),
]) {
writeFileSync(entry, 'console.log("fixture cli");\n');
}
try {
const args =
input !== "environment"
? `--prefix ${JSON.stringify(prefixInput)}${
method === "git" ? ` --git-dir ${JSON.stringify(repoInput)}` : ""
}`
: "";
const result = runInstallCliShell(
[
"set -euo pipefail",
`cd ${JSON.stringify(installRoot)}`,
`source ${JSON.stringify(join(process.cwd(), SCRIPT_PATH))}`,
"install_node() { :; }",
"ensure_git() { :; }",
"refresh_gateway_service_if_loaded() { :; }",
...(method === "git"
? [
"preflight_fresh_git_disk_space() { :; }",
"ensure_pnpm() { :; }",
"ensure_pnpm_binary_for_scripts() { :; }",
"ensure_pnpm_git_prepare_allowlist() { :; }",
"activate_repo_pnpm_version() { :; }",
"cleanup_legacy_submodules() { :; }",
"resolve_git_openclaw_ref() { printf 'main\\n'; }",
"checkout_git_openclaw_ref() { :; }",
"git_install_lockfile_flag() { printf '%s\\n' '--no-frozen-lockfile'; }",
"run_pnpm() { :; }",
"git() { return 0; }",
]
: []),
`main --${method} ${args}`,
`cd ${JSON.stringify(otherRoot)}`,
`${JSON.stringify(join(prefix, "bin", "openclaw"))} --version`,
].join("\n"),
{
HOME: home,
OPENCLAW_GIT_DIR: input === "environment" && method === "git" ? repoInput : undefined,
OPENCLAW_PREFIX: input === "environment" ? prefixInput : undefined,
},
);
expect(result.status, result.stderr || result.stdout).toBe(0);
expect(result.stdout.trim().split("\n").at(-1)).toBe("fixture cli");
} finally {
rmSync(tmp, { force: true, recursive: true });
}
},
);
it("resolves requested git install versions to checkout refs", () => {
const result = runInstallCliShell(`
set -euo pipefail