Files
linux-image-manager/lim/fsutil.py
Kevin Veen-Birkenbach 5bef6177ac feat(image): flash full disk images into LUKS; add Raspberry Pi OS
Support distributions that ship a full .img (Raspberry Pi OS, moode, RetroPie,
Manjaro ARM) rather than a rootfs tarball, by loop-mounting the image and
copying its boot + root partitions into a fresh LUKS container.

- distributions.yml/catalog.py/choosers.py: raspios catalog (lite64/desktop64/
  lite32 via the stable _latest redirects) + choose_raspios.
- plan.py: source_url override so a _latest redirect downloads under an .img.xz
  name that decompress_command recognises.
- transfer.py: transfer_disk_image (loop-mount -> repartition -> LUKS -> rsync
  copy with progress, cp fallback -> fix boot fstab); transfer_image gains
  interactive= and routes encrypted non-arch images here; download_image gains
  force_prompt.
- loopimg.py: losetup attach/detach/partition helper.
- fsutil.drop_fstab_mount + register_encrypted_root: replace a stock image's
  existing / fstab line instead of colliding with it.
- encryption.configure_encryption returns the onion address.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-24 18:15:56 +02:00

50 lines
1.6 KiB
Python

"""Small file manipulation helpers."""
from pathlib import Path
from lim import ui
from lim.errors import LimError
def replace_in_file(search: str, replace: str, path: str | Path) -> None:
"""Replace every literal occurrence of ``search``; fail when absent."""
path = Path(path)
text = path.read_text()
new_text = text.replace(search, replace)
if new_text == text:
raise LimError(f"Search string '{search}' not found in {path}.")
path.write_text(new_text)
def drop_fstab_mount(mount_point: str, path: str | Path) -> None:
"""Remove any (non-comment) fstab line whose mount point field equals mount_point.
Lets a fresh mapper/boot entry replace a stock image's existing one instead
of colliding with it. No-op when the file or a matching line is absent.
"""
path = Path(path)
if not path.exists():
return
kept = [
line
for line in path.read_text().splitlines()
if line.lstrip().startswith("#") or line.split()[1:2] != [mount_point]
]
path.write_text("".join(f"{line}\n" for line in kept))
def ensure_line_in_file(line: str, path: str | Path) -> bool:
"""Append ``line`` unless already present. Returns True when appended."""
path = Path(path)
content = path.read_text() if path.exists() else ""
if line in content.splitlines():
ui.warning(f"File {path} already contains the following entry:")
print(line)
ui.info("Skipped.")
return False
with path.open("a") as handle:
if content and not content.endswith("\n"):
handle.write("\n")
handle.write(line + "\n")
return True