fix(e2e): download non-root installer before execution

Download the non-root Docker smoke installer before executing it.

- preserve the existing 30-second connection and 300-second transfer limits
- clean up the temporary installer on success or failure
- prove failed downloads cannot execute partial installer content

Co-authored-by: thomas.szbay <xydigit-zt@users.noreply.github.com>
Punchcard-Session: golden-lantern-meadow-0x
This commit is contained in:
thomas.szbay
2026-08-11 18:12:52 +08:00
committed by GitHub
parent c719cbbfe9
commit ecc6d5a9fe
2 changed files with 108 additions and 2 deletions
+6 -1
View File
@@ -62,7 +62,12 @@ node -e '
command -v npm >/dev/null
echo "==> Run installer (non-root user)"
curl -fsSL --connect-timeout 30 --max-time 300 -- "$INSTALL_URL" | bash
# This non-root harness downloads first; public smoke and CLI lanes intentionally
# retain their streaming installer contract.
installer="$(mktemp)"
trap 'rm -f "$installer"' EXIT
curl -fsSL --connect-timeout 30 --max-time 300 -o "$installer" -- "$INSTALL_URL"
bash "$installer"
# Ensure PATH picks up user npm prefix
export PATH="$HOME/.npm-global/bin:$PATH"
+102 -1
View File
@@ -71,6 +71,18 @@ function extractInstallE2eInstallerFunction(): string {
return script.slice(start, end);
}
function extractNonrootInstallerStep(): string {
const script = readFileSync(NONROOT_RUNNER_PATH, "utf8");
const startMarker = 'echo "==> Run installer (non-root user)"';
const endMarker = "\n\n# Ensure PATH";
const start = script.indexOf(startMarker);
const end = script.indexOf(endMarker, start + startMarker.length);
if (start < 0 || end <= start) {
throw new Error("non-root installer step was not found");
}
return script.slice(start, end);
}
function extractDockerTimezoneValidator(): string {
const script = readFileSync(DOCKER_SETUP_PATH, "utf8");
const match = script.match(
@@ -188,6 +200,64 @@ function runInstallE2eInstallerFixture(params: {
return { curlArgsPath, markerPath, outputPathCapture, result };
}
function runNonrootInstallerFixture(curlExitCode: number) {
const root = tempDirs.make("openclaw-install-nonroot-download-");
const binDir = join(root, "bin");
const curlArgsPath = join(root, "curl-args.txt");
const markerPath = join(root, "installer-marker.txt");
const outputPathCapture = join(root, "curl-output-path.txt");
mkdirSync(binDir, { recursive: true });
writeFileSync(
join(binDir, "curl"),
[
"#!/bin/sh",
"set -eu",
`printf '%s\\0' "$@" >"$CURL_ARGS_PATH"`,
'output=""',
'while [ "$#" -gt 0 ]; do',
' if [ "$1" = "-o" ]; then',
" shift",
' output="$1"',
" fi",
" shift",
"done",
`printf '%s\\n' 'touch "$INSTALL_MARKER"' >"$output"`,
'chmod +x "$output"',
'printf "%s" "$output" >"$OUTPUT_PATH_CAPTURE"',
'exit "$FAKE_CURL_EXIT"',
"",
].join("\n"),
{ mode: 0o755 },
);
const result = spawnSync(
"/bin/bash",
[
"--noprofile",
"--norc",
"-c",
[
"set -euo pipefail",
'INSTALL_URL="https://installer.example.test/install.sh"',
extractNonrootInstallerStep(),
].join("\n"),
],
{
encoding: "utf8",
env: {
...process.env,
CURL_ARGS_PATH: curlArgsPath,
FAKE_CURL_EXIT: String(curlExitCode),
INSTALL_MARKER: markerPath,
OUTPUT_PATH_CAPTURE: outputPathCapture,
PATH: `${binDir}:${process.env.PATH ?? ""}`,
},
},
);
return { curlArgsPath, markerPath, outputPathCapture, result };
}
function runNonrootNodePreflight(
version: string,
options: { sqlite?: boolean; sqliteVersion?: string } = {},
@@ -1227,8 +1297,39 @@ printf 'status=%s\\n' "$status"
`'set -o pipefail; curl -fsSL --connect-timeout 30 --max-time 300 -- "$OPENCLAW_INSTALL_CLI_URL" | bash -s -- --set-npm-prefix --no-onboard'`,
);
expect(nonrootRunner).toContain(
'curl -fsSL --connect-timeout 30 --max-time 300 -- "$INSTALL_URL" | bash',
'curl -fsSL --connect-timeout 30 --max-time 300 -o "$installer" -- "$INSTALL_URL"',
);
expect(nonrootRunner.indexOf('-o "$installer"')).toBeLessThan(
nonrootRunner.indexOf('bash "$installer"'),
);
});
it("does not execute or retain a non-root installer after curl fails", () => {
const fixture = runNonrootInstallerFixture(28);
const outputPath = readFileSync(fixture.outputPathCapture, "utf8");
expect(fixture.result.status).toBe(28);
expect(readNulSeparatedArgs(fixture.curlArgsPath)).toEqual([
"-fsSL",
"--connect-timeout",
"30",
"--max-time",
"300",
"-o",
outputPath,
"--",
"https://installer.example.test/install.sh",
]);
expect(existsSync(fixture.markerPath)).toBe(false);
expect(existsSync(outputPath)).toBe(false);
});
it("executes and cleans the non-root installer after curl succeeds", () => {
const fixture = runNonrootInstallerFixture(0);
expect(fixture.result.status, fixture.result.stderr).toBe(0);
expect(existsSync(fixture.markerPath)).toBe(true);
expect(existsSync(readFileSync(fixture.outputPathCapture, "utf8"))).toBe(false);
});
it("uses public npm latest as the non-root installer expectation", () => {