From a04b6c3b9c08b2a882aac31b41ea23c70de3dd8c Mon Sep 17 00:00:00 2001 From: Evgenii Alekseev Date: Sun, 15 Mar 2026 20:23:20 +0200 Subject: [PATCH] refactor: move package archive lockup to package info trait --- src/ahriman/core/repository/executor.py | 32 +-------- src/ahriman/core/repository/package_info.py | 43 ++++++++++-- src/ahriman/web/web.py | 1 + .../ahriman/core/repository/test_executor.py | 68 +------------------ .../core/repository/test_package_info.py | 62 +++++++++++++++++ 5 files changed, 103 insertions(+), 103 deletions(-) diff --git a/src/ahriman/core/repository/executor.py b/src/ahriman/core/repository/executor.py index 0ade2895..5e1dedcb 100644 --- a/src/ahriman/core/repository/executor.py +++ b/src/ahriman/core/repository/executor.py @@ -27,7 +27,7 @@ from ahriman.core.build_tools.package_archive import PackageArchive from ahriman.core.build_tools.task import Task from ahriman.core.repository.cleaner import Cleaner from ahriman.core.repository.package_info import PackageInfo -from ahriman.core.utils import atomic_move, filelock, list_flatmap, package_like, safe_filename, symlink_relative +from ahriman.core.utils import atomic_move, filelock, safe_filename, symlink_relative from ahriman.models.changes import Changes from ahriman.models.event import EventType from ahriman.models.package import Package @@ -41,34 +41,6 @@ class Executor(PackageInfo, Cleaner): trait for common repository update processes """ - def _archive_lookup(self, package: Package) -> list[Path]: - """ - check if there is a rebuilt package already - - Args: - package(Package): package to check - - Returns: - list[Path]: list of built packages and signatures if available, empty list otherwise - """ - archive = self.paths.archive_for(package.base) - if not archive.is_dir(): - return [] - - for path in filter(package_like, archive.iterdir()): - # check if package version is the same - built = Package.from_archive(path) - if built.version != package.version: - continue - - # all packages must be either any or same architecture - if not built.supports_architecture(self.repository_id.architecture): - continue - - return list_flatmap(built.packages.values(), lambda single: archive.glob(f"{single.filename}*")) - - return [] - def _archive_rename(self, description: PackageDescription, package_base: str) -> None: """ rename package archive removing special symbols @@ -106,7 +78,7 @@ class Executor(PackageInfo, Cleaner): commit_sha = task.init(path, patches, local_version) loaded_package = Package.from_build(path, self.repository_id.architecture, None) - if prebuilt := list(self._archive_lookup(loaded_package)): + if prebuilt := self.package_archives_lookup(loaded_package): self.logger.info("using prebuilt packages for %s-%s", loaded_package.base, loaded_package.version) built = [] for artifact in prebuilt: diff --git a/src/ahriman/core/repository/package_info.py b/src/ahriman/core/repository/package_info.py index d7f882dc..be5f4560 100644 --- a/src/ahriman/core/repository/package_info.py +++ b/src/ahriman/core/repository/package_info.py @@ -30,10 +30,11 @@ from ahriman.core.build_tools.sources import Sources from ahriman.core.configuration import Configuration from ahriman.core.log import LazyLogging from ahriman.core.status import Client -from ahriman.core.utils import package_like +from ahriman.core.utils import list_flatmap, package_like from ahriman.models.changes import Changes from ahriman.models.package import Package from ahriman.models.repository_id import RepositoryId +from ahriman.models.repository_paths import RepositoryPaths class PackageInfo(LazyLogging): @@ -43,12 +44,14 @@ class PackageInfo(LazyLogging): Attributes: configuration(Configuration): configuration instance pacman(Pacman): alpm wrapper instance + paths(RepositoryPaths): repository paths instance reporter(Client): build status reporter instance repository_id(RepositoryId): repository unique identifier """ configuration: Configuration pacman: Pacman + paths: RepositoryPaths reporter: Client repository_id: RepositoryId @@ -130,11 +133,9 @@ class PackageInfo(LazyLogging): Returns: list[Package]: list of packages belonging to this base, sorted by version by ascension """ - paths = self.configuration.repository_paths - packages: dict[tuple[str, str], Package] = {} # we can't use here load_archives, because it ignores versions - for full_path in filter(package_like, paths.archive_for(package_base).iterdir()): + for full_path in filter(package_like, self.paths.archive_for(package_base).iterdir()): local = Package.from_archive(full_path) if not local.supports_architecture(self.repository_id.architecture): continue @@ -143,6 +144,34 @@ class PackageInfo(LazyLogging): comparator: Callable[[Package, Package], int] = lambda left, right: left.vercmp(right.version) return sorted(packages.values(), key=cmp_to_key(comparator)) + def package_archives_lookup(self, package: Package) -> list[Path]: + """ + check if there is a rebuilt package already + + Args: + package(Package): package to check + + Returns: + list[Path]: list of built packages and signatures if available, empty list otherwise + """ + archive = self.paths.archive_for(package.base) + if not archive.is_dir(): + return [] + + for path in filter(package_like, archive.iterdir()): + # check if package version is the same + built = Package.from_archive(path) + if built.version != package.version: + continue + + # all packages must be either any or same architecture + if not built.supports_architecture(self.repository_id.architecture): + continue + + return list_flatmap(built.packages.values(), lambda single: archive.glob(f"{single.filename}*")) + + return [] + def package_changes(self, package: Package, last_commit_sha: str) -> Changes | None: """ extract package change for the package since last commit if available @@ -157,7 +186,7 @@ class PackageInfo(LazyLogging): with TemporaryDirectory(ignore_cleanup_errors=True) as dir_name: dir_path = Path(dir_name) patches = self.reporter.package_patches_get(package.base, None) - current_commit_sha = Sources.load(dir_path, package, patches, self.configuration.repository_paths) + current_commit_sha = Sources.load(dir_path, package, patches, self.paths) if current_commit_sha != last_commit_sha: return Sources.changes(dir_path, last_commit_sha) @@ -173,7 +202,7 @@ class PackageInfo(LazyLogging): Returns: list[Package]: list of packages properties """ - packages = self.load_archives(filter(package_like, self.configuration.repository_paths.repository.iterdir())) + packages = self.load_archives(filter(package_like, self.paths.repository.iterdir())) if filter_packages: packages = [package for package in packages if package.base in filter_packages] @@ -186,7 +215,7 @@ class PackageInfo(LazyLogging): Returns: list[Path]: list of filenames from the directory """ - return list(filter(package_like, self.configuration.repository_paths.packages.iterdir())) + return list(filter(package_like, self.paths.packages.iterdir())) def packages_depend_on(self, packages: list[Package], depends_on: Iterable[str] | None) -> list[Package]: """ diff --git a/src/ahriman/web/web.py b/src/ahriman/web/web.py index 28434561..0eab64da 100644 --- a/src/ahriman/web/web.py +++ b/src/ahriman/web/web.py @@ -103,6 +103,7 @@ def _create_watcher(path: Path, repository_id: RepositoryId) -> Watcher: # load package info wrapper package_info = PackageInfo() package_info.configuration = configuration + package_info.paths = configuration.repository_paths package_info.repository_id = repository_id return Watcher(client, package_info) diff --git a/tests/ahriman/core/repository/test_executor.py b/tests/ahriman/core/repository/test_executor.py index 4189f2f9..ee489412 100644 --- a/tests/ahriman/core/repository/test_executor.py +++ b/tests/ahriman/core/repository/test_executor.py @@ -1,6 +1,5 @@ import pytest -from dataclasses import replace from pathlib import Path from pytest_mock import MockerFixture from typing import Any @@ -11,72 +10,9 @@ from ahriman.models.changes import Changes from ahriman.models.dependencies import Dependencies from ahriman.models.package import Package from ahriman.models.packagers import Packagers -from ahriman.models.repository_id import RepositoryId from ahriman.models.user import User -def test_archive_lookup(executor: Executor, package_ahriman: Package, package_python_schedule: Package, - mocker: MockerFixture) -> None: - """ - must existing packages which match the version - """ - mocker.patch("pathlib.Path.is_dir", return_value=True) - mocker.patch("pathlib.Path.iterdir", return_value=[ - Path("1.pkg.tar.zst"), - Path("2.pkg.tar.zst"), - Path("3.pkg.tar.zst"), - ]) - mocker.patch("ahriman.models.package.Package.from_archive", side_effect=[ - package_ahriman, - package_python_schedule, - replace(package_ahriman, version="1"), - ]) - glob_mock = mocker.patch("pathlib.Path.glob", return_value=[Path("1.pkg.tar.xz")]) - - assert list(executor._archive_lookup(package_ahriman)) == [Path("1.pkg.tar.xz")] - glob_mock.assert_called_once_with(f"{package_ahriman.packages[package_ahriman.base].filename}*") - - -def test_archive_lookup_version_mismatch(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None: - """ - must return nothing if no packages found with the same version - """ - mocker.patch("pathlib.Path.is_dir", return_value=True) - mocker.patch("pathlib.Path.iterdir", return_value=[ - Path("1.pkg.tar.zst"), - ]) - mocker.patch("ahriman.models.package.Package.from_archive", return_value=replace(package_ahriman, version="1")) - - assert list(executor._archive_lookup(package_ahriman)) == [] - - -def test_archive_lookup_architecture_mismatch(executor: Executor, package_ahriman: Package, - mocker: MockerFixture) -> None: - """ - must return nothing if architecture doesn't match - """ - package_ahriman.packages[package_ahriman.base].architecture = "x86_64" - mocker.patch("pathlib.Path.is_dir", return_value=True) - executor.repository_id = RepositoryId("i686", executor.repository_id.name) - mocker.patch("pathlib.Path.iterdir", return_value=[ - Path("1.pkg.tar.zst"), - ]) - mocker.patch("ahriman.models.package.Package.from_archive", return_value=package_ahriman) - - assert list(executor._archive_lookup(package_ahriman)) == [] - - -def test_archive_lookup_no_archive_directory( - executor: Executor, - package_ahriman: Package, - mocker: MockerFixture) -> None: - """ - must return nothing if no archive directory found - """ - mocker.patch("pathlib.Path.is_dir", return_value=False) - assert list(executor._archive_lookup(package_ahriman)) == [] - - def test_archive_rename(executor: Executor, package_ahriman: Package, mocker: MockerFixture) -> None: """ must correctly remove package archive @@ -110,7 +46,7 @@ def test_package_build(executor: Executor, package_ahriman: Package, mocker: Moc status_client_mock = mocker.patch("ahriman.core.status.Client.set_building") init_mock = mocker.patch("ahriman.core.build_tools.task.Task.init", return_value="sha") package_mock = mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman) - lookup_mock = mocker.patch("ahriman.core.repository.executor.Executor._archive_lookup", return_value=[]) + lookup_mock = mocker.patch("ahriman.core.repository.executor.Executor.package_archives_lookup", return_value=[]) with_packages_mock = mocker.patch("ahriman.models.package.Package.with_packages") rename_mock = mocker.patch("ahriman.core.repository.executor.atomic_move") @@ -131,7 +67,7 @@ def test_package_build_copy(executor: Executor, package_ahriman: Package, mocker mocker.patch("ahriman.core.build_tools.task.Task.build", return_value=[Path(package_ahriman.base)]) mocker.patch("ahriman.core.build_tools.task.Task.init") mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman) - mocker.patch("ahriman.core.repository.executor.Executor._archive_lookup", return_value=[path]) + mocker.patch("ahriman.core.repository.executor.Executor.package_archives_lookup", return_value=[path]) mocker.patch("ahriman.core.repository.executor.atomic_move") mocker.patch("ahriman.models.package.Package.with_packages") copy_mock = mocker.patch("shutil.copy") diff --git a/tests/ahriman/core/repository/test_package_info.py b/tests/ahriman/core/repository/test_package_info.py index 69502629..7f4efb3b 100644 --- a/tests/ahriman/core/repository/test_package_info.py +++ b/tests/ahriman/core/repository/test_package_info.py @@ -8,6 +8,7 @@ from unittest.mock import MagicMock from ahriman.core.repository import Repository from ahriman.models.changes import Changes from ahriman.models.package import Package +from ahriman.models.repository_id import RepositoryId def test_full_depends(repository: Repository, package_ahriman: Package, package_python_schedule: Package, @@ -120,6 +121,67 @@ def test_package_archives_architecture_mismatch(repository: Repository, package_ assert len(result) == 0 +def test_package_archives_lookup(repository: Repository, package_ahriman: Package, package_python_schedule: Package, + mocker: MockerFixture) -> None: + """ + must existing packages which match the version + """ + mocker.patch("pathlib.Path.is_dir", return_value=True) + mocker.patch("pathlib.Path.iterdir", return_value=[ + Path("1.pkg.tar.zst"), + Path("2.pkg.tar.zst"), + Path("3.pkg.tar.zst"), + ]) + mocker.patch("ahriman.models.package.Package.from_archive", side_effect=[ + package_ahriman, + package_python_schedule, + replace(package_ahriman, version="1"), + ]) + glob_mock = mocker.patch("pathlib.Path.glob", return_value=[Path("1.pkg.tar.xz")]) + + assert repository.package_archives_lookup(package_ahriman) == [Path("1.pkg.tar.xz")] + glob_mock.assert_called_once_with(f"{package_ahriman.packages[package_ahriman.base].filename}*") + + +def test_package_archives_lookup_version_mismatch(repository: Repository, package_ahriman: Package, + mocker: MockerFixture) -> None: + """ + must return nothing if no packages found with the same version + """ + mocker.patch("pathlib.Path.is_dir", return_value=True) + mocker.patch("pathlib.Path.iterdir", return_value=[ + Path("1.pkg.tar.zst"), + ]) + mocker.patch("ahriman.models.package.Package.from_archive", return_value=replace(package_ahriman, version="1")) + + assert repository.package_archives_lookup(package_ahriman) == [] + + +def test_package_archives_lookup_architecture_mismatch(repository: Repository, package_ahriman: Package, + mocker: MockerFixture) -> None: + """ + must return nothing if architecture doesn't match + """ + package_ahriman.packages[package_ahriman.base].architecture = "x86_64" + mocker.patch("pathlib.Path.is_dir", return_value=True) + repository.repository_id = RepositoryId("i686", repository.repository_id.name) + mocker.patch("pathlib.Path.iterdir", return_value=[ + Path("1.pkg.tar.zst"), + ]) + mocker.patch("ahriman.models.package.Package.from_archive", return_value=package_ahriman) + + assert repository.package_archives_lookup(package_ahriman) == [] + + +def test_package_archives_lookup_no_archive_directory(repository: Repository, package_ahriman: Package, + mocker: MockerFixture) -> None: + """ + must return nothing if no archive directory found + """ + mocker.patch("pathlib.Path.is_dir", return_value=False) + assert repository.package_archives_lookup(package_ahriman) == [] + + def test_package_changes(repository: Repository, package_ahriman: Package, mocker: MockerFixture) -> None: """ must load package changes