* [gentoo-commits] proj/R_overlay:master commit in: roverlay/setupscript/, roverlay/
@ 2013-09-11 11:14 André Erdmann
0 siblings, 0 replies; 4+ messages in thread
From: André Erdmann @ 2013-09-11 11:14 UTC (permalink / raw
To: gentoo-commits
commit: 03126a24a417833c13b4f27e3e7996d628aef988
Author: André Erdmann <dywi <AT> mailerd <DOT> de>
AuthorDate: Wed Sep 11 11:13:32 2013 +0000
Commit: André Erdmann <dywi <AT> mailerd <DOT> de>
CommitDate: Wed Sep 11 11:13:32 2013 +0000
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=03126a24
split roverlay/setupscript.py into several modules
---
roverlay/runtime.py | 13 +
roverlay/setupscript.py | 1069 --------------------------------------
roverlay/setupscript/__init__.py | 2 +
roverlay/setupscript/baseenv.py | 72 +++
roverlay/setupscript/hookenv.py | 230 ++++++++
roverlay/setupscript/initenv.py | 308 +++++++++++
roverlay/setupscript/runtime.py | 474 +++++++++++++++++
7 files changed, 1099 insertions(+), 1069 deletions(-)
diff --git a/roverlay/runtime.py b/roverlay/runtime.py
index 5ef540d..68580f0 100644
--- a/roverlay/runtime.py
+++ b/roverlay/runtime.py
@@ -272,6 +272,14 @@ class IndependentRuntimeEnvironment ( MinimalRuntimeEnvironment ):
LOG_FORMAT = None
LOG_LEVEL = None
+ @classmethod
+ def run_default_main ( cls, *args, **kwargs ):
+ instance = cls ( *args, **kwargs )
+ instance.setup()
+ instance.default_main()
+ return instance
+ # --- end of run_default_main (...) ---
+
def __init__ ( self, installed=True, stdout=None, stderr=None ):
super ( IndependentRuntimeEnvironment, self ).__init__()
@@ -295,6 +303,11 @@ class IndependentRuntimeEnvironment ( MinimalRuntimeEnvironment ):
self.INSTALLINFO = None
# --- end of __init__ (...) ---
+ @roverlay.util.objects.abstractmethod
+ def default_main ( self ):
+ pass
+ # --- end of default_main (...) ---
+
def create_new_config ( self, config_str=None, apply_defaults=True ):
ctree = roverlay.config.tree.ConfigTree ( register_static=False )
diff --git a/roverlay/setupscript.py b/roverlay/setupscript.py
deleted file mode 100644
index 0e59015..0000000
--- a/roverlay/setupscript.py
+++ /dev/null
@@ -1,1069 +0,0 @@
-# R overlay -- setup script
-# -*- coding: utf-8 -*-
-
-from __future__ import print_function
-
-import argparse
-import collections
-import errno
-import logging
-import os
-import shutil
-import stat
-import sys
-import textwrap
-
-import roverlay.argutil
-import roverlay.argparser
-import roverlay.fsutil
-import roverlay.runtime
-
-import roverlay.config.defconfig
-import roverlay.config.entrymap
-import roverlay.config.entryutil
-
-import roverlay.static.hookinfo
-
-import roverlay.util.counter
-
-if sys.hexversion >= 0x3000000:
- read_user_input = input
-else:
- read_user_input = raw_input
-
-
-
-
-
-
-def arg_stdout_or_fs ( value ):
- if value == '-':
- return value
- else:
- return os.path.abspath ( os.path.expanduser ( value ) )
-# --- end of arg_stdout_or_fs (...) ---
-
-
-
-
-class SetupArgParser ( roverlay.argparser.RoverlayArgumentParser ):
- MULTIPLE_COMMANDS = False
- COMMAND_DESCRIPTION = {
- 'init': 'initialize roverlay\'s config and filesystem layout',
- 'mkconfig': 'generate a config file',
- }
- DEFAULT_COMMAND = "init"
-
- COMMANDS_WITH_PRETEND = frozenset ({ 'init', })
-
- SETUP_TARGETS = ( 'version', 'actions', 'setup', 'config', 'init', )
- PARSE_TARGETS = ( 'actions', 'setup', 'config', 'init', )
-
-
- def setup_setup ( self ):
- arg = self.setup_setup_minimal ( title='common options' )
-
- arg (
- '--output', '-O', metavar="<file|dir|->", dest='output',
- default='-', type=arg_stdout_or_fs,
- flags=self.ARG_WITH_DEFAULT,
- help='output file/dir/stream used by various commands (\'-\' for stdout)',
- )
-
- arg (
- '--pretend', '-p', dest='pretend',
- flags=self.ARG_WITH_DEFAULT|self.ARG_OPT_IN,
- help='show what would be done',
- )
-
- arg (
- '--ask', '-a', dest='wait_confirm',
- flags=self.ARG_WITH_DEFAULT|self.ARG_OPT_IN,
- help='get confirmation before actually doing anything',
- )
-
- return arg
- # --- end of setup_setup (...) ---
-
- def parse_setup ( self ):
- self.parse_setup_minimal()
-
- if self.parsed ['pretend']:
- for cmd in self.get_commands():
- if cmd not in self.__class__.COMMANDS_WITH_PRETEND:
- self.parser.exit (
- "{!r} command does not support --pretend.".format ( cmd )
- )
- # --- end of parse_setup (...) ---
-
- def setup_config ( self ):
- arg = self.add_argument_group (
- "config", title="options for the main config file"
- )
-
- arg (
- '--expand-user', dest='config_expand_user',
- flags=self.ARG_WITH_DEFAULT|self.ARG_OPT_IN,
- help="expand \'~\' to the target user\'s home directory",
- )
-
- arg (
- '--additions-dir', '-A', dest='additions_dir',
- flags=self.ARG_WITH_DEFAULT|self.ARG_META_DIR,
- type=roverlay.argutil.couldbe_dirstr_existing,
- help=(
- 'directory for user-provided content '
- '(patches, hand-written ebuilds, hooks)'
- ),
- )
-
- arg (
- '--variable', '-v', metavar="<key=\"value\">", dest='config_vars',
- default=[], action='append',
- type=roverlay.argutil.is_config_opt,
- help="additional variables",
- )
-
- return arg
- # --- end of setup_config (...) ---
-
- def parse_config ( self ):
- for kv in self.parsed ['config_vars']:
- key, eq_sign, val = kv.partition('=')
- if key in { 'ADDITIONS_DIR', 'OVERLAY_ADDITIONS_DIR' }:
- self.parser.exit (
- 'use \'--additions-dir {0}\' instead of '
- '\'--variable ADDITIONS_DIR={0}\'.'.format ( val )
- )
-
- self.parsed ['config_vars'].append (
- "ADDITIONS_DIR=" + self.parsed ['additions_dir']
- )
- # --- end of parse_config (...) ---
-
- def setup_init ( self ):
- arg = self.add_argument_group (
- 'init', title='options for the \'init\' command'
- )
-
- arg (
- '--enable-default-hooks', dest='want_default_hooks',
- default=True,
- flags=self.ARG_WITH_DEFAULT|self.ARG_OPT_IN,
- help='enable/update the default hooks',
- )
-
- arg (
- '--no-default-hooks', dest='want_default_hooks',
- flags=self.ARG_SHARED|self.ARG_OPT_OUT,
- help='disable the default hooks',
- )
-
- arg (
- '--import-config', '-I', dest='import_config',
- default="symlink",
- choices=[
- "disable",
- "symlink", "symlink=root",
- "symlink=dirs", "symlink=files",
- "copy"
- ],
- metavar='<mode>',
- flags=self.ARG_WITH_DEFAULT,
- help=(
- 'choose whether and how --conf-root should be imported: '
- '%(choices)s'
- ),
- )
-
- arg (
- '--no-import-config', dest='import_config',
- action='store_const', const='disable',
- help='disable config import (same as \'--import-config disable\')',
- )
-
-
- arg (
- '--target-uid', dest='target_uid', default=os.getuid(),
- metavar='<uid>', type=roverlay.argutil.is_uid,
- flags=self.ARG_WITH_DEFAULT,
- help="uid of the user that will run roverlay",
- )
-
- arg (
- '--target-gid', dest='target_gid', default=os.getgid(),
- metavar='<gid>', type=roverlay.argutil.is_gid,
- flags=self.ARG_WITH_DEFAULT,
- help='gid of the user that will run roverlay',
- )
-
- # --- end of setup_init (...) ---
-
- def parse_init ( self ):
- my_uid = os.getuid()
-
- if my_uid and self.parsed ['target_uid'] != my_uid:
- if self.parsed ['pretend']:
- sys.stderr.write (
- "!!! --target-uid: users cannot configure other users.\n\n"
- )
- else:
- self.parser.exit (
- "--target-uid: users cannot configure other users."
- )
- # --- end of parse_init (...) ---
-
-
-# --- end of SetupArgParser ---
-
-
-class SetupEnvironment ( roverlay.runtime.IndependentRuntimeEnvironment ):
-
- LOG_LEVEL = logging.INFO
-
- SHARED_DIR_MODE = roverlay.fsutil.get_stat_mode ( "rwxrwxr-x" )
- PRIVATE_DIR_MODE = roverlay.fsutil.get_stat_mode ( "rwxr-x---" )
- SHARED_FILE_MODE = roverlay.fsutil.get_stat_mode ( "rw-rw-r--" )
- PRIVATE_FILE_MODE = roverlay.fsutil.get_stat_mode ( "rw-r-----" )
-
- def __init__ ( self, *args, **kwargs ):
- super ( SetupEnvironment, self ).__init__ ( *args, **kwargs )
-
- self.UID = os.getuid()
- self.GID = os.getgid()
-
- self.expanduser = None
- self.fs_ops = None
- self.fs_ops_virtual = None
-
- self.want_chown = None
- self.data_root = None
- self.work_root = None
- self.conf_root = None
- self.user_conf_root = None
-
-# not used
-# COLUMNS = os.environ.get ( 'COLUMNS', 78 )
-#
-# self.text_wrapper = textwrap.TextWrapper (
-# width=COLUMNS, initial_indent='', subsequent_indent='',
-# break_long_words=False, break_on_hyphens=False,
-# )
- # --- end of __init__ (...) ---
-
- def create_argparser ( self ):
- instinfo = self.access_constant ( 'INSTALLINFO' )
-
- return SetupArgParser (
- description = 'roverlay setup script',
- defaults = {
- 'work_root' : instinfo ['workroot'],
- 'data_root' : instinfo ['libexec'],
- 'conf_root' : instinfo ['confroot'],
- 'private_conf_root' : instinfo ['workroot'] + os.sep + 'config',
- 'import_config' : 'symlink=root',
- 'additions_dir' : instinfo ['workroot'] + os.sep + 'files',
- },
- )
- # --- end of create_argparser (...) ---
-
- def _get_config_roots ( self ):
- return (
- os.path.realpath ( self.conf_root ),
- os.path.realpath ( self.user_conf_root )
- )
- # --- end of get_config_roots (...) ---
-
- def get_user_config_root ( self ):
- conf_root, user_conf_root = self._get_config_roots()
- if conf_root == user_conf_root:
- return None
- else:
- return user_conf_root
- # --- end of get_user_config_root (...) ---
-
- def get_config_file_path ( self ):
- return (
- self.work_root + os.sep + self.access_constant ( 'config_file_name' )
- )
- # --- end of get_config_file_path (...) ---
-
- def create_new_target_config ( self ):
- return self.create_new_config (
- config_str=self.create_config_file ( expand_user=True )
- )
- # --- end of create_new_target_config (...) ---
-
- def _expanduser_pwd ( self, fspath ):
- return roverlay.fsutil.pwd_expanduser (
- fspath, self.options ['target_uid']
- )
- # --- end of _expanduser_pwd (...) ---
-
- def create_config_file ( self, expand_user=False ):
- conf_creator = roverlay.config.defconfig.RoverlayConfigCreation (
- is_installed = self.is_installed(),
- work_root = (
- self.work_root if expand_user else self.options ['work_root']
- ),
- data_root = (
- self.data_root if expand_user else self.options ['data_root']
- ),
- conf_root = (
- self.user_conf_root if expand_user
- else self.options ['private_conf_root']
- ),
- )
-
- for kv in self.options ['config_vars']:
- key, sepa, value = kv.partition ( '=' )
- if not sepa:
- raise Exception ( "bad variable given: {!r}".format ( kv ) )
- else:
- conf_creator.set_option ( key, value )
-
- return conf_creator.get_str()
- # --- end of create_config_file (...) ---
-
- def write_config_file ( self, output=None, expand_user=None ):
- config_file_str = self.create_config_file (
- expand_user = (
- self.options ['config_expand_user'] if expand_user is None
- else expand_user
- ),
- )
- if not output or output == '-':
- self.info ( config_file_str )
- else:
- with open ( output, 'wt' ) as FH:
- FH.write ( config_file_str )
- # --- end of write_config_file (...) ---
-
- def auto_reconfigure ( self ):
- self.reconfigure ( self.create_config_file() )
- # --- end of auto_reconfigure (...) ---
-
- def setup ( self ):
- self.PWD_INITIAL = os.getcwd()
- self.setup_common()
-
- # ref
- options = self.options
-
- target_uid = options ['target_uid']
- target_gid = options ['target_gid']
-
- self.want_chown = ( target_uid != self.UID or target_gid != self.GID )
-
- if self.UID == target_uid:
- expanduser = os.path.expanduser
- self.expanduser = os.path.expanduser
- else:
- expanduser = self._expanduser_pwd
- self.expanduser = self._expanduser_pwd
-
- self.work_root = expanduser ( options ['work_root'] )
- self.data_root = expanduser ( options ['data_root'] )
- self.conf_root = expanduser ( options ['conf_root'] )
- self.user_conf_root = expanduser ( options ['private_conf_root'] )
-
-
- self.fs_ops_virtual = {
- 'private_dir': roverlay.fsutil.VirtualFsOperations (
- uid=target_uid, gid=target_gid, mode=self.PRIVATE_DIR_MODE,
- ),
- 'shared_dir': roverlay.fsutil.VirtualFsOperations (
- uid=target_uid, gid=target_gid, mode=self.SHARED_DIR_MODE,
- ),
- 'private_file': roverlay.fsutil.VirtualFsOperations (
- uid=target_uid, gid=target_gid, mode=self.PRIVATE_FILE_MODE,
- ),
- 'shared_file': roverlay.fsutil.VirtualFsOperations (
- uid=target_uid, gid=target_gid, mode=self.SHARED_FILE_MODE,
- ),
- }
-
- if options ['pretend']:
- self.fs_ops = self.fs_ops_virtual
- else:
- self.fs_ops = {
- 'private_dir': roverlay.fsutil.FsOperations (
- uid=target_uid, gid=target_gid, mode=self.PRIVATE_DIR_MODE,
- ),
- 'shared_dir': roverlay.fsutil.FsOperations (
- uid=target_uid, gid=target_gid, mode=self.SHARED_DIR_MODE,
- ),
- 'private_file': roverlay.fsutil.FsOperations (
- uid=target_uid, gid=target_gid, mode=self.PRIVATE_FILE_MODE,
- ),
- 'shared_file': roverlay.fsutil.FsOperations (
- uid=target_uid, gid=target_gid, mode=self.SHARED_FILE_MODE,
- ),
- }
-
- # bind fs_ops
- self.private_dir = self.fs_ops ['private_dir']
- self.shared_dir = self.fs_ops ['shared_dir']
- self.private_file = self.fs_ops ['private_file']
- self.shared_file = self.fs_ops ['shared_file']
- # --- end of setup (...) ---
-
- def wait_confirm ( self,
- message=None, message_inline=None,
- prepend_newline=True, append_newline=True
- ):
- try:
- if prepend_newline:
- self.info ( '\n' )
-
- if message is not None:
- self.info ( str ( message ) + '\n' )
-
- if message_inline:
- self.info (
- "Press Enter to continue ({!s}) ... ".format ( message_inline )
- )
- else:
- self.info ( "Press Enter to continue ... " )
-
- self.stdout.flush()
-
- ret = read_user_input().strip()
-
- except ( KeyboardInterrupt, EOFError ):
- self.info ( "\n" )
- sys.exit ( 130 )
- else:
- if append_newline:
- self.info ( '\n' )
-
- return ret
- # --- end of wait_confirm (...) ---
-
- def wait_confirm_can_skip ( self,
- SKIP_WORDS=frozenset({ 'skip', 'no', 'n'}), **kwargs
- ):
- assert 'message_inline' not in kwargs
-
- if self.options ['wait_confirm']:
-
- user_reply = self.wait_confirm (
- message_inline="type {} to skip this step".format (
- '/'.join ( repr( word ) for word in sorted( SKIP_WORDS ) )
- ),
- **kwargs
- )
-
- return user_reply.lower() not in SKIP_WORDS
- else:
- return True
- # --- end of wait_confirm (...) ---
-
- def get_init_env ( self ):
- return SetupInitEnvironment ( self )
- # --- end of get_init_env (...) ---
-
- def get_hook_env ( self ):
- return SetupHookEnvironment ( self )
- # --- end of get_hook_env (...) ---
-
-# --- end of SetupEnvironment ---
-
-
-class SetupSubEnvironment ( object ):
-
- NEEDS_CONFIG_TREE = False
-
- ACTIONS = None
-
- def __init__ ( self, setup_env ):
- super ( SetupSubEnvironment, self ).__init__()
-
- self.setup_env = setup_env
- self.stdout = setup_env.stdout
- self.stderr = setup_env.stderr
- self.info = setup_env.info
- self.error = setup_env.error
-
- if self.NEEDS_CONFIG_TREE:
- self.config = self.setup_env.create_new_target_config()
- else:
- self.config = None
-
- self.setup()
- # --- end of __init__ (...) ---
-
- def setup ( self ):
- pass
- # --- end of setup (...) ---
-
- def run ( self, steps_to_skip=None, verbose_skip=True, steps=None ):
- pretend = self.setup_env.options ['pretend']
- ACTIONS = steps if steps is not None else self.ACTIONS
-
- if ACTIONS:
- if steps_to_skip:
- methods_to_call = [
- (
- None if item[0] in steps_to_skip
- else getattr ( self, 'do_' + item[0] )
- ) for item in ACTIONS
- ]
- else:
- methods_to_call = [
- getattr ( self, 'do_' + item[0] ) for item in ACTIONS
- ]
-
- wait_confirm_can_skip = self.setup_env.wait_confirm_can_skip
-
-
- for method, action in zip ( methods_to_call, ACTIONS ):
- if method is None:
- if verbose_skip:
- self.info ( "{}: skipped.\n".format ( action[0] ) )
-
- elif not action[1]:
- method ( pretend=pretend )
-
- elif wait_confirm_can_skip (
- message=method.__doc__, append_newline=False
- ):
- method ( pretend=pretend )
- else:
- self.info ( "skipped.\n" )
-
- else:
- raise NotImplementedError (
- "{}.{}()".format ( self.__class__.__name__, "do_all" )
- )
- # --- end of run (...) ---
-
-
-# --- end of SetupSubEnvironment ---
-
-
-class SetupInitEnvironment ( SetupSubEnvironment ):
-
- ACTIONS = (
- ( 'pretend', False ),
- ( 'prepare_config_file', False ),
- ( 'import_config', True ),
- ( 'setupdirs', True ),
- ( 'write_config_file', True ),
- ( 'enable_default_hooks', True ),
- )
-
- NEEDS_CONFIG_TREE = True
-
- def setup ( self ):
- self.config_file_str = None
- # --- end of setup (...) ---
-
- IMPORT_CONFIG_DESC = {
- 'disable' : False,
- 'symlink' : 'symlink {conf_root} to {user_conf_root}',
- 'symlink=root' : 'symlink {conf_root} to {user_conf_root}',
- 'symlink=dirs' : (
- 'symlink files/dirs from {conf_root} to {user_conf_root}/'
- ),
- 'symlink=files' : (
- 'recursively copy {conf_root} to {user_conf_root}, '
- 'but symlink files instead of copying them'
- ),
- 'copy' : 'recursively copy {conf_root} to {user_conf_root}',
- }
-
- def gen_pretend_options ( self ):
- options = self.setup_env.options
-
- def get_option ( key ):
- val = options [key]
- if isinstance ( val, str ):
- return val
- elif hasattr ( val, '__iter__' ):
- return ' '.join ( str(x) for x in val )
- elif isinstance ( val, bool ):
- return "yes" if val else "no"
- elif val is None:
- return "<undef>"
- else:
- return str ( val )
- # --- end of get_option (...) ---
-
- comment_path = lambda a, b: a if a == b else ( a + ' (' + b + ')' )
- get_path_option = lambda k, b: comment_path ( get_option ( k ), b )
-
- fmt_vars = {
- 'conf_root' : options ['conf_root'],
- 'user_conf_root': options ['private_conf_root']
- }
-
- yield ( "user/uid", get_option ( 'target_uid' ) )
- yield ( "group/gid", get_option ( 'target_gid' ) )
-
- yield ( "work root",
- get_path_option ( 'work_root', self.setup_env.work_root )
- )
- yield ( "data root",
- get_path_option ( 'data_root', self.setup_env.data_root )
- )
- yield ( "roverlay\'s config root",
- get_path_option ( 'conf_root', self.setup_env.conf_root )
- )
- yield ( "user\'s config root",
- get_path_option ( 'private_conf_root', self.setup_env.user_conf_root )
- )
-
- import_config = get_option ( 'import_config' )
- if import_config == 'disable':
- yield ( "import config", "no" )
- else:
- yield ( "import config",
- "yes, "
- + self.IMPORT_CONFIG_DESC [import_config].format ( **fmt_vars )
- )
-
- yield ( "enable default hooks", get_option ( 'want_default_hooks' ) )
- yield ( "additional config variables", get_option ( 'config_vars' ) )
- # --- end of gen_pretend_options (...) ---
-
- def gen_pretend_lines ( self, append_newline=True ):
- options = list ( self.gen_pretend_options() )
- COLUMNS = os.environ.get ( 'COLUMNS', 78 )
- desc_len = min ( COLUMNS // 2,
- 1 + max ( len(desc) for desc, value in options )
- )
-
- line_wrapper = textwrap.TextWrapper (
- width=COLUMNS, initial_indent='',
- subsequent_indent=( (desc_len+5) * ' ' ),
- break_long_words=False, break_on_hyphens=False,
- )
-
-
- yield 'Configuration:'
- for desc, value in options:
- yield line_wrapper.fill (
- "- {desc:<{l}}: {value}".format (
- desc=desc, l=desc_len, value=value,
- )
- )
-
- if append_newline:
- yield ""
- # --- end of gen_pretend_lines (...) ---
-
- def do_pretend ( self, pretend ):
- """Shows what would be done."""
- self.info ( '\n'.join ( self.gen_pretend_lines() ) + '\n' )
- # --- end of do_pretend (...) ---
-
- def do_prepare_config_file ( self, pretend ):
- """Creates the config file (in memory)."""
- self.config_file_str = self.setup_env.create_config_file (
- expand_user=self.setup_env.options ['config_expand_user']
- )
- # --- end of do_prepare_config_file (...) ---
-
- def do_import_config ( self, pretend ):
- """Imports the config."""
- mode = self.setup_env.options ['import_config']
- fs_ops = self.setup_env.private_dir
- user_conf_root = self.setup_env.get_user_config_root()
- # assert os.path.isdir ( os.path.dirname(user_conf_root) == work_root )
-
- if user_conf_root is None and (
- fs_ops.unlink ( self.setup_env.user_conf_root )
- ):
- # config_root was a symlink
-
- if pretend:
- user_conf_root = self.setup_env.user_conf_root
- else:
- user_conf_root = self.setup_env.get_user_config_root()
-
- # -- end if
-
-
- if user_conf_root is None:
- self.info (
- "user has no private config directory - skipping config import.\n"
- )
-
- elif mode in { 'symlink=root', 'symlink' }:
- if not fs_ops.wipe ( user_conf_root ):
- self.setup_env.die (
- "failed to remove {!r}.\n".format ( user_conf_root )
- )
- elif not fs_ops.symlink ( self.setup_env.conf_root, user_conf_root ):
- self.setup_env.die (
- "could not create symlink to {!r}.".format (
- self.setup_env.conf_root
- )
- )
-
-
- pass
- else:
- raise NotImplementedError ( mode )
- # --- end of do_import_config (...) ---
-
- def do_setupdirs ( self, pretend ):
- """Creates directories with proper permissions."""
- create_subdir_check = roverlay.fsutil.create_subdir_check
- config = self.config
- find_config_path = roverlay.config.entryutil.find_config_path
- dodir_private = self.setup_env.private_dir.dodir
- dodir_shared = self.setup_env.shared_dir.dodir
-
-
- WANT_USERDIR = roverlay.config.entrymap.WANT_USERDIR
- WANT_PRIVATE = roverlay.config.entrymap.WANT_PRIVATE
- WANT_FILEDIR = roverlay.config.entrymap.WANT_FILEDIR
-
- listlike = lambda a: (
- hasattr(a, '__iter__') and not isinstance(a, str)
- )
- iter_values = lambda b: (
- () if b is None else (b if listlike(b) else ( b, ))
- )
-
- dirs_exclude = [
- create_subdir_check ( self.setup_env.conf_root ),
- create_subdir_check ( self.setup_env.data_root ),
- ]
- if self.setup_env.get_user_config_root() is None:
- dirs_exclude.append (
- create_subdir_check ( self.setup_env.user_conf_root )
- )
- else:
- print ( self.setup_env.get_user_config_root() )
-
- # don't print exclude/skip messages more than once per dir
- dirs_already_excluded = set()
-
- private_dirs = set()
- private_dirs_chown = set()
- shared_dirs = set()
- shared_dirs_chown = set()
-
- # it's not necessary to create all of the listed dirs because some of
- # them are automatically created at runtime, but doing so results in
- # a (mostly) complete filesystem layout
- #
- for config_key, entry in (
- roverlay.config.entrymap.CONFIG_ENTRY_MAP.items()
- ):
- if isinstance ( entry, dict ) and 'want_dir_create' in entry:
- for value in iter_values (
- config.get ( find_config_path ( config_key ), None )
- ):
- dirmask = entry ['want_dir_create']
- dirpath = (
- os.path.dirname ( value.rstrip ( os.sep ) )
- if dirmask & WANT_FILEDIR else value.rstrip ( os.sep )
- )
-
- if not dirpath or dirpath in dirs_already_excluded:
- pass
-
- elif any ( ex ( dirpath ) for ex in dirs_exclude ):
- self.info (
- "setupdirs: excluding {!r}\n".format ( dirpath )
- )
- dirs_already_excluded.add ( dirpath )
-
- elif os.path.islink ( dirpath ):
- self.info (
- '{!r} is a symlink - skipping setupdir '
- 'actions.\n'.format ( dirpath )
- )
- dirs_already_excluded.add ( dirpath )
-
- elif dirmask & WANT_USERDIR:
- if dirmask & WANT_PRIVATE:
- private_dirs_chown.add ( dirpath )
- else:
- shared_dirs_chown.add ( dirpath )
-
- elif dirmask & WANT_PRIVATE:
- private_dirs.add ( dirpath )
-
- else:
- shared_dirs.add ( dirpath )
- # -- end for
-
-
- private_dirs -= private_dirs_chown
- shared_dirs_chown -= private_dirs
- shared_dirs -= shared_dirs_chown
-
- for dirpath in shared_dirs:
- dodir_shared ( dirpath, chown=False )
-
- for dirpath in shared_dirs_chown:
- dodir_shared ( dirpath, chown=True )
-
- for dirpath in private_dirs:
- dodir_private ( dirpath, chown=False )
-
- for dirpath in private_dirs_chown:
- dodir_private ( dirpath, chown=True )
-
- self.setup_env.private_dir.chmod_chown ( self.setup_env.work_root )
- # --- end of do_setupdirs (...) ---
-
- def do_write_config_file ( self, pretend ):
- """Writes the config file to disk."""
- cfile = self.setup_env.get_config_file_path()
- if not self.config_file_str:
- self.setup_env.die ( "no config file created!" )
- elif pretend:
- self.info ( "Would write config file to {!r}.\n".format ( cfile ) )
- else:
- with open ( cfile, 'wt' ) as FH:
- FH.write ( self.config_file_str )
-
- self.setup_env.private_file.chmod_chown ( cfile )
- # --- end of do_write_config_file (...) ---
-
- def do_enable_default_hooks ( self, pretend ):
- """Enables the default hooks, e.g. git history creation."""
- hook_env = self.setup_env.get_hook_env()
- if not hook_env.enable_defaults():
- self.setup_env.die ( "failed to enable hooks." )
- # --- end of do_enable_default_hooks (...) ---
-
-# --- end of SetupInitEnvironment ---
-
-
-class HookScript ( object ):
-
- 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]
- else:
- self.default_events = False
- self.priority = None
- self.is_hidden = False
- # --- end of __init__ (...) ---
-
- def is_visible ( self ):
- return not self.is_hidden and (
- self.priority is None or self.priority >= 0
- )
- # --- end of is_visible (...) ---
-
- def __str__ ( self ):
- yesno = lambda k: 'y' if k else 'n'
- return "<{cls} {name!r}, hidden={h} prio={p}>".format (
- cls=self.__class__.__name__,
- name=self.name,
- h=yesno ( self.is_hidden ),
- p=(
- "auto" if self.priority is None else
- ( "IGNORE" if self.priority < 0 else self.priority )
- ),
- )
- # --- end of __str__ (...) ---
-
- def set_priority_from_generator ( self, number_gen, only_if_unset=True ):
- if self.priority is None:
- self.priority = next ( number_gen )
- return True
- elif only_if_unset or self.priority < 0:
- return False
- else:
- self.priority = next ( number_gen )
- return True
- # --- end of set_priority_from_generator (...) ---
-
- def get_dest_name ( self, file_ext='.sh', digit_len=2 ):
- # file_ext has to be .sh, else the script doesn't get recognized
- # by mux.sh
-
- prio = self.priority
- if prio is None or prio < 0:
- raise AssertionError ( "hook script has no priority." )
-
- return "{prio:0>{l}d}-{fname}{f_ext}".format (
- prio=prio, fname=self.name, f_ext=file_ext, l=digit_len,
- )
- # --- end of get_dest_name (...) ---
-
-
-# --- end of HookScript ---
-
-
-class HookScriptDir ( object ):
-
- def __init__ ( self, root ):
- super ( HookScriptDir, self ).__init__()
-
- self.root = root
- self._scripts = collections.OrderedDict()
- # --- end of __init__ (...) ---
-
- def __bool__ ( self ):
- return bool ( self._scripts )
- # --- end of __bool__ (...) ---
-
- def get_script ( self, 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():
- if script.is_visible():
- yield script
- # --- end of iter_scripts (...) ---
-
- def scan ( self ):
- root = self.root
- try:
- filenames = sorted ( os.listdir ( root ) )
- except OSError as oserr:
- if oserr.errno != errno.ENOENT:
- raise
-
- else:
- 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
- # --- end of scan (...) ---
-
-# --- end of HookScriptDir ---
-
-
-class SetupHookEnvironment ( 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'
- )
- else:
- self.user_hook_root = None
- self.writable = 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 )
- # --- end of setup (...) ---
-
- def _link_hook ( self, source, link ):
- if os.path.lexists ( link ):
- linkdest = os.path.realpath ( link )
-
- message = 'Skipping activation of hook {!r} - '.format ( link )
-
- if linkdest == source or linkdest == os.path.realpath ( source ):
- self.info ( message + "already set up.\n" )
- return True
-
- 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" )
-
- return None
- else:
- return self.setup_env.private_file.symlink ( source, link )
- # --- end of _link_hook (...) ---
-
- 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
- self.setup_env.private_dir.dodir ( destdir )
-
- 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:
- success = False
- # -- end if
-
- return success
- # --- end of link_hooks_v (...) ---
-
- def enable_defaults ( self ):
- # not strict: missing hooks are ignored
- success = False
- if self.hook_root:
- success = True
- default_hooks = self.hook_root.get_default_scripts()
- for event, hooks in default_hooks.items():
- if not self.link_hooks_v ( event, hooks ):
- success = False
- # -- end if
-
- return success
- # --- end of enable_defaults (...) ---
-
-
-# --- end of SetupHookEnvironment ---
-
-
-def setup_main():
- env = SetupEnvironment()
- env.setup()
-
- if env.wants_command ( "mkconfig" ):
- env.write_config_file ( env.options ['output'] )
- elif env.wants_command ( "init" ):
- env.get_init_env().run()
-
-
-# --- end of setup_main (...) ---
diff --git a/roverlay/setupscript/__init__.py b/roverlay/setupscript/__init__.py
new file mode 100644
index 0000000..740a69c
--- /dev/null
+++ b/roverlay/setupscript/__init__.py
@@ -0,0 +1,2 @@
+# R overlay -- setup script
+# -*- coding: utf-8 -*-
diff --git a/roverlay/setupscript/baseenv.py b/roverlay/setupscript/baseenv.py
new file mode 100644
index 0000000..349d077
--- /dev/null
+++ b/roverlay/setupscript/baseenv.py
@@ -0,0 +1,72 @@
+# R overlay -- setup script, base env
+# -*- coding: utf-8 -*-
+
+class SetupSubEnvironment ( object ):
+
+ NEEDS_CONFIG_TREE = False
+
+ ACTIONS = None
+
+ def __init__ ( self, setup_env ):
+ super ( SetupSubEnvironment, self ).__init__()
+
+ self.setup_env = setup_env
+ self.stdout = setup_env.stdout
+ self.stderr = setup_env.stderr
+ self.info = setup_env.info
+ self.error = setup_env.error
+
+ if self.NEEDS_CONFIG_TREE:
+ self.config = self.setup_env.create_new_target_config()
+ else:
+ self.config = None
+
+ self.setup()
+ # --- end of __init__ (...) ---
+
+ def setup ( self ):
+ pass
+ # --- end of setup (...) ---
+
+ def run ( self, steps_to_skip=None, verbose_skip=True, steps=None ):
+ pretend = self.setup_env.options ['pretend']
+ ACTIONS = steps if steps is not None else self.ACTIONS
+
+ if ACTIONS:
+ if steps_to_skip:
+ methods_to_call = [
+ (
+ None if item[0] in steps_to_skip
+ else getattr ( self, 'do_' + item[0] )
+ ) for item in ACTIONS
+ ]
+ else:
+ methods_to_call = [
+ getattr ( self, 'do_' + item[0] ) for item in ACTIONS
+ ]
+
+ wait_confirm_can_skip = self.setup_env.wait_confirm_can_skip
+
+
+ for method, action in zip ( methods_to_call, ACTIONS ):
+ if method is None:
+ if verbose_skip:
+ self.info ( "{}: skipped.\n".format ( action[0] ) )
+
+ elif not action[1]:
+ method ( pretend=pretend )
+
+ elif wait_confirm_can_skip (
+ message=method.__doc__, append_newline=False
+ ):
+ method ( pretend=pretend )
+ else:
+ self.info ( "skipped.\n" )
+
+ else:
+ raise NotImplementedError (
+ "{}.{}()".format ( self.__class__.__name__, "do_all" )
+ )
+ # --- end of run (...) ---
+
+# --- end of SetupSubEnvironment ---
diff --git a/roverlay/setupscript/hookenv.py b/roverlay/setupscript/hookenv.py
new file mode 100644
index 0000000..9c5fedb
--- /dev/null
+++ b/roverlay/setupscript/hookenv.py
@@ -0,0 +1,230 @@
+# R overlay -- setup script, env for managing hooks
+# -*- coding: utf-8 -*-
+
+import collections
+import errno
+import os
+
+
+import roverlay.static.hookinfo
+import roverlay.util.counter
+
+
+class HookScript ( object ):
+
+ 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]
+ else:
+ self.default_events = False
+ self.priority = None
+ self.is_hidden = False
+ # --- end of __init__ (...) ---
+
+ def is_visible ( self ):
+ return not self.is_hidden and (
+ self.priority is None or self.priority >= 0
+ )
+ # --- end of is_visible (...) ---
+
+ def __str__ ( self ):
+ yesno = lambda k: 'y' if k else 'n'
+ return "<{cls} {name!r}, hidden={h} prio={p}>".format (
+ cls=self.__class__.__name__,
+ name=self.name,
+ h=yesno ( self.is_hidden ),
+ p=(
+ "auto" if self.priority is None else
+ ( "IGNORE" if self.priority < 0 else self.priority )
+ ),
+ )
+ # --- end of __str__ (...) ---
+
+ def set_priority_from_generator ( self, number_gen, only_if_unset=True ):
+ if self.priority is None:
+ self.priority = next ( number_gen )
+ return True
+ elif only_if_unset or self.priority < 0:
+ return False
+ else:
+ self.priority = next ( number_gen )
+ return True
+ # --- end of set_priority_from_generator (...) ---
+
+ def get_dest_name ( self, file_ext='.sh', digit_len=2 ):
+ # file_ext has to be .sh, else the script doesn't get recognized
+ # by mux.sh
+
+ prio = self.priority
+ if prio is None or prio < 0:
+ raise AssertionError ( "hook script has no priority." )
+
+ return "{prio:0>{l}d}-{fname}{f_ext}".format (
+ prio=prio, fname=self.name, f_ext=file_ext, l=digit_len,
+ )
+ # --- end of get_dest_name (...) ---
+
+
+# --- end of HookScript ---
+
+
+class HookScriptDir ( object ):
+
+ def __init__ ( self, root ):
+ super ( HookScriptDir, self ).__init__()
+
+ self.root = root
+ self._scripts = collections.OrderedDict()
+ # --- end of __init__ (...) ---
+
+ def __bool__ ( self ):
+ return bool ( self._scripts )
+ # --- end of __bool__ (...) ---
+
+ def get_script ( self, 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():
+ if script.is_visible():
+ yield script
+ # --- end of iter_scripts (...) ---
+
+ def scan ( self ):
+ root = self.root
+ try:
+ filenames = sorted ( os.listdir ( root ) )
+ except OSError as oserr:
+ if oserr.errno != errno.ENOENT:
+ raise
+
+ else:
+ 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
+ # --- end of scan (...) ---
+
+# --- 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'
+ )
+ else:
+ self.user_hook_root = None
+ self.writable = 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 )
+ # --- end of setup (...) ---
+
+ def _link_hook ( self, source, link ):
+ if os.path.lexists ( link ):
+ linkdest = os.path.realpath ( link )
+
+ message = 'Skipping activation of hook {!r} - '.format ( link )
+
+ if linkdest == source or linkdest == os.path.realpath ( source ):
+ self.info ( message + "already set up.\n" )
+ return True
+
+ 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" )
+
+ return None
+ else:
+ return self.setup_env.private_file.symlink ( source, link )
+ # --- end of _link_hook (...) ---
+
+ 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
+ self.setup_env.private_dir.dodir ( destdir )
+
+ 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:
+ success = False
+ # -- end if
+
+ return success
+ # --- end of link_hooks_v (...) ---
+
+ def enable_defaults ( self ):
+ # not strict: missing hooks are ignored
+ success = False
+ if self.hook_root:
+ success = True
+ default_hooks = self.hook_root.get_default_scripts()
+ for event, hooks in default_hooks.items():
+ if not self.link_hooks_v ( event, hooks ):
+ success = False
+ # -- end if
+
+ return success
+ # --- end of enable_defaults (...) ---
+
+# --- end of SetupHookEnvironment ---
diff --git a/roverlay/setupscript/initenv.py b/roverlay/setupscript/initenv.py
new file mode 100644
index 0000000..f07953a
--- /dev/null
+++ b/roverlay/setupscript/initenv.py
@@ -0,0 +1,308 @@
+# R overlay -- setup script, env for the "init" command
+# -*- coding: utf-8 -*-
+
+import os
+import textwrap
+
+
+import roverlay.fsutil
+
+import roverlay.config.entrymap
+import roverlay.config.entryutil
+
+import roverlay.setupscript.baseenv
+
+
+class SetupInitEnvironment (
+ roverlay.setupscript.baseenv.SetupSubEnvironment
+):
+
+ ACTIONS = (
+ ( 'pretend', False ),
+ ( 'prepare_config_file', False ),
+ ( 'import_config', True ),
+ ( 'setupdirs', True ),
+ ( 'write_config_file', True ),
+ ( 'enable_default_hooks', True ),
+ )
+
+ NEEDS_CONFIG_TREE = True
+
+ def setup ( self ):
+ self.config_file_str = None
+ # --- end of setup (...) ---
+
+ IMPORT_CONFIG_DESC = {
+ 'disable' : False,
+ 'symlink' : 'symlink {conf_root} to {user_conf_root}',
+ 'symlink=root' : 'symlink {conf_root} to {user_conf_root}',
+ 'symlink=dirs' : (
+ 'symlink files/dirs from {conf_root} to {user_conf_root}/'
+ ),
+ 'symlink=files' : (
+ 'recursively copy {conf_root} to {user_conf_root}, '
+ 'but symlink files instead of copying them'
+ ),
+ 'copy' : 'recursively copy {conf_root} to {user_conf_root}',
+ }
+
+ def gen_pretend_options ( self ):
+ options = self.setup_env.options
+
+ def get_option ( key ):
+ val = options [key]
+ if isinstance ( val, str ):
+ return val
+ elif hasattr ( val, '__iter__' ):
+ return ' '.join ( str(x) for x in val )
+ elif isinstance ( val, bool ):
+ return "yes" if val else "no"
+ elif val is None:
+ return "<undef>"
+ else:
+ return str ( val )
+ # --- end of get_option (...) ---
+
+ comment_path = lambda a, b: a if a == b else ( a + ' (' + b + ')' )
+ get_path_option = lambda k, b: comment_path ( get_option ( k ), b )
+
+ fmt_vars = {
+ 'conf_root' : options ['conf_root'],
+ 'user_conf_root': options ['private_conf_root']
+ }
+
+ yield ( "user/uid", get_option ( 'target_uid' ) )
+ yield ( "group/gid", get_option ( 'target_gid' ) )
+
+ yield ( "work root",
+ get_path_option ( 'work_root', self.setup_env.work_root )
+ )
+ yield ( "data root",
+ get_path_option ( 'data_root', self.setup_env.data_root )
+ )
+ yield ( "roverlay\'s config root",
+ get_path_option ( 'conf_root', self.setup_env.conf_root )
+ )
+ yield ( "user\'s config root",
+ get_path_option ( 'private_conf_root', self.setup_env.user_conf_root )
+ )
+
+ import_config = get_option ( 'import_config' )
+ if import_config == 'disable':
+ yield ( "import config", "no" )
+ else:
+ yield ( "import config",
+ "yes, "
+ + self.IMPORT_CONFIG_DESC [import_config].format ( **fmt_vars )
+ )
+
+ yield ( "enable default hooks", get_option ( 'want_default_hooks' ) )
+ yield ( "additional config variables", get_option ( 'config_vars' ) )
+ # --- end of gen_pretend_options (...) ---
+
+ def gen_pretend_lines ( self, append_newline=True ):
+ options = list ( self.gen_pretend_options() )
+ COLUMNS = os.environ.get ( 'COLUMNS', 78 )
+ desc_len = min ( COLUMNS // 2,
+ 1 + max ( len(desc) for desc, value in options )
+ )
+
+ line_wrapper = textwrap.TextWrapper (
+ width=COLUMNS, initial_indent='',
+ subsequent_indent=( (desc_len+5) * ' ' ),
+ break_long_words=False, break_on_hyphens=False,
+ )
+
+
+ yield 'Configuration:'
+ for desc, value in options:
+ yield line_wrapper.fill (
+ "- {desc:<{l}}: {value}".format (
+ desc=desc, l=desc_len, value=value,
+ )
+ )
+
+ if append_newline:
+ yield ""
+ # --- end of gen_pretend_lines (...) ---
+
+ def do_pretend ( self, pretend ):
+ """Shows what would be done."""
+ self.info ( '\n'.join ( self.gen_pretend_lines() ) + '\n' )
+ # --- end of do_pretend (...) ---
+
+ def do_prepare_config_file ( self, pretend ):
+ """Creates the config file (in memory)."""
+ self.config_file_str = self.setup_env.create_config_file (
+ expand_user=self.setup_env.options ['config_expand_user']
+ )
+ # --- end of do_prepare_config_file (...) ---
+
+ def do_import_config ( self, pretend ):
+ """Imports the config."""
+ mode = self.setup_env.options ['import_config']
+ fs_ops = self.setup_env.private_dir
+ user_conf_root = self.setup_env.get_user_config_root()
+ # assert os.path.isdir ( os.path.dirname(user_conf_root) == work_root )
+
+ if user_conf_root is None and (
+ fs_ops.unlink ( self.setup_env.user_conf_root )
+ ):
+ # config_root was a symlink
+
+ if pretend:
+ user_conf_root = self.setup_env.user_conf_root
+ else:
+ user_conf_root = self.setup_env.get_user_config_root()
+
+ # -- end if
+
+
+ if user_conf_root is None:
+ self.info (
+ "user has no private config directory - skipping config import.\n"
+ )
+
+ elif mode in { 'symlink=root', 'symlink' }:
+ if not fs_ops.wipe ( user_conf_root ):
+ self.setup_env.die (
+ "failed to remove {!r}.\n".format ( user_conf_root )
+ )
+ elif not fs_ops.symlink ( self.setup_env.conf_root, user_conf_root ):
+ self.setup_env.die (
+ "could not create symlink to {!r}.".format (
+ self.setup_env.conf_root
+ )
+ )
+
+
+ pass
+ else:
+ raise NotImplementedError ( mode )
+ # --- end of do_import_config (...) ---
+
+ def do_setupdirs ( self, pretend ):
+ """Creates directories with proper permissions."""
+ create_subdir_check = roverlay.fsutil.create_subdir_check
+ config = self.config
+ find_config_path = roverlay.config.entryutil.find_config_path
+ dodir_private = self.setup_env.private_dir.dodir
+ dodir_shared = self.setup_env.shared_dir.dodir
+
+
+ WANT_USERDIR = roverlay.config.entrymap.WANT_USERDIR
+ WANT_PRIVATE = roverlay.config.entrymap.WANT_PRIVATE
+ WANT_FILEDIR = roverlay.config.entrymap.WANT_FILEDIR
+
+ listlike = lambda a: (
+ hasattr(a, '__iter__') and not isinstance(a, str)
+ )
+ iter_values = lambda b: (
+ () if b is None else (b if listlike(b) else ( b, ))
+ )
+
+ dirs_exclude = [
+ create_subdir_check ( self.setup_env.conf_root ),
+ create_subdir_check ( self.setup_env.data_root ),
+ ]
+ if self.setup_env.get_user_config_root() is None:
+ dirs_exclude.append (
+ create_subdir_check ( self.setup_env.user_conf_root )
+ )
+
+ # don't print exclude/skip messages more than once per dir
+ dirs_already_excluded = set()
+
+ private_dirs = set()
+ private_dirs_chown = set()
+ shared_dirs = set()
+ shared_dirs_chown = set()
+
+ # it's not necessary to create all of the listed dirs because some of
+ # them are automatically created at runtime, but doing so results in
+ # a (mostly) complete filesystem layout
+ #
+ for config_key, entry in (
+ roverlay.config.entrymap.CONFIG_ENTRY_MAP.items()
+ ):
+ if isinstance ( entry, dict ) and 'want_dir_create' in entry:
+ for value in iter_values (
+ config.get ( find_config_path ( config_key ), None )
+ ):
+ dirmask = entry ['want_dir_create']
+ dirpath = (
+ os.path.dirname ( value.rstrip ( os.sep ) )
+ if dirmask & WANT_FILEDIR else value.rstrip ( os.sep )
+ )
+
+ if not dirpath or dirpath in dirs_already_excluded:
+ pass
+
+ elif any ( ex ( dirpath ) for ex in dirs_exclude ):
+ self.info (
+ "setupdirs: excluding {!r}\n".format ( dirpath )
+ )
+ dirs_already_excluded.add ( dirpath )
+
+ elif os.path.islink ( dirpath ):
+ self.info (
+ '{!r} is a symlink - skipping setupdir '
+ 'actions.\n'.format ( dirpath )
+ )
+ dirs_already_excluded.add ( dirpath )
+
+ elif dirmask & WANT_USERDIR:
+ if dirmask & WANT_PRIVATE:
+ private_dirs_chown.add ( dirpath )
+ else:
+ shared_dirs_chown.add ( dirpath )
+
+ elif dirmask & WANT_PRIVATE:
+ private_dirs.add ( dirpath )
+
+ else:
+ shared_dirs.add ( dirpath )
+ # -- end for
+
+
+ private_dirs -= private_dirs_chown
+ shared_dirs_chown -= private_dirs
+ shared_dirs -= shared_dirs_chown
+
+ for dirpath in shared_dirs:
+ dodir_shared ( dirpath, chown=False )
+
+ for dirpath in shared_dirs_chown:
+ dodir_shared ( dirpath, chown=True )
+
+ for dirpath in private_dirs:
+ dodir_private ( dirpath, chown=False )
+
+ for dirpath in private_dirs_chown:
+ dodir_private ( dirpath, chown=True )
+
+ self.setup_env.private_dir.chmod_chown ( self.setup_env.work_root )
+ # --- end of do_setupdirs (...) ---
+
+ def do_write_config_file ( self, pretend ):
+ """Writes the config file to disk."""
+ cfile = self.setup_env.get_config_file_path()
+ if not self.config_file_str:
+ self.setup_env.die ( "no config file created!" )
+ elif pretend:
+ self.info ( "Would write config file to {!r}.\n".format ( cfile ) )
+ else:
+ with open ( cfile, 'wt' ) as FH:
+ FH.write ( self.config_file_str )
+
+ self.setup_env.private_file.chmod_chown ( cfile )
+ # --- end of do_write_config_file (...) ---
+
+ def do_enable_default_hooks ( self, pretend ):
+ """Enables the default hooks, e.g. git history creation."""
+ hook_env = self.setup_env.get_hook_env()
+ if not hook_env.enable_defaults():
+ self.setup_env.die ( "failed to enable hooks." )
+ # --- end of do_enable_default_hooks (...) ---
+
+# --- end of SetupInitEnvironment ---
diff --git a/roverlay/setupscript/runtime.py b/roverlay/setupscript/runtime.py
new file mode 100644
index 0000000..9ba3eec
--- /dev/null
+++ b/roverlay/setupscript/runtime.py
@@ -0,0 +1,474 @@
+# R overlay -- setup script, runtime
+# -*- coding: utf-8 -*-
+
+from __future__ import print_function
+
+import logging
+import os
+import sys
+#import textwrap
+
+import roverlay.argparser
+import roverlay.argutil
+import roverlay.fsutil
+import roverlay.runtime
+
+import roverlay.config.defconfig
+
+import roverlay.setupscript.initenv
+import roverlay.setupscript.hookenv
+
+
+
+if sys.hexversion >= 0x3000000:
+ read_user_input = input
+else:
+ read_user_input = raw_input
+
+
+
+def setup_main_installed():
+ return setup_main ( True )
+# --- end of setup_main_installed (...) ---
+
+def setup_main ( installed ):
+ SetupEnvironment.run_default_main ( installed=installed )
+ return os.EX_OK
+# --- end of setup_main (...) ---
+
+def arg_stdout_or_fs ( value ):
+ if value == '-':
+ return value
+ else:
+ return os.path.abspath ( os.path.expanduser ( value ) )
+# --- end of arg_stdout_or_fs (...) ---
+
+
+class SetupArgParser ( roverlay.argparser.RoverlayArgumentParser ):
+ MULTIPLE_COMMANDS = False
+ COMMAND_DESCRIPTION = {
+ 'init': 'initialize roverlay\'s config and filesystem layout',
+ 'mkconfig': 'generate a config file',
+ }
+ DEFAULT_COMMAND = "init"
+
+ COMMANDS_WITH_PRETEND = frozenset ({ 'init', })
+
+ SETUP_TARGETS = ( 'version', 'actions', 'setup', 'config', 'init', )
+ PARSE_TARGETS = ( 'actions', 'setup', 'config', 'init', )
+
+
+ def setup_setup ( self ):
+ arg = self.setup_setup_minimal ( title='common options' )
+
+ arg (
+ '--output', '-O', metavar="<file|dir|->", dest='output',
+ default='-', type=arg_stdout_or_fs,
+ flags=self.ARG_WITH_DEFAULT,
+ help='output file/dir/stream used by various commands (\'-\' for stdout)',
+ )
+
+ arg (
+ '--pretend', '-p', dest='pretend',
+ flags=self.ARG_WITH_DEFAULT|self.ARG_OPT_IN,
+ help='show what would be done',
+ )
+
+ arg (
+ '--ask', '-a', dest='wait_confirm',
+ flags=self.ARG_WITH_DEFAULT|self.ARG_OPT_IN,
+ help='get confirmation before actually doing anything',
+ )
+
+ return arg
+ # --- end of setup_setup (...) ---
+
+ def parse_setup ( self ):
+ self.parse_setup_minimal()
+
+ if self.parsed ['pretend']:
+ for cmd in self.get_commands():
+ if cmd not in self.__class__.COMMANDS_WITH_PRETEND:
+ self.parser.exit (
+ "{!r} command does not support --pretend.".format ( cmd )
+ )
+ # --- end of parse_setup (...) ---
+
+ def setup_config ( self ):
+ arg = self.add_argument_group (
+ "config", title="options for the main config file"
+ )
+
+ arg (
+ '--expand-user', dest='config_expand_user',
+ flags=self.ARG_WITH_DEFAULT|self.ARG_OPT_IN,
+ help="expand \'~\' to the target user\'s home directory",
+ )
+
+ arg (
+ '--additions-dir', '-A', dest='additions_dir',
+ flags=self.ARG_WITH_DEFAULT|self.ARG_META_DIR,
+ type=roverlay.argutil.couldbe_dirstr_existing,
+ help=(
+ 'directory for user-provided content '
+ '(patches, hand-written ebuilds, hooks)'
+ ),
+ )
+
+ arg (
+ '--variable', '-v', metavar="<key=\"value\">", dest='config_vars',
+ default=[], action='append',
+ type=roverlay.argutil.is_config_opt,
+ help="additional variables",
+ )
+
+ return arg
+ # --- end of setup_config (...) ---
+
+ def parse_config ( self ):
+ for kv in self.parsed ['config_vars']:
+ key, eq_sign, val = kv.partition('=')
+ if key in { 'ADDITIONS_DIR', 'OVERLAY_ADDITIONS_DIR' }:
+ self.parser.exit (
+ 'use \'--additions-dir {0}\' instead of '
+ '\'--variable ADDITIONS_DIR={0}\'.'.format ( val )
+ )
+
+ self.parsed ['config_vars'].append (
+ "ADDITIONS_DIR=" + self.parsed ['additions_dir']
+ )
+ # --- end of parse_config (...) ---
+
+ def setup_init ( self ):
+ arg = self.add_argument_group (
+ 'init', title='options for the \'init\' command'
+ )
+
+ arg (
+ '--enable-default-hooks', dest='want_default_hooks',
+ default=True,
+ flags=self.ARG_WITH_DEFAULT|self.ARG_OPT_IN,
+ help='enable/update the default hooks',
+ )
+
+ arg (
+ '--no-default-hooks', dest='want_default_hooks',
+ flags=self.ARG_SHARED|self.ARG_OPT_OUT,
+ help='disable the default hooks',
+ )
+
+ arg (
+ '--import-config', '-I', dest='import_config',
+ default="symlink",
+ choices=[
+ "disable",
+ "symlink", "symlink=root",
+ "symlink=dirs", "symlink=files",
+ "copy"
+ ],
+ metavar='<mode>',
+ flags=self.ARG_WITH_DEFAULT,
+ help=(
+ 'choose whether and how --conf-root should be imported: '
+ '%(choices)s'
+ ),
+ )
+
+ arg (
+ '--no-import-config', dest='import_config',
+ action='store_const', const='disable',
+ help='disable config import (same as \'--import-config disable\')',
+ )
+
+
+ arg (
+ '--target-uid', dest='target_uid', default=os.getuid(),
+ metavar='<uid>', type=roverlay.argutil.is_uid,
+ flags=self.ARG_WITH_DEFAULT,
+ help="uid of the user that will run roverlay",
+ )
+
+ arg (
+ '--target-gid', dest='target_gid', default=os.getgid(),
+ metavar='<gid>', type=roverlay.argutil.is_gid,
+ flags=self.ARG_WITH_DEFAULT,
+ help='gid of the user that will run roverlay',
+ )
+
+ # --- end of setup_init (...) ---
+
+ def parse_init ( self ):
+ my_uid = os.getuid()
+
+ if my_uid and self.parsed ['target_uid'] != my_uid:
+ if self.parsed ['pretend']:
+ sys.stderr.write (
+ "!!! --target-uid: users cannot configure other users.\n\n"
+ )
+ else:
+ self.parser.exit (
+ "--target-uid: users cannot configure other users."
+ )
+ # --- end of parse_init (...) ---
+
+# --- end of SetupArgParser ---
+
+
+class SetupEnvironment ( roverlay.runtime.IndependentRuntimeEnvironment ):
+
+ LOG_LEVEL = logging.INFO
+
+ SHARED_DIR_MODE = roverlay.fsutil.get_stat_mode ( "rwxrwxr-x" )
+ PRIVATE_DIR_MODE = roverlay.fsutil.get_stat_mode ( "rwxr-x---" )
+ SHARED_FILE_MODE = roverlay.fsutil.get_stat_mode ( "rw-rw-r--" )
+ PRIVATE_FILE_MODE = roverlay.fsutil.get_stat_mode ( "rw-r-----" )
+
+ def __init__ ( self, *args, **kwargs ):
+ super ( SetupEnvironment, self ).__init__ ( *args, **kwargs )
+
+ self.UID = os.getuid()
+ self.GID = os.getgid()
+
+ self.expanduser = None
+ self.fs_ops = None
+ self.fs_ops_virtual = None
+
+ self.want_chown = None
+ self.data_root = None
+ self.work_root = None
+ self.conf_root = None
+ self.user_conf_root = None
+
+# not used
+# COLUMNS = os.environ.get ( 'COLUMNS', 78 )
+#
+# self.text_wrapper = textwrap.TextWrapper (
+# width=COLUMNS, initial_indent='', subsequent_indent='',
+# break_long_words=False, break_on_hyphens=False,
+# )
+ # --- end of __init__ (...) ---
+
+ def create_argparser ( self ):
+ instinfo = self.access_constant ( 'INSTALLINFO' )
+
+ return SetupArgParser (
+ description = 'roverlay setup script',
+ defaults = {
+ 'work_root' : instinfo ['workroot'],
+ 'data_root' : instinfo ['libexec'],
+ 'conf_root' : instinfo ['confroot'],
+ 'private_conf_root' : instinfo ['workroot'] + os.sep + 'config',
+ 'import_config' : 'symlink=root',
+ 'additions_dir' : instinfo ['workroot'] + os.sep + 'files',
+ },
+ )
+ # --- end of create_argparser (...) ---
+
+ def _get_config_roots ( self ):
+ return (
+ os.path.realpath ( self.conf_root ),
+ os.path.realpath ( self.user_conf_root )
+ )
+ # --- end of get_config_roots (...) ---
+
+ def get_user_config_root ( self ):
+ conf_root, user_conf_root = self._get_config_roots()
+ if conf_root == user_conf_root:
+ return None
+ else:
+ return user_conf_root
+ # --- end of get_user_config_root (...) ---
+
+ def get_config_file_path ( self ):
+ return (
+ self.work_root + os.sep + self.access_constant ( 'config_file_name' )
+ )
+ # --- end of get_config_file_path (...) ---
+
+ def create_new_target_config ( self ):
+ return self.create_new_config (
+ config_str=self.create_config_file ( expand_user=True )
+ )
+ # --- end of create_new_target_config (...) ---
+
+ def _expanduser_pwd ( self, fspath ):
+ return roverlay.fsutil.pwd_expanduser (
+ fspath, self.options ['target_uid']
+ )
+ # --- end of _expanduser_pwd (...) ---
+
+ def create_config_file ( self, expand_user=False ):
+ conf_creator = roverlay.config.defconfig.RoverlayConfigCreation (
+ is_installed = self.is_installed(),
+ work_root = (
+ self.work_root if expand_user else self.options ['work_root']
+ ),
+ data_root = (
+ self.data_root if expand_user else self.options ['data_root']
+ ),
+ conf_root = (
+ self.user_conf_root if expand_user
+ else self.options ['private_conf_root']
+ ),
+ )
+
+ for kv in self.options ['config_vars']:
+ key, sepa, value = kv.partition ( '=' )
+ if not sepa:
+ raise Exception ( "bad variable given: {!r}".format ( kv ) )
+ else:
+ conf_creator.set_option ( key, value )
+
+ return conf_creator.get_str()
+ # --- end of create_config_file (...) ---
+
+ def write_config_file ( self, output=None, expand_user=None ):
+ config_file_str = self.create_config_file (
+ expand_user = (
+ self.options ['config_expand_user'] if expand_user is None
+ else expand_user
+ ),
+ )
+ if not output or output == '-':
+ self.info ( config_file_str )
+ else:
+ with open ( output, 'wt' ) as FH:
+ FH.write ( config_file_str )
+ # --- end of write_config_file (...) ---
+
+ def auto_reconfigure ( self ):
+ self.reconfigure ( self.create_config_file() )
+ # --- end of auto_reconfigure (...) ---
+
+ def setup ( self ):
+ self.PWD_INITIAL = os.getcwd()
+ self.setup_common()
+
+ # ref
+ options = self.options
+
+ target_uid = options ['target_uid']
+ target_gid = options ['target_gid']
+
+ self.want_chown = ( target_uid != self.UID or target_gid != self.GID )
+
+ if self.UID == target_uid:
+ expanduser = os.path.expanduser
+ self.expanduser = os.path.expanduser
+ else:
+ expanduser = self._expanduser_pwd
+ self.expanduser = self._expanduser_pwd
+
+ self.work_root = expanduser ( options ['work_root'] )
+ self.data_root = expanduser ( options ['data_root'] )
+ self.conf_root = expanduser ( options ['conf_root'] )
+ self.user_conf_root = expanduser ( options ['private_conf_root'] )
+
+
+ self.fs_ops_virtual = {
+ 'private_dir': roverlay.fsutil.VirtualFsOperations (
+ uid=target_uid, gid=target_gid, mode=self.PRIVATE_DIR_MODE,
+ ),
+ 'shared_dir': roverlay.fsutil.VirtualFsOperations (
+ uid=target_uid, gid=target_gid, mode=self.SHARED_DIR_MODE,
+ ),
+ 'private_file': roverlay.fsutil.VirtualFsOperations (
+ uid=target_uid, gid=target_gid, mode=self.PRIVATE_FILE_MODE,
+ ),
+ 'shared_file': roverlay.fsutil.VirtualFsOperations (
+ uid=target_uid, gid=target_gid, mode=self.SHARED_FILE_MODE,
+ ),
+ }
+
+ if options ['pretend']:
+ self.fs_ops = self.fs_ops_virtual
+ else:
+ self.fs_ops = {
+ 'private_dir': roverlay.fsutil.FsOperations (
+ uid=target_uid, gid=target_gid, mode=self.PRIVATE_DIR_MODE,
+ ),
+ 'shared_dir': roverlay.fsutil.FsOperations (
+ uid=target_uid, gid=target_gid, mode=self.SHARED_DIR_MODE,
+ ),
+ 'private_file': roverlay.fsutil.FsOperations (
+ uid=target_uid, gid=target_gid, mode=self.PRIVATE_FILE_MODE,
+ ),
+ 'shared_file': roverlay.fsutil.FsOperations (
+ uid=target_uid, gid=target_gid, mode=self.SHARED_FILE_MODE,
+ ),
+ }
+
+ # bind fs_ops
+ self.private_dir = self.fs_ops ['private_dir']
+ self.shared_dir = self.fs_ops ['shared_dir']
+ self.private_file = self.fs_ops ['private_file']
+ self.shared_file = self.fs_ops ['shared_file']
+ # --- end of setup (...) ---
+
+ def wait_confirm ( self,
+ message=None, message_inline=None,
+ prepend_newline=True, append_newline=True
+ ):
+ try:
+ if prepend_newline:
+ self.info ( '\n' )
+
+ if message is not None:
+ self.info ( str ( message ) + '\n' )
+
+ if message_inline:
+ self.info (
+ "Press Enter to continue ({!s}) ... ".format ( message_inline )
+ )
+ else:
+ self.info ( "Press Enter to continue ... " )
+
+ self.stdout.flush()
+
+ ret = read_user_input().strip()
+
+ except ( KeyboardInterrupt, EOFError ):
+ self.info ( "\n" )
+ sys.exit ( 130 )
+ else:
+ if append_newline:
+ self.info ( '\n' )
+
+ return ret
+ # --- end of wait_confirm (...) ---
+
+ def wait_confirm_can_skip ( self,
+ SKIP_WORDS=frozenset({ 'skip', 'no', 'n'}), **kwargs
+ ):
+ assert 'message_inline' not in kwargs
+
+ if self.options ['wait_confirm']:
+
+ user_reply = self.wait_confirm (
+ message_inline="type {} to skip this step".format (
+ '/'.join ( repr( word ) for word in sorted( SKIP_WORDS ) )
+ ),
+ **kwargs
+ )
+
+ return user_reply.lower() not in SKIP_WORDS
+ else:
+ return True
+ # --- end of wait_confirm (...) ---
+
+ def get_init_env ( self ):
+ return roverlay.setupscript.initenv.SetupInitEnvironment ( self )
+ # --- end of get_init_env (...) ---
+
+ def get_hook_env ( self ):
+ return roverlay.setupscript.hookenv.SetupHookEnvironment ( self )
+ # --- end of get_hook_env (...) ---
+
+ def default_main ( self ):
+ if self.wants_command ( "mkconfig" ):
+ self.write_config_file ( self.options ['output'] )
+ elif self.wants_command ( "init" ):
+ self.get_init_env().run()
+ # --- end of default_main (...) ---
+
+# --- end of SetupEnvironment ---
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [gentoo-commits] proj/R_overlay:master commit in: roverlay/setupscript/, roverlay/
@ 2013-09-16 13:43 André Erdmann
0 siblings, 0 replies; 4+ messages in thread
From: André Erdmann @ 2013-09-16 13:43 UTC (permalink / raw
To: gentoo-commits
commit: b6d2a9ad04f59e59feca8a14e98470fda1451ad1
Author: André Erdmann <dywi <AT> mailerd <DOT> de>
AuthorDate: Mon Sep 16 10:32:03 2013 +0000
Commit: André Erdmann <dywi <AT> mailerd <DOT> de>
CommitDate: Mon Sep 16 10:32:42 2013 +0000
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=b6d2a9ad
roverlay/fsutil: RWX, FsPermissions
---
roverlay/fsutil.py | 198 +++++++++++++++++++++++++++++-----------
roverlay/setupscript/runtime.py | 8 +-
2 files changed, 148 insertions(+), 58 deletions(-)
diff --git a/roverlay/fsutil.py b/roverlay/fsutil.py
index 7f3296a..2cf0baa 100644
--- a/roverlay/fsutil.py
+++ b/roverlay/fsutil.py
@@ -115,57 +115,147 @@ def get_bitwise_sum ( iterable, initial_value=None ):
return ret
# --- end of get_bitwise_sum (...) ---
-def get_stat_mode ( mode_str ):
+class RWX ( object ):
+
+ @classmethod
+ def from_str ( cls, s, strict=False ):
+ readable, writable, executable = False, False, False
+
+ if strict:
+ _s = s.lower()
+ readable = _s[0] == 'r'
+ writable = _s[1] == 'w'
+ executable = _s[2] == 'x'
+
+ elif s:
+ for char in s.lower():
+ if char == 'r':
+ readable = True
+ elif char == 'w':
+ writable = True
+ elif char == 'x':
+ executable = True
+ # -- end for
+ # -- end if
+
+ return cls ( readable, writable, executable )
+ # --- end of from_str (...) ---
+
+ @classmethod
+ def from_bitmask ( cls, mode, rwx_bits ):
+ return cls (
+ mode & rwx_bits[0], mode & rwx_bits[1], mode & rwx_bits[2],
+ )
+ # --- end of from_bitmask (...) ---
+
+ def __init__ ( self, readable, writable, executable ):
+ super ( RWX, self ).__init__()
+ self.readable = bool ( readable )
+ self.writable = bool ( writable )
+ self.executable = bool ( executable )
+ # --- end of __init__ (...) ---
+
+ def __hash__ ( self ):
+ return id ( self )
+ # --- end of __hash__ (...) ---
+
+ def __repr__ ( self ):
+ return "<{cls.__name__}({val}) at 0x{addr:x}>".format (
+ cls = self.__class__,
+ val = self.get_str(),
+ addr = id ( self ),
+ )
+ # --- end of __repr__ (...) ---
+
+ def get_str ( self, fillchar='-' ):
+ return (
+ ( 'r' if self.readable else fillchar ) +
+ ( 'w' if self.writable else fillchar ) +
+ ( 'x' if self.executable else fillchar )
+ )
+ # --- end of get_str (...) ---
+
+ __str__ = get_str
+
+ def get_bitmask ( self, rwx_bits ):
+ ret = 0
+ if self.readable:
+ ret |= rwx_bits[0]
+
+ if self.writable:
+ ret |= rwx_bits[1]
+
+ if self.executable:
+ ret |= rwx_bits[2]
+
+ return ret
+ # --- end of get_bitmask (...) ---
+
+# --- end of RWX ---
+
+
+class FsPermissions ( object ):
- def iter_mode_values ( mode_str ):
- # rwxrwxrwx
- # 012345678
- # r -> pos % 3 == 0
- # w -> pos % 3 == 1
- # x -> pos % 3 == 2
-
-
- # COULDFIX: parse sticky bit etc. (not necessary, currently)
- if len ( mode_str ) > 9:
- raise ValueError ( mode_str )
-
- for pos, char in enumerate ( mode_str ):
- if char != '-':
- block = pos // 3
- subpos = pos % 3
- if subpos == 0:
- # r
- assert char == 'r'
- if block == 0:
- yield stat.S_IRUSR
- elif block == 1:
- yield stat.S_IRGRP
- else:
- yield stat.S_IROTH
-
- elif subpos == 1:
- # w
- assert char == 'w'
- if block == 0:
- yield stat.S_IWUSR
- elif block == 1:
- yield stat.S_IWGRP
- else:
- yield stat.S_IWOTH
-
- elif subpos == 2:
- # x
- assert char == 'x'
- if block == 0:
- yield stat.S_IXUSR
- elif block == 1:
- yield stat.S_IXGRP
- else:
- yield stat.S_IXOTH
-
- # --- end of iter_mode_values (...) ---
-
- return get_bitwise_sum ( iter_mode_values ( mode_str ) )
+ USR_BITS = ( stat.S_IRUSR, stat.S_IWUSR, stat.S_IXUSR )
+ GRP_BITS = ( stat.S_IRGRP, stat.S_IWGRP, stat.S_IXGRP )
+ OTH_BITS = ( stat.S_IROTH, stat.S_IWOTH, stat.S_IXOTH )
+
+ @classmethod
+ def from_str ( cls, s, strict=False ):
+ rwx_user = RWX.from_str ( s[0:3], strict=strict )
+ rwx_group = RWX.from_str ( s[3:6], strict=strict )
+ rwx_others = RWX.from_str ( s[6:9], strict=strict )
+ return cls ( rwx_user, rwx_group, rwx_others )
+ # --- end of from_str (...) ---
+
+ @classmethod
+ def from_stat_mode ( cls, stat_mode ):
+ return cls (
+ RWX.from_bitmask ( stat_mode, cls.USR_BITS ),
+ RWX.from_bitmask ( stat_mode, cls.GRP_BITS ),
+ RWX.from_bitmask ( stat_mode, cls.OTH_BITS ),
+ )
+ # --- end of from_stat_mode (...) ---
+
+ def __init__ ( self, rwx_user, rwx_group, rwx_others ):
+ super ( FsPermissions, self ).__init__()
+ self.user = rwx_user
+ self.group = rwx_group
+ self.others = rwx_others
+ # --- end of __init__ (...) ---
+
+ def __repr__ ( self ):
+ return "<{cls.__name__}({val}) at 0x{addr:x}>".format (
+ cls = self.__class__,
+ val = self.get_str(),
+ addr = id ( self ),
+ )
+ # --- end of __repr__ (...) ---
+
+ def get_str ( self, fillchar='-' ):
+ return "".join (
+ rwx.get_str ( fillchar=fillchar )
+ for rwx in ( self.user, self.group, self.others )
+ )
+ # --- end of get_str (...) ---
+
+ __str__ = get_str
+
+ def get_stat_mode ( self ):
+ return (
+ self.user.get_bitmask ( self.USR_BITS ) |
+ self.group.get_bitmask ( self.GRP_BITS ) |
+ self.others.get_bitmask ( self.OTH_BITS )
+ )
+ # --- end of get_stat_mode (...) ---
+
+ __int__ = get_stat_mode
+ __index__ = get_stat_mode
+
+# --- end of FsPermissions ---
+
+def get_stat_mode ( mode_str ):
+ return FsPermissions.from_str ( mode_str ).get_stat_mode()
# --- end of get_stat_mode (...) ---
class ChownChmod ( object ):
@@ -176,8 +266,8 @@ class ChownChmod ( object ):
self.pretend = bool ( pretend )
do_chown = uid is not None or gid is not None
- self.uid = -1 if uid is None else uid
- self.gid = -1 if gid is None else gid
+ self.uid = -1 if uid is None else int ( uid )
+ self.gid = -1 if gid is None else int ( gid )
if do_chown:
self.chown_str = "chown {uid:d}:{gid:d} {{}}".format (
@@ -197,7 +287,7 @@ class ChownChmod ( object ):
elif isinstance ( mode, str ):
self.mode = get_stat_mode ( mode )
else:
- self.mode = mode
+ self.mode = int ( mode )
if self.mode is None:
self.chmod_str = "NO CHMOD {}"
@@ -447,7 +537,7 @@ class VirtualFsOperations ( AbstractFsOperations ):
self.info ( ret + "\n" )
def chmod_chown_recursive ( self, root ):
- for word in self.perm_env.chmod_chown_recursive ( root ):
+ for word in self.perm_env.chown_chmod_recursive ( root ):
if word is not None:
self.info ( word + "\n" )
diff --git a/roverlay/setupscript/runtime.py b/roverlay/setupscript/runtime.py
index 6ce804b..f7a38cc 100644
--- a/roverlay/setupscript/runtime.py
+++ b/roverlay/setupscript/runtime.py
@@ -264,10 +264,10 @@ class SetupEnvironment ( roverlay.runtime.IndependentRuntimeEnvironment ):
LOG_LEVEL = logging.INFO
- SHARED_DIR_MODE = roverlay.fsutil.get_stat_mode ( "rwxrwxr-x" )
- PRIVATE_DIR_MODE = roverlay.fsutil.get_stat_mode ( "rwxr-x---" )
- SHARED_FILE_MODE = roverlay.fsutil.get_stat_mode ( "rw-rw-r--" )
- PRIVATE_FILE_MODE = roverlay.fsutil.get_stat_mode ( "rw-r-----" )
+ SHARED_DIR_MODE = "rwxrwxr-x"
+ PRIVATE_DIR_MODE = "rwxr-x---"
+ SHARED_FILE_MODE = "rw-rw-r--"
+ PRIVATE_FILE_MODE = "rw-r-----"
def __init__ ( self, *args, **kwargs ):
super ( SetupEnvironment, self ).__init__ ( *args, **kwargs )
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [gentoo-commits] proj/R_overlay:master commit in: roverlay/setupscript/, roverlay/
@ 2013-09-17 16:40 André Erdmann
0 siblings, 0 replies; 4+ messages in thread
From: André Erdmann @ 2013-09-17 16:40 UTC (permalink / raw
To: gentoo-commits
commit: 5dd5292462c176a1d1e7458ecd69fd4467ed9440
Author: André Erdmann <dywi <AT> mailerd <DOT> de>
AuthorDate: Tue Sep 17 14:10:03 2013 +0000
Commit: André Erdmann <dywi <AT> mailerd <DOT> de>
CommitDate: Tue Sep 17 14:10:03 2013 +0000
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=5dd52924
ArgumentParserProxy: use existing parser
@classmethod create_new_parser(...) can be used to get the old behavior
(ArgumentParserProxy creates the parser).
---
roverlay/argparser.py | 31 ++++++++++++++++++-------------
roverlay/argutil.py | 17 +++++++++++++----
roverlay/runtime.py | 4 +++-
roverlay/setupscript/runtime.py | 6 +++---
roverlay/status.py | 4 +++-
5 files changed, 40 insertions(+), 22 deletions(-)
diff --git a/roverlay/argparser.py b/roverlay/argparser.py
index 154fdce..5b28d0f 100644
--- a/roverlay/argparser.py
+++ b/roverlay/argparser.py
@@ -37,42 +37,45 @@ class RoverlayArgumentParserBase ( roverlay.argutil.ArgumentParserProxy ):
DESCRIPTION_TEMPLATE = None
- def __init__ (
- self, defaults=None, description=True, formatter_class=True,
- format_description=False, **kwargs
+ @classmethod
+ def create_new_parser ( cls,
+ defaults=None, description=True, formatter_class=True,
+ format_description=True, **kwargs
):
if description is True:
- if self.DESCRIPTION_TEMPLATE is None:
+ if cls.DESCRIPTION_TEMPLATE is None:
desc = (
roverlay.core.description_str + '\n'
+ roverlay.core.license_str
)
else:
- desc = self.format_description()
+ desc = cls.format_description()
elif description:
if format_description:
- desc = self.format_description ( description )
+ desc = cls.format_description ( description )
else:
desc = description
else:
desc = None
- super ( RoverlayArgumentParserBase, self ).__init__ (
+ return super ( RoverlayArgumentParserBase, cls ).create_new_parser (
defaults = defaults,
description = desc,
formatter_class = (
argparse.RawDescriptionHelpFormatter
if formatter_class is True else formatter_class
),
- **kwargs
)
+ # --- end of create_new_parser (...) ---
-
+ def __init__ ( self, *args, **kwargs ):
+ super ( RoverlayArgumentParserBase, self ).__init__ ( *args, **kwargs )
self.extra_conf = None
# --- end of __init__ (...) ---
- def format_description ( self, desc=None ):
- return ( self.DESCRIPTION_TEMPLATE if desc is None else desc ).format (
+ @classmethod
+ def format_description ( cls, desc=None ):
+ return ( cls.DESCRIPTION_TEMPLATE if desc is None else desc ).format (
version=roverlay.core.version,
license=roverlay.core.license_str,
)
@@ -566,8 +569,10 @@ class RoverlayArgumentParser ( RoverlayArgumentParserBase ):
COMMAND_DESCRIPTION = None
DEFAULT_COMMAND = None
- def __init__ ( self, default_command=None, **kwargs ):
- super ( RoverlayArgumentParser, self ).__init__ ( **kwargs )
+ def __init__ ( self, parser, default_command=None, defaults=None ):
+ super ( RoverlayArgumentParser, self ).__init__ (
+ parser, defaults=defaults
+ )
self.default_command = (
self.DEFAULT_COMMAND if default_command is None else default_command
)
diff --git a/roverlay/argutil.py b/roverlay/argutil.py
index 90444a3..c3981e5 100644
--- a/roverlay/argutil.py
+++ b/roverlay/argutil.py
@@ -184,11 +184,20 @@ class ArgumentParserProxy ( object ):
STR_SUPPRESS = 'keep'
-
-
- def __init__ ( self, defaults=None, **kwargs ):
+ @classmethod
+ def create_new_parser ( cls, defaults=None, **kwargs ):
+ parser = argparse.ArgumentParser ( **kwargs )
+ return cls ( parser, defaults=defaults )
+ # --- end of create_new_parser (...) ---
+
+ @classmethod
+ def wrap ( cls, parser, defaults=None ):
+ return cls ( parser, defaults=defaults )
+ # --- end of wrap (...) ---
+
+ def __init__ ( self, parser, defaults=None ):
super ( ArgumentParserProxy, self ).__init__()
- self.parser = argparse.ArgumentParser (**kwargs )
+ self.parser = parser
if defaults is None:
self.defaults = dict()
diff --git a/roverlay/runtime.py b/roverlay/runtime.py
index 2c12334..43140b2 100644
--- a/roverlay/runtime.py
+++ b/roverlay/runtime.py
@@ -150,7 +150,9 @@ class RuntimeEnvironmentBase ( MinimalRuntimeEnvironment ):
class RuntimeEnvironment ( RuntimeEnvironmentBase ):
- ARG_PARSER_CLS = roverlay.argparser.RoverlayMainArgumentParser
+ ARG_PARSER_CLS = (
+ roverlay.argparser.RoverlayMainArgumentParser.create_new_parser
+ )
def __init__ ( self, installed, *args, **kw ):
super ( RuntimeEnvironment, self ).__init__ ( installed, *args, **kw )
diff --git a/roverlay/setupscript/runtime.py b/roverlay/setupscript/runtime.py
index 8b51973..75bfff6 100644
--- a/roverlay/setupscript/runtime.py
+++ b/roverlay/setupscript/runtime.py
@@ -47,7 +47,7 @@ def arg_stdout_or_fs ( value ):
# --- end of arg_stdout_or_fs (...) ---
-class SetupArgParser ( roverlay.argparser.RoverlayArgumentParser ):
+class SetupArgumentParser ( roverlay.argparser.RoverlayArgumentParser ):
MULTIPLE_COMMANDS = False
COMMAND_DESCRIPTION = {
'init' : 'initialize roverlay\'s config and filesystem layout',
@@ -262,7 +262,7 @@ class SetupArgParser ( roverlay.argparser.RoverlayArgumentParser ):
return arg
# --- end of setup_hooks (...) ---
-# --- end of SetupArgParser ---
+# --- end of SetupArgumentParser ---
class SetupEnvironment ( roverlay.runtime.IndependentRuntimeEnvironment ):
@@ -326,7 +326,7 @@ class SetupEnvironment ( roverlay.runtime.IndependentRuntimeEnvironment ):
# --- end of get_parser_defaults (...) ---
def create_argparser ( self ):
- return SetupArgParser (
+ return SetupArgumentParser.create_new_parser (
description = 'roverlay setup script',
defaults = self.get_parser_defaults(),
epilog = (
diff --git a/roverlay/status.py b/roverlay/status.py
index 50372b8..cb25edb 100644
--- a/roverlay/status.py
+++ b/roverlay/status.py
@@ -118,7 +118,9 @@ class SelfReferencingDict ( ReferenceableDict ):
class StatusRuntimeEnvironment ( roverlay.runtime.RuntimeEnvironmentBase ):
- ARG_PARSER_CLS = roverlay.argparser.RoverlayStatusArgumentParser
+ ARG_PARSER_CLS = (
+ roverlay.argparser.RoverlayStatusArgumentParser.create_new_parser
+ )
TEMPLATE_ENCODING = 'utf-8'
^ permalink raw reply related [flat|nested] 4+ messages in thread
* [gentoo-commits] proj/R_overlay:master commit in: roverlay/setupscript/, roverlay/
@ 2014-02-22 14:56 André Erdmann
0 siblings, 0 replies; 4+ messages in thread
From: André Erdmann @ 2014-02-22 14:56 UTC (permalink / raw
To: gentoo-commits
commit: 1587a9392d1325315e3feb1de5e59e19dd670b57
Author: André Erdmann <dywi <AT> mailerd <DOT> de>
AuthorDate: Fri Feb 21 18:30:06 2014 +0000
Commit: André Erdmann <dywi <AT> mailerd <DOT> de>
CommitDate: Fri Feb 21 18:30:06 2014 +0000
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=1587a939
roverlay/fsutil: walk_relpath()
Common functionality for get_fs_dict(), walk_copy_tree().
+ pwd_unexpanduser() function
* pwd_expanduser() uses roverlay.util.common.get_home_dir() now
* get_fs_dict(): don't ignore top-level files unless explicitly stated
---
roverlay/fsutil.py | 144 +++++++++++++++++++++++++++++-----------
roverlay/setupscript/hookenv.py | 1 +
2 files changed, 105 insertions(+), 40 deletions(-)
diff --git a/roverlay/fsutil.py b/roverlay/fsutil.py
index d0091c2..0e9d439 100644
--- a/roverlay/fsutil.py
+++ b/roverlay/fsutil.py
@@ -16,11 +16,57 @@ import sys
import roverlay.util.common
import roverlay.util.objects
+from roverlay.util.common import get_home_dir
_OS_CHOWN = getattr ( os, 'lchown', os.chown )
_OS_CHMOD = getattr ( os, 'lchmod', os.chmod )
+def walk_relpath (
+ root, include_root=False,
+ dirname_filter=None, filename_filter=None, prune_empty=False,
+ **os_walk_kwargs
+):
+ """See get_fs_dict().
+
+ arguments:
+ * root --
+ * include_root --
+ * dirname_filter --
+ * filename_filter --
+ * prune_empty --
+ * os_walk_kwargs --
+ """
+ abs_root = os.path.abspath ( root )
+ if include_root:
+ relpath_begin = 1 + abs_root.rfind ( os.sep )
+ else:
+ relpath_begin = 1 + len ( abs_root )
+
+ walker = os.walk ( abs_root, **os_walk_kwargs )
+
+ if dirname_filter or filename_filter:
+ for subroot, dirnames, filenames in walker:
+ if dirname_filter:
+ dirnames[:] = [ d for d in dirnames if dirname_filter ( d ) ]
+
+ if filename_filter:
+ filenames[:] = [ f for f in filenames if filename_filter ( f ) ]
+
+ if not prune_empty or filenames or dirnames:
+ yield ( subroot, subroot[relpath_begin:], dirnames, filenames )
+
+ elif prune_empty:
+ for subroot, dirnames, filenames in walker:
+ if dirnames or filenames:
+ yield ( subroot, subroot[relpath_begin:], dirnames, filenames )
+
+ else:
+ for subroot, dirnames, filenames in walker:
+ yield ( subroot, subroot[relpath_begin:], dirnames, filenames )
+# --- end of walk_relpath (...) ---
+
+
def walk_up ( dirpath, topdown=False, max_iter=None ):
"""Generator that yields all (partial..full) filesystem paths contained
in dirpath.
@@ -75,7 +121,7 @@ def walk_up ( dirpath, topdown=False, max_iter=None ):
def get_fs_dict (
initial_root, create_item=None, dict_cls=dict,
dirname_filter=None, filename_filter=None,
- include_root=False, prune_empty=False, file_key=None,
+ include_root=False, toplevel_files=True, prune_empty=False, file_key=None,
):
"""Creates a dictionary-like object representing the filesystem structure
starting at the given root directory.
@@ -103,6 +149,8 @@ def get_fs_dict (
controls whether the return value should be a dict
starting at initial_root or a dict containing the
initial_root dict. Defaults to False.
+ * toplevel_files -- whether to include top-level files in the dict (True)
+ or not (False). Defaults to True.
* prune_empty -- Whether to remove directory entries without any items.
Defaults to False.
* file_key -- Either a function that returns the dict key for a
@@ -111,41 +159,43 @@ def get_fs_dict (
Inspired by http://code.activestate.com/recipes/577879-create-a-nested-dictionary-from-oswalk/
"""
# TODO(could-do): max_depth=N
- fsdict = dict_cls()
- my_root = os.path.abspath ( initial_root )
+ fsdict = dict_cls()
+ get_file_key = ( lambda x: x ) if file_key is None else file_key
- get_file_key = ( lambda x: x ) if file_key is None else file_key
- dictpath_begin = (
- 1 + ( my_root.rfind ( os.sep ) if include_root else len ( my_root ) )
- )
-
- for root, dirnames, filenames in os.walk ( my_root ):
- if dirname_filter:
- dirnames[:] = [ d for d in dirnames if dirname_filter ( d ) ]
+ for root, dict_relpath, dirnames, filenames in walk_relpath (
+ initial_root, include_root=include_root, prune_empty=prune_empty,
+ dirname_filter=dirname_filter, filename_filter=filename_filter
+ ):
+ if dict_relpath:
+ dictpath = dict_relpath.split ( os.sep )
+ parent = functools.reduce ( dict_cls.get, dictpath[:-1], fsdict )
- if filename_filter:
- filenames[:] = [ f for f in filenames if filename_filter ( f ) ]
+ if create_item is None:
+ parent [dictpath[-1]] = dict_cls.fromkeys (
+ map ( get_file_key, filenames )
+ )
+ else:
+ parent [dictpath[-1]] = dict_cls (
+ (
+ get_file_key ( fname ),
+ create_item ( ( root + os.sep + fname ), fname, root )
+ )
+ for fname in filenames
+ )
- if not prune_empty or filenames or dirnames:
- dict_relpath = root[dictpath_begin:]
+ elif not toplevel_files:
+ pass
- if dict_relpath:
- dictpath = dict_relpath.split ( os.sep )
- parent = functools.reduce ( dict_cls.get, dictpath[:-1], fsdict )
+ elif create_item is None:
+ for fname in filenames:
+ fsdict [get_file_key(fname)] = None
- if create_item is None:
- parent [dictpath[-1]] = dict_cls.fromkeys (
- map ( get_file_key, filenames )
- )
- else:
- parent [dictpath[-1]] = dict_cls (
- (
- get_file_key ( fname ),
- create_item ( ( root + os.sep + fname ), fname, root )
- )
- for fname in filenames
- )
+ else:
+ for fname in filenames:
+ fsdict [get_file_key(fname)] = create_item (
+ ( root + os.sep + fname ), fname, root
+ )
# -- end for
return fsdict
@@ -203,13 +253,29 @@ def pwd_expanduser ( fspath, uid ):
if not fspath or fspath[0] != '~':
return fspath
elif len ( fspath ) < 2:
- return pwd.getpwuid ( uid ).pw_dir
+ return get_home_dir ( uid )
elif fspath[1] == os.sep:
- return pwd.getpwuid ( uid ).pw_dir + fspath[1:]
+ return get_home_dir ( uid ) + fspath[1:]
else:
return fspath
# --- end of pwd_expanduser (...) ---
+def pwd_unexpanduser ( fspath, uid ):
+ """Replaces a user's home directory with "~" in the given filesystem path.
+
+ arguments:
+ * fspath --
+ * uid --
+ """
+ home_dir = get_home_dir ( uid )
+ if not fspath.startswith ( home_dir ):
+ return fspath
+ elif len ( fspath ) == len ( home_dir ):
+ return '~'
+ else:
+ return '~' + fspath[len(home_dir):]
+# --- end of pwd_unexpanduser (...) ---
+
def walk_copy_tree ( source, dest, subdir_root=False, **walk_kwargs ):
"""Generator that iterates over the content of a filesystem tree starting
at source and compares it to the filesystem tree starting at dest (which
@@ -235,13 +301,10 @@ def walk_copy_tree ( source, dest, subdir_root=False, **walk_kwargs ):
* dest -- absolute path to the dest root
* subdir_root -- whether source should be a subdir of dest root or not
Defaults to False.
- * **walk_kwargs -- additional keyword arguments for os.walk()
+ * **walk_kwargs -- additional keyword arguments for walk_relpath()
"""
- source_path = os.path.abspath ( source )
- dest_path = os.path.abspath ( dest )
- relpath_begin = 1 + (
- source_path.rfind ( os.sep ) if subdir_root else len ( source_path )
- )
+ source_path = os.path.abspath ( source )
+ dest_path = os.path.abspath ( dest )
get_entry = lambda path: (
path, os.lstat ( path ) if os.path.lexists ( path ) else None
@@ -250,8 +313,9 @@ def walk_copy_tree ( source, dest, subdir_root=False, **walk_kwargs ):
[ ( get_entry ( s + name ), get_entry ( d + name ) ) for name in names ]
)
- for root, dirnames, filenames in os.walk ( source, **walk_kwargs ):
- root_rel = root[relpath_begin:]
+ for root, root_rel, dirnames, filenames in walk_relpath (
+ source_path, include_root=subdir_root, **walk_kwargs
+ ):
root_dest = ( dest + os.sep + root_rel if root_rel else dest )
dirs = get_stat_list ( root + os.sep, root_dest + os.sep, dirnames )
diff --git a/roverlay/setupscript/hookenv.py b/roverlay/setupscript/hookenv.py
index 23b1c68..46a2344 100644
--- a/roverlay/setupscript/hookenv.py
+++ b/roverlay/setupscript/hookenv.py
@@ -639,6 +639,7 @@ class NestedHookScriptDirBase ( HookScriptDirBase ):
include_root = False,
prune_empty = prune_empty,
file_key = get_script_name,
+ toplevel_files = False,
)
self.scripts.update ( new_scripts )
self.scan_scripts()
^ permalink raw reply related [flat|nested] 4+ messages in thread
end of thread, other threads:[~2014-02-22 14:56 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-09-17 16:40 [gentoo-commits] proj/R_overlay:master commit in: roverlay/setupscript/, roverlay/ André Erdmann
-- strict thread matches above, loose matches on Subject: below --
2014-02-22 14:56 André Erdmann
2013-09-16 13:43 André Erdmann
2013-09-11 11:14 André Erdmann
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox