aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndré Erdmann <dywi@mailerd.de>2013-09-12 18:33:30 +0200
committerAndré Erdmann <dywi@mailerd.de>2013-09-12 18:33:30 +0200
commit1eb711c0e3f609ef0d0845921f9086dc39da2f5c (patch)
treeb23e1b6c97c96fd0e89a0379cbebf54040a1b81c /roverlay/setupscript
parentroverlay/util/dictwalk: dictmerge() (diff)
downloadR_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.py641
-rw-r--r--roverlay/setupscript/initenv.py3
-rw-r--r--roverlay/setupscript/runtime.py38
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 (...) ---