fix: installer removes temporary files after failed commands (#104049)

* fix(installer): retain temp cleanup registrations

* chore: keep installer release notes release-owned
This commit is contained in:
Peter Steinberger
2026-07-10 18:54:56 -07:00
committed by GitHub
parent 3bcb90b2d0
commit f50293c9e7
2 changed files with 45 additions and 11 deletions
+12 -10
View File
@@ -49,10 +49,12 @@ trap abort_install_int INT
trap abort_install_term TERM
mktempfile() {
local f
local output_var="${1:?output variable required}" f
f="$(mktemp)"
# Assign into caller scope; command substitution would lose this cleanup
# registration in its subshell.
TMPFILES+=("$f")
echo "$f"
printf -v "$output_var" '%s' "$f"
}
resolve_openclaw_effective_home() {
@@ -117,7 +119,7 @@ download_file() {
run_remote_bash() {
local url="$1"
local tmp
tmp="$(mktempfile)"
mktempfile tmp
download_file "$url" "$tmp"
/bin/bash "$tmp"
}
@@ -479,8 +481,8 @@ run_with_spinner() {
if [[ -n "$GUM" ]] && gum_is_tty && ! is_shell_function "${1:-}"; then
local gum_err gum_out
gum_err="$(mktempfile)"
gum_out="$(mktempfile)"
mktempfile gum_err
mktempfile gum_out
if "$GUM" spin --spinner dot --title "$title" -- "$@" >"$gum_out" 2>"$gum_err"; then
if is_gum_raw_mode_failure "$gum_out" || is_gum_raw_mode_failure "$gum_err"; then
GUM=""
@@ -523,7 +525,7 @@ run_quiet_step() {
fi
local log
log="$(mktempfile)"
mktempfile log
local showed_progress=false
local cmd_exit=0
@@ -1012,7 +1014,7 @@ print_npm_failure_diagnostics() {
install_openclaw_npm() {
local spec="$1"
local log
log="$(mktempfile)"
mktempfile log
if ! run_npm_global_install "$spec" "$log"; then
local attempted_build_tool_fix=false
if auto_install_build_tools_for_npm_failure "$log"; then
@@ -1860,7 +1862,7 @@ install_node() {
ui_info "Installing Node.js via NodeSource"
if command -v apt-get &> /dev/null; then
local tmp
tmp="$(mktempfile)"
mktempfile tmp
run_required_step "Downloading NodeSource setup script" download_file "https://deb.nodesource.com/setup_${NODE_DEFAULT_MAJOR}.x" "$tmp"
if is_root; then
run_required_step "Configuring NodeSource repository" bash "$tmp"
@@ -1871,7 +1873,7 @@ install_node() {
fi
elif command -v dnf &> /dev/null; then
local tmp
tmp="$(mktempfile)"
mktempfile tmp
run_required_step "Downloading NodeSource setup script" download_file "https://rpm.nodesource.com/setup_${NODE_DEFAULT_MAJOR}.x" "$tmp"
if is_root; then
run_required_step "Configuring NodeSource repository" bash "$tmp"
@@ -1882,7 +1884,7 @@ install_node() {
fi
elif command -v yum &> /dev/null; then
local tmp
tmp="$(mktempfile)"
mktempfile tmp
run_required_step "Downloading NodeSource setup script" download_file "https://rpm.nodesource.com/setup_${NODE_DEFAULT_MAJOR}.x" "$tmp"
if is_root; then
run_required_step "Configuring NodeSource repository" bash "$tmp"
+33 -1
View File
@@ -1,6 +1,14 @@
// Install Sh tests cover install sh script behavior.
import { spawnSync } from "node:child_process";
import { chmodSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
import {
chmodSync,
existsSync,
mkdtempSync,
mkdirSync,
readFileSync,
rmSync,
writeFileSync,
} from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, it } from "vitest";
@@ -50,6 +58,30 @@ describe("install.sh", () => {
}
});
it("removes a downloaded script temp file when remote execution fails", () => {
const tmp = mkdtempSync(join(tmpdir(), "openclaw-install-remote-cleanup-"));
const tempFile = join(tmp, "remote-script.sh");
try {
const result = runInstallShell(
[
"set -euo pipefail",
`source ${JSON.stringify(SCRIPT_PATH)}`,
'mktemp() { : > "$PROBE_PATH"; printf \'%s\\n\' "$PROBE_PATH"; }',
"download_file() { printf '#!/bin/bash\\nexit 42\\n' > \"$2\"; }",
'run_remote_bash "https://example.invalid/setup.sh"',
].join("\n"),
{ PROBE_PATH: tempFile },
);
expect(result.status).toBe(42);
expect(existsSync(tempFile)).toBe(false);
expect(script).not.toMatch(/\$\(\s*mktempfile\s*\)/);
} finally {
rmSync(tmp, { force: true, recursive: true });
}
});
it("runs apt-get through noninteractive wrappers", () => {
expect(script).toContain("apt_get()");
expect(script).toContain('DEBIAN_FRONTEND="${DEBIAN_FRONTEND:-noninteractive}"');