From 23e6d1746191d48b89fdfce7907e51b89e79bc31 Mon Sep 17 00:00:00 2001 From: Matt Richards Date: Mon, 3 May 2021 13:46:20 +1000 Subject: [PATCH] add linking and creation os depedent flags to prevent appdata stuff on linux --- dotbot/dispatcher.py | 4 ++++ dotbot/plugins/create.py | 25 +++++++++++++++++++++---- dotbot/plugins/link.py | 20 ++++++++++++++------ dotbot/util/common.py | 34 +++++++++++++++++++++++++++++++++- 4 files changed, 72 insertions(+), 11 deletions(-) diff --git a/dotbot/dispatcher.py b/dotbot/dispatcher.py index 6696599..258bd01 100644 --- a/dotbot/dispatcher.py +++ b/dotbot/dispatcher.py @@ -1,5 +1,7 @@ import os from argparse import Namespace +from pprint import pprint + from .plugin import Plugin from .messenger import Messenger from .context import Context @@ -7,6 +9,7 @@ import traceback class Dispatcher(object): + """Actually processes the yaml data. Delegates to specialised classes""" def __init__(self, base_directory, only=None, skip=None, options=Namespace()): self._log = Messenger() self._setup_context(base_directory, options) @@ -21,6 +24,7 @@ class Dispatcher(object): self._context = Context(path, options) def dispatch(self, tasks): + pprint(tasks) success = True for task in tasks: for action in task.keys(): diff --git a/dotbot/plugins/create.py b/dotbot/plugins/create.py index 9250443..b4b41f1 100644 --- a/dotbot/plugins/create.py +++ b/dotbot/plugins/create.py @@ -1,5 +1,6 @@ import os import dotbot +from ..util.common import expand_path, on_permitted_os class Create(dotbot.Plugin): @@ -17,14 +18,30 @@ class Create(dotbot.Plugin): raise ValueError('Create cannot handle directive %s' % directive) return self._process_paths(data) - def _process_paths(self, paths): + def _process_paths(self, paths:dict): success = True defaults = self._context.defaults().get('create', {}) - for key in paths: - path = os.path.normpath(os.path.expandvars(os.path.expanduser(key))) + for path in paths: + if isinstance(path, dict): # path is key, with additional args + if len(path) > 1: + raise ValueError(f"Unexpected dict stuff: {path}") + path, path_settings = list(path.items())[0] + print(path, path_settings) + if isinstance(path_settings, dict) is False: + raise ValueError(f"Unexpected path setttings {path}: {path_settings}") + if "os-constraint" in path_settings: + os_constraint = path_settings["os-constraint"] + if on_permitted_os(os_constraint) is False: + self._log.lowinfo(f"Path skipped {expand_path(path)} ({os_constraint} " + f"only)") + continue # skip illegal os + else: + raise KeyError(f"Unexpected path creation setting {path_settings}, only" + f"supported key is 'os-constraint'") + mode = defaults.get('mode', 0o777) # same as the default for os.makedirs if isinstance(paths, dict): - options = paths[key] + options = paths[path] if options: mode = options.get('mode', mode) success &= self._create(path, mode) diff --git a/dotbot/plugins/link.py b/dotbot/plugins/link.py index 7f9ba9f..efe0114 100644 --- a/dotbot/plugins/link.py +++ b/dotbot/plugins/link.py @@ -1,13 +1,13 @@ import os import glob import shutil -from pathlib import Path import dotbot import dotbot.util -import subprocess import textwrap +from dotbot.util.common import on_permitted_os + class Link(dotbot.Plugin): ''' @@ -36,7 +36,9 @@ class Link(dotbot.Plugin): test = defaults.get("if", None) ignore_missing = defaults.get("ignore-missing", False) exclude_paths = defaults.get('exclude', []) - return relative, canonical_path, force, relink, create, use_glob, test, ignore_missing, exclude_paths + os_constraint = defaults.get("os-constraint", None) + return relative, canonical_path, force, relink, create, use_glob, test, ignore_missing, \ + exclude_paths, os_constraint def _process_links(self, links_dict): @@ -44,12 +46,13 @@ class Link(dotbot.Plugin): success = True (relative_default, canonical_path_default, force_flag_default, relink_flag_default, create_dir_flag_default, use_glob_default, shell_command_default, - ignore_missing_default, exclude_paths_default) = self._get_default_flags() + ignore_missing_default, exclude_paths_default, os_constraint_default) = \ + self._get_default_flags() for destination, source_dict in links_dict.items(): destination = os.path.expandvars(destination) - if isinstance(source_dict, dict): + if isinstance(source_dict, dict): # user supplied a "dict" of keys in addition to path path = self._default_source(destination, source_dict.get("path")) # extended config shell_command = source_dict.get("if", shell_command_default) @@ -63,8 +66,13 @@ class Link(dotbot.Plugin): use_glob = source_dict.get("glob", use_glob_default) ignore_missing = source_dict.get("ignore-missing", ignore_missing_default) exclude_paths = source_dict.get("exclude", exclude_paths_default) + os_constraint = source_dict.get("os-constraint", os_constraint_default) + if on_permitted_os(os_constraint, log=None) is False: + expanded_dest = os.path.normpath(os.path.expanduser(destination)) + self._log.lowinfo(f"Skipping link {expanded_dest} ({os_constraint} only)") + continue - else: + else: # user only supplied a path path = self._default_source(destination, source_dict) (shell_command, relative, canonical_path, force_flag, relink_flag, diff --git a/dotbot/util/common.py b/dotbot/util/common.py index 0eab422..1431a58 100644 --- a/dotbot/util/common.py +++ b/dotbot/util/common.py @@ -2,6 +2,8 @@ import os import subprocess import platform +from dotbot.messenger import Messenger + def shell_command(command, cwd=None, enable_stdin=False, enable_stdout=False, enable_stderr=False): with open(os.devnull, "w") as devnull_w, open(os.devnull, "r") as devnull_r: @@ -24,5 +26,35 @@ def shell_command(command, cwd=None, enable_stdin=False, enable_stdout=False, en # `bash -c "..."`. executable = None return subprocess.call( - command, shell=True, executable=executable, stdin=stdin, stdout=stdout, stderr=stderr, cwd=cwd + command, shell=True, executable=executable, stdin=stdin, stdout=stdout, stderr=stderr, + cwd=cwd ) + + +def expand_path(path, abs=False): + """Path expansion util to get the right slashes and variable expansions. + + Note expanduser is needed, ~ is not expanded by expandvars + """ + path = os.path.normpath(os.path.expandvars(os.path.expanduser(path))) + if abs: + return os.path.abspath(path) + else: + return path + + +def on_permitted_os(os_constraint, log: Messenger = None) -> bool: + current_os = platform.system().lower() + if isinstance(os_constraint, str) and os_constraint.lower() == "all": + os_constraint = None + if os_constraint is not None: + os_constraint = os_constraint.lower().replace( + "nt", "windows").replace("wsl", "linux") + if log is not None: + log.info(f"OS is {current_os}, got constraint {os_constraint}") + if os_constraint not in ["windows", "linux"]: + raise KeyError("Unknown/ unsupported operating system constraint " + f"supplied: {os_constraint}") + # Return false if we are on the constrained os + print("on, constraint", current_os, os_constraint) + return current_os == os_constraint