Bring 14 files that predated the ruff-format run into line with the configured formatter (line-length 100); provably formatting-only (ruff format of HEAD == working tree, and ruff format is semantics-preserving). Also refresh two lim.image.tor._KEYGEN_SCRIPT doc references in tor_harness.py to lim.image.initramfs.keygen after the module moved. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
162 lines
4.9 KiB
Python
162 lines
4.9 KiB
Python
"""Command line interface: `lim --type <command>`."""
|
|
|
|
import argparse
|
|
import sys
|
|
from collections.abc import Callable
|
|
from dataclasses import dataclass
|
|
|
|
from lim import system, ui
|
|
from lim.data import crypt, sync
|
|
from lim.errors import LimError
|
|
from lim.image import backup, chroot
|
|
from lim.image import setup as image_setup
|
|
from lim.storage import raid1, single_drive
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class Command:
|
|
func: Callable[[], None]
|
|
description: str
|
|
needs_root: bool
|
|
|
|
|
|
COMMANDS: dict[str, Command] = {
|
|
"image": Command(
|
|
image_setup.run_setup,
|
|
"Linux Image Setup:\n"
|
|
" - Creates partitions and formats them.\n"
|
|
" - Transfers the Linux image file to the device.\n"
|
|
" - Configures boot and root partitions.",
|
|
needs_root=True,
|
|
),
|
|
"single": Command(
|
|
single_drive.setup,
|
|
"Single Drive Encryption Setup:\n"
|
|
" - Sets up disk encryption using LUKS on one drive.\n"
|
|
" - Configures a Btrfs file system for secure storage.",
|
|
needs_root=True,
|
|
),
|
|
"raid1": Command(
|
|
raid1.setup,
|
|
"RAID1 Encryption Setup:\n"
|
|
" - Configures a virtual RAID1 with two drives.\n"
|
|
" - Uses LUKS encryption and a Btrfs RAID1 file system for redundancy.",
|
|
needs_root=True,
|
|
),
|
|
"backup": Command(
|
|
backup.run_backup,
|
|
"Backup Image Setup:\n"
|
|
" - Creates an image backup from a memory device to a file.\n"
|
|
" - Uses dd to transfer the image from the specified device to an image file.",
|
|
needs_root=True,
|
|
),
|
|
"chroot": Command(
|
|
chroot.run_chroot,
|
|
"Chroot Environment Setup:\n"
|
|
" - Mounts partitions and configures the chroot environment for a Linux image.\n"
|
|
" - Provides a shell within the Linux image for system maintenance.",
|
|
needs_root=True,
|
|
),
|
|
"mount": Command(
|
|
single_drive.mount,
|
|
"Mount Encrypted Storage:\n - Unlocks a LUKS partition and mounts it.",
|
|
needs_root=True,
|
|
),
|
|
"umount": Command(
|
|
single_drive.umount,
|
|
"Unmount Encrypted Storage:\n - Unmounts a LUKS partition and closes the mapper.",
|
|
needs_root=True,
|
|
),
|
|
"single-boot": Command(
|
|
single_drive.mount_on_boot,
|
|
"Single Drive Automount Setup:\n"
|
|
" - Creates a LUKS keyfile and registers it in crypttab and fstab.",
|
|
needs_root=True,
|
|
),
|
|
"raid1-boot": Command(
|
|
raid1.mount_on_boot,
|
|
"RAID1 Automount Setup:\n"
|
|
" - Creates LUKS keyfiles for both drives and registers them in crypttab/fstab.",
|
|
needs_root=True,
|
|
),
|
|
"unlock": Command(
|
|
crypt.unlock,
|
|
"Unlock Data:\n - Decrypts the encfs data store into the decrypted folder.",
|
|
needs_root=False,
|
|
),
|
|
"lock": Command(
|
|
crypt.lock,
|
|
"Lock Data:\n - Unmounts the decrypted encfs folder.",
|
|
needs_root=False,
|
|
),
|
|
"import": Command(
|
|
sync.import_from_system,
|
|
"Import Data:\n - Copies personal data from the system into the encrypted store.",
|
|
needs_root=False,
|
|
),
|
|
"export": Command(
|
|
sync.export_to_system,
|
|
"Export Data:\n - Copies personal data from the encrypted store back to the system.",
|
|
needs_root=False,
|
|
),
|
|
}
|
|
|
|
|
|
def build_parser() -> argparse.ArgumentParser:
|
|
epilog_lines = ["Available script types:"]
|
|
epilog_lines += [
|
|
f" {name:<12} - {command.description.splitlines()[0].rstrip(':')}"
|
|
for name, command in COMMANDS.items()
|
|
]
|
|
parser = argparse.ArgumentParser(
|
|
prog="lim",
|
|
description="Linux Image Manager — manages Linux images and encrypted storage.",
|
|
epilog="\n".join(epilog_lines),
|
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
)
|
|
parser.add_argument(
|
|
"--type",
|
|
required=True,
|
|
choices=list(COMMANDS.keys()),
|
|
help="Select the command to execute.",
|
|
)
|
|
parser.add_argument(
|
|
"--auto-confirm",
|
|
action="store_true",
|
|
help="Automatically confirm execution without prompting the user.",
|
|
)
|
|
parser.add_argument(
|
|
"--extra",
|
|
nargs=argparse.REMAINDER,
|
|
default=[],
|
|
help="Deprecated, ignored. Former extra parameters for the shell scripts.",
|
|
)
|
|
return parser
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> None:
|
|
args = build_parser().parse_args(argv)
|
|
command = COMMANDS[args.type]
|
|
|
|
if args.extra:
|
|
ui.warning("--extra is deprecated and ignored.")
|
|
if command.needs_root:
|
|
system.ensure_root()
|
|
|
|
ui.header()
|
|
ui.info(f"Selected command: {args.type}")
|
|
ui.info("Description:")
|
|
print(command.description)
|
|
|
|
try:
|
|
if not args.auto_confirm:
|
|
input("Press Enter to execute or Ctrl+C to cancel...")
|
|
command.func()
|
|
except LimError as exc:
|
|
ui.error(f"{exc} -> Leaving program.")
|
|
sys.exit(1)
|
|
except KeyboardInterrupt:
|
|
print()
|
|
ui.error("Execution aborted by user.")
|
|
sys.exit(1)
|