Support tweaking the shell in config
This commit is contained in:
parent
0dcb4118e2
commit
82d3b0c97c
4 changed files with 40 additions and 31 deletions
|
@ -99,30 +99,30 @@ def handle_requires(_ctx, _params, requires):
|
|||
return expand_requires(requires)
|
||||
|
||||
|
||||
def load(root: Path, name: str, clss):
|
||||
def load(config, root: Path, name: str, clss):
|
||||
for c in clss:
|
||||
i = c(root, name)
|
||||
i = c(config, root, name)
|
||||
if i.test():
|
||||
return i
|
||||
|
||||
|
||||
def load_package(root, name):
|
||||
def load_package(config, root, name):
|
||||
log.debug(f"Attempting to load package {name} from {root}")
|
||||
return load(root, name, [LightPackageV1, PackageV1, PackageV0])
|
||||
return load(config, root, name, [LightPackageV1, PackageV1, PackageV0])
|
||||
|
||||
|
||||
def load_profile(root, name):
|
||||
def load_profile(config, root, name):
|
||||
log.debug(f"Attempting to load profile {name} from {root}")
|
||||
return load(root, name, [ProfileV1, ProfileV0])
|
||||
return load(config, root, name, [ProfileV1, ProfileV0])
|
||||
|
||||
|
||||
def load_packages(root: Path, roots) -> dict:
|
||||
def load_packages(config, root: Path, roots) -> dict:
|
||||
"""Load the configured packages."""
|
||||
|
||||
def _load_package(p):
|
||||
name = str(p.relative_to(root)).replace(".toml", "")
|
||||
try:
|
||||
packages[name] = load_package(p, name)
|
||||
packages[name] = load_package(config, p, name)
|
||||
except Exception as e:
|
||||
log.exception(f"Unable to load {p} due to exception")
|
||||
|
||||
|
@ -146,7 +146,7 @@ def load_packages(root: Path, roots) -> dict:
|
|||
|
||||
# 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)
|
||||
packages[mp_name] = load_profile(config, mp_root, mp_name)
|
||||
|
||||
log.debug(f"Loaded {len(packages)} packages...")
|
||||
return packages
|
||||
|
@ -166,11 +166,11 @@ def missing_require_ignore(r, e):
|
|||
|
||||
|
||||
def build_fs(
|
||||
root: Path, roots, dest: Path, prelude: List[str], missing_require_handler
|
||||
config, 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)
|
||||
packages = load_packages(config, root, roots)
|
||||
|
||||
if not packages:
|
||||
log.warning("Loaded no packages!")
|
||||
|
@ -276,7 +276,7 @@ def configure(ctx, param, filename: Optional[Path]):
|
|||
if filename and filename.exists():
|
||||
ctx.default_map = {}
|
||||
log.debug(f"Loading config from {filename}")
|
||||
cfg = toml.load(filename)
|
||||
ctx.obj = cfg = toml.load(filename)
|
||||
assert cfg["cram"]["version"] >= 1
|
||||
|
||||
task_cfg = cfg.get("cram", {}).get("task", {})
|
||||
|
@ -349,6 +349,7 @@ Features
|
|||
- 0.2.0 initial release
|
||||
- 0.2.1 TOML root config
|
||||
- 0.2.3 Single-file packages (ex. packages.d/foo.toml)
|
||||
- 0.2.4 Support tweaking the shell
|
||||
|
||||
About
|
||||
{__copyright__}, {__author__}.
|
||||
|
@ -410,6 +411,7 @@ def arg_optimize(*args, **kwargs):
|
|||
|
||||
|
||||
@cli.command("apply")
|
||||
@click.pass_obj
|
||||
@click.option("--execute/--dry-run", default=False)
|
||||
@click.option("--force/--no-force", default=False)
|
||||
@click.option("--exec-idempotent/--exec-always", "exec_idempotent", default=True)
|
||||
|
@ -421,6 +423,7 @@ def arg_optimize(*args, **kwargs):
|
|||
@click.argument("confdir", type=Path, callback=handle_path_vars)
|
||||
@click.argument("destdir", type=Path, callback=handle_path_vars)
|
||||
def do_apply(
|
||||
obj,
|
||||
confdir,
|
||||
destdir,
|
||||
state_file,
|
||||
|
@ -453,7 +456,7 @@ def do_apply(
|
|||
# Force an empty state
|
||||
old_fs = Vfs([])
|
||||
|
||||
new_fs = build_fs(root, require_root, dest, require, missing_require)
|
||||
new_fs = build_fs(obj, root, require_root, dest, require, missing_require)
|
||||
log.debug(f"Built new state consisting of {len(new_fs._log)} steps")
|
||||
|
||||
# Middleware processing of the resulting filesystem(s)
|
||||
|
@ -478,13 +481,14 @@ def do_apply(
|
|||
|
||||
|
||||
@cli.command("list")
|
||||
@click.pass_obj
|
||||
@click.option(
|
||||
"-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):
|
||||
def do_list(obj, confdir, requirements, require_root, oneline):
|
||||
"""List out packages, profiles, hosts and subpackages in the <confdir>."""
|
||||
|
||||
root = confdir.resolve().absolute()
|
||||
|
@ -495,7 +499,7 @@ def do_list(confdir, requirements, require_root, oneline):
|
|||
log.fatal(f"{confdir} does not exist!")
|
||||
_exit(1)
|
||||
|
||||
packages = load_packages(root, require_root)
|
||||
packages = load_packages(obj, root, require_root)
|
||||
|
||||
if requirements:
|
||||
dest = Path("~/")
|
||||
|
@ -544,10 +548,11 @@ def do_state(confdir, state_file):
|
|||
|
||||
|
||||
@cli.command("fmt")
|
||||
@click.pass_obj
|
||||
@arg_require_root
|
||||
@click.argument("confdir", type=Path, callback=handle_path_vars)
|
||||
@click.argument("requirement", type=str, callback=handle_path_vars)
|
||||
def do_fmt(confdir: Path, requirement, require_root):
|
||||
def do_fmt(obj, confdir: Path, requirement, require_root):
|
||||
"""Format the specified requirement to a canonical-ish representation."""
|
||||
|
||||
root = confdir.resolve().absolute()
|
||||
|
@ -557,7 +562,7 @@ def do_fmt(confdir: Path, requirement, require_root):
|
|||
log.fatal(f"{confdir} does not exist!")
|
||||
_exit(1)
|
||||
|
||||
packages = load_packages(root, require_root)
|
||||
packages = load_packages(obj, root, require_root)
|
||||
pkg = packages[requirement]
|
||||
json = pkg.json()
|
||||
|
||||
|
@ -606,6 +611,7 @@ require_root = [
|
|||
missing_require = "error"
|
||||
|
||||
[cram.task.apply]
|
||||
shell = ["/bin/sh"]
|
||||
missing_require = "warn"
|
||||
require = [
|
||||
"hosts.d/${FQDN}",
|
||||
|
|
|
@ -8,17 +8,19 @@ from typing import List, Optional
|
|||
|
||||
from vfs import Vfs
|
||||
|
||||
# FIXME: This should be a config somewhere
|
||||
SHELL = "/bin/sh"
|
||||
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], /,
|
||||
def sh(global_config,
|
||||
cmd: List[str], /,
|
||||
env: Optional[dict] = None):
|
||||
|
||||
shell = (global_config or {}).get("cram", {}).get("task", {}).get("apply", {}).get("shell", SHELL)
|
||||
|
||||
prefix = []
|
||||
if env:
|
||||
prefix.append("/usr/bin/env")
|
||||
|
@ -26,7 +28,7 @@ def sh(cmd: List[str], /,
|
|||
v = sh_quote(str(v))
|
||||
prefix.append(f"{k}={v}")
|
||||
|
||||
return tuple(prefix + [SHELL, *cmd])
|
||||
return tuple(prefix + [*shell, *cmd])
|
||||
|
||||
|
||||
def stow(fs: Vfs, src_dir: Path, dest_dir: Path, skip=[]):
|
||||
|
@ -56,7 +58,8 @@ def stow(fs: Vfs, src_dir: Path, dest_dir: Path, skip=[]):
|
|||
|
||||
|
||||
class Package(object):
|
||||
def __init__(self, root: Path, name: str):
|
||||
def __init__(self, global_config, root: Path, name: str):
|
||||
self.global_config = global_config
|
||||
self.root = root
|
||||
self.name = name
|
||||
|
||||
|
|
|
@ -36,21 +36,21 @@ class PackageV0(Package):
|
|||
"""Install this package."""
|
||||
buildf = self.root / "BUILD"
|
||||
if buildf.exists():
|
||||
fs.exec(self.root, sh([str(buildf)]))
|
||||
fs.exec(self.root, sh(self.global_config, [str(buildf)]))
|
||||
|
||||
pref = self.root / "PRE_INSTALL"
|
||||
if pref.exists():
|
||||
fs.exec(self.root, sh([str(pref)]))
|
||||
fs.exec(self.root, sh(self.global_config, [str(pref)]))
|
||||
|
||||
installf = self.root / "INSTALL"
|
||||
if installf.exists():
|
||||
fs.exec(self.root, sh([str(installf)]))
|
||||
fs.exec(self.root, sh(self.global_config, [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)]))
|
||||
fs.exec(self.root, sh(self.global_config, [str(postf)]))
|
||||
|
||||
def _read(self, p: Path):
|
||||
if p.exists():
|
||||
|
@ -94,16 +94,16 @@ class ProfileV0(PackageV0):
|
|||
|
||||
buildf = self.root / "BUILD"
|
||||
if buildf.exists():
|
||||
fs.exec(self.root, sh([str(buildf)]))
|
||||
fs.exec(self.root, sh(self.global_config, [str(buildf)]))
|
||||
|
||||
pref = self.root / "PRE_INSTALL"
|
||||
if pref.exists():
|
||||
fs.exec(self.root, sh([str(pref)]))
|
||||
fs.exec(self.root, sh(self.global_config, [str(pref)]))
|
||||
|
||||
installf = self.root / "INSTALL"
|
||||
if installf.exists():
|
||||
fs.exec(self.root, sh([str(installf)]))
|
||||
fs.exec(self.root, sh(self.global_config, [str(installf)]))
|
||||
|
||||
postf = self.root / "POST_INSTALL"
|
||||
if postf.exists():
|
||||
fs.exec(self.root, sh([str(postf)]))
|
||||
fs.exec(self.root, sh(self.global_config, [str(postf)]))
|
||||
|
|
|
@ -79,7 +79,7 @@ class PackageV1(Package):
|
|||
f = tempf(f"{sum}.sh")
|
||||
with open(f, "w") as fp:
|
||||
fp.write(content)
|
||||
fs.exec(cwd, sh([f]))
|
||||
fs.exec(cwd, sh(self.global_config, [f]))
|
||||
|
||||
def do_build(self, fs: Vfs, dest: Path):
|
||||
self.do_sh_or_script(self.config().get("package", {}).get("build"), fs, dest)
|
||||
|
|
Loading…
Reference in a new issue