Ejemplo n.º 1
0
 def _same_rights(self, left, right):
     """return True if files have the same modes"""
     try:
         lefts = get_file_perm(left)
         rights = get_file_perm(right)
         return lefts == rights
     except OSError as e:
         self.log.err(e)
         return False
Ejemplo n.º 2
0
    def _update(self, path, dotfile):
        """update dotfile from file pointed by path"""
        ret = False
        new_path = None
        ignores = list(set(self.ignore + dotfile.upignore))
        self.ignores = patch_ignores(ignores, dotfile.dst, debug=self.debug)
        if self.debug:
            self.log.dbg('ignore pattern(s): {}'.format(self.ignores))

        deployed_path = os.path.expanduser(path)
        local_path = os.path.join(self.dotpath, dotfile.src)
        local_path = os.path.expanduser(local_path)

        if not os.path.exists(deployed_path):
            msg = '\"{}\" does not exist'
            self.log.err(msg.format(deployed_path))
            return False

        if not os.path.exists(local_path):
            msg = '\"{}\" does not exist, import it first'
            self.log.err(msg.format(local_path))
            return False

        ignore_missing_in_dotdrop = self.ignore_missing_in_dotdrop or \
            dotfile.ignore_missing_in_dotdrop
        if (ignore_missing_in_dotdrop and not os.path.exists(local_path)) or \
                self._ignore([deployed_path, local_path]):
            self.log.sub('\"{}\" ignored'.format(dotfile.key))
            return True
        # apply write transformation if any
        new_path = self._apply_trans_w(deployed_path, dotfile)
        if not new_path:
            return False

        # save current rights
        deployed_mode = get_file_perm(deployed_path)
        local_mode = get_file_perm(local_path)

        # handle the pointed file
        if os.path.isdir(new_path):
            ret = self._handle_dir(new_path, local_path, dotfile)
        else:
            ret = self._handle_file(new_path, local_path, dotfile)

        if deployed_mode != local_mode:
            # mirror rights
            if self.debug:
                m = 'adopt mode {:o} for {}'
                self.log.dbg(m.format(deployed_mode, dotfile.key))
            r = self.conf.update_dotfile(dotfile.key, deployed_mode)
            if r:
                ret = True

        # clean temporary files
        if new_path != deployed_path and os.path.exists(new_path):
            removepath(new_path, logger=self.log)
        return ret
Ejemplo n.º 3
0
 def _comp_mode(self, local_path, deployed_path):
     """compare mode"""
     local_mode = get_file_perm(local_path)
     deployed_mode = get_file_perm(deployed_path)
     if local_mode == deployed_mode:
         return ''
     msg = 'mode differ {} ({:o}) and {} ({:o})'
     self.log.dbg(
         msg.format(local_path, local_mode, deployed_path, deployed_mode))
     ret = 'modes differ for {} ({:o}) vs {:o}\n'
     return ret.format(deployed_path, deployed_mode, local_mode)
Ejemplo n.º 4
0
 def _mirror_rights(self, src, dst):
     srcr = get_file_perm(src)
     dstr = get_file_perm(dst)
     if srcr == dstr:
         return
     msg = 'copy rights from {} ({:o}) to {} ({:o})'
     self.log.dbg(msg.format(src, srcr, dst, dstr))
     try:
         mirror_file_rights(src, dst)
     except OSError as exc:
         self.log.err(exc)
Ejemplo n.º 5
0
 def _comp_mode(self, left, right):
     """compare mode"""
     left_mode = get_file_perm(left)
     right_mode = get_file_perm(right)
     if left_mode == right_mode:
         return ''
     if self.debug:
         msg = 'mode differ {} ({:o}) and {} ({:o})'
         self.log.dbg(msg.format(left, left_mode, right, right_mode))
     ret = 'modes differ for {} ({:o}) vs {:o}\n'
     return ret.format(right, right_mode, left_mode)
Ejemplo n.º 6
0
    def _update(self, path, dotfile):
        """update dotfile from file pointed by path"""
        ret = False
        new_path = None
        ignores = list(set(self.ignore + dotfile.upignore))
        self.ignores = patch_ignores(ignores, dotfile.dst, debug=self.debug)
        if self.debug:
            self.log.dbg('ignore pattern(s): {}'.format(self.ignores))

        path = os.path.expanduser(path)
        dtpath = os.path.join(self.dotpath, dotfile.src)
        dtpath = os.path.expanduser(dtpath)

        if self._ignore([path, dtpath]):
            self.log.sub('\"{}\" ignored'.format(dotfile.key))
            return True
        # apply write transformation if any
        new_path = self._apply_trans_w(path, dotfile)
        if not new_path:
            return False

        # save current rights
        fsmode = get_file_perm(path)
        dfmode = get_file_perm(dtpath)

        # handle the pointed file
        if os.path.isdir(new_path):
            ret = self._handle_dir(new_path, dtpath)
        else:
            ret = self._handle_file(new_path, dtpath)

        if fsmode != dfmode:
            # mirror rights
            if self.debug:
                m = 'adopt mode {:o} for {}'
                self.log.dbg(m.format(fsmode, dotfile.key))
            r = self.conf.update_dotfile(dotfile.key, fsmode)
            if r:
                ret = True

        # clean temporary files
        if new_path != path and os.path.exists(new_path):
            removepath(new_path, logger=self.log)
        return ret
Ejemplo n.º 7
0
    def install(self,
                templater,
                src,
                dst,
                linktype,
                actionexec=None,
                noempty=False,
                ignore=[],
                is_template=True,
                chmod=None,
                force_chmod=False):
        """
        install src to dst

        @templater: the templater object
        @src: dotfile source path in dotpath
        @dst: dotfile destination path in the FS
        @linktype: linktypes.LinkTypes
        @actionexec: action executor callback
        @noempty: render empty template flag
        @ignore: pattern to ignore when installing
        @is_template: this dotfile is a template
        @chmod: rights to apply if any
        @force_chmod: do not ask user to chmod

        return
        - True, None        : success
        - False, error_msg  : error
        - False, None       : ignored
        """
        if not src or not dst:
            # fake dotfile
            if self.debug:
                self.log.dbg('fake dotfile installed')
            self._exec_pre_actions(actionexec)
            return True, None
        if self.debug:
            msg = 'installing \"{}\" to \"{}\" (link: {})'
            self.log.dbg(msg.format(src, dst, str(linktype)))
        src, dst, cont, err = self._check_paths(src, dst, chmod)
        if not cont:
            return self._log_install(cont, err)

        # check source file exists
        src = os.path.join(self.base, src)
        if not os.path.exists(src):
            err = 'source dotfile does not exist: {}'.format(src)
            return self._log_install(False, err)

        self.action_executed = False

        # install to temporary dir
        # and ignore any actions
        if self.totemp:
            r, err, _ = self.install_to_temp(templater,
                                             self.totemp,
                                             src,
                                             dst,
                                             is_template=is_template,
                                             chmod=chmod,
                                             ignore=ignore)
            return self._log_install(r, err)

        isdir = os.path.isdir(src)
        if self.debug:
            self.log.dbg('install {} to {}'.format(src, dst))
            self.log.dbg('\"{}\" is a directory: {}'.format(src, isdir))

        if linktype == LinkTypes.NOLINK:
            # normal file
            if isdir:
                r, err = self._copy_dir(templater,
                                        src,
                                        dst,
                                        actionexec=actionexec,
                                        noempty=noempty,
                                        ignore=ignore,
                                        is_template=is_template,
                                        chmod=chmod)
            else:
                r, err = self._copy_file(templater,
                                         src,
                                         dst,
                                         actionexec=actionexec,
                                         noempty=noempty,
                                         ignore=ignore,
                                         is_template=is_template,
                                         chmod=chmod)
        elif linktype == LinkTypes.LINK:
            # symlink
            r, err = self._link(templater,
                                src,
                                dst,
                                actionexec=actionexec,
                                is_template=is_template)
        elif linktype == LinkTypes.LINK_CHILDREN:
            # symlink direct children
            if not isdir:
                if self.debug:
                    msg = 'symlink children of {} to {}'
                    self.log.dbg(msg.format(src, dst))
                err = 'source dotfile is not a directory: {}'.format(src)
                r = False
            else:
                r, err = self._link_children(templater,
                                             src,
                                             dst,
                                             actionexec=actionexec,
                                             is_template=is_template,
                                             ignore=ignore)

        if self.debug:
            self.log.dbg('before chmod: {} err:{}'.format(r, err))

        if self.dry:
            return self._log_install(r, err)

        # handle chmod
        # - on success (r, not err)
        # - no change (not r, not err)
        # but not when
        # - error (not r, err)
        # - aborted (not r, err)
        if (r or (not r and not err)):
            if not chmod:
                chmod = utils.get_file_perm(src)
            dstperms = utils.get_file_perm(dst)
            if dstperms != chmod:
                # apply mode
                msg = 'chmod {} to {:o}'.format(dst, chmod)
                if not force_chmod and self.safe and not self.log.ask(msg):
                    r = False
                    err = 'aborted'
                else:
                    if not self.comparing:
                        self.log.sub('chmod {} to {:o}'.format(dst, chmod))
                    if utils.chmod(dst, chmod, debug=self.debug):
                        r = True
                    else:
                        r = False
                        err = 'chmod failed'

        return self._log_install(r, err)
Ejemplo n.º 8
0
    def _import(self,
                path,
                import_as=None,
                import_link=LinkTypes.NOLINK,
                import_mode=False):
        """
        import path
        returns:
            1: 1 dotfile imported
            0: ignored
            -1: error
        """

        # normalize path
        dst = path.rstrip(os.sep)
        dst = os.path.abspath(dst)

        # test if must be ignored
        if self._ignore(dst):
            return 0

        # ask confirmation for symlinks
        if self.safe:
            realdst = os.path.realpath(dst)
            if dst != realdst:
                msg = '\"{}\" is a symlink, dereference it and continue?'
                if not self.log.ask(msg.format(dst)):
                    return 0

        # create src path
        src = strip_home(dst)
        if import_as:
            # handle import as
            src = os.path.expanduser(import_as)
            src = src.rstrip(os.sep)
            src = os.path.abspath(src)
            src = strip_home(src)
            if self.debug:
                self.log.dbg('import src for {} as {}'.format(dst, src))
        # with or without dot prefix
        strip = '.' + os.sep
        if self.keepdot:
            strip = os.sep
        src = src.lstrip(strip)

        # get the permission
        perm = get_file_perm(dst)

        # get the link attribute
        linktype = import_link
        if linktype == LinkTypes.LINK_CHILDREN and \
                not os.path.isdir(path):
            self.log.err('importing \"{}\" failed!'.format(path))
            return -1

        if self._already_exists(src, dst):
            return -1

        if self.debug:
            self.log.dbg('import dotfile: src:{} dst:{}'.format(src, dst))

        if not self._prepare_hierarchy(src, dst):
            return -1

        # handle file mode
        chmod = None
        dflperm = get_default_file_perms(dst, self.umask)
        if self.debug:
            self.log.dbg('import mode: {}'.format(import_mode))
        if import_mode or perm != dflperm:
            if self.debug:
                msg = 'adopt mode {:o} (umask {:o})'
                self.log.dbg(msg.format(perm, dflperm))
            chmod = perm

        # add file to config file
        retconf = self.conf.new_dotfile(src, dst, linktype, chmod=chmod)
        if not retconf:
            self.log.warn('\"{}\" ignored during import'.format(path))
            return 0

        self.log.sub('\"{}\" imported'.format(path))
        return 1
Ejemplo n.º 9
0
    def _write(self, src, dst, content=None, actionexec=None, chmod=None):
        """
        copy dotfile / write content to file

        return
        - True, None        : success
        - False, error_msg  : error
        - False, None       : ignored
        - False, 'aborted'    : user aborted
        """
        overwrite = not self.safe
        if self.dry:
            self.log.dry('would install {}'.format(dst))
            return True, None

        if os.path.lexists(dst):
            try:
                os.stat(dst)
            except OSError as e:
                if e.errno == errno.ENOENT:
                    # broken symlink
                    err = 'broken symlink {}'.format(dst)
                    return False, err

            src_mode = chmod
            if not src_mode:
                src_mode = utils.get_file_perm(src)
            if self.diff:
                if not self._is_different(src, dst, content=content):
                    if self.debug:
                        self.log.dbg('{} is the same'.format(dst))
                    return False, None
            if self.safe:
                if self.debug:
                    self.log.dbg('change detected for {}'.format(dst))
                if self.showdiff:
                    # get diff
                    self._show_diff_before_write(src, dst, content=content)
                if not self.log.ask('Overwrite \"{}\"'.format(dst)):
                    return False, 'aborted'
                overwrite = True
        if self.backup and os.path.lexists(dst):
            self._backup(dst)
        base = os.path.dirname(dst)
        if not self._create_dirs(base):
            err = 'creating directory for {}'.format(dst)
            return False, err
        r, e = self._exec_pre_actions(actionexec)
        if not r:
            return False, e
        if self.debug:
            self.log.dbg('install file to \"{}\"'.format(dst))
        # re-check in case action created the file
        if self.safe and not overwrite and os.path.lexists(dst):
            if not self.log.ask('Overwrite \"{}\"'.format(dst)):
                self.log.warn('ignoring {}'.format(dst))
                return False, 'aborted'

        if content:
            # write content the file
            try:
                with open(dst, 'wb') as f:
                    f.write(content)
                shutil.copymode(src, dst)
            except NotADirectoryError as e:
                err = 'opening dest file: {}'.format(e)
                return False, err
            except Exception as e:
                return False, str(e)
        else:
            # copy file
            try:
                shutil.copyfile(src, dst)
                shutil.copymode(src, dst)
            except Exception as e:
                return False, str(e)
        return True, None
Ejemplo n.º 10
0
    def _import(self,
                path,
                import_as=None,
                import_link=LinkTypes.NOLINK,
                import_mode=False):
        """
        import path
        returns:
            1: 1 dotfile imported
            0: ignored
            -1: error
        """

        # normalize path
        dst = path.rstrip(os.sep)
        dst = os.path.abspath(dst)

        # test if must be ignored
        if self._ignore(dst):
            return 0

        # ask confirmation for symlinks
        if self.safe:
            realdst = os.path.realpath(dst)
            if dst != realdst:
                msg = '\"{}\" is a symlink, dereference it and continue?'
                if not self.log.ask(msg.format(dst)):
                    return 0

        # create src path
        src = strip_home(dst)
        if import_as:
            # handle import as
            src = os.path.expanduser(import_as)
            src = src.rstrip(os.sep)
            src = os.path.abspath(src)
            src = strip_home(src)
            self.log.dbg('import src for {} as {}'.format(dst, src))
        # with or without dot prefix
        strip = '.' + os.sep
        if self.keepdot:
            strip = os.sep
        src = src.lstrip(strip)

        # get the permission
        perm = get_file_perm(dst)

        # get the link attribute
        linktype = import_link
        if linktype == LinkTypes.LINK_CHILDREN and \
                not os.path.isdir(path):
            self.log.err('importing \"{}\" failed!'.format(path))
            return -1

        if self._already_exists(src, dst):
            return -1

        self.log.dbg('import dotfile: src:{} dst:{}'.format(src, dst))

        if not self._prepare_hierarchy(src, dst):
            return -1

        return self._import_it(path, src, dst, perm, linktype, import_mode)