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)
def is_template(path, ignore=None, debug=False): """recursively check if any file is a template within path""" if debug: LOG.dbg('is template: {}'.format(path), force=True) path = os.path.expanduser(path) if not os.path.exists(path): # does not exist return False if utils.must_ignore([path], ignore, debug=debug): # must be ignored return False if os.path.isfile(path): # is file return Templategen._is_template(path, ignore=ignore, debug=debug) # is a directory for entry in os.listdir(path): fpath = os.path.join(path, entry) if not os.path.isfile(fpath): # recursively explore directory if Templategen.is_template(fpath, ignore=ignore, debug=debug): return True else: # check if file is a template if Templategen._is_template(fpath, ignore=ignore, debug=debug): return True return False
def _comp_file(self, left, right, ignore): """compare a file""" if utils.must_ignore([left, right], ignore, debug=self.debug): if self.debug: self.log.dbg('ignoring diff {} and {}'.format(left, right)) return '' return self._diff(left, right)
def _ignore(self, path): if must_ignore([path], self.ignore, debug=self.debug): if self.debug: self.log.dbg('ignoring import of {}'.format(path)) self.log.warn('{} ignored'.format(path)) return True return False
def _comp_dir(self, left, right, ignore): """compare a directory""" if not os.path.exists(right): return '' if utils.must_ignore([left, right], ignore, debug=self.debug): if self.debug: self.log.dbg('ignoring diff {} and {}'.format(left, right)) return '' if self.debug: self.log.dbg('compare {} and {}'.format(left, right)) ret = [] comp = filecmp.dircmp(left, right) # handle files only in deployed file for i in comp.left_only: if utils.must_ignore([os.path.join(left, i)], ignore, debug=self.debug): continue ret.append('=> \"{}\" does not exist on local\n'.format(i)) for i in comp.right_only: if utils.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) 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 = list(set(funny)) for i in funny: lfile = os.path.join(left, i) rfile = os.path.join(right, i) diff = self._diff(lfile, rfile, header=True) ret.append(diff) return ''.join(ret)
def _handle_file(self, templater, src, dst, actionexec=None, noempty=False, ignore=[]): """install src to dst when is a file""" if self.debug: self.log.dbg('generate template for {}'.format(src)) self.log.dbg('ignore empty: {}'.format(noempty)) self.log.dbg('ignore pattern: {}'.format(ignore)) if utils.must_ignore([src, dst], ignore, debug=self.debug): if self.debug: self.log.dbg('ignoring install of {} to {}'.format(src, dst)) return False, None if utils.samefile(src, dst): # symlink loop err = 'dotfile points to itself: {}'.format(dst) return False, err saved = templater.add_tmp_vars(self._get_tmp_file_vars(src, dst)) try: content = templater.generate(src) except UndefinedException as e: return False, str(e) finally: templater.restore_vars(saved) if noempty and utils.content_empty(content): if self.debug: self.log.dbg('ignoring empty template: {}'.format(src)) return False, None if content is None: err = 'empty template {}'.format(src) return False, err if not os.path.exists(src): err = 'source dotfile does not exist: {}'.format(src) return False, err st = os.stat(src) ret, err = self._write(src, dst, content, st.st_mode, actionexec=actionexec) if ret < 0: return False, err if ret > 0: if self.debug: self.log.dbg('ignoring {}'.format(dst)) return False, None if ret == 0: if not self.dry and not self.comparing: self.log.sub('copied {} to {}'.format(src, dst)) return True, None err = 'installing {} to {}'.format(src, dst) return False, err
def _comp_file(self, local_path, deployed_path, ignore): """compare a file""" self.log.dbg('compare file {} with {}'.format( local_path, deployed_path, )) if (self.ignore_missing_in_dotdrop and not os.path.exists(local_path)) \ or must_ignore([local_path, deployed_path], ignore, debug=self.debug): self.log.dbg('ignoring diff {} and {}'.format( local_path, deployed_path, )) return '' return self._diff(local_path, deployed_path)
def _is_template(path, ignore): """test if file pointed by path is a template""" if utils.must_ignore([path], ignore, debug=False): return False if not os.path.isfile(path): return False if os.stat(path).st_size == 0: return False markers = [BLOCK_START, VAR_START, COMMENT_START] patterns = [re.compile(marker.encode()) for marker in markers] try: with io.open(path, "r", encoding="utf-8") as f: m = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) for pattern in patterns: if pattern.search(m): return True except UnicodeDecodeError: # is binary so surely no template return False return False
def _comp_dir(self, local_path, deployed_path, ignore): """compare a directory""" 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): 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) return self._compare_dirs(local_path, deployed_path, ignore)
def is_template(path, ignore=[]): """recursively check if any file is a template within path""" path = os.path.expanduser(path) if utils.must_ignore([path], ignore, debug=False): return False if not os.path.exists(path): return False if os.path.isfile(path): # is file return Templategen._is_template(path, ignore=ignore) for entry in os.listdir(path): fpath = os.path.join(path, entry) if not os.path.isfile(fpath): # recursively explore directory if Templategen.is_template(fpath, ignore=ignore): return True else: # check if file is a template if Templategen._is_template(fpath, ignore=ignore): return True return False
def _ignore(self, paths): if utils.must_ignore(paths, self.ignores, debug=self.debug): if self.debug: self.log.dbg('ignoring update for {}'.format(paths)) return True return False
def _install_file(self, templater, src, dst, actionexec=None, noempty=False, ignore=[], template=True): """install src to dst when is a file""" if self.debug: self.log.dbg('deploy file: {}'.format(src)) self.log.dbg('ignore empty: {}'.format(noempty)) self.log.dbg('ignore pattern: {}'.format(ignore)) self.log.dbg('template: {}'.format(template)) self.log.dbg('no empty: {}'.format(noempty)) if utils.must_ignore([src, dst], ignore, debug=self.debug): if self.debug: self.log.dbg('ignoring install of {} to {}'.format(src, dst)) return False, None if utils.samefile(src, dst): # symlink loop err = 'dotfile points to itself: {}'.format(dst) return False, err if not os.path.exists(src): err = 'source dotfile does not exist: {}'.format(src) return False, err # handle the file content = None if template: # template the file saved = templater.add_tmp_vars(self._get_tmp_file_vars(src, dst)) try: content = templater.generate(src) except UndefinedException as e: return False, str(e) finally: templater.restore_vars(saved) if noempty and utils.content_empty(content): if self.debug: self.log.dbg('ignoring empty template: {}'.format(src)) return False, None if content is None: err = 'empty template {}'.format(src) return False, err ret, err = self._write(src, dst, content=content, actionexec=actionexec, template=template) # build return values if ret < 0: # error return False, err if ret > 0: # already exists if self.debug: self.log.dbg('ignoring {}'.format(dst)) return False, None if ret == 0: # success if not self.dry and not self.comparing: self.log.sub('copied {} to {}'.format(src, dst)) return True, None # error err = 'installing {} to {}'.format(src, dst) return False, err
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)
def _copy_file(self, templater, src, dst, actionexec=None, noempty=False, ignore=[], is_template=True, chmod=None): """ install src to dst when is a file return - True, None : success - False, error_msg : error - False, None : ignored - False, 'aborted' : user aborted """ if self.debug: self.log.dbg('deploy file: {}'.format(src)) self.log.dbg('ignore empty: {}'.format(noempty)) self.log.dbg('ignore pattern: {}'.format(ignore)) self.log.dbg('is_template: {}'.format(is_template)) self.log.dbg('no empty: {}'.format(noempty)) # check no loop if utils.samefile(src, dst): err = 'dotfile points to itself: {}'.format(dst) return False, err if utils.must_ignore([src, dst], ignore, debug=self.debug): if self.debug: self.log.dbg('ignoring install of {} to {}'.format(src, dst)) return False, None if utils.samefile(src, dst): # loop err = 'dotfile points to itself: {}'.format(dst) return False, err if not os.path.exists(src): err = 'source dotfile does not exist: {}'.format(src) return False, err # handle the file content = None if is_template: # template the file saved = templater.add_tmp_vars(self._get_tmp_file_vars(src, dst)) try: content = templater.generate(src) except UndefinedException as e: return False, str(e) finally: templater.restore_vars(saved) # test is empty if noempty and utils.content_empty(content): if self.debug: self.log.dbg('ignoring empty template: {}'.format(src)) return False, None if content is None: err = 'empty template {}'.format(src) return False, err # write the file ret, err = self._write(src, dst, content=content, actionexec=actionexec, chmod=chmod) if ret and not err: if not self.dry and not self.comparing: self.log.sub('install {} to {}'.format(src, dst)) return ret, err
def _link_children(self, templater, src, dst, actionexec=None, is_template=True, ignore=[]): """ install link:link_children return - True, None : success - False, error_msg : error - False, None : ignored - False, 'aborted' : user aborted """ parent = os.path.join(self.base, src) if not os.path.lexists(dst): if self.dry: self.log.dry('would create directory "{}"'.format(dst)) else: if not self.comparing: self.log.sub('creating directory "{}"'.format(dst)) self._create_dirs(dst) if os.path.isfile(dst): msg = ''.join([ 'Remove regular file {} and ', 'replace with empty directory?', ]).format(dst) if self.safe and not self.log.ask(msg): return False, 'aborted' os.unlink(dst) self._create_dirs(dst) children = os.listdir(parent) srcs = [ os.path.normpath(os.path.join(parent, child)) for child in children ] dsts = [ os.path.normpath(os.path.join(dst, child)) for child in children ] installed = 0 for i in range(len(children)): subsrc = srcs[i] subdst = dsts[i] if utils.must_ignore([subsrc, subdst], ignore, debug=self.debug): if self.debug: self.log.dbg( 'ignoring install of {} to {}'.format(src, dst), ) continue if self.debug: self.log.dbg('symlink child {} to {}'.format(subsrc, subdst)) if is_template: if self.debug: self.log.dbg('child is a template') self.log.dbg('install to {} and symlink'.format( self.workdir)) tmp = self._pivot_path(subdst, self.workdir, striphome=True) r, e = self.install(templater, subsrc, tmp, LinkTypes.NOLINK, actionexec=actionexec, is_template=is_template) if not r and e and not os.path.exists(tmp): continue subsrc = tmp ret, err = self._symlink(subsrc, subdst, actionexec=actionexec) if ret: installed += 1 # void actionexec if dotfile installed # to prevent from running actions multiple times actionexec = None else: if err: return ret, err return installed > 0, None