diff --git a/projects/cram/src/python/cram/__init__.py b/projects/cram/src/python/cram/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/projects/cram/src/python/cram/__main__.py b/projects/cram/src/python/cram/__main__.py index 2098910..fc9f0fd 100644 --- a/projects/cram/src/python/cram/__main__.py +++ b/projects/cram/src/python/cram/__main__.py @@ -9,9 +9,10 @@ import re import sys from typing import NamedTuple +from vfs import Vfs + import click from toposort import toposort_flatten -from vfs import Vfs log = logging.getLogger(__name__) @@ -68,7 +69,7 @@ class PackageV0(NamedTuple): return requires - def install(self, fs: Vfs, dest): + def install(self, fs: Vfs, dest: Path): """Install this package.""" buildf = self.root / "BUILD" if buildf.exists(): @@ -89,6 +90,26 @@ class PackageV0(NamedTuple): fs.exec(self.root, ["bash", str(postf)]) +class ProfileV0(PackageV0): + def install(self, fs: Vfs, dest: Path): + """Profiles differ from Packages in that they don't support literal files.""" + buildf = self.root / "BUILD" + if buildf.exists(): + fs.exec(self.root, ["bash", str(buildf)]) + + pref = self.root / "PRE_INSTALL" + if pref.exists(): + fs.exec(self.root, ["bash", str(pref)]) + + installf = self.root / "INSTALL" + if installf.exists(): + fs.exec(self.root, ["bash", str(installf)]) + + postf = self.root / "POST_INSTALL" + if postf.exists(): + fs.exec(self.root, ["bash", str(postf)]) + + def load_config(root: Path) -> dict: """Load the configured packages.""" @@ -109,8 +130,8 @@ def load_config(root: Path) -> dict: p, str(p.relative_to(root)) ) - # Register the metapackages themselves - packages[str(mp_root.relative_to(root))] = PackageV0( + # Register the metapackages themselves using the profile type + packages[str(mp_root.relative_to(root))] = ProfileV0( mp_root, str(mp_root.relative_to(root)), True ) @@ -206,13 +227,13 @@ def cli(): pass -@cli.command("apply") +@cli.command() @click.option("--execute/--dry-run", default=False) -@click.option("--optimize/--no-optimize", default=True) @click.option("--state-file", default=".cram.log", type=Path) +@click.option("--optimize/--no-optimize", default=False) @click.argument("confdir", type=Path) @click.argument("destdir", type=Path) -def cmd_apply(confdir, destdir, state_file, execute, optimize): +def apply(confdir, destdir, state_file, execute, optimize): """The entry point of cram.""" # Resolve the two input paths to absolutes @@ -239,35 +260,49 @@ def cmd_apply(confdir, destdir, state_file, execute, optimize): else: for e in executable_fs._log: - print("-", *e) + print("-", e) -@cli.command("show") +@cli.command() @click.option("--state-file", default=".cram.log", type=Path) @click.argument("confdir", type=Path) -def cmd_show(confdir, state_file): +def show(confdir, state_file): """List out the last `apply` state in the /.cram.log or --state-file.""" root = confdir.resolve() - if not state_file.is_absolute(): state_file = root / state_file - fs = load_fs(state_file) - for e in fs._log: print(*e) -@cli.command("list") +@cli.command() @click.argument("confdir", type=Path) -def cmd_list(confdir): +@click.argument("list_packages", nargs=-1) +def list(confdir, list_packages): """List out packages, profiles, hosts and subpackages in the .""" packages = load_config(confdir) - for pname in sorted(packages.keys()): - p = packages[pname] - print(f"{pname}:") - for d in p.requires(): - print(f"- {d}") + + if list_packages: + dest = Path("~/") + for pname in list_packages: + fs = Vfs() + p = packages[pname] + p.install(fs, dest) + print(f"{pname}: ({type(p).__name__})") + print("requires:") + for e in p.requires(): + print(" -", e) + print("log:") + for e in fs._log: + print(" -", *e) + + else: + for pname in sorted(packages.keys()): + p = packages[pname] + print(f"{pname}: ({type(p).__name__})") + for d in p.requires(): + print(f"- {d}") if __name__ == "__main__" or 1: diff --git a/projects/vfs/src/python/vfs/impl.py b/projects/vfs/src/python/vfs/impl.py index b405b75..8c98eb1 100644 --- a/projects/vfs/src/python/vfs/impl.py +++ b/projects/vfs/src/python/vfs/impl.py @@ -3,6 +3,7 @@ The implementation. """ import logging +from shutil import rmtree from subprocess import run @@ -15,14 +16,11 @@ class Vfs(object): def __init__(self, log=None): self._log = log or [] - def execute(self, execute=False): + def execute(self): for e in self._log: _log.debug(e) - if not execute: - continue - - elif e[0] == "exec": + if e[0] == "exec": _, dir, cmd = e run(cmd, cwd=str(dir)) @@ -36,6 +34,7 @@ class Vfs(object): assert not dest.exists() dest.symlink_to(src) + assert dest.exists() elif e[0] == "copy": raise NotImplementedError() @@ -50,7 +49,10 @@ class Vfs(object): elif e[0] == "unlink": _, dest = e - dest.unlink() + if dest.is_dir(): + rmtree(dest) + elif dest.is_file(): + dest.unlink() def _append(self, msg): self._log.append(msg) diff --git a/setup.cfg b/setup.cfg index e013590..c9a464e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -6,7 +6,7 @@ multi_line_output = 3 lines_after_imports = 2 default_section = THIRDPARTY known_localfolder = datalog -sections = FUTURE,STDLIB,LOCALFOLDER,THIRDPARTY, +sections = STDLIB,LOCALFOLDER,FIRSTPARTY,THIRDPARTY force_sort_within_sections = 1 force_alphabetical_sort_within_sections = 1