From 8e19f226405b5d6da03b850b428b5e3e96d054d5 Mon Sep 17 00:00:00 2001 From: Reid 'arrdem' McKenzie Date: Thu, 28 Jul 2022 23:45:00 -0600 Subject: [PATCH] Promote cram out --- README.md | 1 + projects/cram/BUILD | 28 -- projects/cram/README.md | 6 + projects/cram/src/python/cram/__init__.py | 4 - projects/cram/src/python/cram/__main__.py | 335 ------------------ projects/cram/src/python/cram/common.py | 86 ----- projects/cram/src/python/cram/v0.py | 109 ------ projects/cram/src/python/cram/v1.py | 116 ------ projects/cram/test.sh | 96 ----- projects/cram/test/hosts.d/test/pkg.toml | 6 - projects/cram/test/packages.d/p1/foo | 1 - projects/cram/test/packages.d/p2/bar | 1 - projects/cram/test/packages.d/p3/REQUIRES | 2 - projects/cram/test/packages.d/p4/pkg.toml | 6 - projects/cram/test/packages.d/p5/INSTALL | 3 - projects/cram/test/packages.d/p6/pkg.toml | 5 - projects/cram/test/packages.d/p7/pkg.toml | 6 - .../cram/test/profiles.d/default/pkg.toml | 4 - .../profiles.d/default/subpackage/pkg.toml | 6 - 19 files changed, 7 insertions(+), 814 deletions(-) delete mode 100644 projects/cram/BUILD delete mode 100644 projects/cram/src/python/cram/__init__.py delete mode 100644 projects/cram/src/python/cram/__main__.py delete mode 100644 projects/cram/src/python/cram/common.py delete mode 100644 projects/cram/src/python/cram/v0.py delete mode 100644 projects/cram/src/python/cram/v1.py delete mode 100755 projects/cram/test.sh delete mode 100644 projects/cram/test/hosts.d/test/pkg.toml delete mode 100644 projects/cram/test/packages.d/p1/foo delete mode 100644 projects/cram/test/packages.d/p2/bar delete mode 100644 projects/cram/test/packages.d/p3/REQUIRES delete mode 100644 projects/cram/test/packages.d/p4/pkg.toml delete mode 100644 projects/cram/test/packages.d/p5/INSTALL delete mode 100644 projects/cram/test/packages.d/p6/pkg.toml delete mode 100644 projects/cram/test/packages.d/p7/pkg.toml delete mode 100644 projects/cram/test/profiles.d/default/pkg.toml delete mode 100644 projects/cram/test/profiles.d/default/subpackage/pkg.toml diff --git a/README.md b/README.md index 13c92a8..ee0239d 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ And so I'm going the other way; Bazel in a monorepo with subprojects so I'm able - [Datalog](projects/datalog) and the matching [shell](projects/datalog-shell) - [YAML Schema](projects/yamlschema) (JSON schema with knowledge of PyYAML's syntax structure & nice errors) - [Zapp! (now with a new home and releases)](https://github.com/arrdem/rules_zapp) +- [Cram (now with a new home)](https://github.com/arrdem/cram) - [Flowmetal](projects/flowmetal) - [Lilith](projects/lilith) diff --git a/projects/cram/BUILD b/projects/cram/BUILD deleted file mode 100644 index 87783d4..0000000 --- a/projects/cram/BUILD +++ /dev/null @@ -1,28 +0,0 @@ -py_library( - name = "lib", - srcs = glob(["src/python/**/*.py"]), - deps = [ - "//projects/vfs", - py_requirement("click"), - py_requirement("toposort"), - py_requirement("toml"), - ] -) - -zapp_binary( - name = "cram", - main = "src/python/cram/__main__.py", - shebang = "/usr/bin/env python3", - imports = [ - "src/python" - ], - deps = [ - ":lib", - ], -) - -sh_test( - name = "test_cram", - srcs = glob(["test.sh"]), - data = glob(["test/**/*"]) + [":cram"], -) diff --git a/projects/cram/README.md b/projects/cram/README.md index 817c34c..e09ddc0 100644 --- a/projects/cram/README.md +++ b/projects/cram/README.md @@ -1,5 +1,11 @@ # Cram +Cram has graduated [to its own repo](https://github.com/arrdem/cram)! + + +------ + + > To force (people or things) into a place or container that is or appears to be too small to contain them. An alternative to GNU Stow, more some notion of packages with dependencies and install scripts. diff --git a/projects/cram/src/python/cram/__init__.py b/projects/cram/src/python/cram/__init__.py deleted file mode 100644 index 8326682..0000000 --- a/projects/cram/src/python/cram/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -__version__ = "0.1.0" -__author__ = "Reid D. 'arrdem' McKenzie " -__copyright__ = "Copyright 2020" -__license__ = "https://anticapitalist.software/" diff --git a/projects/cram/src/python/cram/__main__.py b/projects/cram/src/python/cram/__main__.py deleted file mode 100644 index d89f405..0000000 --- a/projects/cram/src/python/cram/__main__.py +++ /dev/null @@ -1,335 +0,0 @@ -"""Cram's entry point.""" - -from itertools import chain -import logging -import os -from pathlib import Path -import pickle -from typing import List - -from . import ( - __author__, - __copyright__, - __license__, - __version__, -) -from .v0 import PackageV0, ProfileV0 -from .v1 import PackageV1, ProfileV1 - -import click -import toml -from toposort import toposort_flatten -from vfs import Vfs - - -log = logging.getLogger(__name__) - - -def _exit(val): - logging.shutdown() - exit(val) - - -def load(root: Path, name: str, clss): - for c in clss: - i = c(root, name) - if i.test(): - return i - - -def load_package(root, name): - log.debug(f"Attempting to load package {name} from {root}") - return load(root, name, [PackageV1, PackageV0]) - - -def load_profile(root, name): - log.debug(f"Attempting to load profile {name} from {root}") - return load(root, name, [ProfileV1, ProfileV0]) - - -def load_packages(root: Path) -> dict: - """Load the configured packages.""" - - packages = {} - log.debug(f"Trying to load packages from {root}...") - for p in (root / "packages.d").glob("*"): - name = str(p.relative_to(root)) - packages[name] = load_package(p, name) - - # Add profiles, hosts which contain subpackages. - for mp_root in chain((root / "profiles.d").glob("*"), (root / "hosts.d").glob("*")): - - # First find all subpackages - for p in mp_root.glob("*"): - if p.is_dir(): - name = str(p.relative_to(root)) - packages[name] = load_package(p, name) - - # Register the metapackages themselves using the profile type - mp_name = str(mp_root.relative_to(root)) - packages[mp_name] = load_profile(mp_root, mp_name) - - return packages - - -def build_fs(root: Path, dest: Path, prelude: List[str]) -> Vfs: - """Build a VFS by configuring dest from the given config root.""" - - packages = load_packages(root) - requirements = [] - requirements.extend(prelude) - - if packages: - for p in packages: - log.debug(f"Loaded package {p}") - else: - log.warning("Loaded no packages!") - - for r in requirements: - try: - for d in packages[r].requires(): - if d not in requirements: - requirements.append(d) - except KeyError: - log.fatal(f"Error: Unable to load package {r}") - _exit(1) - - # Compute the topsort graph - requirements = {r: packages[r].requires() for r in requirements} - fs = Vfs() - - # Abstractly execute the current packages - for r in toposort_flatten(requirements): - r = packages[r] - r.install(fs, dest) - - return fs - - -def load_state(statefile: Path) -> Vfs: - """Load a persisted VFS state from disk. Sort of.""" - - oldfs = Vfs([]) - - if statefile.exists(): - log.debug("Loading statefile %s", statefile) - with open(statefile, "rb") as fp: - oldfs._log = pickle.load(fp) - else: - log.warning("No previous statefile %s", statefile) - - return oldfs - - -def simplify(old_fs: Vfs, new_fs: Vfs, /, exec_idempotent=True) -> Vfs: - """Try to reduce a new VFS using diff from the original VFS.""" - - old_fs = old_fs.copy() - new_fs = new_fs.copy() - - # Scrub anything in the new log that's in the old log - for txn in list(old_fs._log): - # Except for execs which are stateful - if txn[0] == "exec" and not exec_idempotent: - continue - - try: - new_fs._log.remove(txn) - except ValueError: - pass - - # Dedupe the new log while preserving order. - keys = set() - deduped = [] - for op in new_fs._log: - key = str(op) - if key not in keys: - keys.add(key) - deduped.append(op) - new_fs._log = deduped - - return new_fs - - -def scrub(old_fs: Vfs, new_fs: Vfs) -> Vfs: - """Try to eliminate files which were previously installed but are no longer used.""" - - old_fs = old_fs.copy() - new_fs = new_fs.copy() - cleanup_fs = Vfs([]) - - # Look for files in the old log which are no longer present in the new log - for txn in old_fs._log: - if txn[0] == "link" and txn not in new_fs._log: - cleanup_fs.unlink(txn[2]) - - elif txn[0] == "mkdir" and txn not in new_fs._log: - cleanup_fs.unlink(txn[1]) - - # Do unlink operations before we do install operations. - # This works around being unable to finely straify uninstall operations over their source packages. - cleanup_fs.merge(new_fs) - - return cleanup_fs - - -@click.group() -@click.version_option(version=1, message=f"""Cram {__version__} - -Documentation - https://github.com/arrdem/source/tree/trunk/projects/cram/ - -Features - - 0.0.0 legacy config format - - 0.1.0 TOML config format - - 0.1.0 log based optimizer - - 0.1.0 idempotent default for scripts - -About - {__copyright__}, {__author__}. - Published under the terms of the {__license__} license. -""") -def cli(): - pass - - -@cli.command("apply") -@click.option("--execute/--dry-run", default=False) -@click.option("--force/--no-force", default=False) -@click.option("--state-file", default=".cram.log", type=Path) -@click.option("--optimize/--no-optimize", default=True) -@click.option("--require", type=str, multiple=True, default=[f"hosts.d/{os.uname()[1].split('.')[0]}", "profiles.d/default"]) -@click.option("--exec-idempotent/--exec-always", "exec_idempotent", default=True) -@click.argument("confdir", type=Path) -@click.argument("destdir", type=Path) -def do_apply(confdir, destdir, state_file, execute, optimize, force, require, exec_idempotent): - """The entry point of cram.""" - - # Resolve the two input paths to absolutes - root = confdir.resolve() - dest = destdir.resolve() - - if not root.is_dir(): - log.fatal(f"{confdir} does not exist!") - _exit(1) - - if not state_file.is_absolute(): - state_file = root / state_file - - if not force: - old_fs = load_state(state_file) - log.debug(f"Loaded old state consisting of {len(old_fs._log)} steps") - else: - # Force an empty state - old_fs = Vfs([]) - - new_fs = build_fs(root, dest, require) - log.debug(f"Built new state consisting of {len(new_fs._log)} steps") - - # Middleware processing of the resulting filesystem(s) - executable_fs = scrub(old_fs, new_fs) - if optimize: - executable_fs = simplify(old_fs, executable_fs, - exec_idempotent=exec_idempotent) - - # Dump the new state. - # Note that we dump the UNOPTIMIZED state, because we want to simplify relative complete states. - def cb(e): - print("-", *e) - - if execute: - executable_fs.execute(callback=cb) - - with open(state_file, "wb") as fp: - pickle.dump(new_fs._log, fp) - - else: - for e in executable_fs._log: - cb(e) - - -@cli.command("list") -@click.argument("confdir", type=Path) -@click.argument("list_packages", nargs=-1) -def do_list(confdir, list_packages): - """List out packages, profiles, hosts and subpackages in the .""" - root = confdir.resolve() - - if not root.is_dir(): - log.fatal(f"{confdir} does not exist!") - _exit(1) - - packages = load_packages(root) - - 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}") - - -@cli.command("state") -@click.option("--state-file", default=".cram.log", type=Path) -@click.argument("confdir", type=Path) -def do_state(confdir, state_file): - """List out the last `apply` state in the /.cram.log or --state-file.""" - root = confdir.resolve() - - if not root.is_dir(): - log.fatal(f"{confdir} does not exist!") - _exit(1) - - if not state_file.is_absolute(): - state_file = root / state_file - - fs = load_state(state_file) - for e in fs._log: - print(*e) - - -@cli.command("fmt") -@click.argument("confdir", type=Path) -@click.argument("requirement", type=str) -def do_migrate(confdir, requirement): - """Format the specified requirement to a canonical-ish representation.""" - - root = confdir.resolve() - - if not root.is_dir(): - log.fatal(f"{confdir} does not exist!") - _exit(1) - - packages = load_packages(root) - pkg = packages[requirement] - json = pkg.json() - - for suffix in pkg.SPECIAL_FILES: - f = (root / requirement / suffix) - if f.exists(): - f.unlink() - - with open(root / requirement / "pkg.toml", "w") as fp: - toml.dump(json, fp) - -if __name__ == "__main__" or 1: - logging.basicConfig( - level=logging.INFO, - format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", - ) - - cli() diff --git a/projects/cram/src/python/cram/common.py b/projects/cram/src/python/cram/common.py deleted file mode 100644 index 35948bd..0000000 --- a/projects/cram/src/python/cram/common.py +++ /dev/null @@ -1,86 +0,0 @@ -#!/usr/bin/env python3 - -import os -from pathlib import Path -from shlex import quote as sh_quote -import sys -from typing import List, Optional - -from vfs import Vfs - - -# FIXME: This should be a config somewhere -SHELL = "/bin/sh" - -# Light monkeypatching because macos ships a "stable" a py -if sys.version_info <= (3, 9, 0): - Path.readlink = lambda p: Path(os.readlink(str(p))) - - -def sh(cmd: List[str], /, - env: Optional[dict] = None): - - prefix = [] - if env: - prefix.append("/usr/bin/env") - for k, v in env.items(): - v = sh_quote(str(v)) - prefix.append(f"{k}={v}") - - return tuple(prefix + [SHELL, *cmd]) - - -def stow(fs: Vfs, src_dir: Path, dest_dir: Path, skip=[]): - """Recursively 'stow' (link) the contents of the source into the destination.""" - - dest_root = Path(dest_dir) - src_root = Path(src_dir) - skip = [src_root / n for n in skip] - - for src in src_root.glob("**/*"): - if src in skip: - continue - - elif src.name.endswith(".gitkeep"): - continue - - dest = dest_root / src.relative_to(src_root) - if src.is_symlink(): - - fs.link(src.readlink().resolve(), dest) - elif src.is_dir(): - fs.mkdir(dest) - fs.chmod(dest, src.stat().st_mode) - - elif src.is_file(): - fs.link(src, dest) - - -class Package(object): - def __init__(self, root: Path, name: str): - self.root = root - self.name = name - - def test(self): - return True - - def requires(self): - return [] - - def install(self, fs: Vfs, dest: Path): - self.do_build(fs, dest) - self.pre_install(fs, dest) - self.do_install(fs, dest) - self.post_install(fs, dest) - - def do_build(self, fs: Vfs, dest: Path): - pass - - def pre_install(self, fs: Vfs, dest: Path): - pass - - def do_install(self, fs: Vfs, dest: Path): - pass - - def post_install(self, fs: Vfs, dest: Path): - pass diff --git a/projects/cram/src/python/cram/v0.py b/projects/cram/src/python/cram/v0.py deleted file mode 100644 index ab8c5db..0000000 --- a/projects/cram/src/python/cram/v0.py +++ /dev/null @@ -1,109 +0,0 @@ -"""Cram's original (v0) configs. - -An ill-considered pseudo-format. -""" - -from pathlib import Path -import re - -from .common import Package, sh, stow - -from vfs import Vfs - - -class PackageV0(Package): - """The original package format from install.sh.""" - - SPECIAL_FILES = ["BUILD", "PRE_INSTALL", "INSTALL", "POST_INSTALL", "REQUIRES"] - - def requires(self): - """Get the dependencies of this package.""" - requiresf = self.root / "REQUIRES" - requires = [] - - # Listed dependencies - if requiresf.exists(): - with open(requiresf) as fp: - for l in fp: - l = l.strip() - l = re.sub(r"\s*#.*\n", "", l) - if l: - requires.append(l) - - return requires - - def install(self, fs: Vfs, dest: Path): - """Install this package.""" - buildf = self.root / "BUILD" - if buildf.exists(): - fs.exec(self.root, sh([str(buildf)])) - - pref = self.root / "PRE_INSTALL" - if pref.exists(): - fs.exec(self.root, sh([str(pref)])) - - installf = self.root / "INSTALL" - if installf.exists(): - fs.exec(self.root, sh([str(installf)])) - else: - stow(fs, self.root, dest, self.SPECIAL_FILES) - - postf = self.root / "POST_INSTALL" - if postf.exists(): - fs.exec(self.root, sh([str(postf)])) - - def _read(self, p: Path): - if p.exists(): - with open(p) as fp: - return fp.read() - else: - return None - - def json(self): - buildt = self._read(self.root / "BUILD") - pret = self._read(self.root / "PRE_INSTALL") - installt = self._read(self.root / "INSTALL") - postt = self._read(self.root / "POST_INSTALL") - - o = {"cram": {"version": 1}, "package": {"require": []}} - - if buildt: - o["package"]["build"] = [{"run": buildt}] - if pret: - o["package"]["pre_install"] = [{"run": pret}] - if installt: - o["package"]["install"] = [{"run": installt}] - if postt: - o["package"]["install"] = [{"run": postt}] - - o["package"]["require"] = [{"name": it} for it in sorted(self.requires())] - - return o - - -class ProfileV0(PackageV0): - def requires(self): - requires = super().requires() - for p in self.root.glob("*"): - if p.is_dir(): - requires.append(self.name + "/" + p.name) - return requires - - 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, sh([str(buildf)])) - - pref = self.root / "PRE_INSTALL" - if pref.exists(): - fs.exec(self.root, sh([str(pref)])) - - installf = self.root / "INSTALL" - if installf.exists(): - fs.exec(self.root, sh([str(installf)])) - - postf = self.root / "POST_INSTALL" - if postf.exists(): - fs.exec(self.root, sh([str(postf)])) diff --git a/projects/cram/src/python/cram/v1.py b/projects/cram/src/python/cram/v1.py deleted file mode 100644 index 72462b3..0000000 --- a/projects/cram/src/python/cram/v1.py +++ /dev/null @@ -1,116 +0,0 @@ -"""Cram's v1 configs. - -Based on well* defined TOML manifests, rather than many files. - -*Okay. Better. -""" - -from hashlib import sha256 -from pathlib import Path -from typing import List, Optional, Union - -from .common import Package, sh, stow - -import toml -from vfs import Vfs - - -def tempf(name): - root = Path("/tmp/stow") - root.mkdir(exist_ok=True, parents=True) - return root / name - - -class PackageV1(Package): - """The v1 package format.""" - - SPECIAL_FILES = ["pkg.toml"] - _config = None - - def config(self): - if not self._config: - with open(self.root / self.SPECIAL_FILES[0], "r") as fp: - self._config = toml.load(fp) - return self._config - - def test(self): - return (self.root / self.SPECIAL_FILES[0]).exists() and self.config().get("cram", {}).get("version") == 1 - - def requires(self): - """Get the dependencies of this package.""" - - def _name(it): - if isinstance(it, str): - return it - elif isinstance(it, dict): - return it["name"] - - return [ - _name(it) for it in self.config().get("package", {}).get("require", []) - ] - - def do_sh_or_script(self, content: Optional[Union[List[str], str]], fs: Vfs, dest: Path, cwd: Path = "/tmp"): - if content is None: - pass - - elif isinstance(content, list): - for c in content: - self.do_sh_or_script(c, fs, dest) - - elif isinstance(content, dict): - self.do_sh_or_script( - content["run"], - fs, - dest, - {"cwd": self.root}.get(content.get("root"), "/tmp") - ) - - elif isinstance(content, str): - sum = sha256() - sum.update(content.encode("utf-8")) - sum = sum.hexdigest() - - installf = self.root / content - if installf.exists(): - with open(installf, "r") as fp: - self.do_sh_or_script(fp.read(), fs, dest) - - elif content: - f = tempf(f"{sum}.sh") - with open(f, "w") as fp: - fp.write(content) - fs.exec(cwd, sh([f])) - - def do_build(self, fs: Vfs, dest: Path): - self.do_sh_or_script(self.config().get("package", {}).get("build"), fs, dest) - - def pre_install(self, fs: Vfs, dest: Path): - self.do_sh_or_script(self.config().get("package", {}).get("pre_install"), fs, dest) - - def do_install(self, fs: Vfs, dest: Path): - if not self.do_sh_or_script(self.config().get("package", {}).get("install"), fs, dest): - stow(fs, self.root, dest, self.SPECIAL_FILES) - - def post_install(self, fs: Vfs, dest: Path): - self.do_sh_or_script(self.config().get("package", {}).get("post_install"), fs, dest) - - - def json(self): - return self.config() - - -class ProfileV1(PackageV1): - """Unline packages, profiles don't support recursive stow of contents.""" - - def do_install(self, fs: Vfs, dest: Path): - self.do_sh_or_script(self.config().get("package", {}).get("install"), fs, dest) - - def requires(self): - requires = super().requires() - - # Implicitly depended subpackages - for p in self.root.glob("*"): - if p.is_dir(): - requires.append(self.name + "/" + p.name) - - return requires diff --git a/projects/cram/test.sh b/projects/cram/test.sh deleted file mode 100755 index 974c9b5..0000000 --- a/projects/cram/test.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env bash - -set -ex - -cd projects/cram - -dest=$(mktemp -d) - -./cram --help - -# Should be able to list all packages -./cram list test/ | grep "packages.d/p1" - -# P3 depends on P1, should show up in the listing -./cram list test/ packages.d/p3 | grep "packages.d/p1" - -# P4 depends on P3, should show up in the listing -./cram list test/ packages.d/p4 | grep "packages.d/p3" - -# The default profile should depend on its subpackage -./cram list test/ profiles.d/default | grep "profiles.d/default/subpackage" - -# And the subpackage has a dep -./cram list test/ profiles.d/default/subpackage | grep "packages.d/p3" - -# Install one package -./cram apply --no-optimize --require packages.d/p1 --execute test/ "${dest}" -[ -L "${dest}"/foo ] -./cram state test/ | grep "${dest}/foo" -rm -r "${dest}"/* - -# Install two transitively (legacy) -./cram apply --no-optimize --require packages.d/p3 --execute test/ "${dest}" -[ -L "${dest}"/foo ] -[ -L "${dest}"/bar ] -./cram state test/ | grep "${dest}/foo" -./cram state test/ | grep "${dest}/bar" -rm -r "${dest}"/* - -# Install two transitively (current) -./cram apply --no-optimize --require packages.d/p4 --execute test/ "${dest}" -[ -L "${dest}"/foo ] -[ -L "${dest}"/bar ] -rm -r "${dest}"/* - -# Install two transitively (current) -./cram apply --no-optimize --require packages.d/p4 --execute test/ "${dest}" -[ -L "${dest}"/foo ] -[ -L "${dest}"/bar ] -rm -r "${dest}"/* - -# Install two transitively (current) -./cram apply --no-optimize --require hosts.d/test --require profiles.d/default --execute test/ "${dest}" -[ -L "${dest}"/foo ] -[ -L "${dest}"/bar ] -rm -r "${dest}"/* - -# INSTALL scripts get run as-is -./cram list test/ packages.d/p5 | grep "packages.d/p5/INSTALL" - -# Inline scripts get pulled out repeatably -./cram list test/ packages.d/p6 | grep "b5bea41b6c623f7c09f1bf24dcae58ebab3c0cdd90ad966bc43a45b44867e12b" - -# Inline scripts get pulled out repeatably, even from the list format -./cram list test/ packages.d/p7 | grep "b5bea41b6c623f7c09f1bf24dcae58ebab3c0cdd90ad966bc43a45b44867e12b" - -# Test log-based optimization -./cram apply --no-optimize --require packages.d/p4 --execute test/ "${dest}" -[ -L "${dest}"/foo ] -[ -L "${dest}"/bar ] -# These paths were already linked, they shouldn't be re-linked when optimizing. -! ./cram apply --require packages.d/p4 --optimize --execute test/ "${dest}" | grep "${dest}/foo" -! ./cram apply --require packages.d/p4 --optimize --execute test/ "${dest}" | grep "${dest}/bar" -rm -r "${dest}"/* - -# Likewise, if we've exec'd this once we shouldn't do it again -./cram apply --no-optimize --require packages.d/p5 --execute test/ "${dest}" -! ./cram apply --require packages.d/p5 --execute test/ "${dest}" | grep "exec" - -# ... unless the user tells us to -./cram apply --no-optimize --require packages.d/p5 --execute test/ "${dest}" -./cram apply --exec-always --require packages.d/p5 --execute test/ "${dest}" | grep "exec" - -# If multiple packages provide the same _effective_ script, do it once -./cram apply --require packages.d/p6 --require packages.d/p7 --execute test/ "${dest}" | sort | uniq -c | grep "/tmp/stow/b5bea41b6c623f7c09f1bf24dcae58ebab3c0cdd90ad966bc43a45b44867e12b.sh" | grep "1 - exec" - -# Test log-based cleanup -./cram apply --require packages.d/p1 --require packages.d/p2 --execute test/ "${dest}" -[ -L "${dest}"/foo ] -[ -L "${dest}"/bar ] -# And how bar shouldn't be installed... -./cram state test/ -./cram apply --require packages.d/p1 --execute test/ "${dest}" -./cram state test/ -[ -L "${dest}"/foo ] -[ ! -L "${dest}"/bar ] diff --git a/projects/cram/test/hosts.d/test/pkg.toml b/projects/cram/test/hosts.d/test/pkg.toml deleted file mode 100644 index 81cad09..0000000 --- a/projects/cram/test/hosts.d/test/pkg.toml +++ /dev/null @@ -1,6 +0,0 @@ -[cram] -version = 1 - -[package] - [[package.require]] - name = "packages.d/p1" diff --git a/projects/cram/test/packages.d/p1/foo b/projects/cram/test/packages.d/p1/foo deleted file mode 100644 index 5716ca5..0000000 --- a/projects/cram/test/packages.d/p1/foo +++ /dev/null @@ -1 +0,0 @@ -bar diff --git a/projects/cram/test/packages.d/p2/bar b/projects/cram/test/packages.d/p2/bar deleted file mode 100644 index 100b0de..0000000 --- a/projects/cram/test/packages.d/p2/bar +++ /dev/null @@ -1 +0,0 @@ -qux diff --git a/projects/cram/test/packages.d/p3/REQUIRES b/projects/cram/test/packages.d/p3/REQUIRES deleted file mode 100644 index d58b400..0000000 --- a/projects/cram/test/packages.d/p3/REQUIRES +++ /dev/null @@ -1,2 +0,0 @@ -packages.d/p1 -packages.d/p2 diff --git a/projects/cram/test/packages.d/p4/pkg.toml b/projects/cram/test/packages.d/p4/pkg.toml deleted file mode 100644 index c9ad8ff..0000000 --- a/projects/cram/test/packages.d/p4/pkg.toml +++ /dev/null @@ -1,6 +0,0 @@ -[cram] -version = 1 - -[package] - [[package.require]] - name = "packages.d/p3" diff --git a/projects/cram/test/packages.d/p5/INSTALL b/projects/cram/test/packages.d/p5/INSTALL deleted file mode 100644 index 50fb1af..0000000 --- a/projects/cram/test/packages.d/p5/INSTALL +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/bash -# A legacy custom install script -true diff --git a/projects/cram/test/packages.d/p6/pkg.toml b/projects/cram/test/packages.d/p6/pkg.toml deleted file mode 100644 index 66d0cda..0000000 --- a/projects/cram/test/packages.d/p6/pkg.toml +++ /dev/null @@ -1,5 +0,0 @@ -[cram] -version = 1 - -[package] -install = "true" diff --git a/projects/cram/test/packages.d/p7/pkg.toml b/projects/cram/test/packages.d/p7/pkg.toml deleted file mode 100644 index 4e13848..0000000 --- a/projects/cram/test/packages.d/p7/pkg.toml +++ /dev/null @@ -1,6 +0,0 @@ -[cram] -version = 1 - -[package] - [[package.install]] - run = "true" diff --git a/projects/cram/test/profiles.d/default/pkg.toml b/projects/cram/test/profiles.d/default/pkg.toml deleted file mode 100644 index bdd71c8..0000000 --- a/projects/cram/test/profiles.d/default/pkg.toml +++ /dev/null @@ -1,4 +0,0 @@ -[cram] -version = 1 - -[package] diff --git a/projects/cram/test/profiles.d/default/subpackage/pkg.toml b/projects/cram/test/profiles.d/default/subpackage/pkg.toml deleted file mode 100644 index 75d94eb..0000000 --- a/projects/cram/test/profiles.d/default/subpackage/pkg.toml +++ /dev/null @@ -1,6 +0,0 @@ -[cram] -version = 1 - -[package] -[[package.require]] -name = "packages.d/p3"