Skip to content

Commit f50293c

Browse files
authored
fix: installer removes temporary files after failed commands (#104049)
* fix(installer): retain temp cleanup registrations * chore: keep installer release notes release-owned
1 parent 3bcb90b commit f50293c

2 files changed

Lines changed: 45 additions & 11 deletions

File tree

scripts/install.sh

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,12 @@ trap abort_install_int INT
4949
trap abort_install_term TERM
5050

5151
mktempfile() {
52-
local f
52+
local output_var="${1:?output variable required}" f
5353
f="$(mktemp)"
54+
# Assign into caller scope; command substitution would lose this cleanup
55+
# registration in its subshell.
5456
TMPFILES+=("$f")
55-
echo "$f"
57+
printf -v "$output_var" '%s' "$f"
5658
}
5759

5860
resolve_openclaw_effective_home() {
@@ -117,7 +119,7 @@ download_file() {
117119
run_remote_bash() {
118120
local url="$1"
119121
local tmp
120-
tmp="$(mktempfile)"
122+
mktempfile tmp
121123
download_file "$url" "$tmp"
122124
/bin/bash "$tmp"
123125
}
@@ -479,8 +481,8 @@ run_with_spinner() {
479481

480482
if [[ -n "$GUM" ]] && gum_is_tty && ! is_shell_function "${1:-}"; then
481483
local gum_err gum_out
482-
gum_err="$(mktempfile)"
483-
gum_out="$(mktempfile)"
484+
mktempfile gum_err
485+
mktempfile gum_out
484486
if "$GUM" spin --spinner dot --title "$title" -- "$@" >"$gum_out" 2>"$gum_err"; then
485487
if is_gum_raw_mode_failure "$gum_out" || is_gum_raw_mode_failure "$gum_err"; then
486488
GUM=""
@@ -523,7 +525,7 @@ run_quiet_step() {
523525
fi
524526

525527
local log
526-
log="$(mktempfile)"
528+
mktempfile log
527529
local showed_progress=false
528530

529531
local cmd_exit=0
@@ -1012,7 +1014,7 @@ print_npm_failure_diagnostics() {
10121014
install_openclaw_npm() {
10131015
local spec="$1"
10141016
local log
1015-
log="$(mktempfile)"
1017+
mktempfile log
10161018
if ! run_npm_global_install "$spec" "$log"; then
10171019
local attempted_build_tool_fix=false
10181020
if auto_install_build_tools_for_npm_failure "$log"; then
@@ -1860,7 +1862,7 @@ install_node() {
18601862
ui_info "Installing Node.js via NodeSource"
18611863
if command -v apt-get &> /dev/null; then
18621864
local tmp
1863-
tmp="$(mktempfile)"
1865+
mktempfile tmp
18641866
run_required_step "Downloading NodeSource setup script" download_file "https://deb.nodesource.com/setup_${NODE_DEFAULT_MAJOR}.x" "$tmp"
18651867
if is_root; then
18661868
run_required_step "Configuring NodeSource repository" bash "$tmp"
@@ -1871,7 +1873,7 @@ install_node() {
18711873
fi
18721874
elif command -v dnf &> /dev/null; then
18731875
local tmp
1874-
tmp="$(mktempfile)"
1876+
mktempfile tmp
18751877
run_required_step "Downloading NodeSource setup script" download_file "https://rpm.nodesource.com/setup_${NODE_DEFAULT_MAJOR}.x" "$tmp"
18761878
if is_root; then
18771879
run_required_step "Configuring NodeSource repository" bash "$tmp"
@@ -1882,7 +1884,7 @@ install_node() {
18821884
fi
18831885
elif command -v yum &> /dev/null; then
18841886
local tmp
1885-
tmp="$(mktempfile)"
1887+
mktempfile tmp
18861888
run_required_step "Downloading NodeSource setup script" download_file "https://rpm.nodesource.com/setup_${NODE_DEFAULT_MAJOR}.x" "$tmp"
18871889
if is_root; then
18881890
run_required_step "Configuring NodeSource repository" bash "$tmp"

test/scripts/install-sh.test.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
// Install Sh tests cover install sh script behavior.
22
import { spawnSync } from "node:child_process";
3-
import { chmodSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
3+
import {
4+
chmodSync,
5+
existsSync,
6+
mkdtempSync,
7+
mkdirSync,
8+
readFileSync,
9+
rmSync,
10+
writeFileSync,
11+
} from "node:fs";
412
import { tmpdir } from "node:os";
513
import { join } from "node:path";
614
import { describe, expect, it } from "vitest";
@@ -50,6 +58,30 @@ describe("install.sh", () => {
5058
}
5159
});
5260

61+
it("removes a downloaded script temp file when remote execution fails", () => {
62+
const tmp = mkdtempSync(join(tmpdir(), "openclaw-install-remote-cleanup-"));
63+
const tempFile = join(tmp, "remote-script.sh");
64+
65+
try {
66+
const result = runInstallShell(
67+
[
68+
"set -euo pipefail",
69+
`source ${JSON.stringify(SCRIPT_PATH)}`,
70+
'mktemp() { : > "$PROBE_PATH"; printf \'%s\\n\' "$PROBE_PATH"; }',
71+
"download_file() { printf '#!/bin/bash\\nexit 42\\n' > \"$2\"; }",
72+
'run_remote_bash "https://example.invalid/setup.sh"',
73+
].join("\n"),
74+
{ PROBE_PATH: tempFile },
75+
);
76+
77+
expect(result.status).toBe(42);
78+
expect(existsSync(tempFile)).toBe(false);
79+
expect(script).not.toMatch(/\$\(\s*mktempfile\s*\)/);
80+
} finally {
81+
rmSync(tmp, { force: true, recursive: true });
82+
}
83+
});
84+
5385
it("runs apt-get through noninteractive wrappers", () => {
5486
expect(script).toContain("apt_get()");
5587
expect(script).toContain('DEBIAN_FRONTEND="${DEBIAN_FRONTEND:-noninteractive}"');

0 commit comments

Comments
 (0)