diff --git a/projects/cram/README.md b/projects/cram/README.md index 458d482..ab8888f 100644 --- a/projects/cram/README.md +++ b/projects/cram/README.md @@ -4,23 +4,20 @@ An alternative to GNU Stow, more some notion of packages with dependencies and install scripts. -## Usage +Think an Ansible or Puppet but anyarch and lite enough to check in with your dotfiles. + +## Overview + +Somewhat like Stow, Cram operates in terms of packages, which are directories with the following structure - ``` -# cram [--dry-run | --execute] -$ cram ~/conf ~/ # --dry-run is the default -``` - -Cram operates in terms of packages, which are directories with the following structure - - -``` -/REQUIRES # A list of other packages this one requires -/BUILD # 1. Perform any compile or package management tasks -/PRE_INSTALL # 2. Any other tasks required before installation occurs -/INSTALL # 3. Do whatever constitutes installation +/REQUIRES # A list of other packages this one requires. +/BUILD # 1. Perform any compile or package management tasks. +/PRE_INSTALL # 2. Any other tasks required before installation occurs. +/INSTALL # 3. Do whatever constitutes installation. + # This supercedes the default copying of files. /POST_INSTALL # 4. Any cleanup or other tasks following installation - -... # Any other files are treated as package contents +... # Any other files are treated as package contents. ``` @@ -34,3 +31,34 @@ Cram reads a config dir with three groups of packages The intent of this tool is to keep GNU Stow's intuitive model of deploying configs via symlinks, and augment it with a useful pattern for talking about "layers" / "packages" of related configs. Cram installs the package `hosts.d/$(hostname)`, and `profiles.d/default` by default. + +## Usage + +``` +$ cram apply [--dry-run|--execute] [--optimize] +``` + +The `apply` task applies a configuration to a destination directory. +The most common uses of this would be `--dry-run`, which functions as a `diff` or `--execute ~/conf ~/` for emulating Stow and installing dotfiles. + +Cram always reads the `.cram.log` state file and diffs the current state against the configured state. +Files and directories no longer defined by the configured state are cleaned up automatically. + +``` +$ cram show +``` + +The `list` task loads up and prints the `.cram.log` state file generated by any previous `cram apply --execute` so you can read a manifest of what cram thinks it did. + +``` +$ cram list +``` + +The `show` task lists out all available packages (eg. packages, profiles, hosts, and subpackages) as a dependency graph. + +## License + +Copyright Reid 'arrdem' McKenzie, 31/10/2021. + +Published under the terms of the MIT license. +See the included `LICENSE` file for more. diff --git a/projects/cram/src/python/cram/__main__.py b/projects/cram/src/python/cram/__main__.py index e09e104..0ea9a4e 100644 --- a/projects/cram/src/python/cram/__main__.py +++ b/projects/cram/src/python/cram/__main__.py @@ -201,7 +201,7 @@ def cli(): @cli.command() @click.option("--execute/--dry-run", default=False) -@click.option("--state-file", default=".cram.log") +@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) @@ -211,10 +211,11 @@ def apply(confdir, destdir, state_file, execute, optimize): # Resolve the two input paths to absolutes root = confdir.resolve() dest = destdir.resolve() - statef = root / state_file + if not state_file.is_absolute(): + state_file = root / state_file new_fs = build_fs(root, dest) - old_fs = load_fs(statef) + old_fs = load_fs(state_file) # Middleware processing of the resulting filesystem(s) executable_fs = scrub(old_fs, new_fs) @@ -226,7 +227,7 @@ def apply(confdir, destdir, state_file, execute, optimize): if execute: executable_fs.execute() - with open(statef, "wb") as fp: + with open(state_file, "wb") as fp: pickle.dump(new_fs._log, fp) else: @@ -235,12 +236,14 @@ def apply(confdir, destdir, state_file, execute, optimize): @cli.command() -@click.option("--state-file", default=".cram.log") +@click.option("--state-file", default=".cram.log", type=Path) @click.argument("confdir", type=Path) def show(confdir, state_file): + """List out the last `apply` state in the /.cram.log or --state-file.""" root = confdir.resolve() - statef = root / state_file - fs = load_fs(statef) + if not state_file.is_absolute(): + state_file = root / state_file + fs = load_fs(state_file) for e in fs._log: print(*e) @@ -248,12 +251,14 @@ def show(confdir, state_file): @cli.command() @click.argument("confdir", type=Path) def list(confdir): + """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}") + print(f"- {d}") + if __name__ == "__main__" or 1: logging.basicConfig(