mirror of
				https://github.com/arcan1s/ahriman.git
				synced 2025-10-31 05:43:41 +00:00 
			
		
		
		
	add ability to add manually stored packages (#40)
* add ability to add manually stored packages * update tests * handle manual packages in remove-unknown method * live fixes also rename branches to has_remotes method and change return type
This commit is contained in:
		| @ -137,9 +137,16 @@ def _set_package_add_parser(root: SubParserAction) -> argparse.ArgumentParser: | |||||||
|     """ |     """ | ||||||
|     parser = root.add_parser("package-add", aliases=["add"], help="add package", description="add package", |     parser = root.add_parser("package-add", aliases=["add"], help="add package", description="add package", | ||||||
|                              epilog="This subcommand should be used for new package addition. It also supports flag " |                              epilog="This subcommand should be used for new package addition. It also supports flag " | ||||||
|                                     "--now in case if you would like to build the package immediately.", |                                     "--now in case if you would like to build the package immediately. " | ||||||
|  |                                     "You can add new package from one of supported sources: " | ||||||
|  |                                     "1) if it is already built package you can specify the path to the archive; " | ||||||
|  |                                     "2) you can also add built packages from the directory (e.g. during the migration " | ||||||
|  |                                     "from another repository source); " | ||||||
|  |                                     "3) it is also possible to add package from local PKGBUILD, but in this case it " | ||||||
|  |                                     "will be ignored during the next automatic updates; " | ||||||
|  |                                     "4) and finally you can add package from AUR.", | ||||||
|                              formatter_class=_formatter) |                              formatter_class=_formatter) | ||||||
|     parser.add_argument("package", help="package base/name or archive path", nargs="+") |     parser.add_argument("package", help="package base/name or path to local files", nargs="+") | ||||||
|     parser.add_argument("-n", "--now", help="run update function after", action="store_true") |     parser.add_argument("-n", "--now", help="run update function after", action="store_true") | ||||||
|     parser.add_argument("-s", "--source", help="package source", |     parser.add_argument("-s", "--source", help="package source", | ||||||
|                         type=PackageSource, choices=PackageSource, default=PackageSource.Auto) |                         type=PackageSource, choices=PackageSource, default=PackageSource.Auto) | ||||||
|  | |||||||
| @ -105,21 +105,30 @@ class Application: | |||||||
|         :param without_dependencies: if set, dependency check will be disabled |         :param without_dependencies: if set, dependency check will be disabled | ||||||
|         """ |         """ | ||||||
|         known_packages = self._known_packages() |         known_packages = self._known_packages() | ||||||
|  |         aur_url = self.configuration.get("alpm", "aur_url") | ||||||
|  |  | ||||||
|  |         def add_archive(src: Path) -> None: | ||||||
|  |             dst = self.repository.paths.packages / src.name | ||||||
|  |             shutil.move(src, dst) | ||||||
|  |  | ||||||
|         def add_directory(path: Path) -> None: |         def add_directory(path: Path) -> None: | ||||||
|             for full_path in filter(package_like, path.iterdir()): |             for full_path in filter(package_like, path.iterdir()): | ||||||
|                 add_archive(full_path) |                 add_archive(full_path) | ||||||
|  |  | ||||||
|         def add_manual(src: str) -> Path: |         def add_local(path: Path) -> Path: | ||||||
|             package = Package.load(src, self.repository.pacman, self.configuration.get("alpm", "aur_url")) |             package = Package.load(path, self.repository.pacman, aur_url) | ||||||
|  |             cache_dir = self.repository.paths.cache_for(package.base) | ||||||
|  |             shutil.copytree(path, cache_dir)  # copy package to store in caches | ||||||
|  |             Sources.init(cache_dir)  # we need to run init command in directory where we do have permissions | ||||||
|  |             shutil.copytree(cache_dir, self.repository.paths.manual_for(package.base))  # copy package for the build | ||||||
|  |             return self.repository.paths.manual_for(package.base) | ||||||
|  |  | ||||||
|  |         def add_remote(src: str) -> Path: | ||||||
|  |             package = Package.load(src, self.repository.pacman, aur_url) | ||||||
|             Sources.load(self.repository.paths.manual_for(package.base), package.git_url, |             Sources.load(self.repository.paths.manual_for(package.base), package.git_url, | ||||||
|                          self.repository.paths.patches_for(package.base)) |                          self.repository.paths.patches_for(package.base)) | ||||||
|             return self.repository.paths.manual_for(package.base) |             return self.repository.paths.manual_for(package.base) | ||||||
|  |  | ||||||
|         def add_archive(src: Path) -> None: |  | ||||||
|             dst = self.repository.paths.packages / src.name |  | ||||||
|             shutil.move(src, dst) |  | ||||||
|  |  | ||||||
|         def process_dependencies(path: Path) -> None: |         def process_dependencies(path: Path) -> None: | ||||||
|             if without_dependencies: |             if without_dependencies: | ||||||
|                 return |                 return | ||||||
| @ -128,12 +137,15 @@ class Application: | |||||||
|  |  | ||||||
|         def process_single(src: str) -> None: |         def process_single(src: str) -> None: | ||||||
|             resolved_source = source.resolve(src) |             resolved_source = source.resolve(src) | ||||||
|             if resolved_source == PackageSource.Directory: |             if resolved_source == PackageSource.Archive: | ||||||
|                 add_directory(Path(src)) |  | ||||||
|             elif resolved_source == PackageSource.Archive: |  | ||||||
|                 add_archive(Path(src)) |                 add_archive(Path(src)) | ||||||
|             else: |             elif resolved_source == PackageSource.AUR: | ||||||
|                 path = add_manual(src) |                 path = add_remote(src) | ||||||
|  |                 process_dependencies(path) | ||||||
|  |             elif resolved_source == PackageSource.Directory: | ||||||
|  |                 add_directory(Path(src)) | ||||||
|  |             elif resolved_source == PackageSource.Local: | ||||||
|  |                 path = add_local(Path(src)) | ||||||
|                 process_dependencies(path) |                 process_dependencies(path) | ||||||
|  |  | ||||||
|         for name in names: |         for name in names: | ||||||
| @ -213,13 +225,22 @@ class Application: | |||||||
|         get packages which were not found in AUR |         get packages which were not found in AUR | ||||||
|         :return: unknown package list |         :return: unknown package list | ||||||
|         """ |         """ | ||||||
|         packages = [] |         def has_aur(package_base: str, aur_url: str) -> bool: | ||||||
|         for base in self.repository.packages(): |  | ||||||
|             try: |             try: | ||||||
|                 _ = Package.from_aur(base.base, base.aur_url) |                 _ = Package.from_aur(package_base, aur_url) | ||||||
|             except Exception: |             except Exception: | ||||||
|                 packages.append(base) |                 return False | ||||||
|         return packages |             return True | ||||||
|  |  | ||||||
|  |         def has_local(package_base: str) -> bool: | ||||||
|  |             cache_dir = self.repository.paths.cache_for(package_base) | ||||||
|  |             return cache_dir.is_dir() and not Sources.has_remotes(cache_dir) | ||||||
|  |  | ||||||
|  |         return [ | ||||||
|  |             package | ||||||
|  |             for package in self.repository.packages() | ||||||
|  |             if not has_aur(package.base, package.aur_url) and not has_local(package.base) | ||||||
|  |         ] | ||||||
|  |  | ||||||
|     def update(self, updates: Iterable[Package]) -> None: |     def update(self, updates: Iterable[Package]) -> None: | ||||||
|         """ |         """ | ||||||
|  | |||||||
| @ -33,72 +33,98 @@ class Sources: | |||||||
|  |  | ||||||
|     logger = logging.getLogger("build_details") |     logger = logging.getLogger("build_details") | ||||||
|  |  | ||||||
|  |     _branch = "master"  # in case if BLM would like to change it | ||||||
|     _check_output = check_output |     _check_output = check_output | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def add(local_path: Path, *pattern: str) -> None: |     def add(sources_dir: Path, *pattern: str) -> None: | ||||||
|         """ |         """ | ||||||
|         track found files via git |         track found files via git | ||||||
|         :param local_path: local path to git repository |         :param sources_dir: local path to git repository | ||||||
|         :param pattern: glob patterns |         :param pattern: glob patterns | ||||||
|         """ |         """ | ||||||
|         # glob directory to find files which match the specified patterns |         # glob directory to find files which match the specified patterns | ||||||
|         found_files: List[Path] = [] |         found_files: List[Path] = [] | ||||||
|         for glob in pattern: |         for glob in pattern: | ||||||
|             found_files.extend(local_path.glob(glob)) |             found_files.extend(sources_dir.glob(glob)) | ||||||
|         Sources.logger.info("found matching files %s", found_files) |         Sources.logger.info("found matching files %s", found_files) | ||||||
|         # add them to index |         # add them to index | ||||||
|         Sources._check_output("git", "add", "--intent-to-add", *[str(fn.relative_to(local_path)) for fn in found_files], |         Sources._check_output("git", "add", "--intent-to-add", | ||||||
|                               exception=None, cwd=local_path, logger=Sources.logger) |                               *[str(fn.relative_to(sources_dir)) for fn in found_files], | ||||||
|  |                               exception=None, cwd=sources_dir, logger=Sources.logger) | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def diff(local_path: Path, patch_path: Path) -> None: |     def diff(sources_dir: Path, patch_path: Path) -> None: | ||||||
|         """ |         """ | ||||||
|         generate diff from the current version and write it to the output file |         generate diff from the current version and write it to the output file | ||||||
|         :param local_path: local path to git repository |         :param sources_dir: local path to git repository | ||||||
|         :param patch_path: path to result patch |         :param patch_path: path to result patch | ||||||
|         """ |         """ | ||||||
|         patch = Sources._check_output("git", "diff", exception=None, cwd=local_path, logger=Sources.logger) |         patch = Sources._check_output("git", "diff", exception=None, cwd=sources_dir, logger=Sources.logger) | ||||||
|         patch_path.write_text(patch) |         patch_path.write_text(patch) | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def fetch(local_path: Path, remote: str, branch: str = "master") -> None: |     def fetch(sources_dir: Path, remote: str) -> None: | ||||||
|         """ |         """ | ||||||
|         either clone repository or update it to origin/`branch` |         either clone repository or update it to origin/`branch` | ||||||
|         :param local_path: local path to fetch |         :param sources_dir: local path to fetch | ||||||
|         :param remote: remote target (from where to fetch) |         :param remote: remote target (from where to fetch) | ||||||
|         :param branch: branch name to checkout, master by default |  | ||||||
|         """ |         """ | ||||||
|         # local directory exists and there is .git directory |         # local directory exists and there is .git directory | ||||||
|         if (local_path / ".git").is_dir(): |         is_initialized_git = (sources_dir / ".git").is_dir() | ||||||
|             Sources.logger.info("update HEAD to remote to %s", local_path) |         if is_initialized_git and not Sources.has_remotes(sources_dir): | ||||||
|             Sources._check_output("git", "fetch", "origin", branch, |             # there is git repository, but no remote configured so far | ||||||
|                                   exception=None, cwd=local_path, logger=Sources.logger) |             Sources.logger.info("skip update at %s because there are no branches configured", sources_dir) | ||||||
|  |             return | ||||||
|  |  | ||||||
|  |         if is_initialized_git: | ||||||
|  |             Sources.logger.info("update HEAD to remote at %s", sources_dir) | ||||||
|  |             Sources._check_output("git", "fetch", "origin", Sources._branch, | ||||||
|  |                                   exception=None, cwd=sources_dir, logger=Sources.logger) | ||||||
|         else: |         else: | ||||||
|             Sources.logger.info("clone remote %s to %s", remote, local_path) |             Sources.logger.info("clone remote %s to %s", remote, sources_dir) | ||||||
|             Sources._check_output("git", "clone", remote, str(local_path), exception=None, logger=Sources.logger) |             Sources._check_output("git", "clone", remote, str(sources_dir), exception=None, logger=Sources.logger) | ||||||
|         # and now force reset to our branch |         # and now force reset to our branch | ||||||
|         Sources._check_output("git", "checkout", "--force", branch, |         Sources._check_output("git", "checkout", "--force", Sources._branch, | ||||||
|                               exception=None, cwd=local_path, logger=Sources.logger) |                               exception=None, cwd=sources_dir, logger=Sources.logger) | ||||||
|         Sources._check_output("git", "reset", "--hard", f"origin/{branch}", |         Sources._check_output("git", "reset", "--hard", f"origin/{Sources._branch}", | ||||||
|                               exception=None, cwd=local_path, logger=Sources.logger) |                               exception=None, cwd=sources_dir, logger=Sources.logger) | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def load(local_path: Path, remote: str, patch_dir: Path) -> None: |     def has_remotes(sources_dir: Path) -> bool: | ||||||
|  |         """ | ||||||
|  |         check if there are remotes for the repository | ||||||
|  |         :param sources_dir: local path to git repository | ||||||
|  |         :return: True in case if there is any remote and false otherwise | ||||||
|  |         """ | ||||||
|  |         remotes = Sources._check_output("git", "remote", exception=None, cwd=sources_dir, logger=Sources.logger) | ||||||
|  |         return bool(remotes) | ||||||
|  |  | ||||||
|  |     @staticmethod | ||||||
|  |     def init(sources_dir: Path) -> None: | ||||||
|  |         """ | ||||||
|  |         create empty git repository at the specified path | ||||||
|  |         :param sources_dir: local path to sources | ||||||
|  |         """ | ||||||
|  |         Sources._check_output("git", "init", "--initial-branch", Sources._branch, | ||||||
|  |                               exception=None, cwd=sources_dir, logger=Sources.logger) | ||||||
|  |  | ||||||
|  |     @staticmethod | ||||||
|  |     def load(sources_dir: Path, remote: str, patch_dir: Path) -> None: | ||||||
|         """ |         """ | ||||||
|         fetch sources from remote and apply patches |         fetch sources from remote and apply patches | ||||||
|         :param local_path: local path to fetch |         :param sources_dir: local path to fetch | ||||||
|         :param remote: remote target (from where to fetch) |         :param remote: remote target (from where to fetch) | ||||||
|         :param patch_dir: path to directory with package patches |         :param patch_dir: path to directory with package patches | ||||||
|         """ |         """ | ||||||
|         Sources.fetch(local_path, remote) |         Sources.fetch(sources_dir, remote) | ||||||
|         Sources.patch_apply(local_path, patch_dir) |         Sources.patch_apply(sources_dir, patch_dir) | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def patch_apply(local_path: Path, patch_dir: Path) -> None: |     def patch_apply(sources_dir: Path, patch_dir: Path) -> None: | ||||||
|         """ |         """ | ||||||
|         apply patches if any |         apply patches if any | ||||||
|         :param local_path: local path to directory with git sources |         :param sources_dir: local path to directory with git sources | ||||||
|         :param patch_dir: path to directory with package patches |         :param patch_dir: path to directory with package patches | ||||||
|         """ |         """ | ||||||
|         # check if even there are patches |         # check if even there are patches | ||||||
| @ -110,15 +136,15 @@ class Sources: | |||||||
|         for patch in patches: |         for patch in patches: | ||||||
|             Sources.logger.info("apply patch %s", patch.name) |             Sources.logger.info("apply patch %s", patch.name) | ||||||
|             Sources._check_output("git", "apply", "--ignore-space-change", "--ignore-whitespace", str(patch), |             Sources._check_output("git", "apply", "--ignore-space-change", "--ignore-whitespace", str(patch), | ||||||
|                                   exception=None, cwd=local_path, logger=Sources.logger) |                                   exception=None, cwd=sources_dir, logger=Sources.logger) | ||||||
|  |  | ||||||
|     @staticmethod |     @staticmethod | ||||||
|     def patch_create(local_path: Path, patch_path: Path, *pattern: str) -> None: |     def patch_create(sources_dir: Path, patch_path: Path, *pattern: str) -> None: | ||||||
|         """ |         """ | ||||||
|         create patch set for the specified local path |         create patch set for the specified local path | ||||||
|         :param local_path: local path to git repository |         :param sources_dir: local path to git repository | ||||||
|         :param patch_path: path to result patch |         :param patch_path: path to result patch | ||||||
|         :param pattern: glob patterns |         :param pattern: glob patterns | ||||||
|         """ |         """ | ||||||
|         Sources.add(local_path, *pattern) |         Sources.add(sources_dir, *pattern) | ||||||
|         Sources.diff(local_path, patch_path) |         Sources.diff(sources_dir, patch_path) | ||||||
|  | |||||||
| @ -72,9 +72,16 @@ class Executor(Cleaner): | |||||||
|         :param packages: list of package names or bases to remove |         :param packages: list of package names or bases to remove | ||||||
|         :return: path to repository database |         :return: path to repository database | ||||||
|         """ |         """ | ||||||
|         def remove_single(package: str, fn: Path) -> None: |         def remove_base(package_base: str) -> None: | ||||||
|             try: |             try: | ||||||
|                 self.repo.remove(package, fn) |                 self.paths.tree_clear(package_base)  # remove all internal files | ||||||
|  |                 self.reporter.remove(package_base)  # we only update status page in case of base removal | ||||||
|  |             except Exception: | ||||||
|  |                 self.logger.exception("could not remove base %s", package_base) | ||||||
|  |  | ||||||
|  |         def remove_package(package: str, fn: Path) -> None: | ||||||
|  |             try: | ||||||
|  |                 self.repo.remove(package, fn)  # remove the package itself | ||||||
|             except Exception: |             except Exception: | ||||||
|                 self.logger.exception("could not remove %s", package) |                 self.logger.exception("could not remove %s", package) | ||||||
|  |  | ||||||
| @ -86,7 +93,7 @@ class Executor(Cleaner): | |||||||
|                     for package, properties in local.packages.items() |                     for package, properties in local.packages.items() | ||||||
|                     if properties.filename is not None |                     if properties.filename is not None | ||||||
|                 } |                 } | ||||||
|                 self.reporter.remove(local.base)  # we only update status page in case of base removal |                 remove_base(local.base) | ||||||
|             elif requested.intersection(local.packages.keys()): |             elif requested.intersection(local.packages.keys()): | ||||||
|                 to_remove = { |                 to_remove = { | ||||||
|                     package: Path(properties.filename) |                     package: Path(properties.filename) | ||||||
| @ -95,8 +102,9 @@ class Executor(Cleaner): | |||||||
|                 } |                 } | ||||||
|             else: |             else: | ||||||
|                 to_remove = {} |                 to_remove = {} | ||||||
|  |  | ||||||
|             for package, filename in to_remove.items(): |             for package, filename in to_remove.items(): | ||||||
|                 remove_single(package, filename) |                 remove_package(package, filename) | ||||||
|  |  | ||||||
|         return self.repo.repo_path |         return self.repo.repo_path | ||||||
|  |  | ||||||
|  | |||||||
| @ -58,7 +58,7 @@ class Properties: | |||||||
|         self.name = configuration.get("repository", "name") |         self.name = configuration.get("repository", "name") | ||||||
|  |  | ||||||
|         self.paths = RepositoryPaths(configuration.getpath("repository", "root"), architecture) |         self.paths = RepositoryPaths(configuration.getpath("repository", "root"), architecture) | ||||||
|         self.paths.create_tree() |         self.paths.tree_create() | ||||||
|  |  | ||||||
|         self.ignore_list = configuration.getlist("build", "ignore_packages", fallback=[]) |         self.ignore_list = configuration.getlist("build", "ignore_packages", fallback=[]) | ||||||
|         self.pacman = Pacman(configuration) |         self.pacman = Pacman(configuration) | ||||||
|  | |||||||
| @ -30,14 +30,16 @@ class PackageSource(Enum): | |||||||
|     package source for addition enumeration |     package source for addition enumeration | ||||||
|     :cvar Auto: automatically determine type of the source |     :cvar Auto: automatically determine type of the source | ||||||
|     :cvar Archive: source is a package archive |     :cvar Archive: source is a package archive | ||||||
|     :cvar Directory: source is a directory which contains packages |  | ||||||
|     :cvar AUR: source is an AUR package for which it should search |     :cvar AUR: source is an AUR package for which it should search | ||||||
|  |     :cvar Directory: source is a directory which contains packages | ||||||
|  |     :cvar Local: source is locally stored PKGBUILD | ||||||
|     """ |     """ | ||||||
|  |  | ||||||
|     Auto = "auto" |     Auto = "auto" | ||||||
|     Archive = "archive" |     Archive = "archive" | ||||||
|     Directory = "directory" |  | ||||||
|     AUR = "aur" |     AUR = "aur" | ||||||
|  |     Directory = "directory" | ||||||
|  |     Local = "local" | ||||||
|  |  | ||||||
|     def resolve(self, source: str) -> PackageSource: |     def resolve(self, source: str) -> PackageSource: | ||||||
|         """ |         """ | ||||||
| @ -47,7 +49,10 @@ class PackageSource(Enum): | |||||||
|         """ |         """ | ||||||
|         if self != PackageSource.Auto: |         if self != PackageSource.Auto: | ||||||
|             return self |             return self | ||||||
|  |  | ||||||
|         maybe_path = Path(source) |         maybe_path = Path(source) | ||||||
|  |         if (maybe_path / "PKGBUILD").is_file(): | ||||||
|  |             return PackageSource.Local | ||||||
|         if maybe_path.is_dir(): |         if maybe_path.is_dir(): | ||||||
|             return PackageSource.Directory |             return PackageSource.Directory | ||||||
|         if maybe_path.is_file() and package_like(maybe_path): |         if maybe_path.is_file() and package_like(maybe_path): | ||||||
|  | |||||||
| @ -19,6 +19,8 @@ | |||||||
| # | # | ||||||
| from __future__ import annotations | from __future__ import annotations | ||||||
|  |  | ||||||
|  | import shutil | ||||||
|  |  | ||||||
| from dataclasses import dataclass | from dataclasses import dataclass | ||||||
| from pathlib import Path | from pathlib import Path | ||||||
| from typing import Set, Type | from typing import Set, Type | ||||||
| @ -101,24 +103,12 @@ class RepositoryPaths: | |||||||
|  |  | ||||||
|     def cache_for(self, package_base: str) -> Path: |     def cache_for(self, package_base: str) -> Path: | ||||||
|         """ |         """ | ||||||
|         get cache path for specific package base |         get path to cached PKGBUILD and package sources for the package base | ||||||
|         :param package_base: package base name |         :param package_base: package base name | ||||||
|         :return: full path to directory for specified package base cache |         :return: full path to directory for specified package base cache | ||||||
|         """ |         """ | ||||||
|         return self.cache / package_base |         return self.cache / package_base | ||||||
|  |  | ||||||
|     def create_tree(self) -> None: |  | ||||||
|         """ |  | ||||||
|         create ahriman working tree |  | ||||||
|         """ |  | ||||||
|         self.cache.mkdir(mode=0o755, parents=True, exist_ok=True) |  | ||||||
|         self.chroot.mkdir(mode=0o755, parents=True, exist_ok=True) |  | ||||||
|         self.manual.mkdir(mode=0o755, parents=True, exist_ok=True) |  | ||||||
|         self.packages.mkdir(mode=0o755, parents=True, exist_ok=True) |  | ||||||
|         self.patches.mkdir(mode=0o755, parents=True, exist_ok=True) |  | ||||||
|         self.repository.mkdir(mode=0o755, parents=True, exist_ok=True) |  | ||||||
|         self.sources.mkdir(mode=0o755, parents=True, exist_ok=True) |  | ||||||
|  |  | ||||||
|     def manual_for(self, package_base: str) -> Path: |     def manual_for(self, package_base: str) -> Path: | ||||||
|         """ |         """ | ||||||
|         get manual path for specific package base |         get manual path for specific package base | ||||||
| @ -137,8 +127,34 @@ class RepositoryPaths: | |||||||
|  |  | ||||||
|     def sources_for(self, package_base: str) -> Path: |     def sources_for(self, package_base: str) -> Path: | ||||||
|         """ |         """ | ||||||
|         get sources path for specific package base |         get path to directory from where build will start for the package base | ||||||
|         :param package_base: package base name |         :param package_base: package base name | ||||||
|         :return: full path to directory for specified package base sources |         :return: full path to directory for specified package base sources | ||||||
|         """ |         """ | ||||||
|         return self.sources / package_base |         return self.sources / package_base | ||||||
|  |  | ||||||
|  |     def tree_clear(self, package_base: str) -> None: | ||||||
|  |         """ | ||||||
|  |         clear package specific files | ||||||
|  |         :param package_base: package base name | ||||||
|  |         """ | ||||||
|  |         for directory in ( | ||||||
|  |                 self.cache_for(package_base), | ||||||
|  |                 self.manual_for(package_base), | ||||||
|  |                 self.patches_for(package_base), | ||||||
|  |                 self.sources_for(package_base)): | ||||||
|  |             shutil.rmtree(directory, ignore_errors=True) | ||||||
|  |  | ||||||
|  |     def tree_create(self) -> None: | ||||||
|  |         """ | ||||||
|  |         create ahriman working tree | ||||||
|  |         """ | ||||||
|  |         for directory in ( | ||||||
|  |                 self.cache, | ||||||
|  |                 self.chroot, | ||||||
|  |                 self.manual, | ||||||
|  |                 self.packages, | ||||||
|  |                 self.patches, | ||||||
|  |                 self.repository, | ||||||
|  |                 self.sources): | ||||||
|  |             directory.mkdir(mode=0o755, parents=True, exist_ok=True) | ||||||
|  | |||||||
| @ -10,11 +10,11 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc | |||||||
|     """ |     """ | ||||||
|     must run command |     must run command | ||||||
|     """ |     """ | ||||||
|     create_tree_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.create_tree") |     tree_create_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create") | ||||||
|     init_mock = mocker.patch("ahriman.core.alpm.repo.Repo.init") |     init_mock = mocker.patch("ahriman.core.alpm.repo.Repo.init") | ||||||
|  |  | ||||||
|     Init.run(args, "x86_64", configuration, True) |     Init.run(args, "x86_64", configuration, True) | ||||||
|     create_tree_mock.assert_called_once() |     tree_create_mock.assert_called_once() | ||||||
|     init_mock.assert_called_once() |     init_mock.assert_called_once() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,5 +1,6 @@ | |||||||
| import pytest | import pytest | ||||||
|  |  | ||||||
|  | from pathlib import Path | ||||||
| from pytest_mock import MockerFixture | from pytest_mock import MockerFixture | ||||||
| from unittest import mock | from unittest import mock | ||||||
|  |  | ||||||
| @ -104,6 +105,43 @@ def test_get_updates_with_filter(application: Application, mocker: MockerFixture | |||||||
|     updates_manual_mock.assert_called_once() |     updates_manual_mock.assert_called_once() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_add_archive(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: | ||||||
|  |     """ | ||||||
|  |     must add package from archive | ||||||
|  |     """ | ||||||
|  |     mocker.patch("ahriman.application.application.Application._known_packages", return_value=set()) | ||||||
|  |     move_mock = mocker.patch("shutil.move") | ||||||
|  |  | ||||||
|  |     application.add([package_ahriman.base], PackageSource.Archive, False) | ||||||
|  |     move_mock.assert_called_once() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_add_remote(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: | ||||||
|  |     """ | ||||||
|  |     must add package from AUR | ||||||
|  |     """ | ||||||
|  |     mocker.patch("ahriman.application.application.Application._known_packages", return_value=set()) | ||||||
|  |     mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman) | ||||||
|  |     load_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.load") | ||||||
|  |  | ||||||
|  |     application.add([package_ahriman.base], PackageSource.AUR, True) | ||||||
|  |     load_mock.assert_called_once() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_add_remote_with_dependencies(application: Application, package_ahriman: Package, | ||||||
|  |                                       mocker: MockerFixture) -> None: | ||||||
|  |     """ | ||||||
|  |     must add package from AUR with dependencies | ||||||
|  |     """ | ||||||
|  |     mocker.patch("ahriman.application.application.Application._known_packages", return_value=set()) | ||||||
|  |     mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman) | ||||||
|  |     mocker.patch("ahriman.core.build_tools.sources.Sources.load") | ||||||
|  |     dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies") | ||||||
|  |  | ||||||
|  |     application.add([package_ahriman.base], PackageSource.AUR, False) | ||||||
|  |     dependencies_mock.assert_called_once() | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_add_directory(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: | def test_add_directory(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: | ||||||
|     """ |     """ | ||||||
|     must add packages from directory |     must add packages from directory | ||||||
| @ -118,43 +156,38 @@ def test_add_directory(application: Application, package_ahriman: Package, mocke | |||||||
|     move_mock.assert_called_once() |     move_mock.assert_called_once() | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_add_manual(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: | def test_add_local(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: | ||||||
|     """ |     """ | ||||||
|     must add package from AUR |     must add package from local sources | ||||||
|     """ |     """ | ||||||
|     mocker.patch("ahriman.application.application.Application._known_packages", return_value=set()) |     mocker.patch("ahriman.application.application.Application._known_packages", return_value=set()) | ||||||
|     mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman) |     mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman) | ||||||
|     load_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.load") |     init_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.init") | ||||||
|  |     copytree_mock = mocker.patch("shutil.copytree") | ||||||
|  |  | ||||||
|     application.add([package_ahriman.base], PackageSource.AUR, True) |     application.add([package_ahriman.base], PackageSource.Local, True) | ||||||
|     load_mock.assert_called_once() |     init_mock.assert_called_once() | ||||||
|  |     copytree_mock.assert_has_calls([ | ||||||
|  |         mock.call(Path(package_ahriman.base), application.repository.paths.cache_for(package_ahriman.base)), | ||||||
|  |         mock.call(application.repository.paths.cache_for(package_ahriman.base), | ||||||
|  |                   application.repository.paths.manual_for(package_ahriman.base)), | ||||||
|  |     ]) | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_add_manual_with_dependencies(application: Application, package_ahriman: Package, | def test_add_local_with_dependencies(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: | ||||||
|                                       mocker: MockerFixture) -> None: |  | ||||||
|     """ |     """ | ||||||
|     must add package from AUR with dependencies |     must add package from local sources with dependencies | ||||||
|     """ |     """ | ||||||
|     mocker.patch("ahriman.application.application.Application._known_packages", return_value=set()) |     mocker.patch("ahriman.application.application.Application._known_packages", return_value=set()) | ||||||
|     mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman) |     mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman) | ||||||
|     mocker.patch("ahriman.core.build_tools.sources.Sources.load") |     mocker.patch("ahriman.core.build_tools.sources.Sources.init") | ||||||
|  |     mocker.patch("shutil.copytree") | ||||||
|     dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies") |     dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies") | ||||||
|  |  | ||||||
|     application.add([package_ahriman.base], PackageSource.AUR, False) |     application.add([package_ahriman.base], PackageSource.Local, False) | ||||||
|     dependencies_mock.assert_called_once() |     dependencies_mock.assert_called_once() | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_add_package(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: |  | ||||||
|     """ |  | ||||||
|     must add package from archive |  | ||||||
|     """ |  | ||||||
|     mocker.patch("ahriman.application.application.Application._known_packages", return_value=set()) |  | ||||||
|     move_mock = mocker.patch("shutil.move") |  | ||||||
|  |  | ||||||
|     application.add([package_ahriman.base], PackageSource.Archive, False) |  | ||||||
|     move_mock.assert_called_once() |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_clean_build(application: Application, mocker: MockerFixture) -> None: | def test_clean_build(application: Application, mocker: MockerFixture) -> None: | ||||||
|     """ |     """ | ||||||
|     must clean build directory |     must clean build directory | ||||||
| @ -282,23 +315,37 @@ def test_sync(application: Application, mocker: MockerFixture) -> None: | |||||||
|     executor_mock.assert_called_once() |     executor_mock.assert_called_once() | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_unknown(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: | def test_unknown_no_aur(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: | ||||||
|     """ |     """ | ||||||
|     must return list of packages missing in aur |     must return empty list in case if there is locally stored PKGBUILD | ||||||
|     """ |     """ | ||||||
|     mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman]) |     mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman]) | ||||||
|     mocker.patch("ahriman.models.package.Package.from_aur", side_effect=Exception()) |     mocker.patch("ahriman.models.package.Package.from_aur", side_effect=Exception()) | ||||||
|  |     mocker.patch("pathlib.Path.is_dir", return_value=True) | ||||||
|  |     mocker.patch("ahriman.core.build_tools.sources.Sources.has_remotes", return_value=False) | ||||||
|  |  | ||||||
|  |     assert not application.unknown() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_unknown_no_aur_no_local(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: | ||||||
|  |     """ | ||||||
|  |     must return list of packages missing in aur and in local storage | ||||||
|  |     """ | ||||||
|  |     mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman]) | ||||||
|  |     mocker.patch("ahriman.models.package.Package.from_aur", side_effect=Exception()) | ||||||
|  |     mocker.patch("pathlib.Path.is_dir", return_value=False) | ||||||
|  |  | ||||||
|     packages = application.unknown() |     packages = application.unknown() | ||||||
|     assert packages == [package_ahriman] |     assert packages == [package_ahriman] | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_unknown_empty(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: | def test_unknown_no_local(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: | ||||||
|     """ |     """ | ||||||
|     must return list of packages missing in aur |     must return empty list in case if there is package in AUR | ||||||
|     """ |     """ | ||||||
|     mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman]) |     mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman]) | ||||||
|     mocker.patch("ahriman.models.package.Package.from_aur") |     mocker.patch("ahriman.models.package.Package.from_aur") | ||||||
|  |     mocker.patch("pathlib.Path.is_dir", return_value=False) | ||||||
|  |  | ||||||
|     assert not application.unknown() |     assert not application.unknown() | ||||||
|  |  | ||||||
|  | |||||||
| @ -35,19 +35,34 @@ def test_diff(mocker: MockerFixture) -> None: | |||||||
|     check_output_mock.assert_called_with("git", "diff", exception=None, cwd=local, logger=pytest.helpers.anyvar(int)) |     check_output_mock.assert_called_with("git", "diff", exception=None, cwd=local, logger=pytest.helpers.anyvar(int)) | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_fetch_existing(mocker: MockerFixture) -> None: | def test_fetch_empty(mocker: MockerFixture) -> None: | ||||||
|     """ |     """ | ||||||
|     must fetch new package via clone command |     must do nothing in case if no branches available | ||||||
|     """ |     """ | ||||||
|     mocker.patch("pathlib.Path.is_dir", return_value=True) |     mocker.patch("pathlib.Path.is_dir", return_value=True) | ||||||
|  |     mocker.patch("ahriman.core.build_tools.sources.Sources.has_remotes", return_value=False) | ||||||
|  |     check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output") | ||||||
|  |  | ||||||
|  |     Sources.fetch(Path("local"), "remote") | ||||||
|  |     check_output_mock.assert_not_called() | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_fetch_existing(mocker: MockerFixture) -> None: | ||||||
|  |     """ | ||||||
|  |     must fetch new package via fetch command | ||||||
|  |     """ | ||||||
|  |     mocker.patch("pathlib.Path.is_dir", return_value=True) | ||||||
|  |     mocker.patch("ahriman.core.build_tools.sources.Sources.has_remotes", return_value=True) | ||||||
|     check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output") |     check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output") | ||||||
|  |  | ||||||
|     local = Path("local") |     local = Path("local") | ||||||
|     Sources.fetch(local, "remote", "master") |     Sources.fetch(local, "remote") | ||||||
|     check_output_mock.assert_has_calls([ |     check_output_mock.assert_has_calls([ | ||||||
|         mock.call("git", "fetch", "origin", "master", exception=None, cwd=local, logger=pytest.helpers.anyvar(int)), |         mock.call("git", "fetch", "origin", Sources._branch, | ||||||
|         mock.call("git", "checkout", "--force", "master", exception=None, cwd=local, logger=pytest.helpers.anyvar(int)), |                   exception=None, cwd=local, logger=pytest.helpers.anyvar(int)), | ||||||
|         mock.call("git", "reset", "--hard", "origin/master", |         mock.call("git", "checkout", "--force", Sources._branch, | ||||||
|  |                   exception=None, cwd=local, logger=pytest.helpers.anyvar(int)), | ||||||
|  |         mock.call("git", "reset", "--hard", f"origin/{Sources._branch}", | ||||||
|                   exception=None, cwd=local, logger=pytest.helpers.anyvar(int)) |                   exception=None, cwd=local, logger=pytest.helpers.anyvar(int)) | ||||||
|     ]) |     ]) | ||||||
|  |  | ||||||
| @ -60,15 +75,47 @@ def test_fetch_new(mocker: MockerFixture) -> None: | |||||||
|     check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output") |     check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output") | ||||||
|  |  | ||||||
|     local = Path("local") |     local = Path("local") | ||||||
|     Sources.fetch(local, "remote", "master") |     Sources.fetch(local, "remote") | ||||||
|     check_output_mock.assert_has_calls([ |     check_output_mock.assert_has_calls([ | ||||||
|         mock.call("git", "clone", "remote", str(local), exception=None, logger=pytest.helpers.anyvar(int)), |         mock.call("git", "clone", "remote", str(local), exception=None, logger=pytest.helpers.anyvar(int)), | ||||||
|         mock.call("git", "checkout", "--force", "master", exception=None, cwd=local, logger=pytest.helpers.anyvar(int)), |         mock.call("git", "checkout", "--force", Sources._branch, | ||||||
|         mock.call("git", "reset", "--hard", "origin/master", |                   exception=None, cwd=local, logger=pytest.helpers.anyvar(int)), | ||||||
|  |         mock.call("git", "reset", "--hard", f"origin/{Sources._branch}", | ||||||
|                   exception=None, cwd=local, logger=pytest.helpers.anyvar(int)) |                   exception=None, cwd=local, logger=pytest.helpers.anyvar(int)) | ||||||
|     ]) |     ]) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_has_remotes(mocker: MockerFixture) -> None: | ||||||
|  |     """ | ||||||
|  |     must ask for remotes | ||||||
|  |     """ | ||||||
|  |     check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output", return_value="origin") | ||||||
|  |  | ||||||
|  |     local = Path("local") | ||||||
|  |     assert Sources.has_remotes(local) | ||||||
|  |     check_output_mock.assert_called_with("git", "remote", exception=None, cwd=local, logger=pytest.helpers.anyvar(int)) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_has_remotes_empty(mocker: MockerFixture) -> None: | ||||||
|  |     """ | ||||||
|  |     must ask for remotes and return false in case if no remotes found | ||||||
|  |     """ | ||||||
|  |     mocker.patch("ahriman.core.build_tools.sources.Sources._check_output", return_value="") | ||||||
|  |     assert not Sources.has_remotes(Path("local")) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_init(mocker: MockerFixture) -> None: | ||||||
|  |     """ | ||||||
|  |     must create empty repository at the specified path | ||||||
|  |     """ | ||||||
|  |     check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output") | ||||||
|  |  | ||||||
|  |     local = Path("local") | ||||||
|  |     Sources.init(local) | ||||||
|  |     check_output_mock.assert_called_with("git", "init", "--initial-branch", Sources._branch, | ||||||
|  |                                          exception=None, cwd=local, logger=pytest.helpers.anyvar(int)) | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_load(mocker: MockerFixture) -> None: | def test_load(mocker: MockerFixture) -> None: | ||||||
|     """ |     """ | ||||||
|     must load packages sources correctly |     must load packages sources correctly | ||||||
|  | |||||||
| @ -60,13 +60,15 @@ def test_process_remove_base(executor: Executor, package_ahriman: Package, mocke | |||||||
|     must run remove process for whole base |     must run remove process for whole base | ||||||
|     """ |     """ | ||||||
|     mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman]) |     mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman]) | ||||||
|  |     tree_clear_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_clear") | ||||||
|     repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove") |     repo_remove_mock = mocker.patch("ahriman.core.alpm.repo.Repo.remove") | ||||||
|     status_client_mock = mocker.patch("ahriman.core.status.client.Client.remove") |     status_client_mock = mocker.patch("ahriman.core.status.client.Client.remove") | ||||||
|  |  | ||||||
|     executor.process_remove([package_ahriman.base]) |     executor.process_remove([package_ahriman.base]) | ||||||
|     # must remove via alpm wrapper |     # must remove via alpm wrapper | ||||||
|     repo_remove_mock.assert_called_once() |     repo_remove_mock.assert_called_once() | ||||||
|     # must update status |     # must update status and remove package files | ||||||
|  |     tree_clear_mock.assert_called_with(package_ahriman.base) | ||||||
|     status_client_mock.assert_called_once() |     status_client_mock.assert_called_once() | ||||||
|  |  | ||||||
|  |  | ||||||
| @ -106,6 +108,15 @@ def test_process_remove_base_single(executor: Executor, package_python_schedule: | |||||||
|  |  | ||||||
|  |  | ||||||
| def test_process_remove_failed(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None: | def test_process_remove_failed(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None: | ||||||
|  |     """ | ||||||
|  |     must suppress tree clear errors during package base removal | ||||||
|  |     """ | ||||||
|  |     mocker.patch("ahriman.core.repository.executor.Executor.packages", return_value=[package_ahriman]) | ||||||
|  |     mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_clear", side_effect=Exception()) | ||||||
|  |     executor.process_remove([package_ahriman.base]) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_process_remove_tree_clear_failed(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None: | ||||||
|     """ |     """ | ||||||
|     must suppress remove errors |     must suppress remove errors | ||||||
|     """ |     """ | ||||||
|  | |||||||
| @ -9,17 +9,17 @@ def test_create_tree_on_load(configuration: Configuration, mocker: MockerFixture | |||||||
|     """ |     """ | ||||||
|     must create tree on load |     must create tree on load | ||||||
|     """ |     """ | ||||||
|     create_tree_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.create_tree") |     tree_create_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create") | ||||||
|     Properties("x86_64", configuration, True) |     Properties("x86_64", configuration, True) | ||||||
|  |  | ||||||
|     create_tree_mock.assert_called_once() |     tree_create_mock.assert_called_once() | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_create_dummy_report_client(configuration: Configuration, mocker: MockerFixture) -> None: | def test_create_dummy_report_client(configuration: Configuration, mocker: MockerFixture) -> None: | ||||||
|     """ |     """ | ||||||
|     must create dummy report client if report is disabled |     must create dummy report client if report is disabled | ||||||
|     """ |     """ | ||||||
|     mocker.patch("ahriman.models.repository_paths.RepositoryPaths.create_tree") |     mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create") | ||||||
|     load_mock = mocker.patch("ahriman.core.status.client.Client.load") |     load_mock = mocker.patch("ahriman.core.status.client.Client.load") | ||||||
|     properties = Properties("x86_64", configuration, True) |     properties = Properties("x86_64", configuration, True) | ||||||
|  |  | ||||||
| @ -31,7 +31,7 @@ def test_create_full_report_client(configuration: Configuration, mocker: MockerF | |||||||
|     """ |     """ | ||||||
|     must create load report client if report is enabled |     must create load report client if report is enabled | ||||||
|     """ |     """ | ||||||
|     mocker.patch("ahriman.models.repository_paths.RepositoryPaths.create_tree") |     mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create") | ||||||
|     load_mock = mocker.patch("ahriman.core.status.client.Client.load") |     load_mock = mocker.patch("ahriman.core.status.client.Client.load") | ||||||
|     Properties("x86_64", configuration, False) |     Properties("x86_64", configuration, False) | ||||||
|  |  | ||||||
|  | |||||||
| @ -1,8 +1,21 @@ | |||||||
| from pytest_mock import MockerFixture | from pytest_mock import MockerFixture | ||||||
|  | from pathlib import Path | ||||||
|  | from typing import Callable | ||||||
|  |  | ||||||
| from ahriman.models.package_source import PackageSource | from ahriman.models.package_source import PackageSource | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def _is_file_mock(is_any_file: bool, is_pkgbuild: bool) -> Callable[[Path], bool]: | ||||||
|  |     """ | ||||||
|  |     helper to mock is_file method | ||||||
|  |     :param is_any_file: value which will be return for any file | ||||||
|  |     :param is_pkgbuild: value which will be return if PKGBUILD like path asked | ||||||
|  |     :return: side effect function for the mocker object | ||||||
|  |     """ | ||||||
|  |     side_effect: Callable[[Path], bool] = lambda source: is_pkgbuild if source.name == "PKGBUILD" else is_any_file | ||||||
|  |     return side_effect | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_resolve_non_auto() -> None: | def test_resolve_non_auto() -> None: | ||||||
|     """ |     """ | ||||||
|     must resolve non auto type to itself |     must resolve non auto type to itself | ||||||
| @ -16,19 +29,10 @@ def test_resolve_archive(mocker: MockerFixture) -> None: | |||||||
|     must resolve auto type into the archive |     must resolve auto type into the archive | ||||||
|     """ |     """ | ||||||
|     mocker.patch("pathlib.Path.is_dir", return_value=False) |     mocker.patch("pathlib.Path.is_dir", return_value=False) | ||||||
|     mocker.patch("pathlib.Path.is_file", return_value=True) |     mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=_is_file_mock(True, False)) | ||||||
|     assert PackageSource.Auto.resolve("linux-5.14.2.arch1-2-x86_64.pkg.tar.zst") == PackageSource.Archive |     assert PackageSource.Auto.resolve("linux-5.14.2.arch1-2-x86_64.pkg.tar.zst") == PackageSource.Archive | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_resolve_directory(mocker: MockerFixture) -> None: |  | ||||||
|     """ |  | ||||||
|     must resolve auto type into the directory |  | ||||||
|     """ |  | ||||||
|     mocker.patch("pathlib.Path.is_dir", return_value=True) |  | ||||||
|     mocker.patch("pathlib.Path.is_file", return_value=False) |  | ||||||
|     assert PackageSource.Auto.resolve("path") == PackageSource.Directory |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_resolve_aur(mocker: MockerFixture) -> None: | def test_resolve_aur(mocker: MockerFixture) -> None: | ||||||
|     """ |     """ | ||||||
|     must resolve auto type into the AUR package |     must resolve auto type into the AUR package | ||||||
| @ -43,5 +47,23 @@ def test_resolve_aur_not_package_like(mocker: MockerFixture) -> None: | |||||||
|     must resolve auto type into the AUR package if it is file, but does not look like a package archive |     must resolve auto type into the AUR package if it is file, but does not look like a package archive | ||||||
|     """ |     """ | ||||||
|     mocker.patch("pathlib.Path.is_dir", return_value=False) |     mocker.patch("pathlib.Path.is_dir", return_value=False) | ||||||
|     mocker.patch("pathlib.Path.is_file", return_value=True) |     mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=_is_file_mock(True, False)) | ||||||
|     assert PackageSource.Auto.resolve("package") == PackageSource.AUR |     assert PackageSource.Auto.resolve("package") == PackageSource.AUR | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_resolve_directory(mocker: MockerFixture) -> None: | ||||||
|  |     """ | ||||||
|  |     must resolve auto type into the directory | ||||||
|  |     """ | ||||||
|  |     mocker.patch("pathlib.Path.is_dir", return_value=True) | ||||||
|  |     mocker.patch("pathlib.Path.is_file", return_value=False) | ||||||
|  |     assert PackageSource.Auto.resolve("path") == PackageSource.Directory | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_resolve_local(mocker: MockerFixture) -> None: | ||||||
|  |     """ | ||||||
|  |     must resolve auto type into the directory | ||||||
|  |     """ | ||||||
|  |     mocker.patch("pathlib.Path.is_dir", return_value=False) | ||||||
|  |     mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=_is_file_mock(True, True)) | ||||||
|  |     assert PackageSource.Auto.resolve("path") == PackageSource.Local | ||||||
|  | |||||||
| @ -23,27 +23,6 @@ def test_cache_for(repository_paths: RepositoryPaths, package_ahriman: Package) | |||||||
|     assert path.parent == repository_paths.cache |     assert path.parent == repository_paths.cache | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_create_tree(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None: |  | ||||||
|     """ |  | ||||||
|     must create whole tree |  | ||||||
|     """ |  | ||||||
|     paths = { |  | ||||||
|         prop |  | ||||||
|         for prop in dir(repository_paths) |  | ||||||
|         if not prop.startswith("_") |  | ||||||
|         and not prop.endswith("_for") |  | ||||||
|         and prop not in ("architecture", "create_tree", "known_architectures", "root") |  | ||||||
|     } |  | ||||||
|     mkdir_mock = mocker.patch("pathlib.Path.mkdir") |  | ||||||
|  |  | ||||||
|     repository_paths.create_tree() |  | ||||||
|     mkdir_mock.assert_has_calls( |  | ||||||
|         [ |  | ||||||
|             mock.call(mode=0o755, parents=True, exist_ok=True) |  | ||||||
|             for _ in paths |  | ||||||
|         ]) |  | ||||||
|  |  | ||||||
|  |  | ||||||
| def test_manual_for(repository_paths: RepositoryPaths, package_ahriman: Package) -> None: | def test_manual_for(repository_paths: RepositoryPaths, package_ahriman: Package) -> None: | ||||||
|     """ |     """ | ||||||
|     must return correct path for manual directory |     must return correct path for manual directory | ||||||
| @ -69,3 +48,41 @@ def test_sources_for(repository_paths: RepositoryPaths, package_ahriman: Package | |||||||
|     path = repository_paths.sources_for(package_ahriman.base) |     path = repository_paths.sources_for(package_ahriman.base) | ||||||
|     assert path.name == package_ahriman.base |     assert path.name == package_ahriman.base | ||||||
|     assert path.parent == repository_paths.sources |     assert path.parent == repository_paths.sources | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_tree_clear(repository_paths: RepositoryPaths, package_ahriman: Package, mocker: MockerFixture) -> None: | ||||||
|  |     """ | ||||||
|  |     must remove any package related files | ||||||
|  |     """ | ||||||
|  |     paths = { | ||||||
|  |         getattr(repository_paths, prop)(package_ahriman.base) | ||||||
|  |         for prop in dir(repository_paths) if prop.endswith("_for") | ||||||
|  |     } | ||||||
|  |     rmtree_mock = mocker.patch("shutil.rmtree") | ||||||
|  |  | ||||||
|  |     repository_paths.tree_clear(package_ahriman.base) | ||||||
|  |     rmtree_mock.assert_has_calls( | ||||||
|  |         [ | ||||||
|  |             mock.call(path, ignore_errors=True) for path in paths | ||||||
|  |         ], any_order=True) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def test_tree_create(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None: | ||||||
|  |     """ | ||||||
|  |     must create whole tree | ||||||
|  |     """ | ||||||
|  |     paths = { | ||||||
|  |         prop | ||||||
|  |         for prop in dir(repository_paths) | ||||||
|  |         if not prop.startswith("_") | ||||||
|  |         and not prop.endswith("_for") | ||||||
|  |         and prop not in ("architecture", "known_architectures", "root", "tree_clear", "tree_create") | ||||||
|  |     } | ||||||
|  |     mkdir_mock = mocker.patch("pathlib.Path.mkdir") | ||||||
|  |  | ||||||
|  |     repository_paths.tree_create() | ||||||
|  |     mkdir_mock.assert_has_calls( | ||||||
|  |         [ | ||||||
|  |             mock.call(mode=0o755, parents=True, exist_ok=True) | ||||||
|  |             for _ in paths | ||||||
|  |         ], any_order=True) | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user