fix(install): leave existing Strix untouched on verify failure

Stage into *.new and only rename after checks pass; abort cleans
temps and keeps the current binary (#1267).
This commit is contained in:
mohammad junayd 2026-09-14 17:38:33 +04:00
parent 14b0aee28b
commit 0636584eef
2 changed files with 63 additions and 8 deletions

View file

@ -137,6 +137,9 @@ check_existing_installation() {
abort_unverified() {
echo -e "${RED}✗ Refusing to install an unverified binary.${NC}"
if [[ -x "$INSTALL_DIR/strix" || -x "$INSTALL_DIR/strix.exe" ]]; then
echo -e "${MUTED}Existing Strix installation left unchanged.${NC}"
fi
echo -e "${RED}Re-run with STRIX_INSTALL_SKIP_VERIFY=1 to override (at your own risk).${NC}"
exit 1
}
@ -271,7 +274,20 @@ download_and_install() {
print_message info "\n${CYAN}🦉 Installing Strix${NC} ${MUTED}version: ${NC}$specific_version"
print_message info "${MUTED}Platform: ${NC}$target\n"
local tmp_dir=$(mktemp -d)
local tmp_dir
tmp_dir=$(mktemp -d)
# Never leave a half-written binary in INSTALL_DIR. Stage to *.new and only
# rename into place after a verified archive has been extracted. On any
# abort (including verification failure), remove the staging file and the
# download temp dir; the current install stays untouched.
cleanup_install_temps() {
cd / >/dev/null 2>&1 || true
rm -rf "$tmp_dir"
rm -f "$INSTALL_DIR/strix.new" "$INSTALL_DIR/strix.exe.new"
}
trap cleanup_install_temps EXIT
cd "$tmp_dir"
echo -e "${MUTED}Downloading...${NC}"
@ -303,15 +319,17 @@ download_and_install() {
echo -e "${MUTED}Extracting...${NC}"
if [ "$os" = "windows" ]; then
unzip -q "$filename"
mv "strix-${specific_version}-${target}.exe" "$INSTALL_DIR/strix.exe"
mv "strix-${specific_version}-${target}.exe" "$INSTALL_DIR/strix.exe.new"
mv -f "$INSTALL_DIR/strix.exe.new" "$INSTALL_DIR/strix.exe"
else
tar -xzf "$filename"
mv "strix-${specific_version}-${target}" "$INSTALL_DIR/strix"
chmod 755 "$INSTALL_DIR/strix"
mv "strix-${specific_version}-${target}" "$INSTALL_DIR/strix.new"
chmod 755 "$INSTALL_DIR/strix.new"
mv -f "$INSTALL_DIR/strix.new" "$INSTALL_DIR/strix"
fi
cd - > /dev/null
rm -rf "$tmp_dir"
trap - EXIT
cleanup_install_temps
echo -e "${GREEN}✓ Strix installed to $INSTALL_DIR${NC}"
}

View file

@ -77,9 +77,13 @@ while [ "$#" -gt 0 ]; do
done
case "$url" in
*/SHA256SUMS)
hash=$(sha256sum "$STRIX_TEST_ARCHIVE" | awk '{print $1}')
name=$(basename "$STRIX_TEST_ARCHIVE")
printf '%s %s\\n' "$hash" "$name" > "$output"
if [ -n "${STRIX_TEST_BAD_CHECKSUM:-}" ]; then
printf '%s %s\\n' "0" "$name" > "$output"
else
hash=$(sha256sum "$STRIX_TEST_ARCHIVE" | awk '{print $1}')
printf '%s %s\\n' "$hash" "$name" > "$output"
fi
;;
*.intoto.jsonl)
printf '{"test":true}\\n' > "$output"
@ -178,3 +182,36 @@ def test_installer_rejects_unsupported_architecture(tmp_path: Path) -> None:
assert "Unsupported OS/Arch: linux/riscv64" in result.stdout
assert not curl_log_path.exists()
assert not (home_path / ".strix").exists()
def test_installer_leaves_existing_install_on_checksum_failure(tmp_path: Path) -> None:
repository_root = Path(__file__).resolve().parents[1]
archive_path = _create_release_archive(tmp_path)
mock_bin = _create_mock_commands(tmp_path, machine="aarch64")
environment, home_path, _curl_log_path = _create_installer_environment(
tmp_path,
archive_path,
mock_bin,
)
environment["STRIX_TEST_BAD_CHECKSUM"] = "1"
install_dir = home_path / ".strix" / "bin"
install_dir.mkdir(parents=True)
existing = install_dir / "strix"
_write_executable(existing, "#!/bin/sh\nprintf 'strix 1.0.0\\n'\n")
before = existing.read_bytes()
result = _run_installer(repository_root, environment)
assert result.returncode != 0
assert "Checksum mismatch" in result.stdout
assert "Existing Strix installation left unchanged" in result.stdout
assert existing.read_bytes() == before
assert not (install_dir / "strix.new").exists()
installed_result = subprocess.run( # noqa: S603
[str(existing), "--version"],
capture_output=True,
text=True,
check=True,
)
assert installed_result.stdout.strip() == "strix 1.0.0"