chore(session): harden chroot package installs (PATH + apt-get update)

- chroot_bash exports a Debian-safe PATH so /usr/sbin tools (update-initramfs,
  useradd, chpasswd, ...) resolve inside the chroot instead of failing with
  code 127.
- install_packages runs apt-get update before install (a stock image ships
  stale lists whose superseded .deb URLs 404) and non-interactive
  apt-get install -y; pacman gains -Sy for the same index-refresh reason.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Kevin Veen-Birkenbach
2026-07-23 01:56:02 +02:00
parent c3a41db796
commit 68ea39b784
3 changed files with 20 additions and 7 deletions

View File

@@ -161,11 +161,16 @@ class ImageSession:
ui.warning("Failed.")
# The host PATH leaks into the chroot; force one that includes the sbin dirs
# where Debian keeps update-initramfs, useradd, chpasswd, ... (else code 127).
_CHROOT_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
def chroot_bash(root_mount_path: Path | str, script: str, error_msg: str | None = None) -> None:
"""Run a bash script inside the image via chroot."""
"""Run a bash script inside the image via chroot (with a Debian-safe PATH)."""
runner.run(
["chroot", str(root_mount_path), "/bin/bash"],
input_text=script,
input_text=f"export PATH={_CHROOT_PATH}\n{script}",
sudo=True,
error_msg=error_msg,
)
@@ -175,8 +180,13 @@ def install_packages(distribution: str, root_mount_path: Path, package_names: st
"""Install packages inside the image with the distribution's package manager."""
ui.info(f"Installing {package_names}...")
if distribution in ("arch", "manjaro"):
chroot_bash(root_mount_path, f"pacman --noconfirm -S --needed {package_names}")
chroot_bash(root_mount_path, f"pacman --noconfirm -Sy --needed {package_names}")
elif distribution in ("moode", "retropie", "raspios"):
chroot_bash(root_mount_path, f"yes | apt install {package_names}")
# apt-get update first: a stock image ships stale lists whose old .deb
# URLs 404 once the mirror has superseded them.
chroot_bash(
root_mount_path,
f"apt-get update\nDEBIAN_FRONTEND=noninteractive apt-get install -y {package_names}",
)
else:
raise LimError("Package manager not supported.")

View File

@@ -101,7 +101,8 @@ class TestInstallPackages:
for kind, cmd, input_text in fake_runner.calls
if kind == "run" and cmd[0] == "chroot"
]
assert chroot_calls == ["pacman --noconfirm -S --needed btrfs-progs"]
assert len(chroot_calls) == 1
assert "pacman --noconfirm -Sy --needed btrfs-progs" in chroot_calls[0]
def test_apt_for_retropie(self, tmp_path, fake_runner):
install_packages("retropie", tmp_path, "btrfs-progs")
@@ -110,7 +111,9 @@ class TestInstallPackages:
for kind, cmd, input_text in fake_runner.calls
if kind == "run" and cmd[0] == "chroot"
]
assert chroot_calls == ["yes | apt install btrfs-progs"]
assert len(chroot_calls) == 1
assert "apt-get update" in chroot_calls[0]
assert "apt-get install -y btrfs-progs" in chroot_calls[0]
def test_unsupported_distribution_raises(self, tmp_path, fake_runner):
with pytest.raises(LimError):

View File

@@ -189,7 +189,7 @@ class TestInitramfsToolsBackend:
apt = [
text
for _, _, text in fake_runner.calls
if text and "apt install" in text and "tor busybox" in text
if text and "apt-get install" in text and "tor busybox" in text
]
assert len(apt) == 1