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
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
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)
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)
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)
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
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)
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
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
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)