mirror of
https://github.com/arcan1s/ahriman.git
synced 2026-03-13 05:23:39 +00:00
feat: support archive listing
This commit is contained in:
@@ -12,6 +12,14 @@ ahriman.application.handlers.add module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.application.handlers.archives module
|
||||||
|
--------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.application.handlers.archives
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.application.handlers.backup module
|
ahriman.application.handlers.backup module
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,14 @@ ahriman.web.views.v1.packages package
|
|||||||
Submodules
|
Submodules
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
ahriman.web.views.v1.packages.archives module
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.packages.archives
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.web.views.v1.packages.changes module
|
ahriman.web.views.v1.packages.changes module
|
||||||
--------------------------------------------
|
--------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -154,13 +154,13 @@ class Application(ApplicationPackages, ApplicationRepository):
|
|||||||
for package_name, packager in missing.items():
|
for package_name, packager in missing.items():
|
||||||
if (source_dir := self.repository.paths.cache_for(package_name)).is_dir():
|
if (source_dir := self.repository.paths.cache_for(package_name)).is_dir():
|
||||||
# there is local cache, load package from it
|
# there is local cache, load package from it
|
||||||
leaf = Package.from_build(source_dir, self.repository.architecture, packager)
|
leaf = Package.from_build(source_dir, self.repository.repository_id.architecture, packager)
|
||||||
else:
|
else:
|
||||||
leaf = Package.from_aur(package_name, packager, include_provides=True)
|
leaf = Package.from_aur(package_name, packager, include_provides=True)
|
||||||
portion[leaf.base] = leaf
|
portion[leaf.base] = leaf
|
||||||
|
|
||||||
# register package in the database
|
# register package in the database
|
||||||
self.repository.reporter.set_unknown(leaf)
|
self.reporter.set_unknown(leaf)
|
||||||
|
|
||||||
return portion
|
return portion
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class ApplicationRepository(ApplicationProperties):
|
|||||||
continue # skip check in case if we can't calculate diff
|
continue # skip check in case if we can't calculate diff
|
||||||
|
|
||||||
if (changes := self.repository.package_changes(package, last_commit_sha)) is not None:
|
if (changes := self.repository.package_changes(package, last_commit_sha)) is not None:
|
||||||
self.repository.reporter.package_changes_update(package.base, changes)
|
self.reporter.package_changes_update(package.base, changes)
|
||||||
|
|
||||||
def clean(self, *, cache: bool, chroot: bool, manual: bool, packages: bool, pacman: bool) -> None:
|
def clean(self, *, cache: bool, chroot: bool, manual: bool, packages: bool, pacman: bool) -> None:
|
||||||
"""
|
"""
|
||||||
|
|||||||
81
src/ahriman/application/handlers/archives.py
Normal file
81
src/ahriman/application/handlers/archives.py
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021-2026 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/>.
|
||||||
|
#
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
from ahriman.application.application import Application
|
||||||
|
from ahriman.application.handlers.handler import Handler, SubParserAction
|
||||||
|
from ahriman.core.configuration import Configuration
|
||||||
|
from ahriman.core.formatters import PackagePrinter
|
||||||
|
from ahriman.models.action import Action
|
||||||
|
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||||
|
from ahriman.models.repository_id import RepositoryId
|
||||||
|
|
||||||
|
|
||||||
|
class Archives(Handler):
|
||||||
|
"""
|
||||||
|
package archives handler
|
||||||
|
"""
|
||||||
|
|
||||||
|
ALLOW_MULTI_ARCHITECTURE_RUN = False # conflicting io
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,
|
||||||
|
report: bool) -> None:
|
||||||
|
"""
|
||||||
|
callback for command line
|
||||||
|
|
||||||
|
Args:
|
||||||
|
args(argparse.Namespace): command line args
|
||||||
|
repository_id(RepositoryId): repository unique identifier
|
||||||
|
configuration(Configuration): configuration instance
|
||||||
|
report(bool): force enable or disable reporting
|
||||||
|
"""
|
||||||
|
application = Application(repository_id, configuration, report=True)
|
||||||
|
|
||||||
|
match args.action:
|
||||||
|
case Action.List:
|
||||||
|
archives = application.repository.package_archives(args.package)
|
||||||
|
for package in archives:
|
||||||
|
PackagePrinter(package, BuildStatus(BuildStatusEnum.Success))(verbose=args.info)
|
||||||
|
|
||||||
|
Archives.check_status(args.exit_code, bool(archives))
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _set_package_archives_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||||
|
"""
|
||||||
|
add parser for package archives subcommand
|
||||||
|
|
||||||
|
Args:
|
||||||
|
root(SubParserAction): subparsers for the commands
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
argparse.ArgumentParser: created argument parser
|
||||||
|
"""
|
||||||
|
parser = root.add_parser("package-archives", help="list package archive versions",
|
||||||
|
description="list available archive versions for the package")
|
||||||
|
parser.add_argument("package", help="package base")
|
||||||
|
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty",
|
||||||
|
action="store_true")
|
||||||
|
parser.add_argument("--info", help="show additional package information",
|
||||||
|
action=argparse.BooleanOptionalAction, default=False)
|
||||||
|
parser.set_defaults(action=Action.List, lock=None, quiet=True, report=False, unsafe=True)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
arguments = [_set_package_archives_parser]
|
||||||
@@ -47,8 +47,7 @@ class Change(Handler):
|
|||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
report(bool): force enable or disable reporting
|
report(bool): force enable or disable reporting
|
||||||
"""
|
"""
|
||||||
application = Application(repository_id, configuration, report=True)
|
client = Application(repository_id, configuration, report=True).reporter
|
||||||
client = application.repository.reporter
|
|
||||||
|
|
||||||
match args.action:
|
match args.action:
|
||||||
case Action.List:
|
case Action.List:
|
||||||
|
|||||||
@@ -48,8 +48,7 @@ class Pkgbuild(Handler):
|
|||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
report(bool): force enable or disable reporting
|
report(bool): force enable or disable reporting
|
||||||
"""
|
"""
|
||||||
application = Application(repository_id, configuration, report=True)
|
client = Application(repository_id, configuration, report=True).reporter
|
||||||
client = application.repository.reporter
|
|
||||||
|
|
||||||
match args.action:
|
match args.action:
|
||||||
case Action.List:
|
case Action.List:
|
||||||
|
|||||||
@@ -44,8 +44,7 @@ class Reload(Handler):
|
|||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
report(bool): force enable or disable reporting
|
report(bool): force enable or disable reporting
|
||||||
"""
|
"""
|
||||||
application = Application(repository_id, configuration, report=True)
|
client = Application(repository_id, configuration, report=True).reporter
|
||||||
client = application.repository.reporter
|
|
||||||
client.configuration_reload()
|
client.configuration_reload()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class Status(Handler):
|
|||||||
report(bool): force enable or disable reporting
|
report(bool): force enable or disable reporting
|
||||||
"""
|
"""
|
||||||
# we are using reporter here
|
# we are using reporter here
|
||||||
client = Application(repository_id, configuration, report=True).repository.reporter
|
client = Application(repository_id, configuration, report=True).reporter
|
||||||
if args.ahriman:
|
if args.ahriman:
|
||||||
service_status = client.status_get()
|
service_status = client.status_get()
|
||||||
StatusPrinter(service_status.status)(verbose=args.info)
|
StatusPrinter(service_status.status)(verbose=args.info)
|
||||||
|
|||||||
@@ -47,8 +47,7 @@ class StatusUpdate(Handler):
|
|||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
report(bool): force enable or disable reporting
|
report(bool): force enable or disable reporting
|
||||||
"""
|
"""
|
||||||
application = Application(repository_id, configuration, report=True)
|
client = Application(repository_id, configuration, report=True).reporter
|
||||||
client = application.repository.reporter
|
|
||||||
|
|
||||||
match args.action:
|
match args.action:
|
||||||
case Action.Update if args.package:
|
case Action.Update if args.package:
|
||||||
|
|||||||
@@ -61,12 +61,11 @@ class Executor(PackageInfo, Cleaner):
|
|||||||
if built.version != package.version:
|
if built.version != package.version:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
packages = built.packages.values()
|
|
||||||
# all packages must be either any or same architecture
|
# all packages must be either any or same architecture
|
||||||
if not all(single.architecture in ("any", self.architecture) for single in packages):
|
if not built.supports_architecture(self.repository_id.architecture):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
return list_flatmap(packages, lambda single: archive.glob(f"{single.filename}*"))
|
return list_flatmap(built.packages.values(), lambda single: archive.glob(f"{single.filename}*"))
|
||||||
|
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -102,11 +101,11 @@ class Executor(PackageInfo, Cleaner):
|
|||||||
"""
|
"""
|
||||||
self.reporter.set_building(package.base)
|
self.reporter.set_building(package.base)
|
||||||
|
|
||||||
task = Task(package, self.configuration, self.architecture, self.paths)
|
task = Task(package, self.configuration, self.repository_id.architecture, self.paths)
|
||||||
patches = self.reporter.package_patches_get(package.base, None)
|
patches = self.reporter.package_patches_get(package.base, None)
|
||||||
commit_sha = task.init(path, patches, local_version)
|
commit_sha = task.init(path, patches, local_version)
|
||||||
|
|
||||||
loaded_package = Package.from_build(path, self.architecture, None)
|
loaded_package = Package.from_build(path, self.repository_id.architecture, None)
|
||||||
if prebuilt := list(self._archive_lookup(loaded_package)):
|
if prebuilt := list(self._archive_lookup(loaded_package)):
|
||||||
self.logger.info("using prebuilt packages for %s-%s", loaded_package.base, loaded_package.version)
|
self.logger.info("using prebuilt packages for %s-%s", loaded_package.base, loaded_package.version)
|
||||||
built = []
|
built = []
|
||||||
@@ -218,7 +217,7 @@ class Executor(PackageInfo, Cleaner):
|
|||||||
except Exception:
|
except Exception:
|
||||||
self.reporter.set_failed(single.base)
|
self.reporter.set_failed(single.base)
|
||||||
result.add_failed(single)
|
result.add_failed(single)
|
||||||
self.logger.exception("%s (%s) build exception", single.base, self.architecture)
|
self.logger.exception("%s (%s) build exception", single.base, self.repository_id.architecture)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ from ahriman.core.status import Client
|
|||||||
from ahriman.core.utils import package_like
|
from ahriman.core.utils import package_like
|
||||||
from ahriman.models.changes import Changes
|
from ahriman.models.changes import Changes
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
|
from ahriman.models.repository_id import RepositoryId
|
||||||
|
|
||||||
|
|
||||||
class PackageInfo(LazyLogging):
|
class PackageInfo(LazyLogging):
|
||||||
@@ -43,11 +44,13 @@ class PackageInfo(LazyLogging):
|
|||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
pacman(Pacman): alpm wrapper instance
|
pacman(Pacman): alpm wrapper instance
|
||||||
reporter(Client): build status reporter instance
|
reporter(Client): build status reporter instance
|
||||||
|
repository_id(RepositoryId): repository unique identifier
|
||||||
"""
|
"""
|
||||||
|
|
||||||
configuration: Configuration
|
configuration: Configuration
|
||||||
pacman: Pacman
|
pacman: Pacman
|
||||||
reporter: Client
|
reporter: Client
|
||||||
|
repository_id: RepositoryId
|
||||||
|
|
||||||
def full_depends(self, package: Package, packages: Iterable[Package]) -> list[str]:
|
def full_depends(self, package: Package, packages: Iterable[Package]) -> list[str]:
|
||||||
"""
|
"""
|
||||||
@@ -133,6 +136,8 @@ class PackageInfo(LazyLogging):
|
|||||||
# we can't use here load_archives, because it ignores versions
|
# we can't use here load_archives, because it ignores versions
|
||||||
for full_path in filter(package_like, paths.archive_for(package_base).iterdir()):
|
for full_path in filter(package_like, paths.archive_for(package_base).iterdir()):
|
||||||
local = Package.from_archive(full_path)
|
local = Package.from_archive(full_path)
|
||||||
|
if not local.supports_architecture(self.repository_id.architecture):
|
||||||
|
continue
|
||||||
packages.setdefault((local.base, local.version), local).packages.update(local.packages)
|
packages.setdefault((local.base, local.version), local).packages.update(local.packages)
|
||||||
|
|
||||||
comparator: Callable[[Package, Package], int] = lambda left, right: left.vercmp(right.version)
|
comparator: Callable[[Package, Package], int] = lambda left, right: left.vercmp(right.version)
|
||||||
|
|||||||
@@ -72,32 +72,12 @@ class RepositoryProperties(EventLogger, LazyLogging):
|
|||||||
self.ignore_list = configuration.getlist("build", "ignore_packages", fallback=[])
|
self.ignore_list = configuration.getlist("build", "ignore_packages", fallback=[])
|
||||||
self.pacman = Pacman(repository_id, configuration, refresh_database=refresh_pacman_database)
|
self.pacman = Pacman(repository_id, configuration, refresh_database=refresh_pacman_database)
|
||||||
self.sign = GPG(configuration)
|
self.sign = GPG(configuration)
|
||||||
self.repo = Repo(self.name, self.paths, self.sign.repository_sign_args)
|
self.repo = Repo(self.repository_id.name, self.paths, self.sign.repository_sign_args)
|
||||||
self.reporter = Client.load(repository_id, configuration, database, report=report)
|
self.reporter = Client.load(repository_id, configuration, database, report=report)
|
||||||
self.triggers = TriggerLoader.load(repository_id, configuration)
|
self.triggers = TriggerLoader.load(repository_id, configuration)
|
||||||
|
|
||||||
self.scan_paths = ScanPaths(configuration.getlist("build", "scan_paths", fallback=[]))
|
self.scan_paths = ScanPaths(configuration.getlist("build", "scan_paths", fallback=[]))
|
||||||
|
|
||||||
@property
|
|
||||||
def architecture(self) -> str:
|
|
||||||
"""
|
|
||||||
repository architecture for backward compatibility
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: repository architecture
|
|
||||||
"""
|
|
||||||
return self.repository_id.architecture
|
|
||||||
|
|
||||||
@property
|
|
||||||
def name(self) -> str:
|
|
||||||
"""
|
|
||||||
repository name for backward compatibility
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: repository name
|
|
||||||
"""
|
|
||||||
return self.repository_id.name
|
|
||||||
|
|
||||||
def packager(self, packagers: Packagers, package_base: str) -> User:
|
def packager(self, packagers: Packagers, package_base: str) -> User:
|
||||||
"""
|
"""
|
||||||
extract packager from configuration having username
|
extract packager from configuration having username
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ class UpdateHandler(PackageInfo, Cleaner):
|
|||||||
)
|
)
|
||||||
|
|
||||||
Sources.fetch(cache_dir, source)
|
Sources.fetch(cache_dir, source)
|
||||||
remote = Package.from_build(cache_dir, self.architecture, None)
|
remote = Package.from_build(cache_dir, self.repository_id.architecture, None)
|
||||||
|
|
||||||
local = packages.get(remote.base)
|
local = packages.get(remote.base)
|
||||||
if local is None:
|
if local is None:
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ from typing import Any, Self
|
|||||||
|
|
||||||
from ahriman.core.exceptions import UnknownPackageError
|
from ahriman.core.exceptions import UnknownPackageError
|
||||||
from ahriman.core.log import LazyLogging
|
from ahriman.core.log import LazyLogging
|
||||||
|
from ahriman.core.repository.package_info import PackageInfo
|
||||||
from ahriman.core.status import Client
|
from ahriman.core.status import Client
|
||||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||||
from ahriman.models.changes import Changes
|
from ahriman.models.changes import Changes
|
||||||
@@ -39,15 +40,18 @@ class Watcher(LazyLogging):
|
|||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
client(Client): reporter instance
|
client(Client): reporter instance
|
||||||
|
package_info(PackageInfo): package info instance
|
||||||
status(BuildStatus): daemon status
|
status(BuildStatus): daemon status
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, client: Client) -> None:
|
def __init__(self, client: Client, package_info: PackageInfo) -> None:
|
||||||
"""
|
"""
|
||||||
Args:
|
Args:
|
||||||
client(Client): reporter instance
|
client(Client): reporter instance
|
||||||
|
package_info(PackageInfo): package info instance
|
||||||
"""
|
"""
|
||||||
self.client = client
|
self.client = client
|
||||||
|
self.package_info = package_info
|
||||||
|
|
||||||
self._lock = Lock()
|
self._lock = Lock()
|
||||||
self._known: dict[str, tuple[Package, BuildStatus]] = {}
|
self._known: dict[str, tuple[Package, BuildStatus]] = {}
|
||||||
@@ -80,6 +84,18 @@ class Watcher(LazyLogging):
|
|||||||
|
|
||||||
logs_rotate: Callable[[int], None]
|
logs_rotate: Callable[[int], None]
|
||||||
|
|
||||||
|
def package_archives(self, package_base: str) -> list[Package]:
|
||||||
|
"""
|
||||||
|
get known package archives
|
||||||
|
|
||||||
|
Args:
|
||||||
|
package_base(str): package base
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list[Package]: list of built package for this package base
|
||||||
|
"""
|
||||||
|
return self.package_info.package_archives(package_base)
|
||||||
|
|
||||||
package_changes_get: Callable[[str], Changes]
|
package_changes_get: Callable[[str], Changes]
|
||||||
|
|
||||||
package_changes_update: Callable[[str, Changes], None]
|
package_changes_update: Callable[[str, Changes], None]
|
||||||
|
|||||||
@@ -137,16 +137,6 @@ class Package(LazyLogging):
|
|||||||
"""
|
"""
|
||||||
return list_flatmap(self.packages.values(), lambda package: package.groups)
|
return list_flatmap(self.packages.values(), lambda package: package.groups)
|
||||||
|
|
||||||
@property
|
|
||||||
def is_single_package(self) -> bool:
|
|
||||||
"""
|
|
||||||
is it possible to transform package base to single package or not
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: true in case if this base has only one package with the same name
|
|
||||||
"""
|
|
||||||
return self.base in self.packages and len(self.packages) == 1
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_vcs(self) -> bool:
|
def is_vcs(self) -> bool:
|
||||||
"""
|
"""
|
||||||
@@ -375,9 +365,22 @@ class Package(LazyLogging):
|
|||||||
Returns:
|
Returns:
|
||||||
str: print-friendly string
|
str: print-friendly string
|
||||||
"""
|
"""
|
||||||
details = "" if self.is_single_package else f" ({" ".join(sorted(self.packages.keys()))})"
|
is_single_package = self.base in self.packages and len(self.packages) == 1
|
||||||
|
details = "" if is_single_package else f" ({" ".join(sorted(self.packages.keys()))})"
|
||||||
return f"{self.base}{details}"
|
return f"{self.base}{details}"
|
||||||
|
|
||||||
|
def supports_architecture(self, architecture: str) -> bool:
|
||||||
|
"""
|
||||||
|
helper to check if the package belongs to the specified architecture
|
||||||
|
|
||||||
|
Args:
|
||||||
|
architecture(str): probe repository architecture
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: ``True`` if all packages are same architecture or any
|
||||||
|
"""
|
||||||
|
return all(single.architecture in ("any", architecture) for single in self.packages.values())
|
||||||
|
|
||||||
def vercmp(self, version: str) -> int:
|
def vercmp(self, version: str) -> int:
|
||||||
"""
|
"""
|
||||||
typed wrapper around :func:`pyalpm.vercmp()`
|
typed wrapper around :func:`pyalpm.vercmp()`
|
||||||
|
|||||||
65
src/ahriman/web/views/v1/packages/archives.py
Normal file
65
src/ahriman/web/views/v1/packages/archives.py
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021-2026 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 aiohttp.web import Response
|
||||||
|
from typing import ClassVar
|
||||||
|
|
||||||
|
from ahriman.models.user_access import UserAccess
|
||||||
|
from ahriman.web.apispec.decorators import apidocs
|
||||||
|
from ahriman.web.schemas import PackageNameSchema, PackageSchema, RepositoryIdSchema
|
||||||
|
from ahriman.web.views.base import BaseView
|
||||||
|
from ahriman.web.views.status_view_guard import StatusViewGuard
|
||||||
|
|
||||||
|
|
||||||
|
class Archives(StatusViewGuard, BaseView):
|
||||||
|
"""
|
||||||
|
package archives web view
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||||
|
"""
|
||||||
|
|
||||||
|
GET_PERMISSION: ClassVar[UserAccess] = UserAccess.Reporter
|
||||||
|
ROUTES = ["/api/v1/packages/{package}/archives"]
|
||||||
|
|
||||||
|
@apidocs(
|
||||||
|
tags=["Packages"],
|
||||||
|
summary="Get package archives",
|
||||||
|
description="Retrieve built package archives for the base",
|
||||||
|
permission=GET_PERMISSION,
|
||||||
|
error_404_description="Package base and/or repository are unknown",
|
||||||
|
schema=PackageSchema(many=True),
|
||||||
|
match_schema=PackageNameSchema,
|
||||||
|
query_schema=RepositoryIdSchema,
|
||||||
|
)
|
||||||
|
async def get(self) -> Response:
|
||||||
|
"""
|
||||||
|
get package archives
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response: 200 with package archives on success
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
HTTPNotFound: if no package was found
|
||||||
|
"""
|
||||||
|
package_base = self.request.match_info["package"]
|
||||||
|
|
||||||
|
archives = self.service(package_base=package_base).package_archives(package_base)
|
||||||
|
|
||||||
|
return self.json_response([archive.view() for archive in archives])
|
||||||
@@ -23,12 +23,14 @@ import logging
|
|||||||
import socket
|
import socket
|
||||||
|
|
||||||
from aiohttp.web import Application, normalize_path_middleware, run_app
|
from aiohttp.web import Application, normalize_path_middleware, run_app
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from ahriman.core.auth import Auth
|
from ahriman.core.auth import Auth
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.database import SQLite
|
from ahriman.core.database import SQLite
|
||||||
from ahriman.core.distributed import WorkersCache
|
from ahriman.core.distributed import WorkersCache
|
||||||
from ahriman.core.exceptions import InitializeError
|
from ahriman.core.exceptions import InitializeError
|
||||||
|
from ahriman.core.repository.package_info import PackageInfo
|
||||||
from ahriman.core.spawn import Spawn
|
from ahriman.core.spawn import Spawn
|
||||||
from ahriman.core.status import Client
|
from ahriman.core.status import Client
|
||||||
from ahriman.core.status.watcher import Watcher
|
from ahriman.core.status.watcher import Watcher
|
||||||
@@ -78,6 +80,34 @@ def _create_socket(configuration: Configuration, application: Application) -> so
|
|||||||
return sock
|
return sock
|
||||||
|
|
||||||
|
|
||||||
|
def _create_watcher(path: Path, repository_id: RepositoryId) -> Watcher:
|
||||||
|
"""
|
||||||
|
build watcher for selected repository
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path(Path): path to configuration file
|
||||||
|
repository_id(RepositoryId): repository unique identifier
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Watcher: watcher instance
|
||||||
|
"""
|
||||||
|
logging.getLogger(__name__).info("load repository %s", repository_id)
|
||||||
|
# load settings explicitly for architecture if any
|
||||||
|
configuration = Configuration.from_path(path, repository_id)
|
||||||
|
|
||||||
|
# load database instance, because it holds identifier
|
||||||
|
database = SQLite.load(configuration)
|
||||||
|
# explicitly load local client
|
||||||
|
client = Client.load(repository_id, configuration, database, report=False)
|
||||||
|
|
||||||
|
# load package info wrapper
|
||||||
|
package_info = PackageInfo()
|
||||||
|
package_info.configuration = configuration
|
||||||
|
package_info.repository_id = repository_id
|
||||||
|
|
||||||
|
return Watcher(client, package_info)
|
||||||
|
|
||||||
|
|
||||||
async def _on_shutdown(application: Application) -> None:
|
async def _on_shutdown(application: Application) -> None:
|
||||||
"""
|
"""
|
||||||
web application shutdown handler
|
web application shutdown handler
|
||||||
@@ -168,18 +198,11 @@ def setup_server(configuration: Configuration, spawner: Spawn, repositories: lis
|
|||||||
# package cache
|
# package cache
|
||||||
if not repositories:
|
if not repositories:
|
||||||
raise InitializeError("No repositories configured, exiting")
|
raise InitializeError("No repositories configured, exiting")
|
||||||
watchers: dict[RepositoryId, Watcher] = {}
|
|
||||||
configuration_path, _ = configuration.check_loaded()
|
configuration_path, _ = configuration.check_loaded()
|
||||||
for repository_id in repositories:
|
application[WatcherKey] = {
|
||||||
application.logger.info("load repository %s", repository_id)
|
repository_id: _create_watcher(configuration_path, repository_id)
|
||||||
# load settings explicitly for architecture if any
|
for repository_id in repositories
|
||||||
repository_configuration = Configuration.from_path(configuration_path, repository_id)
|
}
|
||||||
# load database instance, because it holds identifier
|
|
||||||
database = SQLite.load(repository_configuration)
|
|
||||||
# explicitly load local client
|
|
||||||
client = Client.load(repository_id, repository_configuration, database, report=False)
|
|
||||||
watchers[repository_id] = Watcher(client)
|
|
||||||
application[WatcherKey] = watchers
|
|
||||||
# workers cache
|
# workers cache
|
||||||
application[WorkersKey] = WorkersCache(configuration)
|
application[WorkersKey] = WorkersCache(configuration)
|
||||||
# process spawner
|
# process spawner
|
||||||
|
|||||||
84
tests/ahriman/application/handlers/test_handler_archives.py
Normal file
84
tests/ahriman/application/handlers/test_handler_archives.py
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import argparse
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
|
from ahriman.application.handlers.archives import Archives
|
||||||
|
from ahriman.core.configuration import Configuration
|
||||||
|
from ahriman.core.database import SQLite
|
||||||
|
from ahriman.core.repository import Repository
|
||||||
|
from ahriman.models.action import Action
|
||||||
|
from ahriman.models.package import Package
|
||||||
|
|
||||||
|
|
||||||
|
def _default_args(args: argparse.Namespace) -> argparse.Namespace:
|
||||||
|
"""
|
||||||
|
default arguments for these test cases
|
||||||
|
|
||||||
|
Args:
|
||||||
|
args(argparse.Namespace): command line arguments fixture
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
argparse.Namespace: generated arguments for these test cases
|
||||||
|
"""
|
||||||
|
args.action = Action.List
|
||||||
|
args.exit_code = False
|
||||||
|
args.info = False
|
||||||
|
args.package = "package"
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
||||||
|
package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must run command
|
||||||
|
"""
|
||||||
|
args = _default_args(args)
|
||||||
|
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||||
|
application_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.package_archives",
|
||||||
|
return_value=[package_ahriman])
|
||||||
|
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
|
||||||
|
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
|
||||||
|
|
||||||
|
_, repository_id = configuration.check_loaded()
|
||||||
|
Archives.run(args, repository_id, configuration, report=False)
|
||||||
|
application_mock.assert_called_once_with(args.package)
|
||||||
|
check_mock.assert_called_once_with(False, True)
|
||||||
|
print_mock.assert_called_once_with(verbose=False, log_fn=pytest.helpers.anyvar(int), separator=": ")
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository,
|
||||||
|
mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must raise ExitCode exception on empty archives result
|
||||||
|
"""
|
||||||
|
args = _default_args(args)
|
||||||
|
args.exit_code = True
|
||||||
|
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
|
||||||
|
mocker.patch("ahriman.core.repository.package_info.PackageInfo.package_archives", return_value=[])
|
||||||
|
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
|
||||||
|
|
||||||
|
_, repository_id = configuration.check_loaded()
|
||||||
|
Archives.run(args, repository_id, configuration, report=False)
|
||||||
|
check_mock.assert_called_once_with(True, False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_imply_with_report(args: argparse.Namespace, configuration: Configuration, database: SQLite,
|
||||||
|
mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must create application object with native reporting
|
||||||
|
"""
|
||||||
|
args = _default_args(args)
|
||||||
|
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
|
||||||
|
load_mock = mocker.patch("ahriman.core.repository.Repository.load")
|
||||||
|
|
||||||
|
_, repository_id = configuration.check_loaded()
|
||||||
|
Archives.run(args, repository_id, configuration, report=False)
|
||||||
|
load_mock.assert_called_once_with(repository_id, configuration, database, report=True, refresh_pacman_database=0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_disallow_multi_architecture_run() -> None:
|
||||||
|
"""
|
||||||
|
must not allow multi architecture run
|
||||||
|
"""
|
||||||
|
assert not Archives.ALLOW_MULTI_ARCHITECTURE_RUN
|
||||||
@@ -271,6 +271,22 @@ def test_subparsers_package_add_option_variable_multiple(parser: argparse.Argume
|
|||||||
assert args.variable == ["var1", "var2"]
|
assert args.variable == ["var1", "var2"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_subparsers_package_archives(parser: argparse.ArgumentParser) -> None:
|
||||||
|
"""
|
||||||
|
package-archives command must imply action, exit code, info, lock, quiet, report and unsafe
|
||||||
|
"""
|
||||||
|
args = parser.parse_args(["-a", "x86_64", "-r", "repo", "package-archives", "ahriman"])
|
||||||
|
assert args.action == Action.List
|
||||||
|
assert args.architecture == "x86_64"
|
||||||
|
assert not args.exit_code
|
||||||
|
assert not args.info
|
||||||
|
assert args.lock is None
|
||||||
|
assert args.quiet
|
||||||
|
assert not args.report
|
||||||
|
assert args.repository == "repo"
|
||||||
|
assert args.unsafe
|
||||||
|
|
||||||
|
|
||||||
def test_subparsers_package_changes(parser: argparse.ArgumentParser) -> None:
|
def test_subparsers_package_changes(parser: argparse.ArgumentParser) -> None:
|
||||||
"""
|
"""
|
||||||
package-changes command must imply action, exit code, lock, quiet, report and unsafe
|
package-changes command must imply action, exit code, lock, quiet, report and unsafe
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ from ahriman.core.database import SQLite
|
|||||||
from ahriman.core.database.migrations import Migrations
|
from ahriman.core.database.migrations import Migrations
|
||||||
from ahriman.core.log.log_loader import LogLoader
|
from ahriman.core.log.log_loader import LogLoader
|
||||||
from ahriman.core.repository import Repository
|
from ahriman.core.repository import Repository
|
||||||
|
from ahriman.core.repository.package_info import PackageInfo
|
||||||
from ahriman.core.spawn import Spawn
|
from ahriman.core.spawn import Spawn
|
||||||
from ahriman.core.status import Client
|
from ahriman.core.status import Client
|
||||||
from ahriman.core.status.watcher import Watcher
|
from ahriman.core.status.watcher import Watcher
|
||||||
@@ -688,4 +689,5 @@ def watcher(local_client: Client) -> Watcher:
|
|||||||
Returns:
|
Returns:
|
||||||
Watcher: package status watcher test instance
|
Watcher: package status watcher test instance
|
||||||
"""
|
"""
|
||||||
return Watcher(local_client)
|
package_info = PackageInfo()
|
||||||
|
return Watcher(local_client, package_info)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from ahriman.models.changes import Changes
|
|||||||
from ahriman.models.dependencies import Dependencies
|
from ahriman.models.dependencies import Dependencies
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
from ahriman.models.packagers import Packagers
|
from ahriman.models.packagers import Packagers
|
||||||
|
from ahriman.models.repository_id import RepositoryId
|
||||||
from ahriman.models.user import User
|
from ahriman.models.user import User
|
||||||
|
|
||||||
|
|
||||||
@@ -56,7 +57,7 @@ def test_archive_lookup_architecture_mismatch(executor: Executor, package_ahrima
|
|||||||
"""
|
"""
|
||||||
package_ahriman.packages[package_ahriman.base].architecture = "x86_64"
|
package_ahriman.packages[package_ahriman.base].architecture = "x86_64"
|
||||||
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
mocker.patch("pathlib.Path.is_dir", return_value=True)
|
||||||
mocker.patch("ahriman.core.repository.executor.Executor.architecture", return_value="i686")
|
executor.repository_id = RepositoryId("i686", executor.repository_id.name)
|
||||||
mocker.patch("pathlib.Path.iterdir", return_value=[
|
mocker.patch("pathlib.Path.iterdir", return_value=[
|
||||||
Path("1.pkg.tar.zst"),
|
Path("1.pkg.tar.zst"),
|
||||||
])
|
])
|
||||||
@@ -116,7 +117,7 @@ def test_package_build(executor: Executor, package_ahriman: Package, mocker: Moc
|
|||||||
assert executor._package_build(package_ahriman, Path("local"), "packager", None) == "sha"
|
assert executor._package_build(package_ahriman, Path("local"), "packager", None) == "sha"
|
||||||
status_client_mock.assert_called_once_with(package_ahriman.base)
|
status_client_mock.assert_called_once_with(package_ahriman.base)
|
||||||
init_mock.assert_called_once_with(pytest.helpers.anyvar(int), pytest.helpers.anyvar(int), None)
|
init_mock.assert_called_once_with(pytest.helpers.anyvar(int), pytest.helpers.anyvar(int), None)
|
||||||
package_mock.assert_called_once_with(Path("local"), executor.architecture, None)
|
package_mock.assert_called_once_with(Path("local"), executor.repository_id.architecture, None)
|
||||||
lookup_mock.assert_called_once_with(package_ahriman)
|
lookup_mock.assert_called_once_with(package_ahriman)
|
||||||
with_packages_mock.assert_called_once_with([Path(package_ahriman.base)])
|
with_packages_mock.assert_called_once_with([Path(package_ahriman.base)])
|
||||||
rename_mock.assert_called_once_with(Path(package_ahriman.base), executor.paths.packages / package_ahriman.base)
|
rename_mock.assert_called_once_with(Path(package_ahriman.base), executor.paths.packages / package_ahriman.base)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from dataclasses import replace
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
@@ -95,26 +96,30 @@ def test_package_archives(repository: Repository, package_ahriman: Package, mock
|
|||||||
"""
|
"""
|
||||||
must load package archives sorted by version
|
must load package archives sorted by version
|
||||||
"""
|
"""
|
||||||
from dataclasses import replace
|
|
||||||
from typing import Any
|
|
||||||
|
|
||||||
def package(version: Any, *args: Any, **kwargs: Any) -> Package:
|
|
||||||
generated = replace(package_ahriman, version=str(version))
|
|
||||||
generated.packages = {
|
|
||||||
key: replace(value, filename=str(version))
|
|
||||||
for key, value in generated.packages.items()
|
|
||||||
}
|
|
||||||
return generated
|
|
||||||
|
|
||||||
mocker.patch("ahriman.core.repository.package_info.package_like", return_value=True)
|
mocker.patch("ahriman.core.repository.package_info.package_like", return_value=True)
|
||||||
mocker.patch("pathlib.Path.iterdir", return_value=[Path(str(i)) for i in range(5)])
|
mocker.patch("pathlib.Path.iterdir", return_value=[str(i) for i in range(5)])
|
||||||
mocker.patch("ahriman.models.package.Package.from_archive", side_effect=package)
|
mocker.patch("ahriman.models.package.Package.from_archive",
|
||||||
|
side_effect=lambda version: replace(package_ahriman, version=version))
|
||||||
|
|
||||||
result = repository.package_archives(package_ahriman.base)
|
result = repository.package_archives(package_ahriman.base)
|
||||||
assert len(result) == 5
|
assert len(result) == 5
|
||||||
assert [p.version for p in result] == [str(i) for i in range(5)]
|
assert [p.version for p in result] == [str(i) for i in range(5)]
|
||||||
|
|
||||||
|
|
||||||
|
def test_package_archives_architecture_mismatch(repository: Repository, package_ahriman: Package,
|
||||||
|
mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must skip packages with mismatched architecture
|
||||||
|
"""
|
||||||
|
package_ahriman.packages[package_ahriman.base].architecture = "i686"
|
||||||
|
|
||||||
|
mocker.patch("pathlib.Path.iterdir", return_value=[package_ahriman.packages[package_ahriman.base].filepath])
|
||||||
|
mocker.patch("ahriman.models.package.Package.from_archive", return_value=package_ahriman)
|
||||||
|
|
||||||
|
result = repository.package_archives(package_ahriman.base)
|
||||||
|
assert len(result) == 0
|
||||||
|
|
||||||
|
|
||||||
def test_package_changes(repository: Repository, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_package_changes(repository: Repository, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must load package changes
|
must load package changes
|
||||||
|
|||||||
@@ -6,20 +6,6 @@ from ahriman.models.user import User
|
|||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
|
|
||||||
|
|
||||||
def test_architecture(repository: RepositoryProperties) -> None:
|
|
||||||
"""
|
|
||||||
must provide repository architecture for backward compatibility
|
|
||||||
"""
|
|
||||||
assert repository.architecture == repository.repository_id.architecture
|
|
||||||
|
|
||||||
|
|
||||||
def test_name(repository: RepositoryProperties) -> None:
|
|
||||||
"""
|
|
||||||
must provide repository name for backward compatibility
|
|
||||||
"""
|
|
||||||
assert repository.name == repository.repository_id.name
|
|
||||||
|
|
||||||
|
|
||||||
def test_packager(repository: RepositoryProperties, mocker: MockerFixture) -> None:
|
def test_packager(repository: RepositoryProperties, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must extract packager
|
must extract packager
|
||||||
|
|||||||
@@ -45,6 +45,18 @@ def test_load_known(watcher: Watcher, package_ahriman: Package, mocker: MockerFi
|
|||||||
assert status.status == BuildStatusEnum.Success
|
assert status.status == BuildStatusEnum.Success
|
||||||
|
|
||||||
|
|
||||||
|
def test_package_archives(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must return package archives from package info
|
||||||
|
"""
|
||||||
|
archives_mock = mocker.patch("ahriman.core.repository.package_info.PackageInfo.package_archives",
|
||||||
|
return_value=[package_ahriman])
|
||||||
|
|
||||||
|
result = watcher.package_archives(package_ahriman.base)
|
||||||
|
assert result == [package_ahriman]
|
||||||
|
archives_mock.assert_called_once_with(package_ahriman.base)
|
||||||
|
|
||||||
|
|
||||||
def test_package_get(watcher: Watcher, package_ahriman: Package) -> None:
|
def test_package_get(watcher: Watcher, package_ahriman: Package) -> None:
|
||||||
"""
|
"""
|
||||||
must return package status
|
must return package status
|
||||||
|
|||||||
@@ -101,20 +101,6 @@ def test_groups(package_ahriman: Package) -> None:
|
|||||||
assert sorted(package_ahriman.groups) == package_ahriman.groups
|
assert sorted(package_ahriman.groups) == package_ahriman.groups
|
||||||
|
|
||||||
|
|
||||||
def test_is_single_package_false(package_python_schedule: Package) -> None:
|
|
||||||
"""
|
|
||||||
python-schedule must not be single package
|
|
||||||
"""
|
|
||||||
assert not package_python_schedule.is_single_package
|
|
||||||
|
|
||||||
|
|
||||||
def test_is_single_package_true(package_ahriman: Package) -> None:
|
|
||||||
"""
|
|
||||||
ahriman must be single package
|
|
||||||
"""
|
|
||||||
assert package_ahriman.is_single_package
|
|
||||||
|
|
||||||
|
|
||||||
def test_is_vcs_false(package_ahriman: Package) -> None:
|
def test_is_vcs_false(package_ahriman: Package) -> None:
|
||||||
"""
|
"""
|
||||||
ahriman must not be VCS package
|
ahriman must not be VCS package
|
||||||
@@ -353,6 +339,30 @@ def test_build_status_pretty_print(package_ahriman: Package) -> None:
|
|||||||
assert isinstance(package_ahriman.pretty_print(), str)
|
assert isinstance(package_ahriman.pretty_print(), str)
|
||||||
|
|
||||||
|
|
||||||
|
def test_supports_architecture(package_ahriman: Package) -> None:
|
||||||
|
"""
|
||||||
|
must check if package supports architecture
|
||||||
|
"""
|
||||||
|
package_ahriman.packages[package_ahriman.base].architecture = "x86_64"
|
||||||
|
assert package_ahriman.supports_architecture("x86_64")
|
||||||
|
|
||||||
|
|
||||||
|
def test_supports_architecture_any(package_ahriman: Package) -> None:
|
||||||
|
"""
|
||||||
|
must support any architecture
|
||||||
|
"""
|
||||||
|
package_ahriman.packages[package_ahriman.base].architecture = "any"
|
||||||
|
assert package_ahriman.supports_architecture("x86_64")
|
||||||
|
|
||||||
|
|
||||||
|
def test_supports_architecture_mismatch(package_ahriman: Package) -> None:
|
||||||
|
"""
|
||||||
|
must not support mismatched architecture
|
||||||
|
"""
|
||||||
|
package_ahriman.packages[package_ahriman.base].architecture = "i686"
|
||||||
|
assert not package_ahriman.supports_architecture("x86_64")
|
||||||
|
|
||||||
|
|
||||||
def test_vercmp(package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_vercmp(package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must call vercmp
|
must call vercmp
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from ahriman.core.exceptions import InitializeError
|
|||||||
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.web.keys import ConfigurationKey
|
from ahriman.web.keys import ConfigurationKey
|
||||||
from ahriman.web.web import _create_socket, _on_shutdown, _on_startup, run_server, setup_server
|
from ahriman.web.web import _create_socket, _create_watcher, _on_shutdown, _on_startup, run_server, setup_server
|
||||||
|
|
||||||
|
|
||||||
async def test_create_socket(application: Application, mocker: MockerFixture) -> None:
|
async def test_create_socket(application: Application, mocker: MockerFixture) -> None:
|
||||||
@@ -139,6 +139,20 @@ def test_run_with_socket(application: Application, mocker: MockerFixture) -> Non
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_watcher(configuration: Configuration, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must create watcher for repository
|
||||||
|
"""
|
||||||
|
database_mock = mocker.patch("ahriman.core.database.SQLite.load")
|
||||||
|
client_mock = mocker.patch("ahriman.core.status.Client.load")
|
||||||
|
configuration_path, repository_id = configuration.check_loaded()
|
||||||
|
|
||||||
|
result = _create_watcher(configuration_path, repository_id)
|
||||||
|
assert isinstance(result, Watcher)
|
||||||
|
database_mock.assert_called_once()
|
||||||
|
client_mock.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
def test_setup_no_repositories(configuration: Configuration, spawner: Spawn) -> None:
|
def test_setup_no_repositories(configuration: Configuration, spawner: Spawn) -> None:
|
||||||
"""
|
"""
|
||||||
must raise InitializeError if no repositories set
|
must raise InitializeError if no repositories set
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from aiohttp.test_utils import TestClient
|
||||||
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
|
from ahriman.models.build_status import BuildStatusEnum
|
||||||
|
from ahriman.models.package import Package
|
||||||
|
from ahriman.models.user_access import UserAccess
|
||||||
|
from ahriman.web.views.v1.packages.archives import Archives
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_permission() -> None:
|
||||||
|
"""
|
||||||
|
must return correct permission for the request
|
||||||
|
"""
|
||||||
|
for method in ("GET",):
|
||||||
|
request = pytest.helpers.request("", "", method)
|
||||||
|
assert await Archives.get_permission(request) == UserAccess.Reporter
|
||||||
|
|
||||||
|
|
||||||
|
def test_routes() -> None:
|
||||||
|
"""
|
||||||
|
must return correct routes
|
||||||
|
"""
|
||||||
|
assert Archives.ROUTES == ["/api/v1/packages/{package}/archives"]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get(client: TestClient, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
|
"""
|
||||||
|
must get archives for package
|
||||||
|
"""
|
||||||
|
await client.post(f"/api/v1/packages/{package_ahriman.base}",
|
||||||
|
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
|
||||||
|
mocker.patch("ahriman.core.status.watcher.Watcher.package_archives", return_value=[package_ahriman])
|
||||||
|
response_schema = pytest.helpers.schema_response(Archives.get)
|
||||||
|
|
||||||
|
response = await client.get(f"/api/v1/packages/{package_ahriman.base}/archives")
|
||||||
|
assert response.status == 200
|
||||||
|
|
||||||
|
archives = await response.json()
|
||||||
|
assert not response_schema.validate(archives)
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_not_found(client: TestClient, package_ahriman: Package) -> None:
|
||||||
|
"""
|
||||||
|
must return not found for missing package
|
||||||
|
"""
|
||||||
|
response_schema = pytest.helpers.schema_response(Archives.get, code=404)
|
||||||
|
|
||||||
|
response = await client.get(f"/api/v1/packages/{package_ahriman.base}/archives")
|
||||||
|
assert response.status == 404
|
||||||
|
assert not response_schema.validate(await response.json())
|
||||||
Reference in New Issue
Block a user