def test_renaming_based_on_include(tmpdir): """Targets should be renameable based on the include capture group.""" temp_dir = Path(tmpdir) file1 = temp_dir / 'file1' file1.touch() file2 = temp_dir / 'file2' file2.touch() recursive_dir = temp_dir / 'recursive' recursive_dir.mkdir() file3 = temp_dir / 'recursive' / 'file3' file3.touch() targets = resolve_targets( content=temp_dir, target=Path('/a/b'), include=r'.+(\d)', ) assert targets == { file1: Path('/a/b/1'), file2: Path('/a/b/2'), file3: Path('/a/b/recursive/3'), }
def test_resolving_content_directory(tmpdir): """Directory hierarchy should be preserved at target.""" temp_dir = Path(tmpdir) file1 = temp_dir / 'file1' file1.touch() file2 = temp_dir / 'file2' file2.touch() recursive_dir = temp_dir / 'recursive' recursive_dir.mkdir() file3 = temp_dir / 'recursive' / 'file3' file3.touch() targets = resolve_targets( content=temp_dir, target=Path('/a/b'), include=r'.*', ) assert targets == { file1: Path('/a/b/file1'), file2: Path('/a/b/file2'), file3: Path('/a/b/recursive/file3'), }
def execute(self, dry_run: bool = False) -> Dict[Path, Path]: """ Copy from `content` path to `target` path. :param dry_run: If True, skip and log copy creation(s). :return: Dictionary with content keys and copy values. """ if self.null_object: return {} content = self.option(key='content', path=True) target = self.option(key='target', path=True) include = self.option(key='include', default=r'(.+)') permissions = self.option(key='permissions', default=None) copies = utils.resolve_targets( content=content, target=target, include=include, ) logger = logging.getLogger(__name__) for content, copy in copies.items(): self.copied_files[content].add(copy) log_msg = f'[copy] Content: "{content}" -> Target: "{target}".' if dry_run: logger.info('SKIPPED: ' + log_msg) continue logger.info(log_msg) self.creation_store.mkdir(copy.parent) self.creation_store.backup(path=copy) utils.copy( source=content, destination=copy, follow_symlinks=False, ) self.creation_store.insert_creation( content=content, target=copy, method=persistence.CreationMethod.COPY, ) if permissions and not dry_run: for copy in copies.values(): result = utils.run_shell( command=f'chmod {permissions} "{copy}"', timeout=1, fallback=False, ) if result is False: logger = logging.getLogger(__name__) logger.error( f'Could not set "{permissions}" ' f'permissions for copy "{target}"', ) return copies
def test_resolving_non_existent_file(): """When `content` does not exist, no targets should be returned.""" targets = resolve_targets( content=Path('/does/not/exist'), target=Path('/'), include=r'.*', ) assert targets == {}
def test_resolving_target_file_to_directory(): """When content is a file, but target is a directory, keep filename.""" targets = resolve_targets( content=Path(__file__), target=Path('/tmp'), include=r'.*', ) assert targets == {Path(__file__): Path('/tmp/test_resolve_targets.py')}
def test_resolving_target_of_content_file(): """When `content` is a file, the target root is used.""" targets = resolve_targets( content=Path(__file__), target=Path('/does/not/exist'), include=r'.*', ) assert targets == {Path(__file__): Path('/does/not/exist')}
def execute(self, dry_run: bool = False) -> Dict[Path, Path]: """ Compile template source to target destination. :param dry_run: If True, skip and log compilation(s). :return: Dictionary with template content keys and target path values. """ if self.null_object: # Null objects do nothing return {} elif 'target' not in self._options: # If no target is specified, we create a deterministic target. template = self.option(key='content', path=True) target = self.create_compilation_target(template=template) self._options['target'] = str(target) # These might either be file paths or directory paths template_source = self.option(key='content', path=True) target_source = self.option(key='target', path=True) if not template_source.exists(): logger = logging.getLogger(__name__) logger.error( f'Could not compile template "{template_source}" ' f'to target "{target_source}". No such path!', ) return {} compile_pairs = utils.resolve_targets( content=template_source, target=target_source, include=self.option(key='include', default=r'(.+)'), ) permissions = self.option(key='permissions') for content_file, target_file in compile_pairs.items(): if dry_run: logger = logging.getLogger(__name__) logger.info( f'SKIPPED: ' f'[Compiling] Template: "{content_file}" ' f'-> Target: "{target_file}"', ) else: self.creation_store.backup(path=target_file) compiler.compile_template( template=content_file, target=target_file, context=self.context_store, shell_command_working_directory=self.directory, permissions=permissions, ) self.creation_store.insert_creation( content=content_file, target=target_file, method=persistence.CreationMethod.COMPILE, ) self._performed_compilations[content_file].add(target_file) return compile_pairs
def execute(self, dry_run: bool = False) -> Dict[Path, Path]: """ Symlink to `content` path from `target` path. :param dry_run: If True, skip and log symlink creation(s). :return: Dictionary with content keys and symlink values. """ if self.null_object: return {} content = self.option(key='content', path=True) target = self.option(key='target', path=True) include = self.option(key='include', default=r'(.+)') links = utils.resolve_targets( content=content, target=target, include=include, ) logger = logging.getLogger(__name__) for content, symlink in links.items(): self.symlinked_files[content].add(symlink) log_msg = f'[symlink] Content "{content}" -> Target: "{symlink}".' if symlink.resolve() == content.resolve(): continue if dry_run: logger.info('SKIPPED: ' + log_msg) continue logger.info(log_msg) symlink.parent.mkdir(parents=True, exist_ok=True) self.creation_store.backup(path=symlink) symlink.symlink_to(content) self.creation_store.insert_creation( content=content, target=symlink, method=persistence.CreationMethod.SYMLINK, ) return links
def test_filtering_based_on_include(tmpdir): """Only files that match the regex should be included.""" temp_dir = Path(tmpdir) file1 = temp_dir / 'file1' file1.touch() file2 = temp_dir / 'file2' file2.touch() recursive_dir = temp_dir / 'recursive' recursive_dir.mkdir() file3 = temp_dir / 'recursive' / 'file3' file3.touch() targets = resolve_targets( content=temp_dir, target=Path('/a/b'), include=r'.+3', ) assert targets == { file3: Path('/a/b/recursive/file3'), }