Beispiel #1
0
    def _rec_resolve_profile_include(self, profile):
        """
        recursively resolve include of other profiles's:
        * dotfiles
        * actions
        """
        this_profile = self.profiles[profile]

        # include
        dotfiles = this_profile.get(self.key_profile_dotfiles, [])
        actions = this_profile.get(self.key_profile_actions, [])
        includes = this_profile.get(self.key_profile_include, None)
        if not includes:
            # nothing to include
            return dotfiles, actions
        if self.debug:
            self.log.dbg('{} includes: {}'.format(profile, ','.join(includes)))
            self.log.dbg('{} dotfiles before include: {}'.format(
                profile, dotfiles))
            self.log.dbg('{} actions before include: {}'.format(
                profile, actions))

        seen = []
        for i in uniq_list(includes):
            # ensure no include loop occurs
            if i in seen:
                raise YamlException('\"include loop\"')
            seen.append(i)
            # included profile even exists
            if i not in self.profiles.keys():
                self.log.warn('include unknown profile: {}'.format(i))
                continue
            # recursive resolve
            o_dfs, o_actions = self._rec_resolve_profile_include(i)
            # merge dotfile keys
            dotfiles.extend(o_dfs)
            this_profile[self.key_profile_dotfiles] = uniq_list(dotfiles)
            # merge actions keys
            actions.extend(o_actions)
            this_profile[self.key_profile_actions] = uniq_list(actions)

        dotfiles = this_profile.get(self.key_profile_dotfiles, [])
        actions = this_profile.get(self.key_profile_actions, [])
        if self.debug:
            self.log.dbg('{} dotfiles after include: {}'.format(
                profile, dotfiles))
            self.log.dbg('{} actions after include: {}'.format(
                profile, actions))

        # since dotfiles and actions are resolved here
        # and variables have been already done at the beginning
        # of the parsing, we can clear these include
        self.profiles[profile][self.key_profile_include] = None
        return dotfiles, actions
Beispiel #2
0
    def _comp_dir(self, left, right, ignore):
        """compare a directory"""
        if self.debug:
            self.log.dbg('compare directory {} with {}'.format(left, right))
        if not os.path.exists(right):
            return ''
        if must_ignore([left, right], ignore, debug=self.debug):
            if self.debug:
                self.log.dbg('ignoring diff {} and {}'.format(left, right))
            return ''
        if not os.path.isdir(right):
            return '\"{}\" is a file\n'.format(right)
        if self.debug:
            self.log.dbg('compare {} and {}'.format(left, right))
        ret = []
        comp = filecmp.dircmp(left, right)

        # handle files only in deployed dir
        for i in comp.left_only:
            if must_ignore([os.path.join(left, i)], ignore, debug=self.debug):
                continue
            ret.append('=> \"{}\" does not exist on local\n'.format(i))

        # handle files only in dotpath dir
        for i in comp.right_only:
            if must_ignore([os.path.join(right, i)], ignore, debug=self.debug):
                continue
            ret.append('=> \"{}\" does not exist in dotdrop\n'.format(i))

        # same left and right but different type
        funny = comp.common_funny
        for i in funny:
            lfile = os.path.join(left, i)
            rfile = os.path.join(right, i)
            if must_ignore([lfile, rfile], ignore, debug=self.debug):
                continue
            short = os.path.basename(lfile)
            # file vs dir
            ret.append('=> different type: \"{}\"\n'.format(short))

        # content is different
        funny = comp.diff_files
        funny.extend(comp.funny_files)
        funny = uniq_list(funny)
        for i in funny:
            lfile = os.path.join(left, i)
            rfile = os.path.join(right, i)
            if must_ignore([lfile, rfile], ignore, debug=self.debug):
                continue
            diff = self._diff(lfile, rfile, header=True)
            ret.append(diff)

        # recursively compare subdirs
        for i in comp.common_dirs:
            subleft = os.path.join(left, i)
            subright = os.path.join(right, i)
            ret.extend(self._comp_dir(subleft, subright, ignore))

        return ''.join(ret)
Beispiel #3
0
 def _apply_args_update(self):
     """update specifics"""
     self.update_path = self.args['<path>']
     self.update_iskey = self.args['--key']
     self.update_ignore = self.args['--ignore']
     self.update_ignore.extend(self.upignore)
     self.update_ignore.append('*{}'.format(self.install_backup_suffix))
     self.update_ignore = uniq_list(self.update_ignore)
     self.update_showpatch = self.args['--show-patch']
Beispiel #4
0
 def _apply_args_import(self):
     """import specifics"""
     self.import_path = self.args['<path>']
     self.import_as = self.args['--as']
     self.import_mode = self.args['--preserve-mode'] or self.chmod_on_import
     self.import_ignore = self.args['--ignore']
     self.import_ignore.extend(self.impignore)
     self.import_ignore.append('*{}'.format(self.install_backup_suffix))
     self.import_ignore = uniq_list(self.import_ignore)
Beispiel #5
0
 def _apply_args_compare(self):
     """compare specifics"""
     self.compare_focus = self.args['--file']
     self.compare_ignore = self.args['--ignore']
     self.compare_ignore.extend(self.cmpignore)
     self.compare_ignore.append('*{}'.format(self.install_backup_suffix))
     self.compare_ignore = uniq_list(self.compare_ignore)
     self.compare_fileonly = self.args['--file-only']
     self.ignore_missing_in_dotdrop = self.ignore_missing_in_dotdrop or \
         self.args['--ignore-missing']
Beispiel #6
0
def cmd_detail(o):
    """list details on all files for all dotfile entries"""
    if o.profile not in [p.key for p in o.profiles]:
        LOG.warn('unknown profile \"{}\"'.format(o.profile))
        return
    dotfiles = o.dotfiles
    if o.detail_keys:
        # filtered dotfiles to install
        uniq = uniq_list(o.details_keys)
        dotfiles = [d for d in dotfiles if d.key in uniq]
    LOG.emph('dotfiles details for profile \"{}\":\n'.format(o.profile))
    for d in dotfiles:
        _detail(o.dotpath, d)
    LOG.log('')
Beispiel #7
0
    def _template_dotfiles_paths(self):
        """template dotfiles paths"""
        if self._debug:
            self._dbg('templating dotfiles paths')
        dotfiles = self.dotfiles.copy()

        # make sure no dotfiles path is None
        for dotfile in dotfiles.values():
            src = dotfile[self.key_dotfile_src]
            if src is None:
                dotfile[self.key_dotfile_src] = ''
            dst = dotfile[self.key_dotfile_dst]
            if dst is None:
                dotfile[self.key_dotfile_dst] = ''

        # only keep dotfiles related to the selected profile
        pdfs = []
        pro = self.profiles.get(self._profile, [])
        if pro:
            pdfs = list(pro.get(self.key_profile_dotfiles, []))
        for addpro in self._inc_profiles:
            pro = self.profiles.get(addpro, [])
            if not pro:
                continue
            pdfsalt = pro.get(self.key_profile_dotfiles, [])
            pdfs.extend(pdfsalt)
            pdfs = uniq_list(pdfs)

        if self.key_all not in pdfs:
            # take a subset of the dotfiles
            newdotfiles = {}
            for k, v in dotfiles.items():
                if k in pdfs:
                    newdotfiles[k] = v
            dotfiles = newdotfiles

        for dotfile in dotfiles.values():
            # src
            src = dotfile[self.key_dotfile_src]
            newsrc = self.resolve_dotfile_src(src, templater=self._tmpl)
            dotfile[self.key_dotfile_src] = newsrc
            # dst
            dst = dotfile[self.key_dotfile_dst]
            newdst = self.resolve_dotfile_dst(dst, templater=self._tmpl)
            dotfile[self.key_dotfile_dst] = newdst
Beispiel #8
0
def cmd_install(o):
    """install dotfiles for this profile"""
    dotfiles = o.dotfiles
    prof = o.conf.get_profile()

    adapt_workers(o, LOG)

    pro_pre_actions = prof.get_pre_actions() if prof else []
    pro_post_actions = prof.get_post_actions() if prof else []

    if o.install_keys:
        # filtered dotfiles to install
        uniq = uniq_list(o.install_keys)
        dotfiles = [d for d in dotfiles if d.key in uniq]
    if not dotfiles:
        msg = 'no dotfile to install for this profile (\"{}\")'
        LOG.warn(msg.format(o.profile))
        return False

    # the installer
    tmpdir = None
    if o.install_temporary:
        tmpdir = get_tmpdir()

    installed = []

    # execute profile pre-action
    if o.debug:
        LOG.dbg('run {} profile pre actions'.format(len(pro_pre_actions)))
    t = _get_templater(o)
    ret, err = action_executor(o, pro_pre_actions, [], t, post=False)()
    if not ret:
        return False

    # install each dotfile
    if o.workers > 1:
        # in parallel
        if o.debug:
            LOG.dbg('run with {} workers'.format(o.workers))
        ex = futures.ThreadPoolExecutor(max_workers=o.workers)

        wait_for = []
        for dotfile in dotfiles:
            j = ex.submit(_dotfile_install, o, dotfile, tmpdir=tmpdir)
            wait_for.append(j)
        # check result
        for f in futures.as_completed(wait_for):
            r, key, err = f.result()
            if r:
                installed.append(key)
            elif err:
                LOG.err('installing \"{}\" failed: {}'.format(key, err))
    else:
        # sequentially
        for dotfile in dotfiles:
            r, key, err = _dotfile_install(o, dotfile, tmpdir=tmpdir)
            # check result
            if r:
                installed.append(key)
            elif err:
                LOG.err('installing \"{}\" failed: {}'.format(key, err))

    # execute profile post-action
    if len(installed) > 0 or o.install_force_action:
        if o.debug:
            msg = 'run {} profile post actions'
            LOG.dbg(msg.format(len(pro_post_actions)))
        ret, err = action_executor(o, pro_post_actions, [], t, post=False)()
        if not ret:
            return False

    if o.debug:
        LOG.dbg('install done: installed \"{}\"'.format(','.join(installed)))

    if o.install_temporary:
        LOG.log('\ninstalled to tmp \"{}\".'.format(tmpdir))
    LOG.log('\n{} dotfile(s) installed.'.format(len(installed)))
    return True
Beispiel #9
0
    def _apply_args(self):
        """apply cli args as attribute"""
        # the commands
        self.cmd_profiles = self.args['profiles']
        self.cmd_files = self.args['files']
        self.cmd_install = self.args['install']
        self.cmd_compare = self.args['compare']
        self.cmd_import = self.args['import']
        self.cmd_update = self.args['update']
        self.cmd_detail = self.args['detail']
        self.cmd_remove = self.args['remove']

        # adapt attributes based on arguments
        self.safe = not self.args['--force']

        # import link default value
        self.import_link = self.link_on_import
        if self.args['--link']:
            # overwrite default import link with cli switch
            link = self.args['--link']
            if link not in OPT_LINK.keys():
                self.log.err('bad option for --link: {}'.format(link))
                sys.exit(USAGE)
            self.import_link = OPT_LINK[link]

        # "files" specifics
        self.files_templateonly = self.args['--template']
        self.files_grepable = self.args['--grepable']
        # "profiles" specifics
        self.profiles_grepable = self.args['--grepable']
        # "install" specifics
        self.install_force_action = self.args['--force-actions']
        self.install_temporary = self.args['--temp']
        self.install_keys = self.args['<key>']
        self.install_diff = not self.args['--nodiff']
        self.install_showdiff = self.showdiff or self.args['--showdiff']
        self.install_backup_suffix = BACKUP_SUFFIX
        self.install_default_actions_pre = [
            a for a in self.default_actions if a.kind == Action.pre
        ]
        self.install_default_actions_post = [
            a for a in self.default_actions if a.kind == Action.post
        ]
        self.install_ignore = self.instignore
        # "compare" specifics
        self.compare_focus = self.args['--file']
        self.compare_ignore = self.args['--ignore']
        self.compare_ignore.extend(self.cmpignore)
        self.compare_ignore.append('*{}'.format(self.install_backup_suffix))
        self.compare_ignore = uniq_list(self.compare_ignore)
        # "import" specifics
        self.import_path = self.args['<path>']
        self.import_as = self.args['--as']
        # "update" specifics
        self.update_path = self.args['<path>']
        self.update_iskey = self.args['--key']
        self.update_ignore = self.args['--ignore']
        self.update_ignore.extend(self.upignore)
        self.update_ignore.append('*{}'.format(self.install_backup_suffix))
        self.update_ignore = uniq_list(self.update_ignore)
        self.update_showpatch = self.args['--show-patch']
        # "detail" specifics
        self.detail_keys = self.args['<key>']
        # "remove" specifics
        self.remove_path = self.args['<path>']
        self.remove_iskey = self.args['--key']
Beispiel #10
0
    def _comp_dir(self, local_path, deployed_path, ignore):
        """compare a directory"""
        if self.debug:
            self.log.dbg('compare directory {} with {}'.format(
                local_path,
                deployed_path,
            ))
        if not os.path.exists(deployed_path):
            return ''
        if (self.ignore_missing_in_dotdrop and not
                os.path.exists(local_path)) \
                or must_ignore([local_path, deployed_path], ignore,
                               debug=self.debug):
            if self.debug:
                self.log.dbg('ignoring diff {} and {}'.format(
                    local_path,
                    deployed_path,
                ))
            return ''
        if not os.path.isdir(deployed_path):
            return '\"{}\" is a file\n'.format(deployed_path)
        if self.debug:
            self.log.dbg('compare {} and {}'.format(local_path, deployed_path))
        ret = []
        comp = filecmp.dircmp(local_path, deployed_path)

        # handle files only in deployed dir
        for i in comp.left_only:
            if self.ignore_missing_in_dotdrop:
                continue
            if must_ignore([os.path.join(local_path, i)],
                           ignore,
                           debug=self.debug):
                continue
            ret.append('=> \"{}\" does not exist on destination\n'.format(i))

        # handle files only in dotpath dir
        for i in comp.right_only:
            if must_ignore([os.path.join(deployed_path, i)],
                           ignore,
                           debug=self.debug):
                continue

            if not self.ignore_missing_in_dotdrop:
                ret.append('=> \"{}\" does not exist in dotdrop\n'.format(i))

        # same local_path and deployed_path but different type
        funny = comp.common_funny
        for i in funny:
            source_file = os.path.join(local_path, i)
            deployed_file = os.path.join(deployed_path, i)
            if self.ignore_missing_in_dotdrop and \
                    not os.path.exists(source_file):
                continue
            if must_ignore([source_file, deployed_file],
                           ignore,
                           debug=self.debug):
                continue
            short = os.path.basename(source_file)
            # file vs dir
            ret.append('=> different type: \"{}\"\n'.format(short))

        # content is different
        funny = comp.diff_files
        funny.extend(comp.funny_files)
        funny = uniq_list(funny)
        for i in funny:
            source_file = os.path.join(local_path, i)
            deployed_file = os.path.join(deployed_path, i)
            if self.ignore_missing_in_dotdrop and \
                    not os.path.exists(source_file):
                continue
            if must_ignore([source_file, deployed_file],
                           ignore,
                           debug=self.debug):
                continue
            ret.append(self._diff(source_file, deployed_file, header=True))

        # recursively compare subdirs
        for i in comp.common_dirs:
            sublocal_path = os.path.join(local_path, i)
            subdeployed_path = os.path.join(deployed_path, i)
            ret.extend(self._comp_dir(sublocal_path, subdeployed_path, ignore))

        return ''.join(ret)
Beispiel #11
0
    def _rec_resolve_profile_include(self, profile):
        """
        recursively resolve include of other profiles's:
        * dotfiles
        * actions
        returns dotfiles, actions
        """
        this_profile = self.profiles[profile]

        # considered profile content
        dotfiles = this_profile.get(self.key_profile_dotfiles, []) or []
        actions = this_profile.get(self.key_profile_actions, []) or []
        includes = this_profile.get(self.key_profile_include, []) or []
        if not includes:
            # nothing to include
            return dotfiles, actions

        if self._debug:
            self._dbg('{} includes {}'.format(profile, ','.join(includes)))
            self._dbg('{} dotfiles before include: {}'.format(
                profile, dotfiles))
            self._dbg('{} actions before include: {}'.format(profile, actions))

        seen = []
        for i in uniq_list(includes):
            if self._debug:
                self._dbg('resolving includes "{}" <- "{}"'.format(profile, i))

            # ensure no include loop occurs
            if i in seen:
                raise YamlException('\"include loop\"')
            seen.append(i)
            # included profile even exists
            if i not in self.profiles.keys():
                self._log.warn('include unknown profile: {}'.format(i))
                continue

            # recursive resolve
            if self._debug:
                self._dbg(
                    'recursively resolving includes for profile "{}"'.format(
                        i))
            o_dfs, o_actions = self._rec_resolve_profile_include(i)

            # merge dotfile keys
            if self._debug:
                self._dbg('Merging dotfiles {} <- {}: {} <- {}'.format(
                    profile, i, dotfiles, o_dfs))
            dotfiles.extend(o_dfs)
            this_profile[self.key_profile_dotfiles] = uniq_list(dotfiles)

            # merge actions keys
            if self._debug:
                self._dbg('Merging actions {} <- {}: {} <- {}'.format(
                    profile, i, actions, o_actions))
            actions.extend(o_actions)
            this_profile[self.key_profile_actions] = uniq_list(actions)

        dotfiles = this_profile.get(self.key_profile_dotfiles, [])
        actions = this_profile.get(self.key_profile_actions, [])

        if self._debug:
            self._dbg('{} dotfiles after include: {}'.format(
                profile, dotfiles))
            self._dbg('{} actions after include: {}'.format(profile, actions))

        # since included items are resolved here
        # we can clear these include
        self.profiles[profile][self.key_profile_include] = []
        return dotfiles, actions
Beispiel #12
0
def cmd_install(opts):
    """install dotfiles for this profile"""
    dotfiles = opts.dotfiles
    prof = opts.conf.get_profile()

    adapt_workers(opts, LOG)

    pro_pre_actions = prof.get_pre_actions() if prof else []
    pro_post_actions = prof.get_post_actions() if prof else []

    if opts.install_keys:
        # filtered dotfiles to install
        uniq = uniq_list(opts.install_keys)
        dotfiles = [d for d in dotfiles if d.key in uniq]

    if not dotfiles:
        msg = 'no dotfile to install for this profile (\"{}\")'
        LOG.warn(msg.format(opts.profile))
        return False

    LOG.dbg('dotfiles registered for install: {}'.format(
        [k.key for k in dotfiles]))

    # the installer
    tmpdir = None
    if opts.install_temporary:
        tmpdir = get_tmpdir()

    installed = []

    # clear the workdir
    if opts.install_clear_workdir and not opts.dry:
        LOG.dbg('clearing the workdir under {}'.format(opts.workdir))
        for root, _, files in os.walk(opts.workdir):
            for file in files:
                fpath = os.path.join(root, file)
                removepath(fpath, logger=LOG)

    # execute profile pre-action
    LOG.dbg('run {} profile pre actions'.format(len(pro_pre_actions)))
    templ = _get_templater(opts)
    ret, _ = action_executor(opts, pro_pre_actions, [], templ, post=False)()
    if not ret:
        return False

    # install each dotfile
    if opts.workers > 1:
        # in parallel
        LOG.dbg('run with {} workers'.format(opts.workers))
        ex = futures.ThreadPoolExecutor(max_workers=opts.workers)

        wait_for = []
        for dotfile in dotfiles:
            j = ex.submit(_dotfile_install, opts, dotfile, tmpdir=tmpdir)
            wait_for.append(j)
        # check result
        for fut in futures.as_completed(wait_for):
            tmpret, key, err = fut.result()
            # check result
            if tmpret:
                installed.append(key)
            elif err:
                LOG.err('installing \"{}\" failed: {}'.format(key, err))
    else:
        # sequentially
        for dotfile in dotfiles:
            tmpret, key, err = _dotfile_install(opts, dotfile, tmpdir=tmpdir)
            # check result
            if tmpret:
                installed.append(key)
            elif err:
                LOG.err('installing \"{}\" failed: {}'.format(key, err))

    # execute profile post-action
    if len(installed) > 0 or opts.install_force_action:
        msg = 'run {} profile post actions'
        LOG.dbg(msg.format(len(pro_post_actions)))
        ret, _ = action_executor(opts, pro_post_actions, [], templ,
                                 post=False)()
        if not ret:
            return False

    LOG.dbg('install done: installed \"{}\"'.format(','.join(installed)))

    if opts.install_temporary:
        LOG.log('\ninstalled to tmp \"{}\".'.format(tmpdir))
    LOG.log('\n{} dotfile(s) installed.'.format(len(installed)))
    return True
Beispiel #13
0
def cmd_install(o):
    """install dotfiles for this profile"""
    dotfiles = o.dotfiles
    prof = o.conf.get_profile()
    pro_pre_actions = prof.get_pre_actions() if prof else []
    pro_post_actions = prof.get_post_actions() if prof else []

    if o.install_keys:
        # filtered dotfiles to install
        uniq = uniq_list(o.install_keys)
        dotfiles = [d for d in dotfiles if d.key in uniq]
    if not dotfiles:
        msg = 'no dotfile to install for this profile (\"{}\")'
        LOG.warn(msg.format(o.profile))
        return False

    t = Templategen(base=o.dotpath,
                    variables=o.variables,
                    func_file=o.func_file,
                    filter_file=o.filter_file,
                    debug=o.debug)
    tmpdir = None
    if o.install_temporary:
        tmpdir = get_tmpdir()
    inst = Installer(create=o.create,
                     backup=o.backup,
                     dry=o.dry,
                     safe=o.safe,
                     base=o.dotpath,
                     workdir=o.workdir,
                     diff=o.install_diff,
                     debug=o.debug,
                     totemp=tmpdir,
                     showdiff=o.install_showdiff,
                     backup_suffix=o.install_backup_suffix,
                     diff_cmd=o.diff_command)
    installed = 0
    tvars = t.add_tmp_vars()

    # execute profile pre-action
    if o.debug:
        LOG.dbg('run {} profile pre actions'.format(len(pro_pre_actions)))
    ret, err = action_executor(o, pro_pre_actions, [], t, post=False)()
    if not ret:
        return False

    # install each dotfile
    for dotfile in dotfiles:
        # add dotfile variables
        t.restore_vars(tvars)
        newvars = dotfile.get_dotfile_variables()
        t.add_tmp_vars(newvars=newvars)

        preactions = []
        if not o.install_temporary:
            preactions.extend(dotfile.get_pre_actions())
        defactions = o.install_default_actions_pre
        pre_actions_exec = action_executor(o,
                                           preactions,
                                           defactions,
                                           t,
                                           post=False)

        if o.debug:
            LOG.dbg('installing dotfile: \"{}\"'.format(dotfile.key))
            LOG.dbg(dotfile.prt())
        if hasattr(dotfile, 'link') and dotfile.link == LinkTypes.LINK:
            r, err = inst.link(t,
                               dotfile.src,
                               dotfile.dst,
                               actionexec=pre_actions_exec)
        elif hasattr(dotfile, 'link') and \
                dotfile.link == LinkTypes.LINK_CHILDREN:
            r, err = inst.link_children(t,
                                        dotfile.src,
                                        dotfile.dst,
                                        actionexec=pre_actions_exec)
        else:
            src = dotfile.src
            tmp = None
            if dotfile.trans_r:
                tmp = apply_trans(o.dotpath, dotfile, t, debug=o.debug)
                if not tmp:
                    continue
                src = tmp
            ignores = list(set(o.install_ignore + dotfile.instignore))
            ignores = patch_ignores(ignores, dotfile.dst, debug=o.debug)
            r, err = inst.install(t,
                                  src,
                                  dotfile.dst,
                                  actionexec=pre_actions_exec,
                                  noempty=dotfile.noempty,
                                  ignore=ignores)
            if tmp:
                tmp = os.path.join(o.dotpath, tmp)
                if os.path.exists(tmp):
                    remove(tmp)
        if r:
            # dotfile was installed
            if not o.install_temporary:
                defactions = o.install_default_actions_post
                postactions = dotfile.get_post_actions()
                post_actions_exec = action_executor(o,
                                                    postactions,
                                                    defactions,
                                                    t,
                                                    post=True)
                post_actions_exec()
            installed += 1
        elif not r:
            # dotfile was NOT installed
            if o.install_force_action:
                # pre-actions
                if o.debug:
                    LOG.dbg('force pre action execution ...')
                pre_actions_exec()
                # post-actions
                if o.debug:
                    LOG.dbg('force post action execution ...')
                postactions = dotfile.get_post_actions()
                post_actions_exec = action_executor(o,
                                                    postactions,
                                                    defactions,
                                                    t,
                                                    post=True)
                post_actions_exec()
            if err:
                LOG.err('installing \"{}\" failed: {}'.format(
                    dotfile.key, err))

    # execute profile post-action
    if installed > 0 or o.install_force_action:
        if o.debug:
            msg = 'run {} profile post actions'
            LOG.dbg(msg.format(len(pro_post_actions)))
        ret, err = action_executor(o, pro_post_actions, [], t, post=False)()
        if not ret:
            return False

    if o.debug:
        LOG.dbg('install done')

    if o.install_temporary:
        LOG.log('\ninstalled to tmp \"{}\".'.format(tmpdir))
    LOG.log('\n{} dotfile(s) installed.'.format(installed))
    return True
Beispiel #14
0
    def _rec_resolve_profile_include(self, profile):
        """
        recursively resolve include of other profiles's:
        * dotfiles
        * actions
        * variables
        * dynvariables
        variables/dynvariables are directly merged with the
        global variables (self.variables) if these are
        included in the selected profile
        returns dotfiles, actions, variables, dynvariables
        """
        this_profile = self.profiles[profile]

        # considered profile content
        dotfiles = this_profile.get(self.key_profile_dotfiles, []) or []
        actions = this_profile.get(self.key_profile_actions, []) or []
        includes = this_profile.get(self.key_profile_include, []) or []
        pvars = this_profile.get(self.key_profile_variables, {}) or {}
        pdvars = this_profile.get(self.key_profile_dvariables, {}) or {}
        if not includes:
            # nothing to include
            return dotfiles, actions, pvars, pdvars

        if self.debug:
            self.log.dbg('{} includes {}'.format(profile, ','.join(includes)))
            self.log.dbg('{} dotfiles before include: {}'.format(
                profile, dotfiles))
            self.log.dbg('{} actions before include: {}'.format(
                profile, actions))
            self.log.dbg('{} variables before include: {}'.format(
                profile, pvars))
            self.log.dbg('{} dynvariables before include: {}'.format(
                profile, pdvars))

        seen = []
        for i in uniq_list(includes):
            if self.debug:
                self.log.dbg('resolving includes "{}" <- "{}"'.format(
                    profile, i))

            # ensure no include loop occurs
            if i in seen:
                raise YamlException('\"include loop\"')
            seen.append(i)
            # included profile even exists
            if i not in self.profiles.keys():
                self.log.warn('include unknown profile: {}'.format(i))
                continue

            # recursive resolve
            if self.debug:
                self.log.dbg(
                    'recursively resolving includes for profile "{}"'.format(
                        i))
            o_dfs, o_actions, o_v, o_dv = self._rec_resolve_profile_include(i)

            # merge dotfile keys
            if self.debug:
                self.log.dbg('Merging dotfiles {} <- {}: {} <- {}'.format(
                    profile, i, dotfiles, o_dfs))
            dotfiles.extend(o_dfs)
            this_profile[self.key_profile_dotfiles] = uniq_list(dotfiles)

            # merge actions keys
            if self.debug:
                self.log.dbg('Merging actions {} <- {}: {} <- {}'.format(
                    profile, i, actions, o_actions))
            actions.extend(o_actions)
            this_profile[self.key_profile_actions] = uniq_list(actions)

            # merge variables
            if self.debug:
                self.log.dbg('Merging variables {} <- {}: {} <- {}'.format(
                    profile, i, dict(pvars), dict(o_v)))
            pvars = self._merge_dict(o_v, pvars)
            this_profile[self.key_profile_variables] = pvars

            # merge dynvariables
            if self.debug:
                self.log.dbg(
                    'Merging dynamic variables {} <- {}: {} <- {}'.format(
                        profile, i, dict(pdvars), dict(o_dv)))
            pdvars = self._merge_dict(o_dv, pdvars)
            this_profile[self.key_profile_dvariables] = pdvars

        dotfiles = this_profile.get(self.key_profile_dotfiles, [])
        actions = this_profile.get(self.key_profile_actions, [])
        pvars = this_profile.get(self.key_profile_variables, {}) or {}
        pdvars = this_profile.get(self.key_profile_dvariables, {}) or {}

        if self.debug:
            self.log.dbg('{} dotfiles after include: {}'.format(
                profile, dotfiles))
            self.log.dbg('{} actions after include: {}'.format(
                profile, actions))
            self.log.dbg('{} variables after include: {}'.format(
                profile, pvars))
            self.log.dbg('{} dynvariables after include: {}'.format(
                profile, pdvars))

        if profile == self.profile:
            # Only for the selected profile, we execute dynamic variables and
            # we merge variables/dynvariables into the global variables
            self._shell_exec_dvars(pdvars.keys(), pdvars)
            self.variables = self._merge_dict(pvars, self.variables)
            self.variables = self._merge_dict(pdvars, self.variables)

        # since included items are resolved here
        # we can clear these include
        self.profiles[profile][self.key_profile_include] = None
        return dotfiles, actions, pvars, pdvars