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