Files
dotfiles/python/process.py
T
Richie b252b0bf6e feat(installer): add one-file installer build and custom install ISO
Replace old_installer.py with a curses TUI installer packaged as a
one-file PyInstaller binary (python/installer/build.py, wrapped by
python/installer/package.nix). The default .#installer is patched to run
on foreign Linux live media; the new .#installer-nixos variant keeps its
Nix store interpreter so it runs on NixOS.

Add systems/iso, a minimal NixOS install CD with kernel 6.18 and ZFS 2.4
matching the deployed systems and the installer on PATH; build it with
nix build .#iso. Shared logging and subprocess helpers move out of
common.py into python/logging_config.py and python/process.py.
2026-07-02 14:17:49 -04:00

53 lines
1.7 KiB
Python

"""Small subprocess helpers."""
from __future__ import annotations
import logging
import shutil
from subprocess import run
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from collections.abc import Iterable, Sequence
logger = logging.getLogger(__name__)
class CommandError(RuntimeError):
"""Raised when an external command fails."""
def __init__(self, command: Sequence[str], returncode: int, stderr: str) -> None:
"""Store command failure details."""
command_text = " ".join(command)
super().__init__(f"Failed to run command {command_text!r}: exit {returncode}\n{stderr}")
self.command = command
self.returncode = returncode
self.stderr = stderr
class MissingCommandsError(RuntimeError):
"""Raised when required external commands are not available."""
def __init__(self, commands: Sequence[str]) -> None:
"""Store missing command details."""
missing = ", ".join(commands)
super().__init__(f"Missing required installer commands: {missing}")
self.commands = commands
def require_commands(commands: Iterable[str]) -> None:
"""Raise when one or more executables are missing from PATH."""
missing_commands = sorted({command for command in commands if shutil.which(command) is None})
if missing_commands:
raise MissingCommandsError(missing_commands)
def run_output(command: Sequence[str]) -> str:
"""Run a command and return stdout."""
logger.debug("running command=%s", command)
result = run(command, capture_output=True, text=True, check=False)
if result.returncode != 0:
raise CommandError(command, result.returncode, result.stderr)
return result.stdout