diff --git a/docs/architecture.md b/docs/architecture.md
index 4590c6cf..5c7b7d8e 100644
--- a/docs/architecture.md
+++ b/docs/architecture.md
@@ -12,7 +12,11 @@ Full dependency diagram:
## `ahriman.application` package
-This package contains application (aka executable) related classes and everything for that. It also contains package called `ahriman.application.handlers` in which all available subcommands are described as separated classes derived from base `ahriman.application.handlers.handler.Handler` class. `ahriman.application.ahriman` contains only command line parses and executes specified `Handler` on success, `ahriman.application.application.Application` is a god class which provides interfaces for all repository related actions. `ahriman.application.lock.Lock` is additional class which provides file-based lock and also performs some common checks.
+This package contains application (aka executable) related classes and everything for that. It also contains package called `ahriman.application.handlers` in which all available subcommands are described as separated classes derived from base `ahriman.application.handlers.handler.Handler` class.
+
+`ahriman.application.application.application.Application` (god class) is used for any interaction from parsers with repository, web etc. It is divided into multiple traits by functions (package related and repository related) in the same package.
+
+`ahriman.application.ahriman` contains only command line parses and executes specified `Handler` on success, `ahriman.application.lock.Lock` is additional class which provides file-based lock and also performs some common checks.
## `ahriman.core` package
diff --git a/src/ahriman/application/ahriman.py b/src/ahriman/application/ahriman.py
index 3603d729..069d2a53 100644
--- a/src/ahriman/application/ahriman.py
+++ b/src/ahriman/application/ahriman.py
@@ -295,12 +295,12 @@ def _set_repo_clean_parser(root: SubParserAction) -> argparse.ArgumentParser:
"you should not run this command manually. Also in case if you are going to clear "
"the chroot directories you will need root privileges.",
formatter_class=_formatter)
- parser.add_argument("--no-build", help="do not clear directory with package sources", action="store_true")
- parser.add_argument("--no-cache", help="do not clear directory with package caches", action="store_true")
- parser.add_argument("--no-chroot", help="do not clear build chroot", action="store_true")
- parser.add_argument("--no-manual", help="do not clear directory with manually added packages", action="store_true")
- parser.add_argument("--no-packages", help="do not clear directory with built packages", action="store_true")
- parser.add_argument("--no-patches", help="do not clear directory with patches", action="store_true")
+ parser.add_argument("--build", help="clear directory with package sources", action="store_true")
+ parser.add_argument("--cache", help="clear directory with package caches", action="store_true")
+ parser.add_argument("--chroot", help="clear build chroot", action="store_true")
+ parser.add_argument("--manual", help="clear directory with manually added packages", action="store_true")
+ parser.add_argument("--packages", help="clear directory with built packages", action="store_true")
+ parser.add_argument("--patches", help="clear directory with patches", action="store_true")
parser.set_defaults(handler=handlers.Clean, quiet=True, unsafe=True)
return parser
diff --git a/src/ahriman/application/application.py b/src/ahriman/application/application.py
deleted file mode 100644
index 1c910de3..00000000
--- a/src/ahriman/application/application.py
+++ /dev/null
@@ -1,281 +0,0 @@
-#
-# Copyright (c) 2021 ahriman team.
-#
-# This file is part of ahriman
-# (see https://github.com/arcan1s/ahriman).
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see .
-#
-import logging
-import requests
-import shutil
-
-from pathlib import Path
-from typing import Callable, Iterable, List, Set
-
-from ahriman.core.build_tools.sources import Sources
-from ahriman.core.configuration import Configuration
-from ahriman.core.repository.repository import Repository
-from ahriman.core.tree import Tree
-from ahriman.core.util import package_like
-from ahriman.models.package import Package
-from ahriman.models.package_source import PackageSource
-
-
-class Application:
- """
- base application class
- :ivar architecture: repository architecture
- :ivar configuration: configuration instance
- :ivar logger: application logger
- :ivar repository: repository instance
- """
-
- def __init__(self, architecture: str, configuration: Configuration, no_report: bool) -> None:
- """
- default constructor
- :param architecture: repository architecture
- :param configuration: configuration instance
- :param no_report: force disable reporting
- """
- self.logger = logging.getLogger("root")
- self.configuration = configuration
- self.architecture = architecture
- self.repository = Repository(architecture, configuration, no_report)
-
- def _finalize(self, built_packages: Iterable[Package]) -> None:
- """
- generate report and sync to remote server
- """
- self.report([], built_packages)
- self.sync([], built_packages)
-
- def _known_packages(self) -> Set[str]:
- """
- load packages from repository and pacman repositories
- :return: list of known packages
- """
- known_packages: Set[str] = set()
- # local set
- for base in self.repository.packages():
- for package, properties in base.packages.items():
- known_packages.add(package)
- known_packages.update(properties.provides)
- known_packages.update(self.repository.pacman.all_packages())
- return known_packages
-
- def get_updates(self, filter_packages: List[str], no_aur: bool, no_manual: bool, no_vcs: bool,
- log_fn: Callable[[str], None]) -> List[Package]:
- """
- get list of packages to run update process
- :param filter_packages: do not check every package just specified in the list
- :param no_aur: do not check for aur updates
- :param no_manual: do not check for manual updates
- :param no_vcs: do not check VCS packages
- :param log_fn: logger function to log updates
- :return: list of out-of-dated packages
- """
- updates = []
-
- if not no_aur:
- updates.extend(self.repository.updates_aur(filter_packages, no_vcs))
- if not no_manual:
- updates.extend(self.repository.updates_manual())
-
- for package in updates:
- log_fn(f"{package.base} = {package.version}")
-
- return updates
-
- def add(self, names: Iterable[str], source: PackageSource, without_dependencies: bool) -> None:
- """
- add packages for the next build
- :param names: list of package bases to add
- :param source: package source to add
- :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.copy(src, dst)
-
- def add_aur(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_directory(path: Path) -> None:
- for full_path in filter(package_like, path.iterdir()):
- add_archive(full_path)
-
- 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) -> None:
- dst = self.repository.paths.packages / Path(src).name # URL is path, is not it?
- response = requests.get(src, stream=True)
- response.raise_for_status()
- with dst.open("wb") as local_file:
- for chunk in response.iter_content(chunk_size=1024):
- local_file.write(chunk)
-
- def process_dependencies(path: Path) -> None:
- if without_dependencies:
- return
- dependencies = Package.dependencies(path)
- self.add(dependencies.difference(known_packages), PackageSource.AUR, without_dependencies)
-
- def process_single(src: str) -> None:
- resolved_source = source.resolve(src)
- if resolved_source == PackageSource.Archive:
- add_archive(Path(src))
- elif resolved_source == PackageSource.AUR:
- path = add_aur(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)
- elif resolved_source == PackageSource.Remote:
- add_remote(src)
-
- for name in names:
- process_single(name)
-
- def clean(self, no_build: bool, no_cache: bool, no_chroot: bool, no_manual: bool, no_packages: bool,
- no_patches: bool) -> None:
- """
- run all clean methods. Warning: some functions might not be available under non-root
- :param no_build: do not clear directory with package sources
- :param no_cache: do not clear directory with package caches
- :param no_chroot: do not clear build chroot
- :param no_manual: do not clear directory with manually added packages
- :param no_packages: do not clear directory with built packages
- :param no_patches: do not clear directory with patches
- """
- if not no_build:
- self.repository.clear_build()
- if not no_cache:
- self.repository.clear_cache()
- if not no_chroot:
- self.repository.clear_chroot()
- if not no_manual:
- self.repository.clear_manual()
- if not no_packages:
- self.repository.clear_packages()
- if not no_patches:
- self.repository.clear_patches()
-
- def remove(self, names: Iterable[str]) -> None:
- """
- remove packages from repository
- :param names: list of packages (either base or name) to remove
- """
- self.repository.process_remove(names)
- self._finalize([])
-
- def report(self, target: Iterable[str], built_packages: Iterable[Package]) -> None:
- """
- generate report
- :param target: list of targets to run (e.g. html)
- :param built_packages: list of packages which has just been built
- """
- targets = target or None
- self.repository.process_report(targets, built_packages)
-
- def sign(self, packages: Iterable[str]) -> None:
- """
- sign packages and repository
- :param packages: only sign specified packages
- """
- # copy to prebuilt directory
- for package in self.repository.packages():
- # no one requested this package
- if packages and package.base not in packages:
- continue
- for archive in package.packages.values():
- if archive.filepath is None:
- self.logger.warning("filepath is empty for %s", package.base)
- continue # avoid mypy warning
- src = self.repository.paths.repository / archive.filepath
- dst = self.repository.paths.packages / archive.filepath
- shutil.copy(src, dst)
- # run generic update function
- self.update([])
- # sign repository database if set
- self.repository.sign.process_sign_repository(self.repository.repo.repo_path)
- self._finalize([])
-
- def sync(self, target: Iterable[str], built_packages: Iterable[Package]) -> None:
- """
- sync to remote server
- :param target: list of targets to run (e.g. s3)
- :param built_packages: list of packages which has just been built
- """
- targets = target or None
- self.repository.process_sync(targets, built_packages)
-
- def unknown(self) -> List[Package]:
- """
- get packages which were not found in AUR
- :return: unknown package list
- """
- def has_aur(package_base: str, aur_url: str) -> bool:
- try:
- _ = Package.from_aur(package_base, aur_url)
- except Exception:
- 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:
- """
- run package updates
- :param updates: list of packages to update
- """
- def process_update(paths: Iterable[Path]) -> None:
- if not paths:
- return # don't need to process if no update supplied
- updated = [Package.load(path, self.repository.pacman, self.repository.aur_url) for path in paths]
- self.repository.process_update(paths)
- self._finalize(updated)
-
- # process built packages
- packages = self.repository.packages_built()
- process_update(packages)
-
- # process manual packages
- tree = Tree.load(updates, self.repository.paths)
- for num, level in enumerate(tree.levels()):
- self.logger.info("processing level #%i %s", num, [package.base for package in level])
- packages = self.repository.process_build(level)
- process_update(packages)
diff --git a/src/ahriman/application/application/__init__.py b/src/ahriman/application/application/__init__.py
new file mode 100644
index 00000000..8768f1c7
--- /dev/null
+++ b/src/ahriman/application/application/__init__.py
@@ -0,0 +1,20 @@
+#
+# Copyright (c) 2021 ahriman team.
+#
+# This file is part of ahriman
+# (see https://github.com/arcan1s/ahriman).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+from ahriman.application.application.application import Application
diff --git a/src/ahriman/application/application/application.py b/src/ahriman/application/application/application.py
new file mode 100644
index 00000000..9680c9a2
--- /dev/null
+++ b/src/ahriman/application/application/application.py
@@ -0,0 +1,51 @@
+#
+# Copyright (c) 2021 ahriman team.
+#
+# This file is part of ahriman
+# (see https://github.com/arcan1s/ahriman).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+from typing import Iterable, Set
+
+from ahriman.application.application.packages import Packages
+from ahriman.application.application.repository import Repository
+from ahriman.models.package import Package
+
+
+class Application(Packages, Repository):
+ """
+ base application class
+ """
+
+ def _finalize(self, built_packages: Iterable[Package]) -> None:
+ """
+ generate report and sync to remote server
+ """
+ self.report([], built_packages)
+ self.sync([], built_packages)
+
+ def _known_packages(self) -> Set[str]:
+ """
+ load packages from repository and pacman repositories
+ :return: list of known packages
+ """
+ known_packages: Set[str] = set()
+ # local set
+ for base in self.repository.packages():
+ for package, properties in base.packages.items():
+ known_packages.add(package)
+ known_packages.update(properties.provides)
+ known_packages.update(self.repository.pacman.all_packages())
+ return known_packages
diff --git a/src/ahriman/application/application/packages.py b/src/ahriman/application/application/packages.py
new file mode 100644
index 00000000..7a23f31d
--- /dev/null
+++ b/src/ahriman/application/application/packages.py
@@ -0,0 +1,148 @@
+#
+# Copyright (c) 2021 ahriman team.
+#
+# This file is part of ahriman
+# (see https://github.com/arcan1s/ahriman).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+import requests
+import shutil
+
+from pathlib import Path
+from typing import Any, Iterable, Set
+
+from ahriman.application.application.properties import Properties
+from ahriman.core.build_tools.sources import Sources
+from ahriman.core.util import package_like
+from ahriman.models.package import Package
+from ahriman.models.package_source import PackageSource
+
+
+class Packages(Properties):
+ """
+ package control class
+ """
+
+ def _finalize(self, built_packages: Iterable[Package]) -> None:
+ """
+ generate report and sync to remote server
+ """
+ raise NotImplementedError
+
+ def _known_packages(self) -> Set[str]:
+ """
+ load packages from repository and pacman repositories
+ :return: list of known packages
+ """
+ raise NotImplementedError
+
+ def _add_archive(self, source: str, *_: Any) -> None:
+ """
+ add package from archive
+ :param source: path to package archive
+ """
+ local_path = Path(source)
+ dst = self.repository.paths.packages / local_path.name
+ shutil.copy(local_path, dst)
+
+ def _add_aur(self, source: str, known_packages: Set[str], without_dependencies: bool) -> None:
+ """
+ add package from AUR
+ :param source: package base name
+ :param known_packages: list of packages which are known by the service
+ :param without_dependencies: if set, dependency check will be disabled
+ """
+ aur_url = self.configuration.get("alpm", "aur_url")
+ package = Package.load(source, self.repository.pacman, aur_url)
+ Sources.load(self.repository.paths.manual_for(package.base), package.git_url,
+ self.repository.paths.patches_for(package.base))
+
+ local_path = self.repository.paths.manual_for(package.base)
+ self._process_dependencies(local_path, known_packages, without_dependencies)
+
+ def _add_directory(self, source: str, *_: Any) -> None:
+ """
+ add packages from directory
+ :param source: path to local directory
+ """
+ local_path = Path(source)
+ for full_path in filter(package_like, local_path.iterdir()):
+ self._add_archive(str(full_path))
+
+ def _add_local(self, source: str, known_packages: Set[str], without_dependencies: bool) -> None:
+ """
+ add package from local PKGBUILDs
+ :param source: path to directory with local source files
+ :param known_packages: list of packages which are known by the service
+ :param without_dependencies: if set, dependency check will be disabled
+ """
+ local_path = Path(source)
+ aur_url = self.configuration.get("alpm", "aur_url")
+ package = Package.load(local_path, self.repository.pacman, aur_url)
+ cache_dir = self.repository.paths.cache_for(package.base)
+ shutil.copytree(local_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
+
+ dst = self.repository.paths.manual_for(package.base)
+ shutil.copytree(cache_dir, dst) # copy package for the build
+ self._process_dependencies(dst, known_packages, without_dependencies)
+
+ def _add_remote(self, source: str, *_: Any) -> None:
+ """
+ add package from remote sources (e.g. HTTP)
+ :param remote_url: remote URL to the package archive
+ """
+ dst = self.repository.paths.packages / Path(source).name # URL is path, is not it?
+ response = requests.get(source, stream=True)
+ response.raise_for_status()
+
+ with dst.open("wb") as local_file:
+ for chunk in response.iter_content(chunk_size=1024):
+ local_file.write(chunk)
+
+ def _process_dependencies(self, local_path: Path, known_packages: Set[str], without_dependencies: bool) -> None:
+ """
+ process package dependencies
+ :param local_path: path to local package sources (i.e. cloned AUR repository)
+ :param known_packages: list of packages which are known by the service
+ :param without_dependencies: if set, dependency check will be disabled
+ """
+ if without_dependencies:
+ return
+
+ dependencies = Package.dependencies(local_path)
+ self.add(dependencies.difference(known_packages), PackageSource.AUR, without_dependencies)
+
+ def add(self, names: Iterable[str], source: PackageSource, without_dependencies: bool) -> None:
+ """
+ add packages for the next build
+ :param names: list of package bases to add
+ :param source: package source to add
+ :param without_dependencies: if set, dependency check will be disabled
+ """
+ known_packages = self._known_packages() # speedup dependencies processing
+
+ for name in names:
+ resolved_source = source.resolve(name)
+ fn = getattr(self, f"_add_{resolved_source.value}")
+ fn(name, known_packages, without_dependencies)
+
+ def remove(self, names: Iterable[str]) -> None:
+ """
+ remove packages from repository
+ :param names: list of packages (either base or name) to remove
+ """
+ self.repository.process_remove(names)
+ self._finalize([])
diff --git a/src/ahriman/application/application/properties.py b/src/ahriman/application/application/properties.py
new file mode 100644
index 00000000..549b3b52
--- /dev/null
+++ b/src/ahriman/application/application/properties.py
@@ -0,0 +1,45 @@
+#
+# Copyright (c) 2021 ahriman team.
+#
+# This file is part of ahriman
+# (see https://github.com/arcan1s/ahriman).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+import logging
+
+from ahriman.core.configuration import Configuration
+from ahriman.core.repository import Repository
+
+
+class Properties:
+ """
+ application base properties class
+ :ivar architecture: repository architecture
+ :ivar configuration: configuration instance
+ :ivar logger: application logger
+ :ivar repository: repository instance
+ """
+
+ def __init__(self, architecture: str, configuration: Configuration, no_report: bool) -> None:
+ """
+ default constructor
+ :param architecture: repository architecture
+ :param configuration: configuration instance
+ :param no_report: force disable reporting
+ """
+ self.logger = logging.getLogger("root")
+ self.configuration = configuration
+ self.architecture = architecture
+ self.repository = Repository(architecture, configuration, no_report)
diff --git a/src/ahriman/application/application/repository.py b/src/ahriman/application/application/repository.py
new file mode 100644
index 00000000..8424d397
--- /dev/null
+++ b/src/ahriman/application/application/repository.py
@@ -0,0 +1,172 @@
+#
+# Copyright (c) 2021 ahriman team.
+#
+# This file is part of ahriman
+# (see https://github.com/arcan1s/ahriman).
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+import shutil
+
+from pathlib import Path
+from typing import Callable, Iterable, List
+
+from ahriman.application.application.properties import Properties
+from ahriman.core.build_tools.sources import Sources
+from ahriman.core.tree import Tree
+from ahriman.models.package import Package
+
+
+class Repository(Properties):
+ """
+ repository control class
+ """
+
+ def _finalize(self, built_packages: Iterable[Package]) -> None:
+ """
+ generate report and sync to remote server
+ """
+ raise NotImplementedError
+
+ def clean(self, build: bool, cache: bool, chroot: bool, manual: bool, packages: bool, patches: bool) -> None:
+ """
+ run all clean methods. Warning: some functions might not be available under non-root
+ :param build: clear directory with package sources
+ :param cache: clear directory with package caches
+ :param chroot: clear build chroot
+ :param manual: clear directory with manually added packages
+ :param packages: clear directory with built packages
+ :param patches: clear directory with patches
+ """
+ if build:
+ self.repository.clear_build()
+ if cache:
+ self.repository.clear_cache()
+ if chroot:
+ self.repository.clear_chroot()
+ if manual:
+ self.repository.clear_manual()
+ if packages:
+ self.repository.clear_packages()
+ if patches:
+ self.repository.clear_patches()
+
+ def report(self, target: Iterable[str], built_packages: Iterable[Package]) -> None:
+ """
+ generate report
+ :param target: list of targets to run (e.g. html)
+ :param built_packages: list of packages which has just been built
+ """
+ targets = target or None
+ self.repository.process_report(targets, built_packages)
+
+ def sign(self, packages: Iterable[str]) -> None:
+ """
+ sign packages and repository
+ :param packages: only sign specified packages
+ """
+ # copy to prebuilt directory
+ for package in self.repository.packages():
+ # no one requested this package
+ if packages and package.base not in packages:
+ continue
+ for archive in package.packages.values():
+ if archive.filepath is None:
+ self.logger.warning("filepath is empty for %s", package.base)
+ continue # avoid mypy warning
+ src = self.repository.paths.repository / archive.filepath
+ dst = self.repository.paths.packages / archive.filepath
+ shutil.copy(src, dst)
+ # run generic update function
+ self.update([])
+ # sign repository database if set
+ self.repository.sign.process_sign_repository(self.repository.repo.repo_path)
+ self._finalize([])
+
+ def sync(self, target: Iterable[str], built_packages: Iterable[Package]) -> None:
+ """
+ sync to remote server
+ :param target: list of targets to run (e.g. s3)
+ :param built_packages: list of packages which has just been built
+ """
+ targets = target or None
+ self.repository.process_sync(targets, built_packages)
+
+ def unknown(self) -> List[Package]:
+ """
+ get packages which were not found in AUR
+ :return: unknown package list
+ """
+ def has_aur(package_base: str, aur_url: str) -> bool:
+ try:
+ _ = Package.from_aur(package_base, aur_url)
+ except Exception:
+ 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:
+ """
+ run package updates
+ :param updates: list of packages to update
+ """
+ def process_update(paths: Iterable[Path]) -> None:
+ if not paths:
+ return # don't need to process if no update supplied
+ updated = [Package.load(path, self.repository.pacman, self.repository.aur_url) for path in paths]
+ self.repository.process_update(paths)
+ self._finalize(updated)
+
+ # process built packages
+ packages = self.repository.packages_built()
+ process_update(packages)
+
+ # process manual packages
+ tree = Tree.load(updates, self.repository.paths)
+ for num, level in enumerate(tree.levels()):
+ self.logger.info("processing level #%i %s", num, [package.base for package in level])
+ packages = self.repository.process_build(level)
+ process_update(packages)
+
+ def updates(self, filter_packages: Iterable[str], no_aur: bool, no_manual: bool, no_vcs: bool,
+ log_fn: Callable[[str], None]) -> List[Package]:
+ """
+ get list of packages to run update process
+ :param filter_packages: do not check every package just specified in the list
+ :param no_aur: do not check for aur updates
+ :param no_manual: do not check for manual updates
+ :param no_vcs: do not check VCS packages
+ :param log_fn: logger function to log updates
+ :return: list of out-of-dated packages
+ """
+ updates = []
+
+ if not no_aur:
+ updates.extend(self.repository.updates_aur(filter_packages, no_vcs))
+ if not no_manual:
+ updates.extend(self.repository.updates_manual())
+
+ for package in updates:
+ log_fn(f"{package.base} = {package.version}")
+
+ return updates
diff --git a/src/ahriman/application/handlers/add.py b/src/ahriman/application/handlers/add.py
index 5f2427dc..2cc85024 100644
--- a/src/ahriman/application/handlers/add.py
+++ b/src/ahriman/application/handlers/add.py
@@ -46,5 +46,5 @@ class Add(Handler):
if not args.now:
return
- packages = application.get_updates(args.package, True, False, True, application.logger.info)
+ packages = application.updates(args.package, True, False, True, application.logger.info)
application.update(packages)
diff --git a/src/ahriman/application/handlers/clean.py b/src/ahriman/application/handlers/clean.py
index 7324de09..be2ffe0c 100644
--- a/src/ahriman/application/handlers/clean.py
+++ b/src/ahriman/application/handlers/clean.py
@@ -42,4 +42,4 @@ class Clean(Handler):
:param no_report: force disable reporting
"""
Application(architecture, configuration, no_report).clean(
- args.no_build, args.no_cache, args.no_chroot, args.no_manual, args.no_packages, args.no_patches)
+ args.build, args.cache, args.chroot, args.manual, args.packages, args.patches)
diff --git a/src/ahriman/application/handlers/update.py b/src/ahriman/application/handlers/update.py
index c9bb3fab..972bce0f 100644
--- a/src/ahriman/application/handlers/update.py
+++ b/src/ahriman/application/handlers/update.py
@@ -42,8 +42,8 @@ class Update(Handler):
:param no_report: force disable reporting
"""
application = Application(architecture, configuration, no_report)
- packages = application.get_updates(args.package, args.no_aur, args.no_manual, args.no_vcs,
- Update.log_fn(application, args.dry_run))
+ packages = application.updates(args.package, args.no_aur, args.no_manual, args.no_vcs,
+ Update.log_fn(application, args.dry_run))
if args.dry_run:
return
diff --git a/src/ahriman/core/repository/__init__.py b/src/ahriman/core/repository/__init__.py
index fb32931e..95a3dd5c 100644
--- a/src/ahriman/core/repository/__init__.py
+++ b/src/ahriman/core/repository/__init__.py
@@ -17,3 +17,4 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
+from ahriman.core.repository.repository import Repository
diff --git a/src/ahriman/core/status/watcher.py b/src/ahriman/core/status/watcher.py
index 8d320853..ad62ba76 100644
--- a/src/ahriman/core/status/watcher.py
+++ b/src/ahriman/core/status/watcher.py
@@ -25,7 +25,7 @@ from typing import Any, Dict, List, Optional, Tuple
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import UnknownPackage
-from ahriman.core.repository.repository import Repository
+from ahriman.core.repository import Repository
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.package import Package
diff --git a/tests/ahriman/application/application/conftest.py b/tests/ahriman/application/application/conftest.py
new file mode 100644
index 00000000..67110ab1
--- /dev/null
+++ b/tests/ahriman/application/application/conftest.py
@@ -0,0 +1,44 @@
+import pytest
+
+from pytest_mock import MockerFixture
+
+from ahriman.application.application.packages import Packages
+from ahriman.application.application.properties import Properties
+from ahriman.application.application.repository import Repository
+from ahriman.core.configuration import Configuration
+
+
+@pytest.fixture
+def application_packages(configuration: Configuration, mocker: MockerFixture) -> Packages:
+ """
+ fixture for application with package functions
+ :param configuration: configuration fixture
+ :param mocker: mocker object
+ :return: application test instance
+ """
+ mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
+ return Packages("x86_64", configuration, no_report=True)
+
+
+@pytest.fixture
+def application_properties(configuration: Configuration, mocker: MockerFixture) -> Properties:
+ """
+ fixture for application with properties only
+ :param configuration: configuration fixture
+ :param mocker: mocker object
+ :return: application test instance
+ """
+ mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
+ return Properties("x86_64", configuration, no_report=True)
+
+
+@pytest.fixture
+def application_repository(configuration: Configuration, mocker: MockerFixture) -> Repository:
+ """
+ fixture for application with repository functions
+ :param configuration: configuration fixture
+ :param mocker: mocker object
+ :return: application test instance
+ """
+ mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
+ return Repository("x86_64", configuration, no_report=True)
diff --git a/tests/ahriman/application/application/test_application.py b/tests/ahriman/application/application/test_application.py
new file mode 100644
index 00000000..d0be376d
--- /dev/null
+++ b/tests/ahriman/application/application/test_application.py
@@ -0,0 +1,26 @@
+from pytest_mock import MockerFixture
+
+from ahriman.application.application import Application
+from ahriman.models.package import Package
+
+
+def test_finalize(application: Application, mocker: MockerFixture) -> None:
+ """
+ must report and sync at the last
+ """
+ report_mock = mocker.patch("ahriman.application.application.Application.report")
+ sync_mock = mocker.patch("ahriman.application.application.Application.sync")
+
+ application._finalize([])
+ report_mock.assert_called_once()
+ sync_mock.assert_called_once()
+
+
+def test_known_packages(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
+ """
+ must return not empty list of known packages
+ """
+ mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
+ packages = application._known_packages()
+ assert len(packages) > 1
+ assert package_ahriman.base in packages
diff --git a/tests/ahriman/application/application/test_application_packages.py b/tests/ahriman/application/application/test_application_packages.py
new file mode 100644
index 00000000..73a59b8c
--- /dev/null
+++ b/tests/ahriman/application/application/test_application_packages.py
@@ -0,0 +1,207 @@
+import pytest
+
+from pathlib import Path
+from pytest_mock import MockerFixture
+from unittest import mock
+from unittest.mock import MagicMock
+
+from ahriman.application.application.packages import Packages
+from ahriman.models.package import Package
+from ahriman.models.package_description import PackageDescription
+from ahriman.models.package_source import PackageSource
+
+
+def test_finalize(application_packages: Packages) -> None:
+ """
+ must raise NotImplemented for missing finalize method
+ """
+ with pytest.raises(NotImplementedError):
+ application_packages._finalize([])
+
+
+def test_known_packages(application_packages: Packages) -> None:
+ """
+ must raise NotImplemented for missing finalize method
+ """
+ with pytest.raises(NotImplementedError):
+ application_packages._known_packages()
+
+
+def test_add_archive(application_packages: Packages, package_ahriman: Package, mocker: MockerFixture) -> None:
+ """
+ must add package from archive
+ """
+ copy_mock = mocker.patch("shutil.copy")
+ application_packages._add_archive(package_ahriman.base)
+ copy_mock.assert_called_once()
+
+
+def test_add_aur(application_packages: Packages, package_ahriman: Package, mocker: MockerFixture) -> None:
+ """
+ must add package from AUR
+ """
+ mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
+ load_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.load")
+ dependencies_mock = mocker.patch("ahriman.application.application.packages.Packages._process_dependencies")
+
+ application_packages._add_aur(package_ahriman.base, set(), False)
+ load_mock.assert_called_once()
+ dependencies_mock.assert_called_once()
+
+
+def test_add_directory(application_packages: Packages, package_ahriman: Package, mocker: MockerFixture) -> None:
+ """
+ must add packages from directory
+ """
+ iterdir_mock = mocker.patch("pathlib.Path.iterdir",
+ return_value=[package.filepath for package in package_ahriman.packages.values()])
+ copy_mock = mocker.patch("shutil.copy")
+
+ application_packages._add_directory(package_ahriman.base)
+ iterdir_mock.assert_called_once()
+ copy_mock.assert_called_once()
+
+
+def test_add_local(application_packages: Packages, package_ahriman: Package, mocker: MockerFixture) -> None:
+ """
+ must add package from local sources
+ """
+ mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
+ init_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.init")
+ copytree_mock = mocker.patch("shutil.copytree")
+ dependencies_mock = mocker.patch("ahriman.application.application.packages.Packages._process_dependencies")
+
+ application_packages._add_local(package_ahriman.base, set(), False)
+ init_mock.assert_called_once()
+ copytree_mock.assert_has_calls([
+ mock.call(Path(package_ahriman.base), application_packages.repository.paths.cache_for(package_ahriman.base)),
+ mock.call(application_packages.repository.paths.cache_for(package_ahriman.base),
+ application_packages.repository.paths.manual_for(package_ahriman.base)),
+ ])
+ dependencies_mock.assert_called_once()
+
+
+def test_add_remote(application_packages: Packages, package_description_ahriman: PackageDescription,
+ mocker: MockerFixture) -> None:
+ """
+ must add package from remote source
+ """
+ response_mock = MagicMock()
+ response_mock.iter_content.return_value = ["chunk"]
+ open_mock = mocker.patch("pathlib.Path.open")
+ request_mock = mocker.patch("requests.get", return_value=response_mock)
+ url = f"https://host/{package_description_ahriman.filename}"
+
+ application_packages._add_remote(url)
+ open_mock.assert_called_once_with("wb")
+ request_mock.assert_called_once_with(url, stream=True)
+ response_mock.raise_for_status.assert_called_once()
+
+
+def test_process_dependencies(application_packages: Packages, mocker: MockerFixture) -> None:
+ """
+ must process dependencies addition
+ """
+ missing = {"python"}
+ path = Path("local")
+ dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies", return_value=missing)
+ add_mock = mocker.patch("ahriman.application.application.packages.Packages.add")
+
+ application_packages._process_dependencies(path, set(), False)
+ dependencies_mock.assert_called_once_with(path)
+ add_mock.assert_called_once_with(missing, PackageSource.AUR, False)
+
+
+def test_process_dependencies_missing(application_packages: Packages, mocker: MockerFixture) -> None:
+ """
+ must process dependencies addition only for missing packages
+ """
+ path = Path("local")
+ dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies",
+ return_value={"python", "python-aiohttp"})
+ add_mock = mocker.patch("ahriman.application.application.packages.Packages.add")
+
+ application_packages._process_dependencies(path, {"python"}, False)
+ dependencies_mock.assert_called_once_with(path)
+ add_mock.assert_called_once_with({"python-aiohttp"}, PackageSource.AUR, False)
+
+
+def test_process_dependencies_skip(application_packages: Packages, mocker: MockerFixture) -> None:
+ """
+ must skip dependencies processing
+ """
+ dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies")
+ add_mock = mocker.patch("ahriman.application.application.packages.Packages.add")
+
+ application_packages._process_dependencies(Path("local"), set(), True)
+ dependencies_mock.assert_not_called()
+ add_mock.assert_not_called()
+
+
+def test_add_add_archive(application_packages: Packages, package_ahriman: Package, mocker: MockerFixture) -> None:
+ """
+ must add package from archive via add function
+ """
+ mocker.patch("ahriman.application.application.packages.Packages._known_packages", return_value=set())
+ add_mock = mocker.patch("ahriman.application.application.packages.Packages._add_archive")
+
+ application_packages.add([package_ahriman.base], PackageSource.Archive, False)
+ add_mock.assert_called_once()
+
+
+def test_add_add_aur(application_packages: Packages, package_ahriman: Package, mocker: MockerFixture) -> None:
+ """
+ must add package from AUR via add function
+ """
+ mocker.patch("ahriman.application.application.packages.Packages._known_packages", return_value=set())
+ add_mock = mocker.patch("ahriman.application.application.packages.Packages._add_aur")
+
+ application_packages.add([package_ahriman.base], PackageSource.AUR, True)
+ add_mock.assert_called_once()
+
+
+def test_add_add_directory(application_packages: Packages, package_ahriman: Package, mocker: MockerFixture) -> None:
+ """
+ must add packages from directory via add function
+ """
+ mocker.patch("ahriman.application.application.packages.Packages._known_packages", return_value=set())
+ add_mock = mocker.patch("ahriman.application.application.packages.Packages._add_directory")
+
+ application_packages.add([package_ahriman.base], PackageSource.Directory, False)
+ add_mock.assert_called_once()
+
+
+def test_add_add_local(application_packages: Packages, package_ahriman: Package, mocker: MockerFixture) -> None:
+ """
+ must add package from local sources via add function
+ """
+ mocker.patch("ahriman.application.application.packages.Packages._known_packages", return_value=set())
+ add_mock = mocker.patch("ahriman.application.application.packages.Packages._add_local")
+
+ application_packages.add([package_ahriman.base], PackageSource.Local, False)
+ add_mock.assert_called_once()
+
+
+def test_add_add_remote(application_packages: Packages, package_description_ahriman: PackageDescription,
+ mocker: MockerFixture) -> None:
+ """
+ must add package from remote source via add function
+ """
+ mocker.patch("ahriman.application.application.packages.Packages._known_packages", return_value=set())
+ add_mock = mocker.patch("ahriman.application.application.packages.Packages._add_remote")
+ url = f"https://host/{package_description_ahriman.filename}"
+
+ application_packages.add([url], PackageSource.Remote, False)
+ add_mock.assert_called_once()
+
+
+def test_remove(application_packages: Packages, mocker: MockerFixture) -> None:
+ """
+ must remove package
+ """
+ executor_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_remove")
+ finalize_mock = mocker.patch("ahriman.application.application.packages.Packages._finalize")
+
+ application_packages.remove([])
+ executor_mock.assert_called_once()
+ finalize_mock.assert_called_once()
diff --git a/tests/ahriman/application/application/test_application_properties.py b/tests/ahriman/application/application/test_application_properties.py
new file mode 100644
index 00000000..b4dae48a
--- /dev/null
+++ b/tests/ahriman/application/application/test_application_properties.py
@@ -0,0 +1,8 @@
+from ahriman.application.application.properties import Properties
+
+
+def test_create_tree(application_properties: Properties) -> None:
+ """
+ must have repository attribute
+ """
+ assert application_properties.repository
diff --git a/tests/ahriman/application/application/test_application_repository.py b/tests/ahriman/application/application/test_application_repository.py
new file mode 100644
index 00000000..97208d5b
--- /dev/null
+++ b/tests/ahriman/application/application/test_application_repository.py
@@ -0,0 +1,270 @@
+import pytest
+
+from pytest_mock import MockerFixture
+from unittest import mock
+
+from ahriman.application.application.repository import Repository
+from ahriman.core.tree import Leaf, Tree
+from ahriman.models.package import Package
+
+
+def test_finalize(application_repository: Repository) -> None:
+ """
+ must raise NotImplemented for missing finalize method
+ """
+ with pytest.raises(NotImplementedError):
+ application_repository._finalize([])
+
+
+def test_clean_build(application_repository: Repository, mocker: MockerFixture) -> None:
+ """
+ must clean build directory
+ """
+ clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_build")
+ application_repository.clean(True, False, False, False, False, False)
+ clear_mock.assert_called_once()
+
+
+def test_clean_cache(application_repository: Repository, mocker: MockerFixture) -> None:
+ """
+ must clean cache directory
+ """
+ clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_cache")
+ application_repository.clean(False, True, False, False, False, False)
+ clear_mock.assert_called_once()
+
+
+def test_clean_chroot(application_repository: Repository, mocker: MockerFixture) -> None:
+ """
+ must clean chroot directory
+ """
+ clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_chroot")
+ application_repository.clean(False, False, True, False, False, False)
+ clear_mock.assert_called_once()
+
+
+def test_clean_manual(application_repository: Repository, mocker: MockerFixture) -> None:
+ """
+ must clean manual directory
+ """
+ clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_manual")
+ application_repository.clean(False, False, False, True, False, False)
+ clear_mock.assert_called_once()
+
+
+def test_clean_packages(application_repository: Repository, mocker: MockerFixture) -> None:
+ """
+ must clean packages directory
+ """
+ clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_packages")
+ application_repository.clean(False, False, False, False, True, False)
+ clear_mock.assert_called_once()
+
+
+def test_clean_patches(application_repository: Repository, mocker: MockerFixture) -> None:
+ """
+ must clean packages directory
+ """
+ clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_patches")
+ application_repository.clean(False, False, False, False, False, True)
+ clear_mock.assert_called_once()
+
+
+def test_report(application_repository: Repository, mocker: MockerFixture) -> None:
+ """
+ must generate report
+ """
+ executor_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_report")
+ application_repository.report([], [])
+ executor_mock.assert_called_once()
+
+
+def test_sign(application_repository: Repository, package_ahriman: Package, package_python_schedule: Package,
+ mocker: MockerFixture) -> None:
+ """
+ must sign world
+ """
+ mocker.patch("ahriman.core.repository.repository.Repository.packages",
+ return_value=[package_ahriman, package_python_schedule])
+ copy_mock = mocker.patch("shutil.copy")
+ update_mock = mocker.patch("ahriman.application.application.repository.Repository.update")
+ sign_repository_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process_sign_repository")
+ finalize_mock = mocker.patch("ahriman.application.application.repository.Repository._finalize")
+
+ application_repository.sign([])
+ copy_mock.assert_has_calls([
+ mock.call(pytest.helpers.anyvar(str), pytest.helpers.anyvar(str)),
+ mock.call(pytest.helpers.anyvar(str), pytest.helpers.anyvar(str))
+ ])
+ update_mock.assert_called_once_with([])
+ sign_repository_mock.assert_called_once()
+ finalize_mock.assert_called_once()
+
+
+def test_sign_skip(application_repository: Repository, package_ahriman: Package, mocker: MockerFixture) -> None:
+ """
+ must skip sign packages with empty filename
+ """
+ package_ahriman.packages[package_ahriman.base].filename = None
+ mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
+ mocker.patch("ahriman.application.application.repository.Repository.update")
+ mocker.patch("ahriman.application.application.repository.Repository._finalize")
+
+ application_repository.sign([])
+
+
+def test_sign_specific(application_repository: Repository, package_ahriman: Package, package_python_schedule: Package,
+ mocker: MockerFixture) -> None:
+ """
+ must sign only specified packages
+ """
+ mocker.patch("ahriman.core.repository.repository.Repository.packages",
+ return_value=[package_ahriman, package_python_schedule])
+ copy_mock = mocker.patch("shutil.copy")
+ update_mock = mocker.patch("ahriman.application.application.repository.Repository.update")
+ sign_repository_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process_sign_repository")
+ finalize_mock = mocker.patch("ahriman.application.application.repository.Repository._finalize")
+
+ application_repository.sign([package_ahriman.base])
+ copy_mock.assert_called_once()
+ update_mock.assert_called_once_with([])
+ sign_repository_mock.assert_called_once()
+ finalize_mock.assert_called_once()
+
+
+def test_sync(application_repository: Repository, mocker: MockerFixture) -> None:
+ """
+ must sync to remote
+ """
+ executor_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_sync")
+ application_repository.sync([], [])
+ executor_mock.assert_called_once()
+
+
+def test_unknown_no_aur(application_repository: Repository, package_ahriman: Package, mocker: MockerFixture) -> None:
+ """
+ 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_repository.unknown()
+
+
+def test_unknown_no_aur_no_local(application_repository: Repository, 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_repository.unknown()
+ assert packages == [package_ahriman]
+
+
+def test_unknown_no_local(application_repository: Repository, package_ahriman: Package, mocker: MockerFixture) -> None:
+ """
+ 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_repository.unknown()
+
+
+def test_update(application_repository: Repository, package_ahriman: Package, mocker: MockerFixture) -> None:
+ """
+ must process package updates
+ """
+ paths = [package.filepath for package in package_ahriman.packages.values()]
+ tree = Tree([Leaf(package_ahriman, set())])
+
+ mocker.patch("ahriman.core.tree.Tree.load", return_value=tree)
+ mocker.patch("ahriman.core.repository.repository.Repository.packages_built", return_value=[])
+ mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
+ build_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_build", return_value=paths)
+ update_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_update")
+ finalize_mock = mocker.patch("ahriman.application.application.repository.Repository._finalize")
+
+ application_repository.update([package_ahriman])
+ build_mock.assert_called_once()
+ update_mock.assert_called_once_with(paths)
+ finalize_mock.assert_called_once_with([package_ahriman])
+
+
+def test_updates_all(application_repository: Repository, package_ahriman: Package, mocker: MockerFixture) -> None:
+ """
+ must get updates for all
+ """
+ updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur",
+ return_value=[package_ahriman])
+ updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
+
+ application_repository.updates([], no_aur=False, no_manual=False, no_vcs=False, log_fn=print)
+ updates_aur_mock.assert_called_once_with([], False)
+ updates_manual_mock.assert_called_once()
+
+
+def test_updates_disabled(application_repository: Repository, mocker: MockerFixture) -> None:
+ """
+ must get updates without anything
+ """
+ updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
+ updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
+
+ application_repository.updates([], no_aur=True, no_manual=True, no_vcs=False, log_fn=print)
+ updates_aur_mock.assert_not_called()
+ updates_manual_mock.assert_not_called()
+
+
+def test_updates_no_aur(application_repository: Repository, mocker: MockerFixture) -> None:
+ """
+ must get updates without aur
+ """
+ updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
+ updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
+
+ application_repository.updates([], no_aur=True, no_manual=False, no_vcs=False, log_fn=print)
+ updates_aur_mock.assert_not_called()
+ updates_manual_mock.assert_called_once()
+
+
+def test_updates_no_manual(application_repository: Repository, mocker: MockerFixture) -> None:
+ """
+ must get updates without manual
+ """
+ updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
+ updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
+
+ application_repository.updates([], no_aur=False, no_manual=True, no_vcs=False, log_fn=print)
+ updates_aur_mock.assert_called_once_with([], False)
+ updates_manual_mock.assert_not_called()
+
+
+def test_updates_no_vcs(application_repository: Repository, mocker: MockerFixture) -> None:
+ """
+ must get updates without VCS
+ """
+ updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
+ updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
+
+ application_repository.updates([], no_aur=False, no_manual=False, no_vcs=True, log_fn=print)
+ updates_aur_mock.assert_called_once_with([], True)
+ updates_manual_mock.assert_called_once()
+
+
+def test_updates_with_filter(application_repository: Repository, mocker: MockerFixture) -> None:
+ """
+ must get updates without VCS
+ """
+ updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
+ updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
+
+ application_repository.updates(["filter"], no_aur=False, no_manual=False, no_vcs=False, log_fn=print)
+ updates_aur_mock.assert_called_once_with(["filter"], False)
+ updates_manual_mock.assert_called_once()
diff --git a/tests/ahriman/application/handlers/test_handler_add.py b/tests/ahriman/application/handlers/test_handler_add.py
index 1dbaa15e..703ed20d 100644
--- a/tests/ahriman/application/handlers/test_handler_add.py
+++ b/tests/ahriman/application/handlers/test_handler_add.py
@@ -41,7 +41,7 @@ def test_run_with_updates(args: argparse.Namespace, configuration: Configuration
mocker.patch("ahriman.application.application.Application.add")
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
application_mock = mocker.patch("ahriman.application.application.Application.update")
- updates_mock = mocker.patch("ahriman.application.application.Application.get_updates")
+ updates_mock = mocker.patch("ahriman.application.application.Application.updates")
Add.run(args, "x86_64", configuration, True)
application_mock.assert_called_once()
diff --git a/tests/ahriman/application/handlers/test_handler_clean.py b/tests/ahriman/application/handlers/test_handler_clean.py
index 8ee91491..16bf0709 100644
--- a/tests/ahriman/application/handlers/test_handler_clean.py
+++ b/tests/ahriman/application/handlers/test_handler_clean.py
@@ -12,12 +12,12 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
:param args: command line arguments fixture
:return: generated arguments for these test cases
"""
- args.no_build = False
- args.no_cache = False
- args.no_chroot = False
- args.no_manual = False
- args.no_packages = False
- args.no_patches = False
+ args.build = False
+ args.cache = False
+ args.chroot = False
+ args.manual = False
+ args.packages = False
+ args.patches = False
return args
diff --git a/tests/ahriman/application/handlers/test_handler_update.py b/tests/ahriman/application/handlers/test_handler_update.py
index bc634544..4e8a9b6d 100644
--- a/tests/ahriman/application/handlers/test_handler_update.py
+++ b/tests/ahriman/application/handlers/test_handler_update.py
@@ -28,7 +28,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
args = _default_args(args)
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
application_mock = mocker.patch("ahriman.application.application.Application.update")
- updates_mock = mocker.patch("ahriman.application.application.Application.get_updates")
+ updates_mock = mocker.patch("ahriman.application.application.Application.updates")
Update.run(args, "x86_64", configuration, True)
application_mock.assert_called_once()
@@ -42,7 +42,7 @@ def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, moc
args = _default_args(args)
args.dry_run = True
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
- updates_mock = mocker.patch("ahriman.application.application.Application.get_updates")
+ updates_mock = mocker.patch("ahriman.application.application.Application.updates")
Update.run(args, "x86_64", configuration, True)
updates_mock.assert_called_once()
diff --git a/tests/ahriman/application/test_application.py b/tests/ahriman/application/test_application.py
deleted file mode 100644
index 78694c4b..00000000
--- a/tests/ahriman/application/test_application.py
+++ /dev/null
@@ -1,398 +0,0 @@
-import pytest
-
-from pathlib import Path
-from pytest_mock import MockerFixture
-from unittest import mock
-from unittest.mock import MagicMock
-
-from ahriman.application.application import Application
-from ahriman.core.tree import Leaf, Tree
-from ahriman.models.package import Package
-from ahriman.models.package_description import PackageDescription
-from ahriman.models.package_source import PackageSource
-
-
-def test_finalize(application: Application, mocker: MockerFixture) -> None:
- """
- must report and sync at the last
- """
- report_mock = mocker.patch("ahriman.application.application.Application.report")
- sync_mock = mocker.patch("ahriman.application.application.Application.sync")
-
- application._finalize([])
- report_mock.assert_called_once()
- sync_mock.assert_called_once()
-
-
-def test_known_packages(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
- """
- must return not empty list of known packages
- """
- mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
- packages = application._known_packages()
- assert len(packages) > 1
- assert package_ahriman.base in packages
-
-
-def test_get_updates_all(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
- """
- must get updates for all
- """
- updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur",
- return_value=[package_ahriman])
- updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
-
- application.get_updates([], no_aur=False, no_manual=False, no_vcs=False, log_fn=print)
- updates_aur_mock.assert_called_once_with([], False)
- updates_manual_mock.assert_called_once()
-
-
-def test_get_updates_disabled(application: Application, mocker: MockerFixture) -> None:
- """
- must get updates without anything
- """
- updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
- updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
-
- application.get_updates([], no_aur=True, no_manual=True, no_vcs=False, log_fn=print)
- updates_aur_mock.assert_not_called()
- updates_manual_mock.assert_not_called()
-
-
-def test_get_updates_no_aur(application: Application, mocker: MockerFixture) -> None:
- """
- must get updates without aur
- """
- updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
- updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
-
- application.get_updates([], no_aur=True, no_manual=False, no_vcs=False, log_fn=print)
- updates_aur_mock.assert_not_called()
- updates_manual_mock.assert_called_once()
-
-
-def test_get_updates_no_manual(application: Application, mocker: MockerFixture) -> None:
- """
- must get updates without manual
- """
- updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
- updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
-
- application.get_updates([], no_aur=False, no_manual=True, no_vcs=False, log_fn=print)
- updates_aur_mock.assert_called_once_with([], False)
- updates_manual_mock.assert_not_called()
-
-
-def test_get_updates_no_vcs(application: Application, mocker: MockerFixture) -> None:
- """
- must get updates without VCS
- """
- updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
- updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
-
- application.get_updates([], no_aur=False, no_manual=False, no_vcs=True, log_fn=print)
- updates_aur_mock.assert_called_once_with([], True)
- updates_manual_mock.assert_called_once()
-
-
-def test_get_updates_with_filter(application: Application, mocker: MockerFixture) -> None:
- """
- must get updates without VCS
- """
- updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur")
- updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual")
-
- application.get_updates(["filter"], no_aur=False, no_manual=False, no_vcs=False, log_fn=print)
- updates_aur_mock.assert_called_once_with(["filter"], False)
- 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())
- copy_mock = mocker.patch("shutil.copy")
-
- application.add([package_ahriman.base], PackageSource.Archive, False)
- copy_mock.assert_called_once()
-
-
-def test_add_aur(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_aur_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
- """
- mocker.patch("ahriman.application.application.Application._known_packages", return_value=set())
- iterdir_mock = mocker.patch("pathlib.Path.iterdir",
- return_value=[package.filepath for package in package_ahriman.packages.values()])
- copy_mock = mocker.patch("shutil.copy")
-
- application.add([package_ahriman.base], PackageSource.Directory, False)
- iterdir_mock.assert_called_once()
- copy_mock.assert_called_once()
-
-
-def test_add_local(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
- """
- 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)
- init_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.init")
- copytree_mock = mocker.patch("shutil.copytree")
-
- 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_local_with_dependencies(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
- """
- 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.init")
- mocker.patch("shutil.copytree")
- dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies")
-
- application.add([package_ahriman.base], PackageSource.Local, False)
- dependencies_mock.assert_called_once()
-
-
-def test_add_remote(application: Application, package_description_ahriman: PackageDescription,
- mocker: MockerFixture) -> None:
- """
- must add package from remote source
- """
- mocker.patch("ahriman.application.application.Application._known_packages", return_value=set())
- response_mock = MagicMock()
- response_mock.iter_content.return_value = ["chunk"]
- open_mock = mocker.patch("pathlib.Path.open")
- request_mock = mocker.patch("requests.get", return_value=response_mock)
- url = f"https://host/{package_description_ahriman.filename}"
-
- application.add([url], PackageSource.Remote, False)
- open_mock.assert_called_once_with("wb")
- request_mock.assert_called_once_with(url, stream=True)
- response_mock.raise_for_status.assert_called_once()
-
-
-def test_clean_build(application: Application, mocker: MockerFixture) -> None:
- """
- must clean build directory
- """
- clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_build")
- application.clean(False, True, True, True, True, True)
- clear_mock.assert_called_once()
-
-
-def test_clean_cache(application: Application, mocker: MockerFixture) -> None:
- """
- must clean cache directory
- """
- clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_cache")
- application.clean(True, False, True, True, True, True)
- clear_mock.assert_called_once()
-
-
-def test_clean_chroot(application: Application, mocker: MockerFixture) -> None:
- """
- must clean chroot directory
- """
- clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_chroot")
- application.clean(True, True, False, True, True, True)
- clear_mock.assert_called_once()
-
-
-def test_clean_manual(application: Application, mocker: MockerFixture) -> None:
- """
- must clean manual directory
- """
- clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_manual")
- application.clean(True, True, True, False, True, True)
- clear_mock.assert_called_once()
-
-
-def test_clean_packages(application: Application, mocker: MockerFixture) -> None:
- """
- must clean packages directory
- """
- clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_packages")
- application.clean(True, True, True, True, False, True)
- clear_mock.assert_called_once()
-
-
-def test_clean_patches(application: Application, mocker: MockerFixture) -> None:
- """
- must clean packages directory
- """
- clear_mock = mocker.patch("ahriman.core.repository.cleaner.Cleaner.clear_patches")
- application.clean(True, True, True, True, True, False)
- clear_mock.assert_called_once()
-
-
-def test_remove(application: Application, mocker: MockerFixture) -> None:
- """
- must remove package
- """
- executor_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_remove")
- finalize_mock = mocker.patch("ahriman.application.application.Application._finalize")
-
- application.remove([])
- executor_mock.assert_called_once()
- finalize_mock.assert_called_once()
-
-
-def test_report(application: Application, mocker: MockerFixture) -> None:
- """
- must generate report
- """
- executor_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_report")
- application.report([], [])
- executor_mock.assert_called_once()
-
-
-def test_sign(application: Application, package_ahriman: Package, package_python_schedule: Package,
- mocker: MockerFixture) -> None:
- """
- must sign world
- """
- mocker.patch("ahriman.core.repository.repository.Repository.packages",
- return_value=[package_ahriman, package_python_schedule])
- copy_mock = mocker.patch("shutil.copy")
- update_mock = mocker.patch("ahriman.application.application.Application.update")
- sign_repository_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process_sign_repository")
- finalize_mock = mocker.patch("ahriman.application.application.Application._finalize")
-
- application.sign([])
- copy_mock.assert_has_calls([
- mock.call(pytest.helpers.anyvar(str), pytest.helpers.anyvar(str)),
- mock.call(pytest.helpers.anyvar(str), pytest.helpers.anyvar(str))
- ])
- update_mock.assert_called_once_with([])
- sign_repository_mock.assert_called_once()
- finalize_mock.assert_called_once()
-
-
-def test_sign_skip(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
- """
- must skip sign packages with empty filename
- """
- package_ahriman.packages[package_ahriman.base].filename = None
- mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman])
- mocker.patch("ahriman.application.application.Application.update")
-
- application.sign([])
-
-
-def test_sign_specific(application: Application, package_ahriman: Package, package_python_schedule: Package,
- mocker: MockerFixture) -> None:
- """
- must sign only specified packages
- """
- mocker.patch("ahriman.core.repository.repository.Repository.packages",
- return_value=[package_ahriman, package_python_schedule])
- copy_mock = mocker.patch("shutil.copy")
- update_mock = mocker.patch("ahriman.application.application.Application.update")
- sign_repository_mock = mocker.patch("ahriman.core.sign.gpg.GPG.process_sign_repository")
- finalize_mock = mocker.patch("ahriman.application.application.Application._finalize")
-
- application.sign([package_ahriman.base])
- copy_mock.assert_called_once()
- update_mock.assert_called_once_with([])
- sign_repository_mock.assert_called_once()
- finalize_mock.assert_called_once()
-
-
-def test_sync(application: Application, mocker: MockerFixture) -> None:
- """
- must sync to remote
- """
- executor_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_sync")
- application.sync([], [])
- executor_mock.assert_called_once()
-
-
-def test_unknown_no_aur(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
- """
- 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_no_local(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
- """
- 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()
-
-
-def test_update(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None:
- """
- must process package updates
- """
- paths = [package.filepath for package in package_ahriman.packages.values()]
- tree = Tree([Leaf(package_ahriman, set())])
-
- mocker.patch("ahriman.core.tree.Tree.load", return_value=tree)
- mocker.patch("ahriman.core.repository.repository.Repository.packages_built", return_value=[])
- mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
- build_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_build", return_value=paths)
- update_mock = mocker.patch("ahriman.core.repository.executor.Executor.process_update")
- finalize_mock = mocker.patch("ahriman.application.application.Application._finalize")
-
- application.update([package_ahriman])
- build_mock.assert_called_once()
- update_mock.assert_called_once_with(paths)
- finalize_mock.assert_called_once_with([package_ahriman])
diff --git a/tests/ahriman/core/repository/conftest.py b/tests/ahriman/core/repository/conftest.py
index e3f481dc..b5bda167 100644
--- a/tests/ahriman/core/repository/conftest.py
+++ b/tests/ahriman/core/repository/conftest.py
@@ -3,10 +3,10 @@ import pytest
from pytest_mock import MockerFixture
from ahriman.core.configuration import Configuration
+from ahriman.core.repository import Repository
from ahriman.core.repository.cleaner import Cleaner
from ahriman.core.repository.executor import Executor
from ahriman.core.repository.properties import Properties
-from ahriman.core.repository.repository import Repository
from ahriman.core.repository.update_handler import UpdateHandler
diff --git a/tests/ahriman/core/repository/test_repository.py b/tests/ahriman/core/repository/test_repository.py
index 5995b608..19120272 100644
--- a/tests/ahriman/core/repository/test_repository.py
+++ b/tests/ahriman/core/repository/test_repository.py
@@ -1,7 +1,7 @@
from pathlib import Path
from pytest_mock import MockerFixture
-from ahriman.core.repository.repository import Repository
+from ahriman.core.repository import Repository
from ahriman.models.package import Package