mirror of
				https://github.com/arcan1s/ahriman.git
				synced 2025-11-03 23:33: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",
 | 
			
		||||
                             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)
 | 
			
		||||
    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("-s", "--source", help="package source",
 | 
			
		||||
                        type=PackageSource, choices=PackageSource, default=PackageSource.Auto)
 | 
			
		||||
 | 
			
		||||
@ -105,21 +105,30 @@ class Application:
 | 
			
		||||
        :param without_dependencies: if set, dependency check will be disabled
 | 
			
		||||
        """
 | 
			
		||||
        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:
 | 
			
		||||
            for full_path in filter(package_like, path.iterdir()):
 | 
			
		||||
                add_archive(full_path)
 | 
			
		||||
 | 
			
		||||
        def add_manual(src: str) -> Path:
 | 
			
		||||
            package = Package.load(src, self.repository.pacman, self.configuration.get("alpm", "aur_url"))
 | 
			
		||||
        def add_local(path: Path) -> Path:
 | 
			
		||||
            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,
 | 
			
		||||
                         self.repository.paths.patches_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:
 | 
			
		||||
            if without_dependencies:
 | 
			
		||||
                return
 | 
			
		||||
@ -128,12 +137,15 @@ class Application:
 | 
			
		||||
 | 
			
		||||
        def process_single(src: str) -> None:
 | 
			
		||||
            resolved_source = source.resolve(src)
 | 
			
		||||
            if resolved_source == PackageSource.Directory:
 | 
			
		||||
                add_directory(Path(src))
 | 
			
		||||
            elif resolved_source == PackageSource.Archive:
 | 
			
		||||
            if resolved_source == PackageSource.Archive:
 | 
			
		||||
                add_archive(Path(src))
 | 
			
		||||
            else:
 | 
			
		||||
                path = add_manual(src)
 | 
			
		||||
            elif resolved_source == PackageSource.AUR:
 | 
			
		||||
                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)
 | 
			
		||||
 | 
			
		||||
        for name in names:
 | 
			
		||||
@ -213,13 +225,22 @@ class Application:
 | 
			
		||||
        get packages which were not found in AUR
 | 
			
		||||
        :return: unknown package list
 | 
			
		||||
        """
 | 
			
		||||
        packages = []
 | 
			
		||||
        for base in self.repository.packages():
 | 
			
		||||
        def has_aur(package_base: str, aur_url: str) -> bool:
 | 
			
		||||
            try:
 | 
			
		||||
                _ = Package.from_aur(base.base, base.aur_url)
 | 
			
		||||
                _ = Package.from_aur(package_base, aur_url)
 | 
			
		||||
            except Exception:
 | 
			
		||||
                packages.append(base)
 | 
			
		||||
        return packages
 | 
			
		||||
                return False
 | 
			
		||||
            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:
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
@ -33,72 +33,98 @@ class Sources:
 | 
			
		||||
 | 
			
		||||
    logger = logging.getLogger("build_details")
 | 
			
		||||
 | 
			
		||||
    _branch = "master"  # in case if BLM would like to change it
 | 
			
		||||
    _check_output = check_output
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def add(local_path: Path, *pattern: str) -> None:
 | 
			
		||||
    def add(sources_dir: Path, *pattern: str) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        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
 | 
			
		||||
        """
 | 
			
		||||
        # glob directory to find files which match the specified patterns
 | 
			
		||||
        found_files: List[Path] = []
 | 
			
		||||
        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)
 | 
			
		||||
        # add them to index
 | 
			
		||||
        Sources._check_output("git", "add", "--intent-to-add", *[str(fn.relative_to(local_path)) for fn in found_files],
 | 
			
		||||
                              exception=None, cwd=local_path, logger=Sources.logger)
 | 
			
		||||
        Sources._check_output("git", "add", "--intent-to-add",
 | 
			
		||||
                              *[str(fn.relative_to(sources_dir)) for fn in found_files],
 | 
			
		||||
                              exception=None, cwd=sources_dir, logger=Sources.logger)
 | 
			
		||||
 | 
			
		||||
    @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
 | 
			
		||||
        :param local_path: local path to git repository
 | 
			
		||||
        :param sources_dir: local path to git repository
 | 
			
		||||
        :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)
 | 
			
		||||
 | 
			
		||||
    @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`
 | 
			
		||||
        :param local_path: local path to fetch
 | 
			
		||||
        :param sources_dir: local path 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
 | 
			
		||||
        if (local_path / ".git").is_dir():
 | 
			
		||||
            Sources.logger.info("update HEAD to remote to %s", local_path)
 | 
			
		||||
            Sources._check_output("git", "fetch", "origin", branch,
 | 
			
		||||
                                  exception=None, cwd=local_path, logger=Sources.logger)
 | 
			
		||||
        is_initialized_git = (sources_dir / ".git").is_dir()
 | 
			
		||||
        if is_initialized_git and not Sources.has_remotes(sources_dir):
 | 
			
		||||
            # there is git repository, but no remote configured so far
 | 
			
		||||
            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:
 | 
			
		||||
            Sources.logger.info("clone remote %s to %s", remote, local_path)
 | 
			
		||||
            Sources._check_output("git", "clone", remote, str(local_path), exception=None, logger=Sources.logger)
 | 
			
		||||
            Sources.logger.info("clone remote %s to %s", remote, sources_dir)
 | 
			
		||||
            Sources._check_output("git", "clone", remote, str(sources_dir), exception=None, logger=Sources.logger)
 | 
			
		||||
        # and now force reset to our branch
 | 
			
		||||
        Sources._check_output("git", "checkout", "--force", branch,
 | 
			
		||||
                              exception=None, cwd=local_path, logger=Sources.logger)
 | 
			
		||||
        Sources._check_output("git", "reset", "--hard", f"origin/{branch}",
 | 
			
		||||
                              exception=None, cwd=local_path, logger=Sources.logger)
 | 
			
		||||
        Sources._check_output("git", "checkout", "--force", Sources._branch,
 | 
			
		||||
                              exception=None, cwd=sources_dir, logger=Sources.logger)
 | 
			
		||||
        Sources._check_output("git", "reset", "--hard", f"origin/{Sources._branch}",
 | 
			
		||||
                              exception=None, cwd=sources_dir, logger=Sources.logger)
 | 
			
		||||
 | 
			
		||||
    @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
 | 
			
		||||
        :param local_path: local path to fetch
 | 
			
		||||
        :param sources_dir: local path to fetch
 | 
			
		||||
        :param remote: remote target (from where to fetch)
 | 
			
		||||
        :param patch_dir: path to directory with package patches
 | 
			
		||||
        """
 | 
			
		||||
        Sources.fetch(local_path, remote)
 | 
			
		||||
        Sources.patch_apply(local_path, patch_dir)
 | 
			
		||||
        Sources.fetch(sources_dir, remote)
 | 
			
		||||
        Sources.patch_apply(sources_dir, patch_dir)
 | 
			
		||||
 | 
			
		||||
    @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
 | 
			
		||||
        :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
 | 
			
		||||
        """
 | 
			
		||||
        # check if even there are patches
 | 
			
		||||
@ -110,15 +136,15 @@ class Sources:
 | 
			
		||||
        for patch in patches:
 | 
			
		||||
            Sources.logger.info("apply patch %s", patch.name)
 | 
			
		||||
            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
 | 
			
		||||
    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
 | 
			
		||||
        :param local_path: local path to git repository
 | 
			
		||||
        :param sources_dir: local path to git repository
 | 
			
		||||
        :param patch_path: path to result patch
 | 
			
		||||
        :param pattern: glob patterns
 | 
			
		||||
        """
 | 
			
		||||
        Sources.add(local_path, *pattern)
 | 
			
		||||
        Sources.diff(local_path, patch_path)
 | 
			
		||||
        Sources.add(sources_dir, *pattern)
 | 
			
		||||
        Sources.diff(sources_dir, patch_path)
 | 
			
		||||
 | 
			
		||||
@ -72,9 +72,16 @@ class Executor(Cleaner):
 | 
			
		||||
        :param packages: list of package names or bases to remove
 | 
			
		||||
        :return: path to repository database
 | 
			
		||||
        """
 | 
			
		||||
        def remove_single(package: str, fn: Path) -> None:
 | 
			
		||||
        def remove_base(package_base: str) -> None:
 | 
			
		||||
            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:
 | 
			
		||||
                self.logger.exception("could not remove %s", package)
 | 
			
		||||
 | 
			
		||||
@ -86,7 +93,7 @@ class Executor(Cleaner):
 | 
			
		||||
                    for package, properties in local.packages.items()
 | 
			
		||||
                    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()):
 | 
			
		||||
                to_remove = {
 | 
			
		||||
                    package: Path(properties.filename)
 | 
			
		||||
@ -95,8 +102,9 @@ class Executor(Cleaner):
 | 
			
		||||
                }
 | 
			
		||||
            else:
 | 
			
		||||
                to_remove = {}
 | 
			
		||||
 | 
			
		||||
            for package, filename in to_remove.items():
 | 
			
		||||
                remove_single(package, filename)
 | 
			
		||||
                remove_package(package, filename)
 | 
			
		||||
 | 
			
		||||
        return self.repo.repo_path
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -58,7 +58,7 @@ class Properties:
 | 
			
		||||
        self.name = configuration.get("repository", "name")
 | 
			
		||||
 | 
			
		||||
        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.pacman = Pacman(configuration)
 | 
			
		||||
 | 
			
		||||
@ -30,14 +30,16 @@ class PackageSource(Enum):
 | 
			
		||||
    package source for addition enumeration
 | 
			
		||||
    :cvar Auto: automatically determine type of the source
 | 
			
		||||
    :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 Directory: source is a directory which contains packages
 | 
			
		||||
    :cvar Local: source is locally stored PKGBUILD
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    Auto = "auto"
 | 
			
		||||
    Archive = "archive"
 | 
			
		||||
    Directory = "directory"
 | 
			
		||||
    AUR = "aur"
 | 
			
		||||
    Directory = "directory"
 | 
			
		||||
    Local = "local"
 | 
			
		||||
 | 
			
		||||
    def resolve(self, source: str) -> PackageSource:
 | 
			
		||||
        """
 | 
			
		||||
@ -47,7 +49,10 @@ class PackageSource(Enum):
 | 
			
		||||
        """
 | 
			
		||||
        if self != PackageSource.Auto:
 | 
			
		||||
            return self
 | 
			
		||||
 | 
			
		||||
        maybe_path = Path(source)
 | 
			
		||||
        if (maybe_path / "PKGBUILD").is_file():
 | 
			
		||||
            return PackageSource.Local
 | 
			
		||||
        if maybe_path.is_dir():
 | 
			
		||||
            return PackageSource.Directory
 | 
			
		||||
        if maybe_path.is_file() and package_like(maybe_path):
 | 
			
		||||
 | 
			
		||||
@ -19,6 +19,8 @@
 | 
			
		||||
#
 | 
			
		||||
from __future__ import annotations
 | 
			
		||||
 | 
			
		||||
import shutil
 | 
			
		||||
 | 
			
		||||
from dataclasses import dataclass
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from typing import Set, Type
 | 
			
		||||
@ -101,24 +103,12 @@ class RepositoryPaths:
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
        :return: full path to directory for specified package base cache
 | 
			
		||||
        """
 | 
			
		||||
        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:
 | 
			
		||||
        """
 | 
			
		||||
        get manual path for specific package base
 | 
			
		||||
@ -137,8 +127,34 @@ class RepositoryPaths:
 | 
			
		||||
 | 
			
		||||
    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
 | 
			
		||||
        :return: full path to directory for specified package base sources
 | 
			
		||||
        """
 | 
			
		||||
        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
 | 
			
		||||
    """
 | 
			
		||||
    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.run(args, "x86_64", configuration, True)
 | 
			
		||||
    create_tree_mock.assert_called_once()
 | 
			
		||||
    tree_create_mock.assert_called_once()
 | 
			
		||||
    init_mock.assert_called_once()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,6 @@
 | 
			
		||||
import pytest
 | 
			
		||||
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
from unittest import mock
 | 
			
		||||
 | 
			
		||||
@ -104,6 +105,43 @@ def test_get_updates_with_filter(application: Application, mocker: MockerFixture
 | 
			
		||||
    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:
 | 
			
		||||
    """
 | 
			
		||||
    must add packages from directory
 | 
			
		||||
@ -118,43 +156,38 @@ def test_add_directory(application: Application, package_ahriman: Package, mocke
 | 
			
		||||
    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.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)
 | 
			
		||||
    load_mock.assert_called_once()
 | 
			
		||||
    application.add([package_ahriman.base], PackageSource.Local, True)
 | 
			
		||||
    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,
 | 
			
		||||
                                      mocker: MockerFixture) -> None:
 | 
			
		||||
def test_add_local_with_dependencies(application: Application, package_ahriman: Package, 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.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")
 | 
			
		||||
 | 
			
		||||
    application.add([package_ahriman.base], PackageSource.AUR, False)
 | 
			
		||||
    application.add([package_ahriman.base], PackageSource.Local, False)
 | 
			
		||||
    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:
 | 
			
		||||
    """
 | 
			
		||||
    must clean build directory
 | 
			
		||||
@ -282,23 +315,37 @@ def test_sync(application: Application, mocker: MockerFixture) -> None:
 | 
			
		||||
    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.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()
 | 
			
		||||
    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.models.package.Package.from_aur")
 | 
			
		||||
    mocker.patch("pathlib.Path.is_dir", return_value=False)
 | 
			
		||||
 | 
			
		||||
    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))
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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("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")
 | 
			
		||||
 | 
			
		||||
    local = Path("local")
 | 
			
		||||
    Sources.fetch(local, "remote", "master")
 | 
			
		||||
    Sources.fetch(local, "remote")
 | 
			
		||||
    check_output_mock.assert_has_calls([
 | 
			
		||||
        mock.call("git", "fetch", "origin", "master", exception=None, cwd=local, logger=pytest.helpers.anyvar(int)),
 | 
			
		||||
        mock.call("git", "checkout", "--force", "master", exception=None, cwd=local, logger=pytest.helpers.anyvar(int)),
 | 
			
		||||
        mock.call("git", "reset", "--hard", "origin/master",
 | 
			
		||||
        mock.call("git", "fetch", "origin", Sources._branch,
 | 
			
		||||
                  exception=None, cwd=local, logger=pytest.helpers.anyvar(int)),
 | 
			
		||||
        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))
 | 
			
		||||
    ])
 | 
			
		||||
 | 
			
		||||
@ -60,15 +75,47 @@ def test_fetch_new(mocker: MockerFixture) -> None:
 | 
			
		||||
    check_output_mock = mocker.patch("ahriman.core.build_tools.sources.Sources._check_output")
 | 
			
		||||
 | 
			
		||||
    local = Path("local")
 | 
			
		||||
    Sources.fetch(local, "remote", "master")
 | 
			
		||||
    Sources.fetch(local, "remote")
 | 
			
		||||
    check_output_mock.assert_has_calls([
 | 
			
		||||
        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", "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))
 | 
			
		||||
    ])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
    """
 | 
			
		||||
    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
 | 
			
		||||
    """
 | 
			
		||||
    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")
 | 
			
		||||
    status_client_mock = mocker.patch("ahriman.core.status.client.Client.remove")
 | 
			
		||||
 | 
			
		||||
    executor.process_remove([package_ahriman.base])
 | 
			
		||||
    # must remove via alpm wrapper
 | 
			
		||||
    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()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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:
 | 
			
		||||
    """
 | 
			
		||||
    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
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
@ -9,17 +9,17 @@ def test_create_tree_on_load(configuration: Configuration, mocker: MockerFixture
 | 
			
		||||
    """
 | 
			
		||||
    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)
 | 
			
		||||
 | 
			
		||||
    create_tree_mock.assert_called_once()
 | 
			
		||||
    tree_create_mock.assert_called_once()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_create_dummy_report_client(configuration: Configuration, mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    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")
 | 
			
		||||
    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
 | 
			
		||||
    """
 | 
			
		||||
    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")
 | 
			
		||||
    Properties("x86_64", configuration, False)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,21 @@
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from typing import Callable
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
    """
 | 
			
		||||
    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
 | 
			
		||||
    """
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
    """
 | 
			
		||||
    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
 | 
			
		||||
    """
 | 
			
		||||
    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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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:
 | 
			
		||||
    """
 | 
			
		||||
    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)
 | 
			
		||||
    assert path.name == package_ahriman.base
 | 
			
		||||
    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