add ability to specify package source explicitly during the addition

This commit is contained in:
Evgenii Alekseev 2021-09-26 09:55:14 +03:00
parent 266d2bd77d
commit 427ba0f0ea
9 changed files with 128 additions and 17 deletions

View File

@ -26,6 +26,7 @@ from pathlib import Path
from ahriman import version from ahriman import version
from ahriman.application import handlers from ahriman.application import handlers
from ahriman.models.build_status import BuildStatusEnum from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package_source import PackageSource
from ahriman.models.sign_settings import SignSettings from ahriman.models.sign_settings import SignSettings
from ahriman.models.user_access import UserAccess from ahriman.models.user_access import UserAccess
@ -91,6 +92,8 @@ def _set_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
formatter_class=argparse.ArgumentDefaultsHelpFormatter) formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("package", help="package base/name or archive path", nargs="+") 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("--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.add_argument("--without-dependencies", help="do not add dependencies", action="store_true")
parser.set_defaults(handler=handlers.Add, architecture=[]) parser.set_defaults(handler=handlers.Add, architecture=[])
return parser return parser

View File

@ -29,6 +29,7 @@ from ahriman.core.repository.repository import Repository
from ahriman.core.tree import Tree from ahriman.core.tree import Tree
from ahriman.core.util import package_like from ahriman.core.util import package_like
from ahriman.models.package import Package from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
class Application: class Application:
@ -96,10 +97,11 @@ class Application:
return updates 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 add packages for the next build
:param names: list of package bases to add :param names: list of package bases to add
:param source: package source to add
:param without_dependencies: if set, dependency check will be disabled :param without_dependencies: if set, dependency check will be disabled
""" """
known_packages = self._known_packages() known_packages = self._known_packages()
@ -122,14 +124,14 @@ class Application:
if without_dependencies: if without_dependencies:
return return
dependencies = Package.dependencies(path) 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: def process_single(src: str) -> None:
maybe_path = Path(src) resolved_source = source.resolve(src)
if maybe_path.is_dir(): if resolved_source == PackageSource.Directory:
add_directory(maybe_path) add_directory(Path(src))
elif maybe_path.is_file(): elif resolved_source == PackageSource.Archive:
add_archive(maybe_path) add_archive(Path(src))
else: else:
path = add_manual(src) path = add_manual(src)
process_dependencies(path) process_dependencies(path)

View File

@ -42,7 +42,7 @@ class Add(Handler):
:param no_report: force disable reporting :param no_report: force disable reporting
""" """
application = Application(architecture, configuration, no_report) 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: if not args.now:
return return

View File

@ -28,6 +28,7 @@ from threading import Lock, Thread
from typing import Callable, Dict, Iterable, Tuple from typing import Callable, Dict, Iterable, Tuple
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.package_source import PackageSource
class Spawn(Thread): class Spawn(Thread):
@ -79,7 +80,9 @@ class Spawn(Thread):
:param packages: packages list to add :param packages: packages list to add
:param now: build packages now :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) self.spawn_process("add", *packages, **kwargs)
def packages_remove(self, packages: Iterable[str]) -> None: def packages_remove(self, packages: Iterable[str]) -> None:

View File

@ -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 <http://www.gnu.org/licenses/>.
#
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

View File

@ -4,6 +4,7 @@ from pytest_mock import MockerFixture
from ahriman.application.handlers import Add from ahriman.application.handlers import Add
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.models.package_source import PackageSource
def _default_args(args: argparse.Namespace) -> argparse.Namespace: def _default_args(args: argparse.Namespace) -> argparse.Namespace:
@ -14,6 +15,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
""" """
args.package = [] args.package = []
args.now = False args.now = False
args.source = PackageSource.Auto
args.without_dependencies = False args.without_dependencies = False
return args return args

View File

@ -6,6 +6,7 @@ from unittest import mock
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.core.tree import Leaf, Tree from ahriman.core.tree import Leaf, Tree
from ahriman.models.package import Package from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource
def test_finalize(application: Application, mocker: MockerFixture) -> None: 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 must add packages from directory
""" """
mocker.patch("ahriman.application.application.Application._known_packages", return_value=set()) 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", iterdir_mock = mocker.patch("pathlib.Path.iterdir",
return_value=[package.filepath for package in package_ahriman.packages.values()]) return_value=[package.filepath for package in package_ahriman.packages.values()])
move_mock = mocker.patch("shutil.move") 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() iterdir_mock.assert_called_once()
move_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) mocker.patch("ahriman.models.package.Package.load", return_value=package_ahriman)
fetch_mock = mocker.patch("ahriman.core.build_tools.task.Task.fetch") 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() 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") mocker.patch("ahriman.core.build_tools.task.Task.fetch")
dependencies_mock = mocker.patch("ahriman.models.package.Package.dependencies") 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() dependencies_mock.assert_called_once()
@ -149,10 +149,9 @@ def test_add_package(application: Application, package_ahriman: Package, mocker:
must add package from archive must add package from archive
""" """
mocker.patch("ahriman.application.application.Application._known_packages", return_value=set()) 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") 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() move_mock.assert_called_once()

View File

@ -42,7 +42,7 @@ def test_packages_add(spawner: Spawn, mocker: MockerFixture) -> None:
""" """
spawn_mock = mocker.patch("ahriman.core.spawn.Spawn.spawn_process") spawn_mock = mocker.patch("ahriman.core.spawn.Spawn.spawn_process")
spawner.packages_add(["ahriman", "linux"], now=False) 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: 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") spawn_mock = mocker.patch("ahriman.core.spawn.Spawn.spawn_process")
spawner.packages_add(["ahriman", "linux"], now=True) 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: def test_packages_remove(spawner: Spawn, mocker: MockerFixture) -> None:

View File

@ -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