From 427ba0f0eaa5c3934bb9ba78fbc331c2abaa3722 Mon Sep 17 00:00:00 2001 From: Evgeniy Alekseev Date: Sun, 26 Sep 2021 09:55:14 +0300 Subject: [PATCH] add ability to specify package source explicitly during the addition --- src/ahriman/application/ahriman.py | 3 + src/ahriman/application/application.py | 16 +++--- src/ahriman/application/handlers/add.py | 2 +- src/ahriman/core/spawn.py | 5 +- src/ahriman/models/package_source.py | 55 +++++++++++++++++++ .../application/handlers/test_handler_add.py | 2 + tests/ahriman/application/test_application.py | 11 ++-- tests/ahriman/core/test_spawn.py | 4 +- tests/ahriman/models/test_package_source.py | 47 ++++++++++++++++ 9 files changed, 128 insertions(+), 17 deletions(-) create mode 100644 src/ahriman/models/package_source.py create mode 100644 tests/ahriman/models/test_package_source.py diff --git a/src/ahriman/application/ahriman.py b/src/ahriman/application/ahriman.py index ebb10536..60170c75 100644 --- a/src/ahriman/application/ahriman.py +++ b/src/ahriman/application/ahriman.py @@ -26,6 +26,7 @@ from pathlib import Path from ahriman import version from ahriman.application import handlers from ahriman.models.build_status import BuildStatusEnum +from ahriman.models.package_source import PackageSource from ahriman.models.sign_settings import SignSettings from ahriman.models.user_access import UserAccess @@ -91,6 +92,8 @@ def _set_add_parser(root: SubParserAction) -> argparse.ArgumentParser: formatter_class=argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("package", help="package base/name or archive path", nargs="+") parser.add_argument("--now", help="run update function after", action="store_true") + parser.add_argument("--source", help="package source", choices=PackageSource, type=PackageSource, + default=PackageSource.Auto) parser.add_argument("--without-dependencies", help="do not add dependencies", action="store_true") parser.set_defaults(handler=handlers.Add, architecture=[]) return parser diff --git a/src/ahriman/application/application.py b/src/ahriman/application/application.py index 0f9f9edd..07111db4 100644 --- a/src/ahriman/application/application.py +++ b/src/ahriman/application/application.py @@ -29,6 +29,7 @@ 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: @@ -96,10 +97,11 @@ class Application: return updates - def add(self, names: Iterable[str], without_dependencies: bool) -> None: + 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() @@ -122,14 +124,14 @@ class Application: if without_dependencies: return dependencies = Package.dependencies(path) - self.add(dependencies.difference(known_packages), without_dependencies) + self.add(dependencies.difference(known_packages), PackageSource.AUR, without_dependencies) def process_single(src: str) -> None: - maybe_path = Path(src) - if maybe_path.is_dir(): - add_directory(maybe_path) - elif maybe_path.is_file(): - add_archive(maybe_path) + resolved_source = source.resolve(src) + if resolved_source == PackageSource.Directory: + add_directory(Path(src)) + elif resolved_source == PackageSource.Archive: + add_archive(Path(src)) else: path = add_manual(src) process_dependencies(path) diff --git a/src/ahriman/application/handlers/add.py b/src/ahriman/application/handlers/add.py index 34409de9..5f2427dc 100644 --- a/src/ahriman/application/handlers/add.py +++ b/src/ahriman/application/handlers/add.py @@ -42,7 +42,7 @@ class Add(Handler): :param no_report: force disable reporting """ application = Application(architecture, configuration, no_report) - application.add(args.package, args.without_dependencies) + application.add(args.package, args.source, args.without_dependencies) if not args.now: return diff --git a/src/ahriman/core/spawn.py b/src/ahriman/core/spawn.py index eb094d54..d4dafdbe 100644 --- a/src/ahriman/core/spawn.py +++ b/src/ahriman/core/spawn.py @@ -28,6 +28,7 @@ from threading import Lock, Thread from typing import Callable, Dict, Iterable, Tuple from ahriman.core.configuration import Configuration +from ahriman.models.package_source import PackageSource class Spawn(Thread): @@ -79,7 +80,9 @@ class Spawn(Thread): :param packages: packages list to add :param now: build packages now """ - kwargs = {"now": ""} if now else {} + kwargs = {"source": PackageSource.AUR.value} # avoid abusing by building non-aur packages + if now: + kwargs["now"] = "" self.spawn_process("add", *packages, **kwargs) def packages_remove(self, packages: Iterable[str]) -> None: diff --git a/src/ahriman/models/package_source.py b/src/ahriman/models/package_source.py new file mode 100644 index 00000000..b40c9dbb --- /dev/null +++ b/src/ahriman/models/package_source.py @@ -0,0 +1,55 @@ +# +# 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 __future__ import annotations + +from enum import Enum +from pathlib import Path + +from ahriman.core.util import package_like + + +class PackageSource(Enum): + """ + package source for addition enumeration + :cvar Auto: automatically determine type of the source + :cvar Archive: source is a package archive + :cvar Directory: source is a directory which contains packages + :cvar AUR: source is an AUR package for which it should search + """ + + Auto = "auto" + Archive = "archive" + Directory = "directory" + AUR = "aur" + + def resolve(self, source: str) -> PackageSource: + """ + resolve auto into the correct type + :param source: source of the package + :return: non-auto type of the package source + """ + if self != PackageSource.Auto: + return self + maybe_path = Path(source) + if maybe_path.is_dir(): + return PackageSource.Directory + if maybe_path.is_file() and package_like(maybe_path): + return PackageSource.Archive + return PackageSource.AUR diff --git a/tests/ahriman/application/handlers/test_handler_add.py b/tests/ahriman/application/handlers/test_handler_add.py index baad1abf..224d9fba 100644 --- a/tests/ahriman/application/handlers/test_handler_add.py +++ b/tests/ahriman/application/handlers/test_handler_add.py @@ -4,6 +4,7 @@ from pytest_mock import MockerFixture from ahriman.application.handlers import Add from ahriman.core.configuration import Configuration +from ahriman.models.package_source import PackageSource def _default_args(args: argparse.Namespace) -> argparse.Namespace: @@ -14,6 +15,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace: """ args.package = [] args.now = False + args.source = PackageSource.Auto args.without_dependencies = False return args diff --git a/tests/ahriman/application/test_application.py b/tests/ahriman/application/test_application.py index 8643c6eb..85f1b25b 100644 --- a/tests/ahriman/application/test_application.py +++ b/tests/ahriman/application/test_application.py @@ -6,6 +6,7 @@ from unittest import mock from ahriman.application.application import Application from ahriman.core.tree import Leaf, Tree from ahriman.models.package import Package +from ahriman.models.package_source import PackageSource def test_finalize(application: Application, mocker: MockerFixture) -> None: @@ -108,12 +109,11 @@ def test_add_directory(application: Application, package_ahriman: Package, mocke must add packages from directory """ mocker.patch("ahriman.application.application.Application._known_packages", return_value=set()) - mocker.patch("pathlib.Path.is_dir", return_value=True) iterdir_mock = mocker.patch("pathlib.Path.iterdir", return_value=[package.filepath for package in package_ahriman.packages.values()]) move_mock = mocker.patch("shutil.move") - application.add([package_ahriman.base], False) + application.add([package_ahriman.base], PackageSource.Directory, False) iterdir_mock.assert_called_once() move_mock.assert_called_once() @@ -126,7 +126,7 @@ def test_add_manual(application: Application, package_ahriman: Package, mocker: mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman) fetch_mock = mocker.patch("ahriman.core.build_tools.task.Task.fetch") - application.add([package_ahriman.base], True) + application.add([package_ahriman.base], PackageSource.AUR, True) fetch_mock.assert_called_once() @@ -140,7 +140,7 @@ def test_add_manual_with_dependencies(application: Application, package_ahriman: mocker.patch("ahriman.core.build_tools.task.Task.fetch") dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies") - application.add([package_ahriman.base], False) + application.add([package_ahriman.base], PackageSource.AUR, False) dependencies_mock.assert_called_once() @@ -149,10 +149,9 @@ def test_add_package(application: Application, package_ahriman: Package, mocker: must add package from archive """ mocker.patch("ahriman.application.application.Application._known_packages", return_value=set()) - mocker.patch("pathlib.Path.is_file", return_value=True) move_mock = mocker.patch("shutil.move") - application.add([package_ahriman.base], False) + application.add([package_ahriman.base], PackageSource.Archive, False) move_mock.assert_called_once() diff --git a/tests/ahriman/core/test_spawn.py b/tests/ahriman/core/test_spawn.py index a64579d8..695dda61 100644 --- a/tests/ahriman/core/test_spawn.py +++ b/tests/ahriman/core/test_spawn.py @@ -42,7 +42,7 @@ def test_packages_add(spawner: Spawn, mocker: MockerFixture) -> None: """ spawn_mock = mocker.patch("ahriman.core.spawn.Spawn.spawn_process") spawner.packages_add(["ahriman", "linux"], now=False) - spawn_mock.assert_called_with("add", "ahriman", "linux") + spawn_mock.assert_called_with("add", "ahriman", "linux", source="aur") def test_packages_add_with_build(spawner: Spawn, mocker: MockerFixture) -> None: @@ -51,7 +51,7 @@ def test_packages_add_with_build(spawner: Spawn, mocker: MockerFixture) -> None: """ spawn_mock = mocker.patch("ahriman.core.spawn.Spawn.spawn_process") spawner.packages_add(["ahriman", "linux"], now=True) - spawn_mock.assert_called_with("add", "ahriman", "linux", now="") + spawn_mock.assert_called_with("add", "ahriman", "linux", source="aur", now="") def test_packages_remove(spawner: Spawn, mocker: MockerFixture) -> None: diff --git a/tests/ahriman/models/test_package_source.py b/tests/ahriman/models/test_package_source.py new file mode 100644 index 00000000..78068a5d --- /dev/null +++ b/tests/ahriman/models/test_package_source.py @@ -0,0 +1,47 @@ +from pytest_mock import MockerFixture + +from ahriman.models.package_source import PackageSource + + +def test_resolve_non_auto() -> None: + """ + must resolve non auto type to itself + """ + for source in filter(lambda src: src != PackageSource.Auto, PackageSource): + assert source.resolve("") == source + + +def test_resolve_archive(mocker: MockerFixture) -> None: + """ + must resolve auto type into the archive + """ + mocker.patch("pathlib.Path.is_dir", return_value=False) + mocker.patch("pathlib.Path.is_file", return_value=True) + assert PackageSource.Auto.resolve("linux-5.14.2.arch1-2-x86_64.pkg.tar.zst") == PackageSource.Archive + + +def test_resolve_directory(mocker: MockerFixture) -> None: + """ + must resolve auto type into the directory + """ + mocker.patch("pathlib.Path.is_dir", return_value=True) + mocker.patch("pathlib.Path.is_file", return_value=False) + assert PackageSource.Auto.resolve("path") == PackageSource.Directory + + +def test_resolve_aur(mocker: MockerFixture) -> None: + """ + must resolve auto type into the AUR package + """ + mocker.patch("pathlib.Path.is_dir", return_value=False) + mocker.patch("pathlib.Path.is_file", return_value=False) + assert PackageSource.Auto.resolve("package") == PackageSource.AUR + + +def test_resolve_aur_not_package_like(mocker: MockerFixture) -> None: + """ + must resolve auto type into the AUR package if it is file, but does not look like a package archive + """ + mocker.patch("pathlib.Path.is_dir", return_value=False) + mocker.patch("pathlib.Path.is_file", return_value=True) + assert PackageSource.Auto.resolve("package") == PackageSource.AUR