diff options
author | André Erdmann <dywi@mailerd.de> | 2013-09-12 18:33:30 +0200 |
---|---|---|
committer | André Erdmann <dywi@mailerd.de> | 2013-09-12 18:33:30 +0200 |
commit | 1eb711c0e3f609ef0d0845921f9086dc39da2f5c (patch) | |
tree | b23e1b6c97c96fd0e89a0379cbebf54040a1b81c /roverlay/setupscript | |
parent | roverlay/util/dictwalk: dictmerge() (diff) | |
download | R_overlay-1eb711c0e3f609ef0d0845921f9086dc39da2f5c.tar.gz R_overlay-1eb711c0e3f609ef0d0845921f9086dc39da2f5c.tar.bz2 R_overlay-1eb711c0e3f609ef0d0845921f9086dc39da2f5c.zip |
roverlay/setupscript: "hooks" command
Added a "hooks" command for managing hooks. Currently, it is able to print
information about hooks enabled/available. The underlying code (HookScript*,
UserHookScript* classes) supports more, e.g. linking hooks.
Also added a "--overwrite-hooks [<when>]" option to roverlay-setup, which can be
used to control which hooks will be overwritten, e.g. when running the init
command.
Diffstat (limited to 'roverlay/setupscript')
-rw-r--r-- | roverlay/setupscript/hookenv.py | 641 | ||||
-rw-r--r-- | roverlay/setupscript/initenv.py | 3 | ||||
-rw-r--r-- | roverlay/setupscript/runtime.py | 38 |
3 files changed, 595 insertions, 87 deletions
diff --git a/roverlay/setupscript/hookenv.py b/roverlay/setupscript/hookenv.py index e07b4e7..ebfe0d1 100644 --- a/roverlay/setupscript/hookenv.py +++ b/roverlay/setupscript/hookenv.py @@ -6,40 +6,130 @@ import collections import errno +import fnmatch +import itertools import os +import roverlay.fsutil import roverlay.static.hookinfo import roverlay.util.counter +import roverlay.util.dictwalk +import roverlay.util.objects +import roverlay.setupscript.baseenv + + +class HookOverwriteControl ( object ): + OV_NONE = 0 + OV_SYM_DEAD = 2**0 + OV_SYM_EXIST = 2**1 + OV_SYM = OV_SYM_DEAD|OV_SYM_EXIST + OV_FILE = 2**2 + OV_ALL = ( 2**3 ) - 1 + + OV_KEYWORDS = { + 'none' : OV_NONE, + 'dead' : OV_SYM_DEAD, + 'links' : OV_SYM, + 'all' : OV_ALL, + } + + @classmethod + def from_str ( cls, vstr ): + return cls ( cls.OV_KEYWORDS[vstr] ) + # --- end of from_str (...) --- + + def __init__ ( self, value ): + super ( HookOverwriteControl, self ).__init__() + assert isinstance ( value, int ) and value >= 0 + self.value = value + # --- end of __init__ (...) --- + def can_overwrite ( self, mask=None ): + if mask is None: + return self.value == self.OV_NONE + else: + return self.value & mask + # --- end of can_overwrite (...) --- -class HookScript ( object ): + def overwrite_dead_symlinks ( self ): + return self.value & self.OV_SYM_DEAD + + def overwrite_symlinks ( self ): + return self.value & self.OV_SYM + + def overwrite_all ( self ): + return self.value == self.OV_ALL + + def get_str ( self ): + value = self.value + def gen_words(): + if value == self.OV_NONE: + yield "none" + else: + if value & self.OV_SYM_EXIST: + if value & self.OV_SYM_DEAD: + yield "symlinks" + else: + yield "symlinks to existing files" + elif value & self.OV_SYM_DEAD: + yield "broken symlinks" + + if value & self.OV_FILE: + yield "files" + # --- end of gen_words (...) --- + + return ', '.join ( gen_words() ) + " (0x{:x})".format ( value ) + # --- end of get_str (...) --- + + __str__ = get_str + +# --- end of HookOverwriteControl --- + + +class HookScriptBase ( roverlay.util.objects.Referenceable ): + + CACHE_REF = True + + def __init__ ( self, + fspath, filename=None, priority=None, is_hidden=False + ): + """HookScriptBase constructor. + + arguments: + * fspath -- absolute path to the hook script + * filename -- name of the hook script + Defaults to os.path.basename(fspath). + * priority -- priority of the hook script. Defaults to auto-detect. + * is_hidden -- whether the script is "hidden" or not. Defaults to False. + """ + super ( HookScriptBase, self ).__init__() - def __init__ ( self, fspath, filename=None ): - super ( HookScript, self ).__init__() fname = ( filename if filename is not None else os.path.basename ( fspath ) ) - self.fspath = fspath - self.name = os.path.splitext ( fname )[0] or fname - static_entry = roverlay.static.hookinfo.get ( self.name, None ) - - if static_entry is not None: - self.default_events = static_entry[0] - self.priority = static_entry[1] - self.is_hidden = static_entry[2] + self.filename = fname + + if priority is True: + prio_str, dash, remainder = fname.partition ( '-' ) + if prio_str and dash and remainder: + try: + prio = int ( prio_str, 10 ) + except ValueError: + self.priority = None + else: + self.priority = prio + fname = remainder + else: + self.priority = None else: - self.default_events = False - self.priority = None - self.is_hidden = False - # --- end of __init__ (...) --- + self.priority = priority - def is_visible ( self ): - return not self.is_hidden and ( - self.priority is None or self.priority >= 0 - ) - # --- end of is_visible (...) --- + self.name = os.path.splitext ( fname )[0] + self.fspath = fspath + self.is_hidden = is_hidden + # --- end of __init__ (...) --- def __str__ ( self ): yesno = lambda k: 'y' if k else 'n' @@ -54,6 +144,118 @@ class HookScript ( object ): ) # --- end of __str__ (...) --- + def get_static_info ( self ): + return roverlay.static.hookinfo.get ( self.name, None ) + # --- end of get_static_info (...) --- + + def is_visible ( self ): + return not self.is_hidden and ( + self.priority is None or self.priority >= 0 + ) + # --- end of is_visible (...) --- + + @roverlay.util.objects.abstractmethod + def get_hookscript ( self ): + pass + # --- end of get_hookscript (...) --- + + @roverlay.util.objects.abstractmethod + def get_hookscript_path ( self ): + pass + # --- end of get_hookscript_path (...) --- + + @roverlay.util.objects.abstractmethod + def get_dest_name ( self, *args, **kwargs ): + pass + # --- end of get_dest_name (...) --- + +# --- end of HookScriptBase --- + + +class UserHookScript ( HookScriptBase ): + + def __init__ ( self, fspath, filename=None, event=None ): + super ( UserHookScript, self ).__init__ ( + fspath, filename=filename, priority=True + ) + + self.hook_script_fspath = os.path.realpath ( self.fspath ) + self.hook_script_ref = ( + False if not os.path.islink ( self.fspath ) else None + ) + + self.event = event + # --- end of __init__ (...) --- + + def set_hookscript ( self, script_obj, strict=True ): + if script_obj and self.hook_script_ref is False: + raise Exception ( + "user hook script {} is not a link!".format ( self.fspath ) + ) + elif script_obj is None or script_obj is False: + self.hook_script_ref = False + + else: + self.hook_script_ref = script_obj.get_ref() + script_obj.add_user_script ( self ) + # --- end of set_hookscript (...) --- + + def has_hookscript ( self ): + return self.hook_script_ref is not None + # --- end of has_hookscript (...) --- + + def get_hookscript ( self ): + ref = self.hook_script_ref + if ref is False: + return None + elif ref is None: + raise roverlay.util.objects.ObjectDisappeared() + else: + return ref.deref_safe() + # --- end of get_hookscript (...) --- + + def get_hookscript_path ( self ): + return self.hook_script_fspath + # --- end of get_hookscript_path (...) --- + + def get_dest_name ( self ): + return self.filename + # --- end of get_dest_name (...) --- + +# --- end of UserHookScript --- + + +class HookScript ( HookScriptBase ): + + def __init__ ( self, fspath, filename=None ): + super ( HookScript, self ).__init__ ( fspath, filename=filename ) + + static_entry = self.get_static_info() + if static_entry is not None: + self.default_events = static_entry[0] + self.priority = static_entry[1] + self.is_hidden = static_entry[2] + else: + self.default_events = False + + self.user_script_refs = set() + # --- end of __init__ (...) --- + + def add_user_script ( self, user_script ): + self.user_script_refs.add ( user_script.get_ref() ) + # --- end of add_user_script (...) --- + + def iter_user_scripts ( self, ignore_missing=True ): + if ignore_missing: + for ref in self.user_script_refs: + obj = ref.deref_unsafe() + if obj is not None: + yield obj + else: + for ref in self.user_script_refs: + yield obj.deref_safe() + # --- end of iter_user_scripts (...) --- + def set_priority_from_generator ( self, number_gen, only_if_unset=True ): if self.priority is None: self.priority = next ( number_gen ) @@ -78,53 +280,47 @@ class HookScript ( object ): ) # --- end of get_dest_name (...) --- + def get_hookscript ( self ): + return self + # --- end of get_hookscript (...) --- + + def get_hookscript_path ( self ): + return self.fspath + # --- end of get_hookscript_path (...) --- # --- end of HookScript --- -class HookScriptDir ( object ): +class HookScriptDirBase ( roverlay.util.objects.Referenceable ): + + HOOK_SCRIPT_CLS = None def __init__ ( self, root ): - super ( HookScriptDir, self ).__init__() + super ( HookScriptDirBase, self ).__init__() - self.root = root - self._scripts = collections.OrderedDict() + self.root = root + self.scripts = collections.OrderedDict() + self.writable = None # --- end of __init__ (...) --- def __bool__ ( self ): - return bool ( self._scripts ) + return bool ( self.scripts ) # --- end of __bool__ (...) --- + def get_fspath ( self, relpath=None ): + if relpath: + return self.root + os.sep + str ( relpath ) + else: + return self.root + # --- end of get_fspath (...) --- + def get_script ( self, name ): - script = self._scripts [name] + script = self.scripts [name] return script if script.is_visible() else None # --- end of get_scripts (...) --- - def iter_default_scripts ( self, unpack=False ): - if unpack: - for script in self._scripts.values(): - if script.default_events: - for event in script.default_events: - yield ( event, script ) - else: - for script in self._scripts.values(): - if script.default_events: - yield script - # --- end of iter_default_scripts (...) --- - - def get_default_scripts ( self ): - scripts = dict() - for event, script in self.iter_default_scripts ( unpack=True ): - if event not in scripts: - scripts [event] = [ script ] - else: - scripts [event].append ( script ) - - return scripts - # --- end of get_default_scripts (...) --- - def iter_scripts ( self ): - for script in self._scripts.values(): + for script in self.scripts.values(): if script.is_visible(): yield script # --- end of iter_scripts (...) --- @@ -136,80 +332,354 @@ class HookScriptDir ( object ): except OSError as oserr: if oserr.errno != errno.ENOENT: raise - else: + HOOK_CLS = self.HOOK_SCRIPT_CLS for fname in filenames: fspath = root + os.sep + fname if os.path.isfile ( fspath ): - script_obj = HookScript ( fspath, fname ) - self._scripts [script_obj.name] = script_obj + script_obj = HOOK_CLS ( fspath, filename=fname ) + self.scripts [script_obj.name] = script_obj # --- end of scan (...) --- +# --- end of HookScriptDirBase --- + + +class NestedHookScriptDirBase ( HookScriptDirBase ): + SUBDIR_CLS = collections.OrderedDict + DIRNAMES_IGNORE = frozenset({ '.*', }) + FILENAMES_IGNORE = frozenset({ '.*', }) + + def dirname_filter ( self, dirname, _fnmatch=fnmatch.fnmatch ): + for pattern in self.DIRNAMES_IGNORE: + if _fnmatch ( dirname, pattern ): + return False + return True + # --- end of dirname_filter (...) --- + + def get_script ( self, name ): + return [ + script for script in self.iter_scripts() if script.name == name + ] + # --- end of get_script (...) --- + + def filename_filter ( self, filename, _fnmatch=fnmatch.fnmatch ): + for pattern in self.FILENAMES_IGNORE: + if _fnmatch ( filename, pattern ): + return False + return True + # --- end of filename_filter (...) --- + + def create_hookscript ( self, fspath, filename, root ): + return self.HOOK_SCRIPT_CLS ( fspath, filename=filename ) + # --- end of create_hookscript (...) --- + + def scan ( self, prune_empty=True ): + SUBDIR_CLS = self.SUBDIR_CLS + + self.scripts = roverlay.fsutil.get_fs_dict ( + self.root, create_item=self.create_hookscript, + dict_cls=self.SUBDIR_CLS, dirname_filter=self.dirname_filter, + filename_filter=self.filename_filter, include_root=False, + prune_empty=True, + ) + + for event, hook in self.iter_scripts(): + if hook.event is None: + hook.event = event + # --- end of scan (...) --- + + def iter_scripts ( self ): + # roverlay uses per-event subdirs containing hook files + SUBDIR_CLS = self.SUBDIR_CLS + for event, subdir in self.scripts.items(): + if isinstance ( subdir, SUBDIR_CLS ): + for hook in subdir.values(): + if isinstance ( hook, HookScriptBase ) and hook.is_visible(): + #if not isinstance ( hook, SUBDIR_CLS ): + yield ( event, hook ) + # --- end of iter_scripts (...) --- + +# --- end of NestedHookScriptDirBase --- + + +class UserHookScriptDir ( NestedHookScriptDirBase ): + + HOOK_SCRIPT_CLS = UserHookScript + + def create_hookdir_refs ( self, + hook_dir, overwrite=False, compare_fspath=True + ): + for event, user_script in self.iter_scripts(): + if overwrite or not user_script.has_hookscript(): + try: + hook = hook_dir.get_script ( user_script.name ) + except KeyError: + pass + else: + if hook is not None and ( + not compare_fspath or user_script.fspath == hook.fspath + ): + user_script.set_hookscript ( hook ) + # --- end of create_hookdir_refs (...) --- + + def make_hookdir_refs ( self, hook_dir, overwrite=False ): + # try exact fs path matches first, then use name-based ones + self.create_hookdir_refs ( hook_dir, compare_fspath=True ) + self.create_hookdir_refs ( hook_dir, compare_fspath=False ) + # --- end of make_hookdir_refs (...) --- + + def register_hook_link_unsafe ( self, event_name, hook, link, link_name ): + subdir = self.scripts.get ( event_name, None ) + if subdir is None or link_name not in subdir: + if subdir is None: + subdir = self.SUBDIR_CLS() + self.scripts [event_name] = subdir + # -- end if + + entry = self.HOOKDIR_CLS ( link, filename=link_name ) + subdir [link_name] = entry + else: + entry = subdir [link_name] + # -- end if + + entry.set_hookscript ( hook, strict=False ) + if entry.event is None: + entry.event = event_name + elif entry.event != event_name: + raise AssertionError ( "entry.event != event_name" ) + return True + # --- end of register_hook_link_unsafe (...) --- + + def iter_nonlinked ( self ): + for event, script in self.iter_scripts(): + if not script.has_hookscript(): + yield script + # --- end of iter_nonlinked (...) --- + +# --- end of UserHookScriptDir --- + + +class HookScriptDir ( HookScriptDirBase ): + + HOOK_SCRIPT_CLS = HookScript + + def iter_linked ( self ): + # 2-tuple ( hook_script, list ( linked_user_scripts ) ) + for script in self.iter_scripts(): + yield ( script, list ( script.iter_user_scripts() ) ) + # --- end of iter_linked (...) --- + + def iter_default_scripts ( self, unpack=False ): + if unpack: + for script in self.iter_scripts(): + if script.default_events: + for event in script.default_events: + yield ( event, script ) + else: + for script in self.iter_scripts(): + if script.default_events: + yield script + # --- end of iter_default_scripts (...) --- + + def get_default_scripts ( self ): + return roverlay.util.dictwalk.dictmerge ( + self.iter_default_scripts ( unpack=True ), + get_value=lambda kv:kv[1] + ) + # --- end of get_default_scripts (...) --- + # --- end of HookScriptDir --- + class SetupHookEnvironment ( roverlay.setupscript.baseenv.SetupSubEnvironment ): NEEDS_CONFIG_TREE = True - def setup ( self ): - additions_dir = self.config.get ( 'OVERLAY.additions_dir', None ) - if additions_dir: - self.user_hook_root = os.path.join ( additions_dir, 'hooks' ) - self.writable = self.setup_env.private_file.check_writable ( - self.user_hook_root + os.sep + '.keep' + def format_hook_info_lines ( self, + info, sort_info=True, append_newline=False + ): + max_name_len = min ( 30, max ( len(x[0]) for x in info ) ) + + event_names = set() + for name, ev_prio in info: + event_names.update ( item[0] for item in ev_prio ) + + event_words = [ + ( ev, (4+len(ev)) * ' ' ) for ev in sorted ( event_names ) + ] + + if sort_info: + my_info = sorted ( + info, + key=lambda k: ( not k[1], k[0] ) ) else: - self.user_hook_root = None - self.writable = None + my_info = info + + for name, event_prio_list in my_info: + events = dict ( event_prio_list ) + get_prio = lambda p: ( "UU" if p is None else p ) + + yield "{name:>{nlen}} | {ev}".format ( + name=name, nlen=max_name_len, + ev=' '.join ( + ( + "{name}({prio:0>2})".format ( + name=ev, prio=get_prio ( events[ev] ) + ) if ev in events else replacement + for ev, replacement in event_words + ) + ) + ).rstrip() + # -- end for + + if append_newline: + yield "" + # --- end of format_hook_info_lines (...) --- + + def get_hook_root_info ( self, nonlinked_only=False ): + if nonlinked_only: + return [ + ( script.name, [] ) + for script, user_scripts in self.hook_root.iter_linked() + if not user_scripts + ] + else: + return [ + ( + script.name, + [ ( s.event or "undef", s.priority ) for s in user_scripts ] + ) + for script, user_scripts in self.hook_root.iter_linked() + ] + # --- end of get_hook_root_info (...) --- + + def get_user_hook_info ( self ): + return [ + ( s.name, [ ( s.event or "undef", s.priority ) ] ) + for event, s in self.user_hooks.iter_scripts() + ] + # --- end of get_user_hook_info (...) --- + + def gen_hook_info_lines ( self, append_newline=True ): + info = ( + self.get_user_hook_info() + + self.get_hook_root_info ( nonlinked_only=True ) + ) + for line in self.format_hook_info_lines ( + info, append_newline=append_newline + ): + yield line + # --- end of gen_hook_info_lines (...) --- + + def setup ( self ): + self.hook_overwrite_control = self.setup_env.hook_overwrite + + additions_dir = self.config.get ( 'OVERLAY.additions_dir', None ) self.hook_root = HookScriptDir ( os.path.join ( self.setup_env.data_root, 'hooks' ) ) self.hook_root.scan() self._prio_gen = roverlay.util.counter.UnsafeCounter ( 30 ) + + + if additions_dir: + self.user_hooks = UserHookScriptDir ( + os.path.join ( additions_dir, 'hooks' ) + ) + self.user_hooks.writable = ( + self.setup_env.private_file.check_writable ( + self.user_hooks.get_fspath ( '.keep' ) + ) + ) + self.user_hooks.scan() + if self.hook_root: + self.user_hooks.make_hookdir_refs ( self.hook_root ) + else: + self.user_hooks = None # --- end of setup (...) --- - def _link_hook ( self, source, link ): + def check_link_allowed ( self, source, link, link_name ): if os.path.lexists ( link ): - linkdest = os.path.realpath ( link ) + allow_overwrite = False + + skip_message = ( + 'Skipping activation of hook {!r} - '.format ( link_name ) + ) + ov_message = 'Overwriting hook {!r} - '.format ( link_name ) + + + if os.path.islink ( link ): + linkdest = os.path.realpath ( link ) + + if linkdest == source or linkdest == os.path.realpath ( source ): + self.info ( skip_message + "already set up.\n" ) + allow_overwrite = None - message = 'Skipping activation of hook {!r} - '.format ( link ) + elif os.path.exists ( link ): + if self.hook_overwrite_control.overwrite_symlinks(): + self.info ( ov_message + "symlink.\n" ) + allow_overwrite = True + else: + self.error ( skip_message + "symlink.\n" ) - if linkdest == source or linkdest == os.path.realpath ( source ): - self.info ( message + "already set up.\n" ) - return True + elif self.hook_overwrite_control.overwrite_dead_symlinks(): + self.info ( ov_message + "broken symlink.\n" ) + allow_overwrite = True + + else: + self.error ( skip_message + "broken symlink.\n" ) + + elif os.path.isfile ( link ): + if self.hook_overwrite_control.overwrite_all(): + self.error ( ov_message + "file.\n" ) + allow_overwrite = True + else: + self.error ( skip_message + "file.\n" ) - elif link != linkdest: - # symlink or link was relative - self.error ( message + "is a link to another file.\n" ) else: - self.error ( message + "exists, but is not a link.\n" ) + self.error ( skip_message + "not a file!\n" ) - return None + # -- end if + + return allow_overwrite else: - return self.setup_env.private_file.symlink ( source, link ) - # --- end of _link_hook (...) --- + return True + # --- end of check_link_allowed (...) --- def link_hooks_v ( self, event_name, hooks ): success = False - if self.writable and self.user_hook_root: - destdir = self.user_hook_root + os.sep + event_name + if self.user_hooks is not None and self.user_hooks.writable: + + destdir = self.user_hooks.get_fspath ( event_name ) self.setup_env.private_dir.dodir ( destdir ) + # note that there is a race condition when users manipulate their + # hook dir while running roverlay-setup to_link = [] for script in hooks: script.set_priority_from_generator ( self._prio_gen ) - to_link.append ( - ( script.fspath, destdir + os.sep + script.get_dest_name() ) - ) - - success = True - for source, link_name in to_link: - if self._link_hook ( source, link_name ) is False: + dest_name = script.get_dest_name() + link = destdir + os.sep + dest_name + + if self.check_link_allowed ( script.fspath, link, dest_name ): + to_link.append ( ( script, dest_name, link ) ) + # -- end for + + register_link = self.user_hooks.register_hook_link_unsafe + symlink = self.setup_env.private_file.symlink + success = True + + for script, dest_name, link in to_link: + have_link = symlink ( script.fspath, link ) + if have_link: + register_link ( event_name, script, link, dest_name ) + elif have_link is not None: success = False # -- end if @@ -230,4 +700,9 @@ class SetupHookEnvironment ( return success # --- end of enable_defaults (...) --- + def run ( self ): + # TODO + self.info ( '\n'.join ( self.gen_hook_info_lines() ) ) + # --- end of run (...) --- + # --- end of SetupHookEnvironment --- diff --git a/roverlay/setupscript/initenv.py b/roverlay/setupscript/initenv.py index febe39e..5e511f8 100644 --- a/roverlay/setupscript/initenv.py +++ b/roverlay/setupscript/initenv.py @@ -101,6 +101,9 @@ class SetupInitEnvironment ( ) yield ( "enable default hooks", get_option ( 'want_default_hooks' ) ) + + yield ( "overwrite hooks", self.setup_env.hook_overwrite.get_str() ) + yield ( "additional config variables", get_option ( 'config_vars' ) ) # --- end of gen_pretend_options (...) --- diff --git a/roverlay/setupscript/runtime.py b/roverlay/setupscript/runtime.py index e094951..5a55515 100644 --- a/roverlay/setupscript/runtime.py +++ b/roverlay/setupscript/runtime.py @@ -50,14 +50,18 @@ def arg_stdout_or_fs ( value ): class SetupArgParser ( roverlay.argparser.RoverlayArgumentParser ): MULTIPLE_COMMANDS = False COMMAND_DESCRIPTION = { - 'init': 'initialize roverlay\'s config and filesystem layout', - 'mkconfig': 'generate a config file', + 'init' : 'initialize roverlay\'s config and filesystem layout', + 'mkconfig' : 'generate a config file', + 'hooks' : 'manage hook files', } DEFAULT_COMMAND = "init" - COMMANDS_WITH_PRETEND = frozenset ({ 'init', }) + COMMANDS_WITH_PRETEND = frozenset ({ 'init', 'hooks', }) - SETUP_TARGETS = ( 'version', 'actions', 'setup', 'config', 'init', ) + SETUP_TARGETS = ( + 'usage', 'version', + 'actions', 'setup', 'config', 'init', 'hooks', + ) PARSE_TARGETS = ( 'actions', 'setup', 'config', 'init', ) @@ -214,6 +218,25 @@ class SetupArgParser ( roverlay.argparser.RoverlayArgumentParser ): ) # --- end of parse_init (...) --- + def setup_hooks ( self ): + arg = self.add_argument_group ( + "hooks", title="options for managing hooks" + ) + + arg ( + "--overwrite-hooks", dest='hook_overwrite', + default="dead", const="links", nargs="?", metavar="<when>", + choices=[ 'none', 'dead', 'links', 'all', ], + flags=self.ARG_WITH_DEFAULT, + help=( + 'control hook overwrite behavior ' + '(%(choices)s; \'%(const)s\' if specified without an arg)' + ), + ) + + return arg + # --- end of setup_hooks (...) --- + # --- end of SetupArgParser --- @@ -366,6 +389,11 @@ class SetupEnvironment ( roverlay.runtime.IndependentRuntimeEnvironment ): self.data_root = expanduser ( options ['data_root'] ) self.conf_root = expanduser ( options ['conf_root'] ) self.user_conf_root = expanduser ( options ['private_conf_root'] ) + self.hook_overwrite = ( + roverlay.setupscript.hookenv.HookOverwriteControl.from_str ( + options ['hook_overwrite'] + ) + ) self.fs_ops_virtual = { @@ -470,6 +498,8 @@ class SetupEnvironment ( roverlay.runtime.IndependentRuntimeEnvironment ): def default_main ( self ): if self.wants_command ( "mkconfig" ): self.write_config_file ( self.options ['output'] ) + elif self.wants_command ( "hooks" ): + self.get_hook_env().run() elif self.wants_command ( "init" ): self.get_init_env().run() # --- end of default_main (...) --- |