WIP: shaking out TOML errors
This commit is contained in:
parent
a19e89e5cc
commit
2bd82a7a32
2 changed files with 99 additions and 65 deletions
|
@ -154,25 +154,25 @@ def load_packages(root: Path, roots) -> dict:
|
|||
return packages
|
||||
|
||||
|
||||
def missing_require_fatal(r):
|
||||
def missing_require_fatal(r, e):
|
||||
log.fatal(f"Unable to load package {r}")
|
||||
_exit(1)
|
||||
|
||||
|
||||
def missing_require_warn(r):
|
||||
def missing_require_warn(r, e):
|
||||
log.warn(f"Unable to load package {r}")
|
||||
|
||||
|
||||
def missing_require_ignore(r):
|
||||
pass
|
||||
def missing_require_ignore(r, e):
|
||||
return
|
||||
|
||||
|
||||
def build_fs(root: Path, roots, dest: Path, prelude: List[str], missing_require_handler) -> Vfs:
|
||||
def build_fs(
|
||||
root: Path, roots, dest: Path, prelude: List[str], missing_require_handler
|
||||
) -> Vfs:
|
||||
"""Build a VFS by configuring dest from the given config root."""
|
||||
|
||||
packages = load_packages(root, roots)
|
||||
requirements = []
|
||||
requirements.extend(prelude)
|
||||
|
||||
if packages:
|
||||
for p in packages:
|
||||
|
@ -180,21 +180,23 @@ def build_fs(root: Path, roots, dest: Path, prelude: List[str], missing_require_
|
|||
else:
|
||||
log.warning("Loaded no packages!")
|
||||
|
||||
requirements = list(prelude)
|
||||
for r in list(requirements):
|
||||
try:
|
||||
for d in packages[r].requires():
|
||||
if d not in requirements:
|
||||
requirements.append(d)
|
||||
except KeyError:
|
||||
missing_require_handler(r)
|
||||
except KeyError as e:
|
||||
missing_require_handler(r, e)
|
||||
requirements.remove(r)
|
||||
|
||||
log.debug(f"Mapped requirements {prelude} to {requirements}")
|
||||
# Compute the topsort graph
|
||||
requirements = {r: packages[r].requires() for r in requirements}
|
||||
requirements_graph = {r: packages[r].requires() for r in requirements}
|
||||
fs = Vfs()
|
||||
|
||||
# Abstractly execute the current packages
|
||||
for r in toposort_flatten(requirements):
|
||||
for r in toposort_flatten(requirements_graph):
|
||||
r = packages[r]
|
||||
r.install(fs, dest)
|
||||
|
||||
|
@ -304,6 +306,8 @@ def configure(ctx, param, filename: Optional[Path]):
|
|||
if error:
|
||||
exit(1)
|
||||
|
||||
pprint(ctx.default_map)
|
||||
|
||||
|
||||
@click.group(invoke_without_command=True)
|
||||
@click.option(
|
||||
|
@ -340,37 +344,72 @@ 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(
|
||||
def arg_statefile(*args, **kwargs):
|
||||
return click.option(
|
||||
"--state-file", default=".cram.log", type=Path, callback=handle_path_vars
|
||||
)(*args, **kwargs)
|
||||
|
||||
|
||||
def arg_require(*args, **kwargs):
|
||||
return click.option(
|
||||
"--require",
|
||||
type=str,
|
||||
multiple=True,
|
||||
default=[
|
||||
"hosts.d/${HOSTNAME}",
|
||||
"profiles.d/default"
|
||||
],
|
||||
default=["hosts.d/${HOSTNAME}", "profiles.d/default"],
|
||||
callback=handle_requires,
|
||||
)
|
||||
@click.option(
|
||||
)(*args, **kwargs)
|
||||
|
||||
|
||||
def arg_require_root(*args, **kwargs):
|
||||
return click.option(
|
||||
"--require-root",
|
||||
type=(str, click.Choice(["profile", "package"])),
|
||||
multiple=True,
|
||||
callback=lambda ctx, param, pairs: groupby([(y, expandvars(x)) for x, y in pairs], first, second)
|
||||
)
|
||||
@click.option(
|
||||
default=[("profiles.d", "profile"), ("hosts.d", "profile"), ("packages.d", "package")],
|
||||
callback=lambda ctx, param, pairs: groupby(
|
||||
[(y, expandvars(x)) for x, y in pairs], first, second
|
||||
),
|
||||
)(*args, **kwargs)
|
||||
|
||||
|
||||
def arg_missing_require(*args, **kwargs):
|
||||
return click.option(
|
||||
"--missing-require",
|
||||
type=click.Choice(["error", "warn", "ignore"]),
|
||||
callback=lambda ctx, param, it: {"ignore": missing_require_ignore, "warn": missing_require_warn, "error": missing_require_fatal}[it]
|
||||
)
|
||||
callback=lambda ctx, param, it: {
|
||||
"ignore": missing_require_ignore,
|
||||
"warn": missing_require_warn,
|
||||
"error": missing_require_fatal,
|
||||
}[it],
|
||||
)(*args, **kwargs)
|
||||
|
||||
|
||||
def arg_optimize(*args, **kwargs):
|
||||
return click.option("--optimize/--no-optimize", default=True)(*args, **kwargs)
|
||||
|
||||
|
||||
@cli.command("apply")
|
||||
@click.option("--execute/--dry-run", default=False)
|
||||
@click.option("--force/--no-force", default=False)
|
||||
@click.option("--exec-idempotent/--exec-always", "exec_idempotent", default=True)
|
||||
@arg_statefile
|
||||
@arg_require
|
||||
@arg_require_root
|
||||
@arg_optimize
|
||||
@arg_missing_require
|
||||
@click.argument("confdir", type=Path, callback=handle_path_vars)
|
||||
@click.argument("destdir", type=Path, callback=handle_path_vars)
|
||||
def do_apply(
|
||||
confdir, destdir, state_file, execute, optimize, force, require, require_root, missing_require, exec_idempotent,
|
||||
confdir,
|
||||
destdir,
|
||||
state_file,
|
||||
execute,
|
||||
optimize,
|
||||
force,
|
||||
require,
|
||||
require_root,
|
||||
missing_require,
|
||||
exec_idempotent,
|
||||
):
|
||||
"""The entry point of cram."""
|
||||
|
||||
|
@ -418,22 +457,15 @@ def do_apply(
|
|||
|
||||
@cli.command("list")
|
||||
@click.option(
|
||||
"-1", "--oneline",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
help="Only list names of resources"
|
||||
)
|
||||
@click.option(
|
||||
"--require-root",
|
||||
type=(str, click.Choice(["profile", "package"])),
|
||||
multiple=True,
|
||||
callback=lambda ctx, param, pairs: groupby([(y, expandvars(x)) for x, y in pairs], first, second)
|
||||
"-1", "--oneline", is_flag=True, default=False, help="Only list names of resources"
|
||||
)
|
||||
@arg_require_root
|
||||
@click.argument("confdir", type=Path, callback=handle_path_vars)
|
||||
@click.argument("requirements", nargs=-1, callback=handle_requires)
|
||||
def do_list(confdir, requirements, require_root, oneline):
|
||||
"""List out packages, profiles, hosts and subpackages in the <confdir>."""
|
||||
root = confdir.resolve()
|
||||
log.debug(f"confdir: {root}, roots: {require_root}, requirements: {requirements}")
|
||||
|
||||
if not root.is_dir():
|
||||
log.fatal(f"{confdir} does not exist!")
|
||||
|
@ -467,7 +499,7 @@ def do_list(confdir, requirements, require_root, oneline):
|
|||
|
||||
|
||||
@cli.command("state")
|
||||
@click.option("--state-file", default=".cram.log", type=Path)
|
||||
@arg_statefile
|
||||
@click.argument("confdir", type=Path, callback=handle_path_vars)
|
||||
def do_state(confdir, state_file):
|
||||
"""List out the last `apply` state in the <confdir>/.cram.log or --state-file."""
|
||||
|
@ -486,13 +518,8 @@ def do_state(confdir, state_file):
|
|||
|
||||
|
||||
@cli.command("fmt")
|
||||
@arg_require_root
|
||||
@click.argument("confdir", type=Path, callback=handle_path_vars)
|
||||
@click.option(
|
||||
"--require-root",
|
||||
type=(str, click.Choice(["profile", "package"])),
|
||||
multiple=True,
|
||||
callback=lambda ctx, param, pairs: groupby([(y, expandvars(x)) for x, y in pairs], first, second)
|
||||
)
|
||||
@click.argument("requirement", type=str, callback=handle_path_vars)
|
||||
def do_fmt(confdir, requirement, require_root):
|
||||
"""Format the specified requirement to a canonical-ish representation."""
|
||||
|
@ -523,14 +550,15 @@ def do_init(confdir: Path, force: bool):
|
|||
"""Initialize an empty Cram repo."""
|
||||
|
||||
confdir.mkdir(parents=True, exist_ok=True)
|
||||
rootfile = confdir / 'cram.toml'
|
||||
rootfile = confdir / "cram.toml"
|
||||
if rootfile.exists() and not force:
|
||||
log.fatal("cram.toml exists! Provide -f/--force to overwrite")
|
||||
exit(1)
|
||||
_exit(1)
|
||||
|
||||
log.info("Writing initial cram.toml...")
|
||||
with open(rootfile, 'w') as fp:
|
||||
fp.write("""\
|
||||
with open(rootfile, "w") as fp:
|
||||
fp.write(
|
||||
"""\
|
||||
[cram]
|
||||
version = 1
|
||||
|
||||
|
@ -540,22 +568,27 @@ destdir = "${HOME}"
|
|||
state_file = "${PWD}/.cram.log"
|
||||
optimize = true
|
||||
exec_idempotent = true
|
||||
require_roots = {
|
||||
"packages.d" = "package",
|
||||
"profiles.d" = "profile"
|
||||
"hosts.d" = "profile",
|
||||
}
|
||||
# Where to load requirements from, and the requirement type
|
||||
# Types: package, profile
|
||||
require_root = [
|
||||
["${PWD}/packages.d", "package"],
|
||||
["${PWD}/profiles.d", "profile"],
|
||||
["${PWD}/hosts.d", "profile"],
|
||||
]
|
||||
# Choice([error, warn, ignore])
|
||||
missing_require = "error"
|
||||
|
||||
[cram.task.apply]
|
||||
missing_require = "ignore"
|
||||
require = [
|
||||
"hosts.d/${FQDN}",
|
||||
"hosts.d/${HOSTNAME}",
|
||||
"profiles.d/default",
|
||||
]
|
||||
""")
|
||||
"""
|
||||
)
|
||||
|
||||
for d in ['profiles.d', 'packages.d', 'hosts.d']:
|
||||
for d in ["profiles.d", "packages.d", "hosts.d"]:
|
||||
(confdir / d).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
|
|
|
@ -20,7 +20,8 @@ missing_require = "error"
|
|||
[cram.task.apply]
|
||||
missing_require = "ignore"
|
||||
require = [
|
||||
"hosts.d/${FQDN}",
|
||||
"hosts.d/${HOSTNAME}",
|
||||
# "hosts.d/${FQDN}",
|
||||
# "hosts.d/${HOSTNAME}",
|
||||
"hosts.d/test",
|
||||
"profiles.d/default",
|
||||
]
|
||||
|
|
Loading…
Reference in a new issue