use own aur wrapper (#49)

This commit is contained in:
Evgenii Alekseev 2022-02-06 03:44:57 +03:00 committed by GitHub
parent f54a2fe740
commit 9197b416e6
25 changed files with 673 additions and 132 deletions

View File

@ -7,7 +7,7 @@ pkgdesc="ArcH Linux ReposItory MANager"
arch=('any') arch=('any')
url="https://github.com/arcan1s/ahriman" url="https://github.com/arcan1s/ahriman"
license=('GPL3') license=('GPL3')
depends=('devtools' 'git' 'pyalpm' 'python-aur' 'python-passlib' 'python-srcinfo') depends=('devtools' 'git' 'pyalpm' 'python-inflection' 'python-passlib' 'python-srcinfo')
makedepends=('python-pip') makedepends=('python-pip')
optdepends=('breezy: -bzr packages support' optdepends=('breezy: -bzr packages support'
'darcs: -darcs packages support' 'darcs: -darcs packages support'

View File

@ -29,7 +29,7 @@ setup(
dependency_links=[ dependency_links=[
], ],
install_requires=[ install_requires=[
"aur", "inflection",
"passlib", "passlib",
"pyalpm", "pyalpm",
"requests", "requests",

View File

@ -17,12 +17,11 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
import aur # type: ignore
from typing import List, Optional from typing import List, Optional
from ahriman.application.formatters.printer import Printer from ahriman.application.formatters.printer import Printer
from ahriman.core.util import pretty_datetime from ahriman.core.util import pretty_datetime
from ahriman.models.aur_package import AURPackage
from ahriman.models.property import Property from ahriman.models.property import Property
@ -31,7 +30,7 @@ class AurPrinter(Printer):
print content of the AUR package print content of the AUR package
""" """
def __init__(self, package: aur.Package) -> None: def __init__(self, package: AURPackage) -> None:
""" """
default constructor default constructor
:param package: AUR package description :param package: AUR package description
@ -46,12 +45,12 @@ class AurPrinter(Printer):
return [ return [
Property("Package base", self.content.package_base), Property("Package base", self.content.package_base),
Property("Description", self.content.description, is_required=True), Property("Description", self.content.description, is_required=True),
Property("Upstream URL", self.content.url), Property("Upstream URL", self.content.url or ""),
Property("Licenses", self.content.license), # it should be actually a list Property("Licenses", ",".join(self.content.license)),
Property("Maintainer", self.content.maintainer or ""), # I think it is optional Property("Maintainer", self.content.maintainer or ""),
Property("First submitted", pretty_datetime(self.content.first_submitted)), Property("First submitted", pretty_datetime(self.content.first_submitted)),
Property("Last updated", pretty_datetime(self.content.last_modified)), Property("Last updated", pretty_datetime(self.content.last_modified)),
# more fields coming https://github.com/cdown/aur/pull/29 Property("Keywords", ",".join(self.content.keywords)),
] ]
def title(self) -> Optional[str]: def title(self) -> Optional[str]:

View File

@ -18,24 +18,27 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
import argparse import argparse
import aur # type: ignore
from dataclasses import fields
from typing import Callable, Iterable, List, Tuple, Type from typing import Callable, Iterable, List, Tuple, Type
from ahriman.application.formatters.aur_printer import AurPrinter from ahriman.application.formatters.aur_printer import AurPrinter
from ahriman.application.handlers.handler import Handler from ahriman.application.handlers.handler import Handler
from ahriman.core.alpm.aur import AUR
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import InvalidOption from ahriman.core.exceptions import InvalidOption
from ahriman.core.util import aur_search from ahriman.models.aur_package import AURPackage
class Search(Handler): class Search(Handler):
""" """
packages search handler packages search handler
:cvar SORT_FIELDS: allowed fields to sort the package list
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
SORT_FIELDS = set(aur.Package._fields) # later we will have to remove some fields from here (lists) # later we will have to remove some fields from here (lists)
SORT_FIELDS = {pair.name for pair in fields(AURPackage)}
@classmethod @classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
@ -47,12 +50,12 @@ class Search(Handler):
:param configuration: configuration instance :param configuration: configuration instance
:param no_report: force disable reporting :param no_report: force disable reporting
""" """
packages_list = aur_search(*args.search) packages_list = AUR.multisearch(*args.search)
for package in Search.sort(packages_list, args.sort_by): for package in Search.sort(packages_list, args.sort_by):
AurPrinter(package).print(args.info) AurPrinter(package).print(args.info)
@staticmethod @staticmethod
def sort(packages: Iterable[aur.Package], sort_by: str) -> List[aur.Package]: def sort(packages: Iterable[AURPackage], sort_by: str) -> List[AURPackage]:
""" """
sort package list by specified field sort package list by specified field
:param packages: packages list to sort :param packages: packages list to sort
@ -63,6 +66,6 @@ class Search(Handler):
raise InvalidOption(sort_by) raise InvalidOption(sort_by)
# always sort by package name at the last # always sort by package name at the last
# well technically it is not a string, but we can deal with it # well technically it is not a string, but we can deal with it
comparator: Callable[[aur.Package], Tuple[str, str]] =\ comparator: Callable[[AURPackage], Tuple[str, str]] =\
lambda package: (getattr(package, sort_by), package.name) lambda package: (getattr(package, sort_by), package.name)
return sorted(packages, key=comparator) return sorted(packages, key=comparator)

View File

@ -0,0 +1,152 @@
#
# 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
import logging
import requests
from typing import Any, Dict, List, Optional, Type
from ahriman.core.exceptions import InvalidPackageInfo
from ahriman.core.util import exception_response_text
from ahriman.models.aur_package import AURPackage
class AUR:
"""
AUR RPC wrapper
:cvar DEFAULT_RPC_URL: default AUR RPC url
:cvar DEFAULT_RPC_VERSION: default AUR RPC version
:ivar logger: class logger
:ivar rpc_url: AUR RPC url
:ivar rpc_version: AUR RPC version
"""
DEFAULT_RPC_URL = "https://aur.archlinux.org/rpc"
DEFAULT_RPC_VERSION = "5"
def __init__(self, rpc_url: Optional[str] = None, rpc_version: Optional[str] = None) -> None:
"""
default constructor
:param rpc_url: AUR RPC url
:param rpc_version: AUR RPC version
"""
self.rpc_url = rpc_url or self.DEFAULT_RPC_URL
self.rpc_version = rpc_version or self.DEFAULT_RPC_VERSION
self.logger = logging.getLogger("build_details")
@classmethod
def info(cls: Type[AUR], package_name: str) -> AURPackage:
"""
get package info by its name
:param package_name: package name to search
:return: package which match the package name
"""
return cls().package_info(package_name)
@classmethod
def multisearch(cls: Type[AUR], *keywords: str) -> List[AURPackage]:
"""
search in AUR by using API with multiple words. This method is required in order to handle
https://bugs.archlinux.org/task/49133. In addition short words will be dropped
:param keywords: search terms, e.g. "ahriman", "is", "cool"
:return: list of packages each of them matches all search terms
"""
instance = cls()
packages: Dict[str, AURPackage] = {}
for term in filter(lambda word: len(word) > 3, keywords):
portion = instance.search(term)
packages = {
package.package_base: package
for package in portion
if package.package_base in packages or not packages
}
return list(packages.values())
@classmethod
def search(cls: Type[AUR], *keywords: str) -> List[AURPackage]:
"""
search package in AUR web
:param keywords: keywords to search
:return: list of packages which match the criteria
"""
return cls().package_search(*keywords)
@staticmethod
def parse_response(response: Dict[str, Any]) -> List[AURPackage]:
"""
parse RPC response to package list
:param response: RPC response json
:return: list of parsed packages
"""
response_type = response["type"]
if response_type == "error":
error_details = response.get("error", "Unknown API error")
raise InvalidPackageInfo(error_details)
return [AURPackage.from_json(package) for package in response["results"]]
def make_request(self, request_type: str, *args: str, **kwargs: str) -> List[AURPackage]:
"""
perform request to AUR RPC
:param request_type: AUR request type, e.g. search, info
:param args: list of arguments to be passed as args query parameter
:param kwargs: list of additional named parameters like by
:return: response parsed to package list
"""
query: Dict[str, Any] = {
"type": request_type,
"v": self.rpc_version
}
arg_query = "arg[]" if len(args) > 1 else "arg"
query[arg_query] = list(args)
for key, value in kwargs.items():
query[key] = value
try:
response = requests.get(self.rpc_url, params=query)
response.raise_for_status()
return self.parse_response(response.json())
except requests.HTTPError as e:
self.logger.exception(
"could not perform request by using type %s: %s",
request_type,
exception_response_text(e))
raise
except Exception:
self.logger.exception("could not perform request by using type %s", request_type)
raise
def package_info(self, package_name: str) -> AURPackage:
"""
get package info by its name
:param package_name: package name to search
:return: package which match the package name
"""
packages = self.make_request("info", package_name)
return next(package for package in packages if package.name == package_name)
def package_search(self, *keywords: str, by: str = "name-desc") -> List[AURPackage]:
"""
search package in AUR web
:param keywords: keywords to search
:param by: search by the field
:return: list of packages which match the criteria
"""
return self.make_request("search", *keywords, by=by)

View File

@ -17,7 +17,6 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
import aur # type: ignore
import datetime import datetime
import os import os
import subprocess import subprocess
@ -25,29 +24,11 @@ import requests
from logging import Logger from logging import Logger
from pathlib import Path from pathlib import Path
from typing import Any, Dict, Generator, Iterable, List, Optional, Union from typing import Any, Dict, Generator, Iterable, Optional, Union
from ahriman.core.exceptions import InvalidOption, UnsafeRun from ahriman.core.exceptions import InvalidOption, UnsafeRun
def aur_search(*terms: str) -> List[aur.Package]:
"""
search in AUR by using API with multiple words. This method is required in order to handle
https://bugs.archlinux.org/task/49133. In addition short words will be dropped
:param terms: search terms, e.g. "ahriman", "is", "cool"
:return: list of packages each of them matches all search terms
"""
packages: Dict[str, aur.Package] = {}
for term in filter(lambda word: len(word) > 3, terms):
portion = aur.search(term)
packages = {
package.package_base: package
for package in portion
if package.package_base in packages or not packages
}
return list(packages.values())
def check_output(*args: str, exception: Optional[Exception], cwd: Optional[Path] = None, def check_output(*args: str, exception: Optional[Exception], cwd: Optional[Path] = None,
input_data: Optional[str] = None, logger: Optional[Logger] = None) -> str: input_data: Optional[str] = None, logger: Optional[Logger] = None) -> str:
""" """

View File

@ -0,0 +1,109 @@
#
# 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
import datetime
import inflection
from dataclasses import dataclass, field, fields
from typing import Any, Callable, Dict, List, Optional, Type
from ahriman.core.util import filter_json
@dataclass
class AURPackage:
"""
AUR package descriptor
:ivar id: package ID
:ivar name: package name
:ivar package_base_id: package base ID
:ivar version: package base version
:ivar description: package base description
:ivar url: package upstream URL
:ivar num_votes: number of votes for the package
:ivar polularity: package popularity
:ivar out_of_date: package out of date timestamp if any
:ivar maintainer: package maintainer
:ivar first_submitted: timestamp of the first package submission
:ivar last_modified: timestamp of the last package submission
:ivar url_path: AUR package path
:ivar depends: list of package dependencies
:ivar make_depends: list of package make dependencies
:ivar opt_depends: list of package optional dependencies
:ivar conflicts: conflicts list for the package
:ivar provides: list of packages which this package provides
:ivar license: list of package licenses
:ivar keywords: list of package keywords
"""
id: int
name: str
package_base_id: int
package_base: str
version: str
description: str
num_votes: int
popularity: float
first_submitted: datetime.datetime
last_modified: datetime.datetime
url_path: str
url: Optional[str] = None
out_of_date: Optional[datetime.datetime] = None
maintainer: Optional[str] = None
depends: List[str] = field(default_factory=list)
make_depends: List[str] = field(default_factory=list)
opt_depends: List[str] = field(default_factory=list)
conflicts: List[str] = field(default_factory=list)
provides: List[str] = field(default_factory=list)
license: List[str] = field(default_factory=list)
keywords: List[str] = field(default_factory=list)
@classmethod
def from_json(cls: Type[AURPackage], dump: Dict[str, Any]) -> AURPackage:
"""
construct package descriptor from RPC properties
:param dump: json dump body
:return: AUR package descriptor
"""
# filter to only known fields
known_fields = [pair.name for pair in fields(cls)]
properties = cls.convert(dump)
return cls(**filter_json(properties, known_fields))
@staticmethod
def convert(descriptor: Dict[str, Any]) -> Dict[str, Any]:
"""
covert AUR RPC key names to package keys
:param descriptor: RPC package descriptor
:return: package descriptor with names converted to snake case
"""
identity_mapper: Callable[[Any], Any] = lambda value: value
value_mapper: Dict[str, Callable[[Any], Any]] = {
"out_of_date": lambda value: datetime.datetime.utcfromtimestamp(value) if value is not None else None,
"first_submitted": datetime.datetime.utcfromtimestamp,
"last_modified": datetime.datetime.utcfromtimestamp,
}
result: Dict[str, Any] = {}
for api_key, api_value in descriptor.items():
property_key = inflection.underscore(api_key)
mapper = value_mapper.get(property_key, identity_mapper)
result[property_key] = mapper(api_value)
return result

View File

@ -19,7 +19,6 @@
# #
from __future__ import annotations from __future__ import annotations
import aur # type: ignore
import copy import copy
import logging import logging
@ -29,6 +28,7 @@ from pyalpm import vercmp # type: ignore
from srcinfo.parse import parse_srcinfo # type: ignore from srcinfo.parse import parse_srcinfo # type: ignore
from typing import Any, Dict, Iterable, List, Optional, Set, Type from typing import Any, Dict, Iterable, List, Optional, Set, Type
from ahriman.core.alpm.aur import AUR
from ahriman.core.alpm.pacman import Pacman from ahriman.core.alpm.pacman import Pacman
from ahriman.core.exceptions import InvalidPackageInfo from ahriman.core.exceptions import InvalidPackageInfo
from ahriman.core.util import check_output from ahriman.core.util import check_output
@ -129,7 +129,7 @@ class Package:
:param aur_url: AUR root url :param aur_url: AUR root url
:return: package properties :return: package properties
""" """
package = aur.info(name) package = AUR.info(name)
return cls(package.package_base, package.version, aur_url, {package.name: PackageDescription()}) return cls(package.package_base, package.version, aur_url, {package.name: PackageDescription()})
@classmethod @classmethod

View File

@ -25,8 +25,8 @@ from aiohttp import web
from aiohttp.web import middleware, Request from aiohttp.web import middleware, Request
from aiohttp.web_response import StreamResponse from aiohttp.web_response import StreamResponse
from aiohttp.web_urldispatcher import StaticResource from aiohttp.web_urldispatcher import StaticResource
from aiohttp_session import setup as setup_session # type: ignore from aiohttp_session import setup as setup_session
from aiohttp_session.cookie_storage import EncryptedCookieStorage # type: ignore from aiohttp_session.cookie_storage import EncryptedCookieStorage
from cryptography import fernet from cryptography import fernet
from typing import Optional from typing import Optional

View File

@ -17,12 +17,11 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
import aur # type: ignore
from aiohttp.web import HTTPNotFound, Response, json_response from aiohttp.web import HTTPNotFound, Response, json_response
from typing import Callable, List from typing import Callable, List
from ahriman.core.util import aur_search from ahriman.core.alpm.aur import AUR
from ahriman.models.aur_package import AURPackage
from ahriman.models.user_access import UserAccess from ahriman.models.user_access import UserAccess
from ahriman.web.views.base import BaseView from ahriman.web.views.base import BaseView
@ -45,11 +44,11 @@ class SearchView(BaseView):
:return: 200 with found package bases and descriptions sorted by base :return: 200 with found package bases and descriptions sorted by base
""" """
search: List[str] = self.request.query.getall("for", default=[]) search: List[str] = self.request.query.getall("for", default=[])
packages = aur_search(*search) packages = AUR.multisearch(*search)
if not packages: if not packages:
raise HTTPNotFound(reason=f"No packages found for terms: {search}") raise HTTPNotFound(reason=f"No packages found for terms: {search}")
comparator: Callable[[aur.Package], str] = lambda item: str(item.package_base) comparator: Callable[[AURPackage], str] = lambda item: str(item.package_base)
response = [ response = [
{ {
"package": package.package_base, "package": package.package_base,

View File

@ -1,4 +1,3 @@
import aur
import pytest import pytest
from ahriman.application.formatters.aur_printer import AurPrinter from ahriman.application.formatters.aur_printer import AurPrinter
@ -7,12 +6,13 @@ from ahriman.application.formatters.package_printer import PackagePrinter
from ahriman.application.formatters.status_printer import StatusPrinter from ahriman.application.formatters.status_printer import StatusPrinter
from ahriman.application.formatters.string_printer import StringPrinter from ahriman.application.formatters.string_printer import StringPrinter
from ahriman.application.formatters.update_printer import UpdatePrinter from ahriman.application.formatters.update_printer import UpdatePrinter
from ahriman.models.aur_package import AURPackage
from ahriman.models.build_status import BuildStatus from ahriman.models.build_status import BuildStatus
from ahriman.models.package import Package from ahriman.models.package import Package
@pytest.fixture @pytest.fixture
def aur_package_ahriman_printer(aur_package_ahriman: aur.Package) -> AurPrinter: def aur_package_ahriman_printer(aur_package_ahriman: AURPackage) -> AurPrinter:
""" """
fixture for AUR package printer fixture for AUR package printer
:param aur_package_ahriman: AUR package fixture :param aur_package_ahriman: AUR package fixture

View File

@ -1,5 +1,5 @@
import argparse import argparse
import aur import dataclasses
import pytest import pytest
from pytest_mock import MockerFixture from pytest_mock import MockerFixture
@ -7,6 +7,7 @@ from pytest_mock import MockerFixture
from ahriman.application.handlers import Search from ahriman.application.handlers import Search
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import InvalidOption from ahriman.core.exceptions import InvalidOption
from ahriman.models.aur_package import AURPackage
def _default_args(args: argparse.Namespace) -> argparse.Namespace: def _default_args(args: argparse.Namespace) -> argparse.Namespace:
@ -21,13 +22,13 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
return args return args
def test_run(args: argparse.Namespace, configuration: Configuration, aur_package_ahriman: aur.Package, def test_run(args: argparse.Namespace, configuration: Configuration, aur_package_ahriman: AURPackage,
mocker: MockerFixture) -> None: mocker: MockerFixture) -> None:
""" """
must run command must run command
""" """
args = _default_args(args) args = _default_args(args)
search_mock = mocker.patch("ahriman.application.handlers.search.aur_search", return_value=[aur_package_ahriman]) search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.multisearch", return_value=[aur_package_ahriman])
print_mock = mocker.patch("ahriman.application.formatters.printer.Printer.print") print_mock = mocker.patch("ahriman.application.formatters.printer.Printer.print")
Search.run(args, "x86_64", configuration, True) Search.run(args, "x86_64", configuration, True)
@ -35,38 +36,38 @@ def test_run(args: argparse.Namespace, configuration: Configuration, aur_package
print_mock.assert_called_once() print_mock.assert_called_once()
def test_run_sort(args: argparse.Namespace, configuration: Configuration, aur_package_ahriman: aur.Package, def test_run_sort(args: argparse.Namespace, configuration: Configuration, aur_package_ahriman: AURPackage,
mocker: MockerFixture) -> None: mocker: MockerFixture) -> None:
""" """
must run command with sorting must run command with sorting
""" """
args = _default_args(args) args = _default_args(args)
mocker.patch("ahriman.application.handlers.search.aur_search", return_value=[aur_package_ahriman]) mocker.patch("ahriman.core.alpm.aur.AUR.multisearch", return_value=[aur_package_ahriman])
sort_mock = mocker.patch("ahriman.application.handlers.search.Search.sort") sort_mock = mocker.patch("ahriman.application.handlers.search.Search.sort")
Search.run(args, "x86_64", configuration, True) Search.run(args, "x86_64", configuration, True)
sort_mock.assert_called_once_with([aur_package_ahriman], "name") sort_mock.assert_called_once_with([aur_package_ahriman], "name")
def test_run_sort_by(args: argparse.Namespace, configuration: Configuration, aur_package_ahriman: aur.Package, def test_run_sort_by(args: argparse.Namespace, configuration: Configuration, aur_package_ahriman: AURPackage,
mocker: MockerFixture) -> None: mocker: MockerFixture) -> None:
""" """
must run command with sorting by specified field must run command with sorting by specified field
""" """
args = _default_args(args) args = _default_args(args)
args.sort_by = "field" args.sort_by = "field"
mocker.patch("ahriman.application.handlers.search.aur_search", return_value=[aur_package_ahriman]) mocker.patch("ahriman.core.alpm.aur.AUR.multisearch", return_value=[aur_package_ahriman])
sort_mock = mocker.patch("ahriman.application.handlers.search.Search.sort") sort_mock = mocker.patch("ahriman.application.handlers.search.Search.sort")
Search.run(args, "x86_64", configuration, True) Search.run(args, "x86_64", configuration, True)
sort_mock.assert_called_once_with([aur_package_ahriman], "field") sort_mock.assert_called_once_with([aur_package_ahriman], "field")
def test_sort(aur_package_ahriman: aur.Package) -> None: def test_sort(aur_package_ahriman: AURPackage) -> None:
""" """
must sort package list must sort package list
""" """
another = aur_package_ahriman._replace(name="1", package_base="base") another = dataclasses.replace(aur_package_ahriman, name="1", package_base="base")
# sort by name # sort by name
assert Search.sort([aur_package_ahriman, another], "name") == [another, aur_package_ahriman] assert Search.sort([aur_package_ahriman, another], "name") == [another, aur_package_ahriman]
# sort by another field # sort by another field
@ -75,7 +76,7 @@ def test_sort(aur_package_ahriman: aur.Package) -> None:
assert Search.sort([aur_package_ahriman, another], "version") == [another, aur_package_ahriman] assert Search.sort([aur_package_ahriman, another], "version") == [another, aur_package_ahriman]
def test_sort_exception(aur_package_ahriman: aur.Package) -> None: def test_sort_exception(aur_package_ahriman: AURPackage) -> None:
""" """
must raise an exception on unknown sorting field must raise an exception on unknown sorting field
""" """
@ -94,4 +95,5 @@ def test_sort_fields() -> None:
""" """
must store valid field list which are allowed to be used for sorting must store valid field list which are allowed to be used for sorting
""" """
assert all(field in aur.Package._fields for field in Search.SORT_FIELDS) expected = {pair.name for pair in dataclasses.fields(AURPackage)}
assert all(field in expected for field in Search.SORT_FIELDS)

View File

@ -1,4 +1,4 @@
import aur import datetime
import pytest import pytest
from pathlib import Path from pathlib import Path
@ -10,6 +10,7 @@ from ahriman.core.auth.auth import Auth
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.spawn import Spawn from ahriman.core.spawn import Spawn
from ahriman.core.status.watcher import Watcher from ahriman.core.status.watcher import Watcher
from ahriman.models.aur_package import AURPackage
from ahriman.models.package import Package from ahriman.models.package import Package
from ahriman.models.package_description import PackageDescription from ahriman.models.package_description import PackageDescription
from ahriman.models.repository_paths import RepositoryPaths from ahriman.models.repository_paths import RepositoryPaths
@ -48,28 +49,56 @@ def anyvar(cls: Type[T], strict: bool = False) -> T:
# generic fixtures # generic fixtures
@pytest.fixture @pytest.fixture
def aur_package_ahriman(package_ahriman: Package) -> aur.Package: def aur_package_ahriman() -> AURPackage:
""" """
fixture for AUR package fixture for AUR package
:param package_ahriman: package fixture
:return: AUR package test instance :return: AUR package test instance
""" """
return aur.Package( return AURPackage(
num_votes=None, id=1009791,
description=package_ahriman.packages[package_ahriman.base].description, name="ahriman",
url_path=package_ahriman.web_url, package_base_id=165427,
last_modified=None, package_base="ahriman",
name=package_ahriman.base, version="1.7.0-1",
description="ArcH Linux ReposItory MANager",
num_votes=0,
popularity=0,
first_submitted=datetime.datetime(2021, 4, 9, 22, 44, 45),
last_modified=datetime.datetime(2021, 12, 25, 23, 11, 11),
url_path="/cgit/aur.git/snapshot/ahriman.tar.gz",
url="https://github.com/arcan1s/ahriman",
out_of_date=None, out_of_date=None,
id=None, maintainer="arcanis",
first_submitted=None, depends=[
maintainer=None, "devtools",
version=package_ahriman.version, "git",
license=package_ahriman.packages[package_ahriman.base].licenses, "pyalpm",
url=None, "python-aur",
package_base=package_ahriman.base, "python-passlib",
package_base_id=None, "python-srcinfo",
category_id=None) ],
make_depends=["python-pip"],
opt_depends=[
"breezy",
"darcs",
"mercurial",
"python-aioauth-client",
"python-aiohttp",
"python-aiohttp-debugtoolbar",
"python-aiohttp-jinja2",
"python-aiohttp-security",
"python-aiohttp-session",
"python-boto3",
"python-cryptography",
"python-jinja",
"rsync",
"subversion",
],
conflicts=[],
provides=[],
license=["GPL3"],
keywords=[],
)
@pytest.fixture @pytest.fixture
@ -103,7 +132,7 @@ def package_ahriman(package_description_ahriman: PackageDescription) -> Package:
packages = {"ahriman": package_description_ahriman} packages = {"ahriman": package_description_ahriman}
return Package( return Package(
base="ahriman", base="ahriman",
version="0.12.1-1", version="1.7.0-1",
aur_url="https://aur.archlinux.org", aur_url="https://aur.archlinux.org",
packages=packages) packages=packages)
@ -139,9 +168,16 @@ def package_description_ahriman() -> PackageDescription:
architecture="x86_64", architecture="x86_64",
archive_size=4200, archive_size=4200,
build_date=42, build_date=42,
depends=["devtools", "git", "pyalpm", "python-aur", "python-srcinfo"], depends=[
"devtools",
"git",
"pyalpm",
"python-aur",
"python-passlib",
"python-srcinfo",
],
description="ArcH Linux ReposItory MANager", description="ArcH Linux ReposItory MANager",
filename="ahriman-0.12.1-1-any.pkg.tar.zst", filename="ahriman-1.7.0-1-any.pkg.tar.zst",
groups=[], groups=[],
installed_size=4200000, installed_size=4200000,
licenses=["GPL3"], licenses=["GPL3"],

View File

@ -0,0 +1,12 @@
import pytest
from ahriman.core.alpm.aur import AUR
@pytest.fixture
def aur() -> AUR:
"""
aur helper fixture
:return: aur helper instance
"""
return AUR()

View File

@ -0,0 +1,173 @@
import json
import pytest
import requests
from pathlib import Path
from pytest_mock import MockerFixture
from unittest import mock
from unittest.mock import MagicMock
from ahriman.core.alpm.aur import AUR
from ahriman.core.exceptions import InvalidPackageInfo
from ahriman.models.aur_package import AURPackage
def _get_response(resource_path_root: Path) -> str:
"""
load response from resource file
:param resource_path_root: path to resource root
:return: response text
"""
return (resource_path_root / "models" / "package_ahriman_aur").read_text()
def test_info(mocker: MockerFixture) -> None:
"""
must call info method
"""
info_mock = mocker.patch("ahriman.core.alpm.aur.AUR.package_info")
AUR.info("ahriman")
info_mock.assert_called_once_with("ahriman")
def test_multisearch(aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must search in AUR with multiple words
"""
terms = ["ahriman", "is", "cool"]
search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.search", return_value=[aur_package_ahriman])
assert AUR.multisearch(*terms) == [aur_package_ahriman]
search_mock.assert_has_calls([mock.call("ahriman"), mock.call("cool")])
def test_multisearch_empty(mocker: MockerFixture) -> None:
"""
must return empty list if no long terms supplied
"""
terms = ["it", "is"]
search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.search")
assert AUR.multisearch(*terms) == []
search_mock.assert_not_called()
def test_multisearch_single(aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must search in AUR with one word
"""
search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.search", return_value=[aur_package_ahriman])
assert AUR.multisearch("ahriman") == [aur_package_ahriman]
search_mock.assert_called_once_with("ahriman")
def test_search(mocker: MockerFixture) -> None:
"""
must call search method
"""
search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.package_search")
AUR.search("ahriman")
search_mock.assert_called_once_with("ahriman")
def test_parse_response(aur_package_ahriman: AURPackage, resource_path_root: Path) -> None:
"""
must parse success response
"""
response = _get_response(resource_path_root)
assert AUR.parse_response(json.loads(response)) == [aur_package_ahriman]
def test_parse_response_error(resource_path_root: Path) -> None:
"""
must raise exception on invalid response
"""
response = (resource_path_root / "models" / "aur_error").read_text()
with pytest.raises(InvalidPackageInfo, match="Incorrect request type specified."):
AUR.parse_response(json.loads(response))
def test_parse_response_unknown_error(resource_path_root: Path) -> None:
"""
must raise exception on invalid response with empty error message
"""
with pytest.raises(InvalidPackageInfo, match="Unknown API error"):
AUR.parse_response({"type": "error"})
def test_make_request(aur: AUR, aur_package_ahriman: AURPackage,
mocker: MockerFixture, resource_path_root: Path) -> None:
"""
must perform request to AUR
"""
response_mock = MagicMock()
response_mock.json.return_value = json.loads(_get_response(resource_path_root))
request_mock = mocker.patch("requests.get", return_value=response_mock)
assert aur.make_request("info", "ahriman") == [aur_package_ahriman]
request_mock.assert_called_once_with(
"https://aur.archlinux.org/rpc", params={"v": "5", "type": "info", "arg": ["ahriman"]})
def test_make_request_multi_arg(aur: AUR, aur_package_ahriman: AURPackage,
mocker: MockerFixture, resource_path_root: Path) -> None:
"""
must perform request to AUR with multiple args
"""
response_mock = MagicMock()
response_mock.json.return_value = json.loads(_get_response(resource_path_root))
request_mock = mocker.patch("requests.get", return_value=response_mock)
assert aur.make_request("search", "ahriman", "is", "cool") == [aur_package_ahriman]
request_mock.assert_called_once_with(
"https://aur.archlinux.org/rpc", params={"v": "5", "type": "search", "arg[]": ["ahriman", "is", "cool"]})
def test_make_request_with_kwargs(aur: AUR, aur_package_ahriman: AURPackage,
mocker: MockerFixture, resource_path_root: Path) -> None:
"""
must perform request to AUR with named parameters
"""
response_mock = MagicMock()
response_mock.json.return_value = json.loads(_get_response(resource_path_root))
request_mock = mocker.patch("requests.get", return_value=response_mock)
assert aur.make_request("search", "ahriman", by="name") == [aur_package_ahriman]
request_mock.assert_called_once_with(
"https://aur.archlinux.org/rpc", params={"v": "5", "type": "search", "arg": ["ahriman"], "by": "name"})
def test_make_request_failed(aur: AUR, mocker: MockerFixture) -> None:
"""
must reraise generic exception
"""
mocker.patch("requests.get", side_effect=Exception())
with pytest.raises(Exception):
aur.make_request("info", "ahriman")
def test_make_request_failed_http_error(aur: AUR, mocker: MockerFixture) -> None:
"""
must reraise http exception
"""
mocker.patch("requests.get", side_effect=requests.exceptions.HTTPError())
with pytest.raises(requests.exceptions.HTTPError):
aur.make_request("info", "ahriman")
def test_package_info(aur: AUR, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must make request for info
"""
request_mock = mocker.patch("ahriman.core.alpm.aur.AUR.make_request", return_value=[aur_package_ahriman])
assert aur.package_info(aur_package_ahriman.name) == aur_package_ahriman
request_mock.assert_called_once_with("info", aur_package_ahriman.name)
def test_package_search(aur: AUR, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must make request for search
"""
request_mock = mocker.patch("ahriman.core.alpm.aur.AUR.make_request", return_value=[aur_package_ahriman])
assert aur.package_search(aur_package_ahriman.name, by="name") == [aur_package_ahriman]
request_mock.assert_called_once_with("search", aur_package_ahriman.name, by="name")

View File

@ -1,4 +1,3 @@
import aur
import datetime import datetime
import logging import logging
import pytest import pytest
@ -6,45 +5,12 @@ import subprocess
from pathlib import Path from pathlib import Path
from pytest_mock import MockerFixture from pytest_mock import MockerFixture
from unittest import mock
from ahriman.core.exceptions import InvalidOption, UnsafeRun from ahriman.core.exceptions import InvalidOption, UnsafeRun
from ahriman.core.util import aur_search, check_output, check_user, filter_json, package_like, pretty_datetime, \ from ahriman.core.util import check_output, check_user, filter_json, package_like, pretty_datetime, pretty_size, walk
pretty_size, walk
from ahriman.models.package import Package from ahriman.models.package import Package
def test_aur_search(aur_package_ahriman: aur.Package, mocker: MockerFixture) -> None:
"""
must search in AUR with multiple words
"""
terms = ["ahriman", "is", "cool"]
search_mock = mocker.patch("aur.search", return_value=[aur_package_ahriman])
assert aur_search(*terms) == [aur_package_ahriman]
search_mock.assert_has_calls([mock.call("ahriman"), mock.call("cool")])
def test_aur_search_empty(mocker: MockerFixture) -> None:
"""
must return empty list if no long terms supplied
"""
terms = ["it", "is"]
search_mock = mocker.patch("aur.search")
assert aur_search(*terms) == []
search_mock.assert_not_called()
def test_aur_search_single(aur_package_ahriman: aur.Package, mocker: MockerFixture) -> None:
"""
must search in AUR with one word
"""
search_mock = mocker.patch("aur.search", return_value=[aur_package_ahriman])
assert aur_search("ahriman") == [aur_package_ahriman]
search_mock.assert_called_once_with("ahriman")
def test_check_output(mocker: MockerFixture) -> None: def test_check_output(mocker: MockerFixture) -> None:
""" """
must run command and log result must run command and log result
@ -127,7 +93,7 @@ def test_filter_json(package_ahriman: Package) -> None:
def test_filter_json_empty_value(package_ahriman: Package) -> None: def test_filter_json_empty_value(package_ahriman: Package) -> None:
""" """
must return empty values from object must filter empty values from object
""" """
probe = package_ahriman.view() probe = package_ahriman.view()
probe["base"] = None probe["base"] = None
@ -238,8 +204,10 @@ def test_walk(resource_path_root: Path) -> None:
expected = sorted([ expected = sorted([
resource_path_root / "core/ahriman.ini", resource_path_root / "core/ahriman.ini",
resource_path_root / "core/logging.ini", resource_path_root / "core/logging.ini",
resource_path_root / "models/aur_error",
resource_path_root / "models/big_file_checksum", resource_path_root / "models/big_file_checksum",
resource_path_root / "models/empty_file_checksum", resource_path_root / "models/empty_file_checksum",
resource_path_root / "models/package_ahriman_aur",
resource_path_root / "models/package_ahriman_srcinfo", resource_path_root / "models/package_ahriman_srcinfo",
resource_path_root / "models/package_tpacpi-bat-git_srcinfo", resource_path_root / "models/package_tpacpi-bat-git_srcinfo",
resource_path_root / "models/package_yay_srcinfo", resource_path_root / "models/package_yay_srcinfo",

View File

@ -22,7 +22,7 @@ def test_calculate_hash_small(resource_path_root: Path) -> None:
must calculate checksum for path which is single chunk must calculate checksum for path which is single chunk
""" """
path = resource_path_root / "models" / "package_ahriman_srcinfo" path = resource_path_root / "models" / "package_ahriman_srcinfo"
assert HttpUpload.calculate_hash(path) == "a55f82198e56061295d405aeb58f4062" assert HttpUpload.calculate_hash(path) == "c0aaf6ebf95ca9206dc8ba1d8ff10af3"
def test_get_body_get_hashes() -> None: def test_get_body_get_hashes() -> None:

View File

@ -31,7 +31,7 @@ def test_calculate_etag_small(resource_path_root: Path) -> None:
must calculate checksum for path which is single chunk must calculate checksum for path which is single chunk
""" """
path = resource_path_root / "models" / "package_ahriman_srcinfo" path = resource_path_root / "models" / "package_ahriman_srcinfo"
assert S3.calculate_etag(path, _chunk_size) == "a55f82198e56061295d405aeb58f4062" assert S3.calculate_etag(path, _chunk_size) == "c0aaf6ebf95ca9206dc8ba1d8ff10af3"
def test_files_remove(s3_remote_objects: List[Any]) -> None: def test_files_remove(s3_remote_objects: List[Any]) -> None:

View File

@ -0,0 +1,47 @@
import datetime
import json
from dataclasses import asdict, fields
from pathlib import Path
from pytest_mock import MockerFixture
from typing import Any, Dict
from ahriman.models.aur_package import AURPackage
def _get_data(resource_path_root: Path) -> Dict[str, Any]:
"""
load package description from resource file
:param resource_path_root: path to resource root
:return: json descriptor
"""
response = (resource_path_root / "models" / "package_ahriman_aur").read_text()
return json.loads(response)["results"][0]
def test_from_json(aur_package_ahriman: AURPackage, resource_path_root: Path) -> None:
"""
must load package from json
"""
model = _get_data(resource_path_root)
assert AURPackage.from_json(model) == aur_package_ahriman
def test_from_json_2(aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must load the same package from json
"""
mocker.patch("ahriman.models.aur_package.AURPackage.convert", side_effect=lambda v: v)
assert AURPackage.from_json(asdict(aur_package_ahriman)) == aur_package_ahriman
def test_convert(aur_package_ahriman: AURPackage, resource_path_root: Path) -> None:
"""
must convert fields to snakecase and also apply converters
"""
model = _get_data(resource_path_root)
converted = AURPackage.convert(model)
known_fields = [pair.name for pair in fields(AURPackage)]
assert all(field in known_fields for field in converted)
assert isinstance(converted.get("first_submitted"), datetime.datetime)
assert isinstance(converted.get("last_modified"), datetime.datetime)

View File

@ -2,9 +2,10 @@ import pytest
from pathlib import Path from pathlib import Path
from pytest_mock import MockerFixture from pytest_mock import MockerFixture
from unittest.mock import MagicMock, PropertyMock from unittest.mock import MagicMock
from ahriman.core.exceptions import InvalidPackageInfo from ahriman.core.exceptions import InvalidPackageInfo
from ahriman.models.aur_package import AURPackage
from ahriman.models.package import Package from ahriman.models.package import Package
from ahriman.models.package_source import PackageSource from ahriman.models.package_source import PackageSource
from ahriman.models.repository_paths import RepositoryPaths from ahriman.models.repository_paths import RepositoryPaths
@ -96,15 +97,11 @@ def test_from_archive(package_ahriman: Package, pyalpm_handle: MagicMock, mocker
assert Package.from_archive(Path("path"), pyalpm_handle, package_ahriman.aur_url) == package_ahriman assert Package.from_archive(Path("path"), pyalpm_handle, package_ahriman.aur_url) == package_ahriman
def test_from_aur(package_ahriman: Package, mocker: MockerFixture) -> None: def test_from_aur(package_ahriman: Package, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
""" """
must construct package from aur must construct package from aur
""" """
mock = MagicMock() mocker.patch("ahriman.core.alpm.aur.AUR.info", return_value=aur_package_ahriman)
type(mock).name = PropertyMock(return_value=package_ahriman.base)
type(mock).package_base = PropertyMock(return_value=package_ahriman.base)
type(mock).version = PropertyMock(return_value=package_ahriman.version)
mocker.patch("aur.info", return_value=mock)
package = Package.from_aur(package_ahriman.base, package_ahriman.aur_url) package = Package.from_aur(package_ahriman.base, package_ahriman.aur_url)
assert package_ahriman.base == package.base assert package_ahriman.base == package.base

View File

@ -1,9 +1,9 @@
import aur
import pytest import pytest
from aiohttp.test_utils import TestClient from aiohttp.test_utils import TestClient
from pytest_mock import MockerFixture from pytest_mock import MockerFixture
from ahriman.models.aur_package import AURPackage
from ahriman.models.user_access import UserAccess from ahriman.models.user_access import UserAccess
from ahriman.web.views.service.search import SearchView from ahriman.web.views.service.search import SearchView
@ -17,11 +17,11 @@ async def test_get_permission() -> None:
assert await SearchView.get_permission(request) == UserAccess.Read assert await SearchView.get_permission(request) == UserAccess.Read
async def test_get(client: TestClient, aur_package_ahriman: aur.Package, mocker: MockerFixture) -> None: async def test_get(client: TestClient, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
""" """
must call get request correctly must call get request correctly
""" """
mocker.patch("ahriman.web.views.service.search.aur_search", return_value=[aur_package_ahriman]) mocker.patch("ahriman.core.alpm.aur.AUR.multisearch", return_value=[aur_package_ahriman])
response = await client.get("/service-api/v1/search", params={"for": "ahriman"}) response = await client.get("/service-api/v1/search", params={"for": "ahriman"})
assert response.ok assert response.ok
@ -33,7 +33,7 @@ async def test_get_exception(client: TestClient, mocker: MockerFixture) -> None:
""" """
must raise 400 on empty search string must raise 400 on empty search string
""" """
search_mock = mocker.patch("ahriman.web.views.service.search.aur_search", return_value=[]) search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.multisearch", return_value=[])
response = await client.get("/service-api/v1/search") response = await client.get("/service-api/v1/search")
assert response.status == 404 assert response.status == 404
@ -44,7 +44,7 @@ async def test_get_join(client: TestClient, mocker: MockerFixture) -> None:
""" """
must join search args with space must join search args with space
""" """
search_mock = mocker.patch("ahriman.web.views.service.search.aur_search") search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.multisearch")
response = await client.get("/service-api/v1/search", params=[("for", "ahriman"), ("for", "maybe")]) response = await client.get("/service-api/v1/search", params=[("for", "ahriman"), ("for", "maybe")])
assert response.ok assert response.ok

View File

@ -1,4 +1,5 @@
import pytest import pytest
from aiohttp.test_utils import TestClient from aiohttp.test_utils import TestClient
from pytest_mock import MockerFixture from pytest_mock import MockerFixture
from unittest.mock import MagicMock from unittest.mock import MagicMock
@ -31,7 +32,7 @@ async def test_get_redirect_to_oauth(client_with_auth: TestClient) -> None:
must redirect to OAuth service provider in case if no code is supplied must redirect to OAuth service provider in case if no code is supplied
""" """
oauth = client_with_auth.app["validator"] = MagicMock(spec=OAuth) oauth = client_with_auth.app["validator"] = MagicMock(spec=OAuth)
oauth.get_oauth_url.return_value = "https://example.com" oauth.get_oauth_url.return_value = "https://httpbin.org"
get_response = await client_with_auth.get("/user-api/v1/login") get_response = await client_with_auth.get("/user-api/v1/login")
assert get_response.ok assert get_response.ok
@ -43,7 +44,7 @@ async def test_get_redirect_to_oauth_empty_code(client_with_auth: TestClient) ->
must redirect to OAuth service provider in case if empty code is supplied must redirect to OAuth service provider in case if empty code is supplied
""" """
oauth = client_with_auth.app["validator"] = MagicMock(spec=OAuth) oauth = client_with_auth.app["validator"] = MagicMock(spec=OAuth)
oauth.get_oauth_url.return_value = "https://example.com" oauth.get_oauth_url.return_value = "https://httpbin.org"
get_response = await client_with_auth.get("/user-api/v1/login", params={"code": ""}) get_response = await client_with_auth.get("/user-api/v1/login", params={"code": ""})
assert get_response.ok assert get_response.ok

View File

@ -0,0 +1,7 @@
{
"error": "Incorrect request type specified.",
"resultcount": 0,
"results": [],
"type": "error",
"version": 5
}

View File

@ -0,0 +1,54 @@
{
"resultcount": 1,
"results": [
{
"Depends": [
"devtools",
"git",
"pyalpm",
"python-aur",
"python-passlib",
"python-srcinfo"
],
"Description": "ArcH Linux ReposItory MANager",
"FirstSubmitted": 1618008285,
"ID": 1009791,
"Keywords": [],
"LastModified": 1640473871,
"License": [
"GPL3"
],
"Maintainer": "arcanis",
"MakeDepends": [
"python-pip"
],
"Name": "ahriman",
"NumVotes": 0,
"OptDepends": [
"breezy",
"darcs",
"mercurial",
"python-aioauth-client",
"python-aiohttp",
"python-aiohttp-debugtoolbar",
"python-aiohttp-jinja2",
"python-aiohttp-security",
"python-aiohttp-session",
"python-boto3",
"python-cryptography",
"python-jinja",
"rsync",
"subversion"
],
"OutOfDate": null,
"PackageBase": "ahriman",
"PackageBaseID": 165427,
"Popularity": 0,
"URL": "https://github.com/arcan1s/ahriman",
"URLPath": "/cgit/aur.git/snapshot/ahriman.tar.gz",
"Version": "1.7.0-1"
}
],
"type": "multiinfo",
"version": 5
}

View File

@ -1,6 +1,6 @@
pkgbase = ahriman pkgbase = ahriman
pkgdesc = ArcH Linux ReposItory MANager pkgdesc = ArcH Linux ReposItory MANager
pkgver = 0.12.1 pkgver = 1.7.0
pkgrel = 1 pkgrel = 1
url = https://github.com/arcan1s/ahriman url = https://github.com/arcan1s/ahriman
arch = any arch = any
@ -10,6 +10,7 @@ pkgbase = ahriman
depends = git depends = git
depends = pyalpm depends = pyalpm
depends = python-aur depends = python-aur
depends = python-passlib
depends = python-srcinfo depends = python-srcinfo
optdepends = aws-cli: sync to s3 optdepends = aws-cli: sync to s3
optdepends = breezy: -bzr packages support optdepends = breezy: -bzr packages support
@ -24,7 +25,7 @@ pkgbase = ahriman
optdepends = subversion: -svn packages support optdepends = subversion: -svn packages support
backup = etc/ahriman.ini backup = etc/ahriman.ini
backup = etc/ahriman.ini.d/logging.ini backup = etc/ahriman.ini.d/logging.ini
source = https://github.com/arcan1s/ahriman/releases/download/0.12.1/ahriman-0.12.1-src.tar.xz source = https://github.com/arcan1s/ahriman/releases/download/1.7.0/ahriman-1.7.0-src.tar.xz
source = ahriman.sysusers source = ahriman.sysusers
source = ahriman.tmpfiles source = ahriman.tmpfiles
sha512sums = 8acc57f937d587ca665c29092cadddbaf3ba0b80e870b80d1551e283aba8f21306f9030a26fec8c71ab5863316f5f5f061b7ddc63cdff9e6d5a885f28ef1893d sha512sums = 8acc57f937d587ca665c29092cadddbaf3ba0b80e870b80d1551e283aba8f21306f9030a26fec8c71ab5863316f5f5f061b7ddc63cdff9e6d5a885f28ef1893d