From 40fa94afbb0ab61c39869bc9d43f2fdd0f9ca1d1 Mon Sep 17 00:00:00 2001 From: Evgenii Alekseev Date: Sun, 25 Aug 2024 16:22:11 +0300 Subject: [PATCH] feat: replace scan paths options to single one It has been found that previous system didn't allow to configure specific cases (e.g. a whitelisted directory inside /usr/lib/cmake). The current solution replaces two options to single one, which also allows a regular expressions Also PackageArchive class has been moved to core package, because it is more about service rather than model --- CONTRIBUTING.md | 2 +- README.md | 2 +- docs/ahriman.core.build_tools.rst | 8 ++++ docs/ahriman.models.rst | 8 ---- docs/configuration.rst | 3 +- docs/faq/general.rst | 2 +- package/share/ahriman/settings/ahriman.ini | 6 +-- .../build_tools}/package_archive.py | 20 +++++--- src/ahriman/core/configuration/schema.py | 24 ++++------ src/ahriman/core/repository/executor.py | 2 +- .../core/repository/repository_properties.py | 5 +- src/ahriman/models/scan_paths.py | 31 ++++++------ tests/ahriman/conftest.py | 15 ++++++ tests/ahriman/core/build_tools/conftest.py | 29 +++++++++++ .../build_tools}/test_package_archive.py | 41 ++++++++-------- .../ahriman/core/repository/test_executor.py | 2 +- tests/ahriman/models/conftest.py | 45 ----------------- tests/ahriman/models/test_scan_paths.py | 48 ++++++++----------- tests/testresources/core/ahriman.ini | 3 +- 19 files changed, 141 insertions(+), 155 deletions(-) rename src/ahriman/{models => core/build_tools}/package_archive.py (95%) rename tests/ahriman/{models => core/build_tools}/test_package_archive.py (82%) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 662cfa93e..34eb1e7f0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -92,7 +92,7 @@ Again, the most checks can be performed by `tox` command, though some additional ``` * Type annotations are the must, even for local functions. For the function argument `self` (for instance methods) and `cls` (for class methods) should not be annotated. -* For collection types built-in classes must be used if possible (e.g. `dict` instead of `typing.Dict`, `tuple` instead of `typing.Tuple`). In case if built-in type is not available, but `collections.abc` provides interface, it must be used (e.g. `collections.abc.Awaitable` instead of `typing.Awaitable`, `collections.abc.Iterable` instead of `typing.Iterable`). For union classes, the bar operator (`|`) must be used (e.g. `float | int` instead of `typing.Union[float, int]`), which also includes `typinng.Optional` (e.g. `str | None` instead of `Optional[str]`). +* For collection types built-in classes must be used if possible (e.g. `dict` instead of `typing.Dict`, `tuple` instead of `typing.Tuple`). In case if built-in type is not available, but `collections.abc` provides interface, it must be used (e.g. `collections.abc.Awaitable` instead of `typing.Awaitable`, `collections.abc.Iterable` instead of `typing.Iterable`). For union classes, the bar operator (`|`) must be used (e.g. `float | int` instead of `typing.Union[float, int]`), which also includes `typing.Optional` (e.g. `str | None` instead of `Optional[str]`). * `classmethod` should (almost) always return `Self`. In case of mypy warning (e.g. if there is a branch in which function doesn't return the instance of `cls`) consider using `staticmethod` instead. * Recommended order of function definitions in class: diff --git a/README.md b/README.md index fa7a267e1..3583c63db 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![tests status](https://github.com/arcan1s/ahriman/actions/workflows/tests.yml/badge.svg)](https://github.com/arcan1s/ahriman/actions/workflows/tests.yml) [![setup status](https://github.com/arcan1s/ahriman/actions/workflows/setup.yml/badge.svg)](https://github.com/arcan1s/ahriman/actions/workflows/setup.yml) -[![Docker Image Version (latest semver)](https://img.shields.io/docker/v/arcan1s/ahriman?label=Docker%20image)](https://hub.docker.com/r/arcan1s/ahriman) +[![Docker Image Version (latest semver)](https://img.shields.io/docker/v/arcan1s/ahriman?label=Docker%20image&sort=semver)](https://hub.docker.com/r/arcan1s/ahriman) [![CodeFactor](https://www.codefactor.io/repository/github/arcan1s/ahriman/badge)](https://www.codefactor.io/repository/github/arcan1s/ahriman) [![Documentation Status](https://readthedocs.org/projects/ahriman/badge/?version=latest)](https://ahriman.readthedocs.io) diff --git a/docs/ahriman.core.build_tools.rst b/docs/ahriman.core.build_tools.rst index 7e9ddd85c..4ef0608a1 100644 --- a/docs/ahriman.core.build_tools.rst +++ b/docs/ahriman.core.build_tools.rst @@ -4,6 +4,14 @@ ahriman.core.build\_tools package Submodules ---------- +ahriman.core.build\_tools.package\_archive module +------------------------------------------------- + +.. automodule:: ahriman.core.build_tools.package_archive + :members: + :no-undoc-members: + :show-inheritance: + ahriman.core.build\_tools.sources module ---------------------------------------- diff --git a/docs/ahriman.models.rst b/docs/ahriman.models.rst index 084075f00..c2b53818b 100644 --- a/docs/ahriman.models.rst +++ b/docs/ahriman.models.rst @@ -124,14 +124,6 @@ ahriman.models.package module :no-undoc-members: :show-inheritance: -ahriman.models.package\_archive module --------------------------------------- - -.. automodule:: ahriman.models.package_archive - :members: - :no-undoc-members: - :show-inheritance: - ahriman.models.package\_description module ------------------------------------------ diff --git a/docs/configuration.rst b/docs/configuration.rst index 9f0c9c0d1..4a416aa80 100644 --- a/docs/configuration.rst +++ b/docs/configuration.rst @@ -81,14 +81,13 @@ Authorized users are stored inside internal database, if any of external provide Build related configuration. Group name can refer to architecture, e.g. ``build:x86_64`` can be used for x86_64 architecture specific settings. -* ``allowed_scan_paths`` - paths to be used for implicit dependencies scan, scape separated list of paths, optional. * ``archbuild_flags`` - additional flags passed to ``archbuild`` command, space separated list of strings, optional. -* ``blacklisted_scan_paths`` - paths to be excluded for implicit dependencies scan, scape separated list of paths, optional. Normally all elements of this option must be child paths of any of ``allowed_scan_paths`` element. * ``build_command`` - default build command, string, required. * ``ignore_packages`` - list packages to ignore during a regular update (manual update will still work), space separated list of strings, optional. * ``include_debug_packages`` - distribute debug packages, boolean, optional, default ``yes``. * ``makepkg_flags`` - additional flags passed to ``makepkg`` command, space separated list of strings, optional. * ``makechrootpkg_flags`` - additional flags passed to ``makechrootpkg`` command, space separated list of strings, optional. +* ``scan_paths`` - paths to be used for implicit dependencies scan, space separated list of strings, optional. If any of those paths is matched against the path, it will be added to the allowed list. * ``triggers`` - list of ``ahriman.core.triggers.Trigger`` class implementation (e.g. ``ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger``) which will be loaded and run at the end of processing, space separated list of strings, optional. You can also specify triggers by their paths, e.g. ``/usr/lib/python3.10/site-packages/ahriman/core/report/report.py.ReportTrigger``. Triggers are run in the order of definition. * ``triggers_known`` - optional list of ``ahriman.core.triggers.Trigger`` class implementations which are not run automatically and used only for trigger discovery and configuration validation. * ``vcs_allowed_age`` - maximal age in seconds of the VCS packages before their version will be updated with its remote source, integer, optional, default is 7 days. diff --git a/docs/faq/general.rst b/docs/faq/general.rst index 51503b933..8ac04879d 100644 --- a/docs/faq/general.rst +++ b/docs/faq/general.rst @@ -379,7 +379,7 @@ After the success build the application extracts all linked libraries and used d In order to disable this check completely, the ``--no-check-files`` flag can be used. -In addition, there is possibility to control paths which will be used for checking, by using options ``build.allowed_scan_paths`` and ``build.blacklisted_scan_paths``. Leaving ``build.allowed_scan_paths`` blank will effectively disable any check too. +In addition, there is possibility to control paths which will be used for checking, by using option ``build.scan_paths``, which supports regular expressions. Leaving ``build.scan_paths`` blank will effectively disable any check too. How to install built packages ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/package/share/ahriman/settings/ahriman.ini b/package/share/ahriman/settings/ahriman.ini index 9fd5fc4a2..9c22d7fa7 100644 --- a/package/share/ahriman/settings/ahriman.ini +++ b/package/share/ahriman/settings/ahriman.ini @@ -50,12 +50,8 @@ allow_read_only = yes ;salt = [build] -; List of paths to be used for implicit dependency scan -allowed_scan_paths = /usr/lib ; List of additional flags passed to archbuild command. ;archbuild_flags = -; List of paths to be excluded for implicit dependency scan. Usually they should be subpaths of allowed_scan_paths -blacklisted_scan_paths = /usr/lib/cmake ; Path to build command ;build_command = ; List of packages to be ignored during automatic updates. @@ -66,6 +62,8 @@ blacklisted_scan_paths = /usr/lib/cmake ;makechrootpkg_flags = ; List of additional flags passed to makepkg command. makepkg_flags = --nocolor --ignorearch +; List of paths to be used for implicit dependency scan. Regular expressions are supported +scan_paths = ^usr/lib(?!/cmake).*$ ; List of enabled triggers in the order of calls. triggers = ahriman.core.gitremote.RemotePullTrigger ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger ahriman.core.gitremote.RemotePushTrigger ; List of well-known triggers. Used only for configuration purposes. diff --git a/src/ahriman/models/package_archive.py b/src/ahriman/core/build_tools/package_archive.py similarity index 95% rename from src/ahriman/models/package_archive.py rename to src/ahriman/core/build_tools/package_archive.py index e2a1a45ec..fe2f737c7 100644 --- a/src/ahriman/models/package_archive.py +++ b/src/ahriman/core/build_tools/package_archive.py @@ -17,7 +17,6 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -from dataclasses import dataclass from elftools.elf.dynamic import DynamicSection from elftools.elf.elffile import ELFFile from pathlib import Path @@ -33,7 +32,6 @@ from ahriman.models.scan_paths import ScanPaths -@dataclass class PackageArchive: """ helper for package archives @@ -45,10 +43,20 @@ class PackageArchive: scan_paths(ScanPaths): scan paths holder """ - root: Path - package: Package - pacman: Pacman - scan_paths: ScanPaths + def __init__(self, root: Path, package: Package, pacman: Pacman, scan_paths: ScanPaths) -> None: + """ + default constructor + + Args: + root(Path): path to root filesystem + package(Package): package descriptor + pacman(Pacman): alpm wrapper instance + scan_paths(ScanPaths): scan paths holder + """ + self.root = root + self.package = package + self.pacman = pacman + self.scan_paths = scan_paths @staticmethod def dynamic_needed(binary_path: Path) -> list[str]: diff --git a/src/ahriman/core/configuration/schema.py b/src/ahriman/core/configuration/schema.py index aa9528e78..1a8822932 100644 --- a/src/ahriman/core/configuration/schema.py +++ b/src/ahriman/core/configuration/schema.py @@ -169,14 +169,6 @@ "build": { "type": "dict", "schema": { - "allowed_scan_paths": { - "type": "list", - "coerce": "list", - "schema": { - "type": "path", - "coerce": "absolute_path", - }, - }, "archbuild_flags": { "type": "list", "coerce": "list", @@ -185,14 +177,6 @@ "empty": False, }, }, - "blacklisted_scan_paths": { - "type": "list", - "coerce": "list", - "schema": { - "type": "path", - "coerce": "absolute_path", - }, - }, "build_command": { "type": "string", "required": True, @@ -226,6 +210,14 @@ "empty": False, }, }, + "scan_paths": { + "type": "list", + "coerce": "list", + "schema": { + "type": "string", + "empty": False, + }, + }, "triggers": { "type": "list", "coerce": "list", diff --git a/src/ahriman/core/repository/executor.py b/src/ahriman/core/repository/executor.py index 4e5ad25e3..89e203098 100644 --- a/src/ahriman/core/repository/executor.py +++ b/src/ahriman/core/repository/executor.py @@ -23,13 +23,13 @@ from pathlib import Path from tempfile import TemporaryDirectory +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 safe_filename from ahriman.models.changes import Changes from ahriman.models.package import Package -from ahriman.models.package_archive import PackageArchive from ahriman.models.package_description import PackageDescription from ahriman.models.packagers import Packagers from ahriman.models.result import Result diff --git a/src/ahriman/core/repository/repository_properties.py b/src/ahriman/core/repository/repository_properties.py index d8f160ba2..832d0e4be 100644 --- a/src/ahriman/core/repository/repository_properties.py +++ b/src/ahriman/core/repository/repository_properties.py @@ -80,10 +80,7 @@ def __init__(self, repository_id: RepositoryId, configuration: Configuration, da self.reporter = Client.load(repository_id, configuration, database, report=report) self.triggers = TriggerLoader.load(repository_id, configuration) - self.scan_paths = ScanPaths( - allowed_paths=configuration.getpathlist("build", "allowed_scan_paths", fallback=[]), - blacklisted_paths=configuration.getpathlist("build", "blacklisted_scan_paths", fallback=[]), - ) + self.scan_paths = ScanPaths(configuration.getlist("build", "scan_paths", fallback=[])) @property def architecture(self) -> str: diff --git a/src/ahriman/models/scan_paths.py b/src/ahriman/models/scan_paths.py index 646d9d73c..7d7b64362 100644 --- a/src/ahriman/models/scan_paths.py +++ b/src/ahriman/models/scan_paths.py @@ -17,29 +17,33 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +import re + from dataclasses import dataclass +from functools import cached_property from pathlib import Path -@dataclass(frozen=True, kw_only=True) +@dataclass(frozen=True) class ScanPaths: """ paths used for scan filesystem Attributes: - allowed_paths(list[Path]): list of whitelisted paths - blacklisted_paths(list[Path]): list of paths to be skipped from scan + paths(list[str]): list of regular expressions to be used to match paths """ - allowed_paths: list[Path] - blacklisted_paths: list[Path] + paths: list[str] - def __post_init__(self) -> None: + @cached_property + def patterns(self) -> list[re.Pattern[str]]: """ - compute relative to / paths + compiled regular expressions + + Returns: + list[re.Pattern]: a list of compiled regular expressions """ - object.__setattr__(self, "allowed_paths", [path.relative_to("/") for path in self.allowed_paths]) - object.__setattr__(self, "blacklisted_paths", [path.relative_to("/") for path in self.blacklisted_paths]) + return [re.compile(path) for path in self.paths] def is_allowed(self, path: Path) -> bool: """ @@ -49,10 +53,7 @@ def is_allowed(self, path: Path) -> bool: path(Path): path to be checked Returns: - bool: ``True`` in case if :attr:`allowed_paths` contains element which is parent for the path and - :attr:`blacklisted_paths` doesn't and ``False`` otherwise + bool: ``True`` in case if :attr:`paths` contains at least one element to which the path is matched + and ``False`` otherwise """ - if any(path.is_relative_to(blacklisted) for blacklisted in self.blacklisted_paths): - return False # path is blacklisted - # check if we actually have to check this path - return any(path.is_relative_to(allowed) for allowed in self.allowed_paths) + return any(pattern.match(str(path)) for pattern in self.patterns) diff --git a/tests/ahriman/conftest.py b/tests/ahriman/conftest.py index 8b45d7480..b944d63b1 100644 --- a/tests/ahriman/conftest.py +++ b/tests/ahriman/conftest.py @@ -25,6 +25,7 @@ from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_paths import RepositoryPaths from ahriman.models.result import Result +from ahriman.models.scan_paths import ScanPaths from ahriman.models.user import User from ahriman.models.user_access import UserAccess @@ -587,6 +588,20 @@ def result(package_ahriman: Package) -> Result: return result +@pytest.fixture +def scan_paths(configuration: Configuration) -> ScanPaths: + """ + scan paths fixture + + Args: + configuration(Configuration): configuration test instance + + Returns: + ScanPaths: scan paths test instance + """ + return ScanPaths(configuration.getlist("build", "scan_paths", fallback=[])) + + @pytest.fixture def spawner(configuration: Configuration) -> Spawn: """ diff --git a/tests/ahriman/core/build_tools/conftest.py b/tests/ahriman/core/build_tools/conftest.py index 2dfd63348..88246a260 100644 --- a/tests/ahriman/core/build_tools/conftest.py +++ b/tests/ahriman/core/build_tools/conftest.py @@ -1,6 +1,35 @@ import pytest +from pytest_mock import MockerFixture +from typing import Any + +from ahriman.core.alpm.pacman import Pacman +from ahriman.core.build_tools.package_archive import PackageArchive from ahriman.core.build_tools.sources import Sources +from ahriman.models.package import Package +from ahriman.models.repository_paths import RepositoryPaths +from ahriman.models.scan_paths import ScanPaths + + +@pytest.fixture +def package_archive_ahriman(package_ahriman: Package, repository_paths: RepositoryPaths, pacman: Pacman, + scan_paths: ScanPaths, passwd: Any, mocker: MockerFixture) -> PackageArchive: + """ + package archive fixture + + Args: + package_ahriman(Package): package test instance + repository_paths(RepositoryPaths): repository paths test instance + pacman(Pacman): pacman test instance + scan_paths(ScanPaths): scan paths test instance + passwd(Any): passwd structure test instance + mocker(MockerFixture): mocker object + + Returns: + PackageArchive: package archive test instance + """ + mocker.patch("ahriman.models.repository_paths.getpwuid", return_value=passwd) + return PackageArchive(repository_paths.build_directory, package_ahriman, pacman, scan_paths) @pytest.fixture diff --git a/tests/ahriman/models/test_package_archive.py b/tests/ahriman/core/build_tools/test_package_archive.py similarity index 82% rename from tests/ahriman/models/test_package_archive.py rename to tests/ahriman/core/build_tools/test_package_archive.py index 80b6381ff..73d39a337 100644 --- a/tests/ahriman/models/test_package_archive.py +++ b/tests/ahriman/core/build_tools/test_package_archive.py @@ -3,16 +3,16 @@ from pytest_mock import MockerFixture from unittest.mock import MagicMock, PropertyMock +from ahriman.core.build_tools.package_archive import PackageArchive from ahriman.core.exceptions import UnknownPackageError from ahriman.models.filesystem_package import FilesystemPackage -from ahriman.models.package_archive import PackageArchive def test_dynamic_needed(mocker: MockerFixture) -> None: """ must correctly define list of dynamically linked libraries """ - mocker.patch("ahriman.models.package_archive.PackageArchive.is_elf", return_value=True) + mocker.patch("ahriman.core.build_tools.package_archive.PackageArchive.is_elf", return_value=True) linked = PackageArchive.dynamic_needed(Path(".tox") / "tests" / "bin" / "python") assert linked @@ -24,7 +24,7 @@ def test_dynamic_needed_not_elf(mocker: MockerFixture) -> None: """ must skip checking if not an elf file """ - mocker.patch("ahriman.models.package_archive.PackageArchive.is_elf", return_value=False) + mocker.patch("ahriman.core.build_tools.package_archive.PackageArchive.is_elf", return_value=False) assert not PackageArchive.dynamic_needed(Path(".tox") / "tests" / "bin" / "python") @@ -32,7 +32,7 @@ def test_dynamic_needed_no_section(mocker: MockerFixture) -> None: """ must skip checking if there was no dynamic section found """ - mocker.patch("ahriman.models.package_archive.PackageArchive.is_elf", return_value=True) + mocker.patch("ahriman.core.build_tools.package_archive.PackageArchive.is_elf", return_value=True) mocker.patch("elftools.elf.elffile.ELFFile.iter_sections", return_value=[]) assert not PackageArchive.dynamic_needed(Path(".tox") / "tests" / "bin" / "python") @@ -109,8 +109,8 @@ def test_raw_dependencies_packages(package_archive_ahriman: PackageArchive, mock files=[Path("package2") / "file4", Path("package2") / "file3"], ), } - mocker.patch("ahriman.models.package_archive.PackageArchive.installed_packages", return_value=packages) - mocker.patch("ahriman.models.package_archive.PackageArchive.depends_on_paths", return_value=( + mocker.patch("ahriman.core.build_tools.package_archive.PackageArchive.installed_packages", return_value=packages) + mocker.patch("ahriman.core.build_tools.package_archive.PackageArchive.depends_on_paths", return_value=( {"file1", "file3"}, {Path("usr") / "dir2", Path("dir3"), Path("package2") / "dir4"}, )) @@ -165,17 +165,19 @@ def test_depends_on(package_archive_ahriman: PackageArchive, mocker: MockerFixtu """ must extract packages and files which are dependencies for the package """ - raw_mock = mocker.patch("ahriman.models.package_archive.PackageArchive._raw_dependencies_packages", + raw_mock = mocker.patch("ahriman.core.build_tools.package_archive.PackageArchive._raw_dependencies_packages", return_value="1") - refined_mock = mocker.patch("ahriman.models.package_archive.PackageArchive._refine_dependencies", return_value={ - Path("package1") / "file1": [FilesystemPackage(package_name="package1", depends=set(), opt_depends=set())], - Path("package2") / "file3": [FilesystemPackage(package_name="package2", depends=set(), opt_depends=set())], - Path("package2") / "dir4": [FilesystemPackage(package_name="package2", depends=set(), opt_depends=set())], - Path("usr") / "dir2": [ - FilesystemPackage(package_name="package1", depends=set(), opt_depends=set()), - FilesystemPackage(package_name="package2", depends=set(), opt_depends=set()), - ], - }) + refined_mock = mocker.patch( + "ahriman.core.build_tools.package_archive.PackageArchive._refine_dependencies", return_value={ + Path("package1") / "file1": [FilesystemPackage(package_name="package1", depends=set(), opt_depends=set())], + Path("package2") / "file3": [FilesystemPackage(package_name="package2", depends=set(), opt_depends=set())], + Path("package2") / "dir4": [FilesystemPackage(package_name="package2", depends=set(), opt_depends=set())], + Path("usr") / "dir2": [ + FilesystemPackage(package_name="package1", depends=set(), opt_depends=set()), + FilesystemPackage(package_name="package2", depends=set(), opt_depends=set()), + ], + } + ) result = package_archive_ahriman.depends_on() raw_mock.assert_called_once_with() @@ -194,8 +196,9 @@ def test_depends_on_paths(package_archive_ahriman: PackageArchive, mocker: Mocke """ package_dir = package_archive_ahriman.root / "build" / \ package_archive_ahriman.package.base / "pkg" / package_archive_ahriman.package.base - dynamic_mock = mocker.patch("ahriman.models.package_archive.PackageArchive.dynamic_needed", return_value=["lib"]) - walk_mock = mocker.patch("ahriman.models.package_archive.walk", return_value=[ + dynamic_mock = mocker.patch("ahriman.core.build_tools.package_archive.PackageArchive.dynamic_needed", + return_value=["lib"]) + walk_mock = mocker.patch("ahriman.core.build_tools.package_archive.walk", return_value=[ package_dir / "root" / "file", Path("directory"), ]) @@ -213,7 +216,7 @@ def test_installed_packages(package_archive_ahriman: PackageArchive, mocker: Moc """ must load list of installed packages and their files """ - walk_mock = mocker.patch("ahriman.models.package_archive.walk", return_value=[ + walk_mock = mocker.patch("ahriman.core.build_tools.package_archive.walk", return_value=[ Path("ahriman-2.13.3-1") / "desc", Path("ahriman-2.13.3-1") / "files", ]) diff --git a/tests/ahriman/core/repository/test_executor.py b/tests/ahriman/core/repository/test_executor.py index 9025634ac..54f3b09b3 100644 --- a/tests/ahriman/core/repository/test_executor.py +++ b/tests/ahriman/core/repository/test_executor.py @@ -24,7 +24,7 @@ def test_process_build(executor: Executor, package_ahriman: Package, passwd: Any move_mock = mocker.patch("shutil.move") status_client_mock = mocker.patch("ahriman.core.status.Client.set_building") commit_sha_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_update") - depends_on_mock = mocker.patch("ahriman.models.package_archive.PackageArchive.depends_on", + depends_on_mock = mocker.patch("ahriman.core.build_tools.package_archive.PackageArchive.depends_on", return_value=Dependencies()) dependencies_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_dependencies_update") diff --git a/tests/ahriman/models/conftest.py b/tests/ahriman/models/conftest.py index 0db51496c..b60fec975 100644 --- a/tests/ahriman/models/conftest.py +++ b/tests/ahriman/models/conftest.py @@ -1,24 +1,17 @@ import pytest -from typing import Any from unittest.mock import MagicMock, PropertyMock -from pytest_mock import MockerFixture from ahriman import __version__ -from ahriman.core.alpm.pacman import Pacman from ahriman.core.alpm.remote import AUR -from ahriman.core.configuration import Configuration from ahriman.models.build_status import BuildStatus, BuildStatusEnum from ahriman.models.counters import Counters from ahriman.models.filesystem_package import FilesystemPackage from ahriman.models.internal_status import InternalStatus from ahriman.models.package import Package -from ahriman.models.package_archive import PackageArchive from ahriman.models.package_description import PackageDescription from ahriman.models.package_source import PackageSource from ahriman.models.remote_source import RemoteSource -from ahriman.models.repository_paths import RepositoryPaths -from ahriman.models.scan_paths import ScanPaths @pytest.fixture @@ -77,27 +70,6 @@ def internal_status(counters: Counters) -> InternalStatus: repository="aur-clone") -@pytest.fixture -def package_archive_ahriman(package_ahriman: Package, repository_paths: RepositoryPaths, pacman: Pacman, - scan_paths: ScanPaths, passwd: Any, mocker: MockerFixture) -> PackageArchive: - """ - package archive fixture - - Args: - package_ahriman(Package): package test instance - repository_paths(RepositoryPaths): repository paths test instance - pacman(Pacman): pacman test instance - scan_paths(ScanPaths): scan paths test instance - passwd(Any): passwd structure test instance - mocker(MockerFixture): mocker object - - Returns: - PackageArchive: package archive test instance - """ - mocker.patch("ahriman.models.repository_paths.getpwuid", return_value=passwd) - return PackageArchive(repository_paths.build_directory, package_ahriman, pacman, scan_paths) - - @pytest.fixture def package_tpacpi_bat_git() -> Package: """ @@ -161,20 +133,3 @@ def pyalpm_package_description_ahriman(package_description_ahriman: PackageDescr type(mock).provides = PropertyMock(return_value=package_description_ahriman.provides) type(mock).url = PropertyMock(return_value=package_description_ahriman.url) return mock - - -@pytest.fixture -def scan_paths(configuration: Configuration) -> ScanPaths: - """ - scan paths fixture - - Args: - configuration(Configuration): configuration test instance - - Returns: - ScanPaths: scan paths test instance - """ - return ScanPaths( - allowed_paths=configuration.getpathlist("build", "allowed_scan_paths"), - blacklisted_paths=configuration.getpathlist("build", "blacklisted_scan_paths"), - ) diff --git a/tests/ahriman/models/test_scan_paths.py b/tests/ahriman/models/test_scan_paths.py index 6f8d7a3ae..462a921a2 100644 --- a/tests/ahriman/models/test_scan_paths.py +++ b/tests/ahriman/models/test_scan_paths.py @@ -3,40 +3,30 @@ from ahriman.models.scan_paths import ScanPaths -def test_post_init(scan_paths: ScanPaths) -> None: - """ - must convert paths to / relative - """ - assert all(not path.is_absolute() for path in scan_paths.allowed_paths) - assert all(not path.is_absolute() for path in scan_paths.blacklisted_paths) - - def test_is_allowed() -> None: """ must check if path is subpath of one in allowed list """ - assert ScanPaths(allowed_paths=[Path("/") / "usr"], blacklisted_paths=[]).is_allowed(Path("usr")) - assert ScanPaths(allowed_paths=[Path("/") / "usr"], blacklisted_paths=[]).is_allowed(Path("usr") / "lib") - assert not ScanPaths(allowed_paths=[Path("/") / "usr"], blacklisted_paths=[]).is_allowed(Path("var")) + assert ScanPaths(["usr"]).is_allowed(Path("usr")) + assert ScanPaths(["usr"]).is_allowed(Path("usr") / "lib") + assert not ScanPaths(["usr"]).is_allowed(Path("var")) + assert ScanPaths(["usr(?!/lib)"]).is_allowed(Path("usr")) + assert ScanPaths(["usr(?!/lib)", "var"]).is_allowed(Path("var")) + assert not ScanPaths(["usr(?!/lib)"]).is_allowed(Path("usr") / "lib") -def test_is_blacklisted() -> None: + +def test_is_allowed_default(scan_paths: ScanPaths) -> None: """ - must check if path is not subpath of one in blacklist + must provide expected default configuration """ - assert ScanPaths( - allowed_paths=[Path("/") / "usr"], - blacklisted_paths=[Path("/") / "usr" / "lib"], - ).is_allowed(Path("usr")) - assert ScanPaths( - allowed_paths=[Path("/") / "usr", Path("/") / "var"], - blacklisted_paths=[Path("/") / "usr" / "lib"], - ).is_allowed(Path("var")) - assert not ScanPaths( - allowed_paths=[Path("/") / "usr"], - blacklisted_paths=[Path("/") / "usr" / "lib"], - ).is_allowed(Path(" usr") / "lib") - assert not ScanPaths( - allowed_paths=[Path("/") / "usr"], - blacklisted_paths=[Path("/") / "usr" / "lib"], - ).is_allowed(Path("usr") / "lib" / "qt") + assert not scan_paths.is_allowed(Path("usr")) + assert not scan_paths.is_allowed(Path("var")) + + assert scan_paths.is_allowed(Path("usr") / "lib") + assert scan_paths.is_allowed(Path("usr") / "lib" / "libm.so") + + # cmake case + assert scan_paths.is_allowed(Path("usr") / "lib" / "libcmake.so") + assert not scan_paths.is_allowed(Path("usr") / "lib" / "cmake") + assert not scan_paths.is_allowed(Path("usr") / "lib" / "cmake" / "file.cmake") diff --git a/tests/testresources/core/ahriman.ini b/tests/testresources/core/ahriman.ini index 74889ecf4..fe88b9124 100644 --- a/tests/testresources/core/ahriman.ini +++ b/tests/testresources/core/ahriman.ini @@ -20,13 +20,12 @@ salt = salt allow_read_only = no [build] -allowed_scan_paths = /usr/lib archbuild_flags = -blacklisted_scan_paths = /usr/lib/cmake build_command = extra-x86_64-build ignore_packages = makechrootpkg_flags = makepkg_flags = --skippgpcheck +scan_paths = ^usr/lib(?!/cmake).*$ triggers = ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger triggers_known = ahriman.core.distributed.WorkerLoaderTrigger ahriman.core.distributed.WorkerRegisterTrigger ahriman.core.distributed.WorkerTrigger ahriman.core.distributed.WorkerUnregisterTrigger ahriman.core.gitremote.RemotePullTrigger ahriman.core.gitremote.RemotePushTrigger ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger ahriman.core.support.KeyringTrigger ahriman.core.support.MirrorlistTrigger