mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
fix(e2e): bound official installer download (#108937)
* fix(e2e): bound official installer download * test(e2e): cover downloaded installer execution * fix(e2e): harden installer download proof Co-authored-by: Alix-007 <li.long15@xydigit.com> * test(e2e): type installer fixture environment --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -150,15 +150,21 @@ preinstall_previous_version() {
|
||||
fi
|
||||
}
|
||||
|
||||
run_official_installer() {
|
||||
run_official_installer() (
|
||||
local installer
|
||||
# time_phase disables errexit, so setup and download failures must return explicitly.
|
||||
installer="$(mktemp)" || return
|
||||
trap 'rm -f "$installer"' EXIT
|
||||
|
||||
curl -fsSL --connect-timeout 10 --max-time 120 "$INSTALL_URL" -o "$installer" || return
|
||||
if [[ "$INSTALL_TAG" == "beta" ]]; then
|
||||
curl -fsSL "$INSTALL_URL" | OPENCLAW_BETA=1 bash
|
||||
OPENCLAW_BETA=1 bash "$installer"
|
||||
elif [[ "$INSTALL_TAG" != "latest" ]]; then
|
||||
curl -fsSL "$INSTALL_URL" | OPENCLAW_VERSION="$INSTALL_TAG" bash
|
||||
OPENCLAW_VERSION="$INSTALL_TAG" bash "$installer"
|
||||
else
|
||||
curl -fsSL "$INSTALL_URL" | bash
|
||||
bash "$installer"
|
||||
fi
|
||||
}
|
||||
)
|
||||
|
||||
verify_installed_version() {
|
||||
INSTALLED_VERSION="$(openclaw --version 2>/dev/null | head -n 1 | tr -d '\r')"
|
||||
|
||||
@@ -4348,8 +4348,8 @@ heartbeat_elapsed="\${BASH_REMATCH[1]}"
|
||||
it("passes installer tag env to bash, not curl", () => {
|
||||
const runner = readFileSync(INSTALL_E2E_RUNNER_PATH, "utf8");
|
||||
|
||||
expect(runner).toContain('curl -fsSL "$INSTALL_URL" | OPENCLAW_BETA=1 bash');
|
||||
expect(runner).toContain('curl -fsSL "$INSTALL_URL" | OPENCLAW_VERSION="$INSTALL_TAG" bash');
|
||||
expect(runner).toContain('OPENCLAW_BETA=1 bash "$installer"');
|
||||
expect(runner).toContain('OPENCLAW_VERSION="$INSTALL_TAG" bash "$installer"');
|
||||
expect(runner).not.toContain('OPENCLAW_BETA=1 curl -fsSL "$INSTALL_URL" | bash');
|
||||
expect(runner).not.toContain(
|
||||
'OPENCLAW_VERSION="$INSTALL_TAG" curl -fsSL "$INSTALL_URL" | bash',
|
||||
|
||||
@@ -59,6 +59,87 @@ function extractNonrootNodePreflight(): string {
|
||||
return expectDefined(match[1], "non-root smoke Node preflight capture");
|
||||
}
|
||||
|
||||
function extractInstallE2eInstallerFunction(): string {
|
||||
const script = readFileSync(INSTALL_E2E_RUNNER_PATH, "utf8");
|
||||
const startMarker = "run_official_installer() (\n";
|
||||
const endMarker = "\n\nverify_installed_version()";
|
||||
const start = script.indexOf(startMarker);
|
||||
const end = script.indexOf(endMarker, start + startMarker.length);
|
||||
if (start < 0 || end <= start) {
|
||||
throw new Error("install E2E installer function was not found");
|
||||
}
|
||||
return script.slice(start, end);
|
||||
}
|
||||
|
||||
function runInstallE2eInstallerFixture(params: {
|
||||
curlExitCode?: number;
|
||||
installTag: string;
|
||||
installerBody: string;
|
||||
}) {
|
||||
const root = tempDirs.make("openclaw-install-e2e-download-");
|
||||
const binDir = join(root, "bin");
|
||||
const curlPath = join(binDir, "curl");
|
||||
const curlArgsPath = join(root, "curl-args.txt");
|
||||
const installerSourcePath = join(root, "installer-source.sh");
|
||||
const markerPath = join(root, "installer-marker.txt");
|
||||
const outputPathCapture = join(root, "curl-output-path.txt");
|
||||
mkdirSync(binDir, { recursive: true });
|
||||
writeFileSync(installerSourcePath, params.installerBody);
|
||||
writeFileSync(
|
||||
curlPath,
|
||||
[
|
||||
"#!/bin/sh",
|
||||
"set -eu",
|
||||
'printf \'%s\\n\' "$*" >"$CURL_ARGS_PATH"',
|
||||
'output=""',
|
||||
'while [ "$#" -gt 0 ]; do',
|
||||
' if [ "$1" = "-o" ]; then',
|
||||
" shift",
|
||||
' output="$1"',
|
||||
" fi",
|
||||
" shift",
|
||||
"done",
|
||||
'cp "$FAKE_INSTALLER_SOURCE" "$output"',
|
||||
'printf "%s" "$output" >"$OUTPUT_PATH_CAPTURE"',
|
||||
'exit "$FAKE_CURL_EXIT"',
|
||||
"",
|
||||
].join("\n"),
|
||||
{ mode: 0o755 },
|
||||
);
|
||||
|
||||
const env: NodeJS.ProcessEnv = {
|
||||
...process.env,
|
||||
CURL_ARGS_PATH: curlArgsPath,
|
||||
FAKE_CURL_EXIT: String(params.curlExitCode ?? 0),
|
||||
FAKE_INSTALLER_SOURCE: installerSourcePath,
|
||||
INSTALL_MARKER: markerPath,
|
||||
OUTPUT_PATH_CAPTURE: outputPathCapture,
|
||||
PATH: `${binDir}:${process.env.PATH ?? ""}`,
|
||||
};
|
||||
delete env.OPENCLAW_BETA;
|
||||
delete env.OPENCLAW_VERSION;
|
||||
|
||||
const result = spawnSync(
|
||||
"/bin/bash",
|
||||
[
|
||||
"-c",
|
||||
[
|
||||
"set -u",
|
||||
extractInstallE2eInstallerFunction(),
|
||||
'INSTALL_URL="https://installer.example.test/install.sh"',
|
||||
`INSTALL_TAG=${JSON.stringify(params.installTag)}`,
|
||||
"run_official_installer",
|
||||
].join("\n"),
|
||||
],
|
||||
{
|
||||
encoding: "utf8",
|
||||
env,
|
||||
},
|
||||
);
|
||||
|
||||
return { curlArgsPath, markerPath, outputPathCapture, result };
|
||||
}
|
||||
|
||||
function runNonrootNodePreflight(
|
||||
version: string,
|
||||
options: { sqlite?: boolean; sqliteVersion?: string } = {},
|
||||
@@ -960,6 +1041,40 @@ printf 'status=%s\\n' "$status"
|
||||
});
|
||||
|
||||
describe("install-sh E2E runner", () => {
|
||||
it("does not execute a partial installer after a bounded download fails", () => {
|
||||
const fixture = runInstallE2eInstallerFixture({
|
||||
curlExitCode: 28,
|
||||
installerBody: 'touch "$INSTALL_MARKER"\n',
|
||||
installTag: "latest",
|
||||
});
|
||||
|
||||
expect(fixture.result.status).toBe(28);
|
||||
expect(readFileSync(fixture.curlArgsPath, "utf8")).toContain(
|
||||
"-fsSL --connect-timeout 10 --max-time 120 https://installer.example.test/install.sh -o",
|
||||
);
|
||||
expect(existsSync(fixture.markerPath)).toBe(false);
|
||||
expect(existsSync(readFileSync(fixture.outputPathCapture, "utf8"))).toBe(false);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["latest", "|"],
|
||||
["beta", "1|"],
|
||||
["2026.7.1", "|2026.7.1"],
|
||||
])(
|
||||
"executes a complete %s installer with the expected tag environment",
|
||||
(installTag, expected) => {
|
||||
const fixture = runInstallE2eInstallerFixture({
|
||||
installTag,
|
||||
installerBody:
|
||||
'printf "%s|%s" "${OPENCLAW_BETA-}" "${OPENCLAW_VERSION-}" >"$INSTALL_MARKER"\n',
|
||||
});
|
||||
|
||||
expect(fixture.result.status, fixture.result.stderr).toBe(0);
|
||||
expect(readFileSync(fixture.markerPath, "utf8")).toBe(expected);
|
||||
expect(existsSync(readFileSync(fixture.outputPathCapture, "utf8"))).toBe(false);
|
||||
},
|
||||
);
|
||||
|
||||
it("normalizes Docker wrapper timing and toggle knobs before forwarding", () => {
|
||||
const wrapper = readFileSync(INSTALL_E2E_DOCKER_PATH, "utf8");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user