diff --git a/src/python/cram/__main__.py b/src/python/cram/__main__.py index 6ed5097..3eb5dda 100644 --- a/src/python/cram/__main__.py +++ b/src/python/cram/__main__.py @@ -7,6 +7,7 @@ import pickle from typing import List, Optional, Union import re import platform +from pprint import pformat from collections import defaultdict from . import ( @@ -126,7 +127,7 @@ def load_packages(root: Path, roots) -> dict: packages = {} for r in roots.get("package", []): - r = root / r + r = root / expandvars(r) log.debug(f"Trying to load packages from {r}...") for p in r.glob("*"): name = str(p.relative_to(root)) @@ -134,7 +135,7 @@ def load_packages(root: Path, roots) -> dict: for r in roots.get("profile", []): # Add profiles, hosts which contain subpackages. - r = root / r + r = root / expandvars(r) log.debug(f"Trying to load profiles from {r}...") for mp_root in r.glob("*"): # First find all subpackages @@ -172,13 +173,11 @@ def build_fs( packages = load_packages(root, roots) - if packages: - for p in packages: - log.debug(f"Loaded package {p}") - else: + if not packages: log.warning("Loaded no packages!") requirements = list(prelude) + dirty = list() for r in requirements: try: for d in packages[r].requires(): @@ -186,11 +185,14 @@ def build_fs( requirements.append(d) except KeyError as e: missing_require_handler(r, e) - requirements.remove(r) + dirty.append(r) + + for r in dirty: + requirements.remove(r) - log.info(f"Mapped requirements {prelude} to {requirements}") # Compute the topsort graph requirements_graph = {r: packages[r].requires() for r in requirements} + log.debug(pformat(requirements_graph)) fs = Vfs() # Abstractly execute the current packages @@ -198,6 +200,7 @@ def build_fs( r = packages[r] r.install(fs, dest) + log.info(f"Computed an initial plan of {len(fs._log)} steps") return fs @@ -221,6 +224,7 @@ def simplify(old_fs: Vfs, new_fs: Vfs, /, exec_idempotent=True) -> Vfs: old_fs = old_fs.clone() new_fs = new_fs.clone() + initial_new_steps = len(new_fs._log) # Scrub anything in the new log that's in the old log for txn in list(old_fs._log): @@ -243,6 +247,7 @@ def simplify(old_fs: Vfs, new_fs: Vfs, /, exec_idempotent=True) -> Vfs: deduped.append(op) new_fs._log = deduped + log.info(f"Optimized out {initial_new_steps - len(new_fs._log)} steps") return new_fs @@ -304,10 +309,19 @@ def configure(ctx, param, filename: Optional[Path]): exit(1) +def count_to_level(count: int): + if count == 0: + return logging.WARN + elif count == 1: + return logging.INFO + elif count > 1: + return logging.DEBUG + + @click.group(invoke_without_command=True) @click.option( "--config", - type=click.Path(dir_okay=False), + type=Path, default=upsearch("cram.toml"), callback=configure, is_eager=True, @@ -315,6 +329,12 @@ def configure(ctx, param, filename: Optional[Path]): help="Read option defaults from the specified TOML file", show_default=True, ) +@click.option( + "-v", + "--verbose", + count=True, + callback=lambda ctx, param, count: count_to_level(count), +) @click.version_option( version=__version__, message=f"""Cram {__version__} @@ -335,8 +355,11 @@ About Published under the terms of the {__license__} license. """, ) -def cli(): - pass +def cli(verbose): + logging.basicConfig( + level=verbose, + format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", + ) def arg_statefile(*args, **kwargs): @@ -575,9 +598,9 @@ exec_idempotent = true # Where to load requirements from, and the requirement type # Types: package, profile require_root = [ - ["package", "${PWD}/packages.d"], - ["profile", "${PWD}/profiles.d"], - ["profile", "${PWD}/hosts.d"], + ["package", "packages.d"], + ["profile", "profiles.d"], + ["profile", "hosts.d"], ] # Choice([error, warn, ignore]) missing_require = "error" @@ -596,10 +619,5 @@ require = [ (confdir / d).mkdir(parents=True, exist_ok=True) -if __name__ == "__main__" or 1: - logging.basicConfig( - level=logging.INFO, - format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", - ) - +if __name__ == "__main__": cli()