diff --git a/docs/ahriman.core.rst b/docs/ahriman.core.rst
index 36551728..b69f9d62 100644
--- a/docs/ahriman.core.rst
+++ b/docs/ahriman.core.rst
@@ -36,6 +36,14 @@ ahriman.core.exceptions module
:no-undoc-members:
:show-inheritance:
+ahriman.core.module\_loader module
+----------------------------------
+
+.. automodule:: ahriman.core.module_loader
+ :members:
+ :no-undoc-members:
+ :show-inheritance:
+
ahriman.core.spawn module
-------------------------
diff --git a/docs/architecture.rst b/docs/architecture.rst
index 780d10ef..0e2313ed 100644
--- a/docs/architecture.rst
+++ b/docs/architecture.rst
@@ -18,7 +18,7 @@ Full dependency diagram:
``ahriman.application`` package
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-This package contains application (aka executable) related classes and everything for it. It also contains package called ``ahriman.application.handlers`` in which all available subcommands are described as separated classes derived from the base ``ahriman.application.handlers.Handler`` class.
+This package contains application (aka executable) related classes and everything for it. It also contains package called ``ahriman.application.handlers`` in which all available subcommands are described as separated classes derived from the base ``ahriman.application.handlers.handler.Handler`` class.
``ahriman.application.application.Application`` (god class) is used for any interaction from parsers with repository. It is divided into multiple traits by functions (package related and repository related) in the same package.
diff --git a/pylint_plugins/import_order.py b/pylint_plugins/import_order.py
index 06bfd622..923cff79 100644
--- a/pylint_plugins/import_order.py
+++ b/pylint_plugins/import_order.py
@@ -30,12 +30,14 @@ class ImportType(StrEnum):
import type enumeration
Attributes:
+ Future(MethodTypeOrder): (class attribute) from __future__ import
Package(MethodTypeOrder): (class attribute) package import
PackageFrom(MethodTypeOrder): (class attribute) package import, from clause
System(ImportType): (class attribute) system installed packages
SystemFrom(MethodTypeOrder): (class attribute) system installed packages, from clause
"""
+ Future = "future"
Package = "package"
PackageFrom = "package-from"
System = "system"
@@ -70,6 +72,7 @@ class ImportOrder(BaseRawFileChecker):
"import-type-order",
{
"default": [
+ "future",
"system",
"system-from",
"package",
@@ -91,7 +94,7 @@ class ImportOrder(BaseRawFileChecker):
)
@staticmethod
- def imports(source: Iterable[Any], start_lineno: int = 0) -> list[nodes.Import | nodes.ImportFrom]:
+ def imports(source: Iterable[Any], start_lineno: int = 0) -> Iterable[nodes.Import | nodes.ImportFrom]:
"""
extract import nodes from list of raw nodes
@@ -100,7 +103,7 @@ class ImportOrder(BaseRawFileChecker):
start_lineno(int, optional): minimal allowed line number (Default value = 0)
Returns:
- list[nodes.Import | nodes.ImportFrom]: list of import nodes
+ Iterable[nodes.Import | nodes.ImportFrom]: list of import nodes
"""
def is_defined_import(imports: Any) -> bool:
@@ -108,7 +111,7 @@ class ImportOrder(BaseRawFileChecker):
and imports.lineno is not None \
and imports.lineno >= start_lineno
- return list(filter(is_defined_import, source))
+ return sorted(filter(is_defined_import, source), key=lambda imports: imports.lineno)
def check_from_imports(self, imports: nodes.ImportFrom) -> None:
"""
@@ -124,30 +127,36 @@ class ImportOrder(BaseRawFileChecker):
self.add_message("from-imports-out-of-order", line=imports.lineno, args=(real, expected))
break
- def check_imports(self, imports: list[nodes.Import | nodes.ImportFrom], root_package: str) -> None:
+ def check_imports(self, imports: Iterable[nodes.Import | nodes.ImportFrom], root_package: str) -> None:
"""
check imports
Args:
- imports(list[nodes.Import | nodes.ImportFrom]): list of imports in their defined order
+ imports(Iterable[nodes.Import | nodes.ImportFrom]): list of imports in their defined order
root_package(str): root package name
"""
last_statement: tuple[int, str] | None = None
for statement in imports:
# define types and perform specific checks
- if isinstance(statement, nodes.ImportFrom):
- import_name = statement.modname
- root, *_ = import_name.split(".", maxsplit=1)
- import_type = ImportType.PackageFrom if root_package == root else ImportType.SystemFrom
- # check from import itself
- self.check_from_imports(statement)
- else:
- import_name = next(name for name, _ in statement.names)
- root, *_ = import_name.split(".", maxsplit=1)[0]
- import_type = ImportType.Package if root_package == root else ImportType.System
- # check import itself
- self.check_package_imports(statement)
+ match statement:
+ case nodes.ImportFrom() if statement.modname == "__future__":
+ import_name = statement.modname
+ import_type = ImportType.Future
+ case nodes.ImportFrom():
+ import_name = statement.modname
+ root, *_ = import_name.split(".", maxsplit=1)
+ import_type = ImportType.PackageFrom if root_package == root else ImportType.SystemFrom
+ # check from import itself
+ self.check_from_imports(statement)
+ case nodes.Import():
+ import_name = next(name for name, _ in statement.names)
+ root, *_ = import_name.split(".", maxsplit=1)
+ import_type = ImportType.Package if root_package == root else ImportType.System
+ # check import itself
+ self.check_package_imports(statement)
+ case _:
+ continue
# extract index
try:
diff --git a/src/ahriman/application/ahriman.py b/src/ahriman/application/ahriman.py
index 53f3ce3f..63ed92c1 100644
--- a/src/ahriman/application/ahriman.py
+++ b/src/ahriman/application/ahriman.py
@@ -17,36 +17,23 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-# pylint: disable=too-many-lines
import argparse
from pathlib import Path
-from typing import TypeVar
+
+import ahriman.application.handlers
from ahriman import __version__
-from ahriman.application import handlers
+from ahriman.application.handlers.handler import Handler
from ahriman.application.help_formatter import _HelpFormatter
-from ahriman.core.utils import enum_values, extract_user
-from ahriman.models.action import Action
-from ahriman.models.build_status import BuildStatusEnum
-from ahriman.models.event import EventType
+from ahriman.core.module_loader import implementations
+from ahriman.core.utils import enum_values
from ahriman.models.log_handler import LogHandler
-from ahriman.models.package_source import PackageSource
-from ahriman.models.sign_settings import SignSettings
-from ahriman.models.user_access import UserAccess
__all__: list[str] = []
-# this workaround is for several things
-# firstly python devs don't think that is it error and asking you for workarounds https://bugs.python.org/issue41592
-# secondly linters don't like when you are importing private members
-# thirdly new mypy doesn't like _SubParsersAction and thinks it is a template
-SubParserAction = TypeVar("SubParserAction", bound="argparse._SubParsersAction[argparse.ArgumentParser]")
-
-
-# pylint: disable=too-many-statements
def _parser() -> argparse.ArgumentParser:
"""
command line parser generator
@@ -102,1110 +89,15 @@ Start web service (requires additional configuration):
subparsers = parser.add_subparsers(title="command", help="command to run", dest="command")
- _set_aur_search_parser(subparsers)
- _set_help_commands_unsafe_parser(subparsers)
- _set_help_parser(subparsers)
- _set_help_updates_parser(subparsers)
- _set_help_version_parser(subparsers)
- _set_package_add_parser(subparsers)
- _set_package_changes_parser(subparsers)
- _set_package_changes_remove_parser(subparsers)
- _set_package_copy_parser(subparsers)
- _set_package_remove_parser(subparsers)
- _set_package_status_parser(subparsers)
- _set_package_status_remove_parser(subparsers)
- _set_package_status_update_parser(subparsers)
- _set_patch_add_parser(subparsers)
- _set_patch_list_parser(subparsers)
- _set_patch_remove_parser(subparsers)
- _set_patch_set_add_parser(subparsers)
- _set_repo_backup_parser(subparsers)
- _set_repo_check_parser(subparsers)
- _set_repo_create_keyring_parser(subparsers)
- _set_repo_create_mirrorlist_parser(subparsers)
- _set_repo_daemon_parser(subparsers)
- _set_repo_rebuild_parser(subparsers)
- _set_repo_remove_unknown_parser(subparsers)
- _set_repo_report_parser(subparsers)
- _set_repo_restore_parser(subparsers)
- _set_repo_sign_parser(subparsers)
- _set_repo_statistics_parser(subparsers)
- _set_repo_status_update_parser(subparsers)
- _set_repo_sync_parser(subparsers)
- _set_repo_tree_parser(subparsers)
- _set_repo_triggers_parser(subparsers)
- _set_repo_update_parser(subparsers)
- _set_service_clean_parser(subparsers)
- _set_service_config_parser(subparsers)
- _set_service_config_validate_parser(subparsers)
- _set_service_key_import_parser(subparsers)
- _set_service_repositories(subparsers)
- _set_service_run(subparsers)
- _set_service_setup_parser(subparsers)
- _set_service_shell_parser(subparsers)
- _set_service_tree_migrate_parser(subparsers)
- _set_user_add_parser(subparsers)
- _set_user_list_parser(subparsers)
- _set_user_remove_parser(subparsers)
- _set_web_parser(subparsers)
+ for handler in implementations(ahriman.application.handlers, Handler):
+ for subparser_parser in handler.arguments:
+ subparser = subparser_parser(subparsers)
+ subparser.formatter_class = _HelpFormatter
+ subparser.set_defaults(handler=handler, parser=_parser)
return parser
-def _set_aur_search_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for AUR search subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("aur-search", aliases=["search"], help="search for package",
- description="search for package in AUR using API", formatter_class=_HelpFormatter)
- parser.add_argument("search", help="search terms, can be specified multiple times, the result will match all terms",
- nargs="+")
- 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.add_argument("--sort-by", help="sort field by this field. In case if two packages have the same value of "
- "the specified field, they will be always sorted by name",
- default="name", choices=sorted(handlers.Search.SORT_FIELDS))
- parser.set_defaults(handler=handlers.Search, architecture="", lock=None, quiet=True, report=False,
- repository="", unsafe=True)
- return parser
-
-
-def _set_help_commands_unsafe_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for listing unsafe commands
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("help-commands-unsafe", help="list unsafe commands",
- description="list unsafe commands as defined in default args",
- formatter_class=_HelpFormatter)
- parser.add_argument("subcommand", help="instead of showing commands, just test command line for unsafe subcommand "
- "and return 0 in case if command is safe and 1 otherwise", nargs="*")
- parser.set_defaults(handler=handlers.UnsafeCommands, architecture="", lock=None, quiet=True, report=False,
- repository="", unsafe=True, parser=_parser)
- return parser
-
-
-def _set_help_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for listing help subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("help", help="show help message",
- description="show help message for application or command and exit",
- formatter_class=_HelpFormatter)
- parser.add_argument("subcommand", help="show help message for specific command", nargs="?")
- parser.set_defaults(handler=handlers.Help, architecture="", lock=None, quiet=True, report=False, repository="",
- unsafe=True, parser=_parser)
- return parser
-
-
-def _set_help_updates_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for service update check subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("help-updates", help="check for service updates",
- description="request AUR for current version and compare with current service version",
- formatter_class=_HelpFormatter)
- parser.add_argument("-e", "--exit-code", help="return non-zero exit code if updates available", action="store_true")
- parser.set_defaults(handler=handlers.ServiceUpdates, architecture="", lock=None, quiet=True, report=False,
- repository="", unsafe=True)
- return parser
-
-
-def _set_help_version_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for version subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("help-version", aliases=["version"], help="application version",
- description="print application and its dependencies versions",
- formatter_class=_HelpFormatter)
- parser.set_defaults(handler=handlers.Versions, architecture="", lock=None, quiet=True, report=False,
- repository="", unsafe=True)
- return parser
-
-
-def _set_package_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for package addition subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("package-add", aliases=["add", "package-update"], help="add package",
- description="add existing or new package to the build queue",
- epilog="This subcommand should be used for new package addition. It also supports flag "
- "--now in case if you would like to build the package immediately. "
- "You can add new package from one of supported sources:\n\n"
- "1. If it is already built package you can specify the path to the archive.\n"
- "2. You can also add built packages from the directory (e.g. during the migration "
- "from another repository source).\n"
- "3. It is also possible to add package from local PKGBUILD, but in this case it "
- "will be ignored during the next automatic updates.\n"
- "4. Ahriman supports downloading archives from remote (e.g. HTTP) sources.\n"
- "5. Finally you can add package from AUR.",
- formatter_class=_HelpFormatter)
- parser.add_argument("package", help="package source (base name, path to local files, remote URL)", nargs="+")
- parser.add_argument("--changes", help="calculate changes from the latest known commit if available",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("--dependencies", help="process missing package dependencies",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
- parser.add_argument("--increment", help="increment package release (pkgrel) version on duplicate",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("-n", "--now", help="run update function after", action="store_true")
- parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
- "-yy to force refresh even if up to date",
- action="count", default=False)
- parser.add_argument("-s", "--source", help="explicitly specify the package source for this command",
- type=PackageSource, choices=enum_values(PackageSource), default=PackageSource.Auto)
- parser.add_argument("-u", "--username", help="build as user", default=extract_user())
- parser.add_argument("-v", "--variable", help="apply specified makepkg variables to the next build", action="append")
- parser.set_defaults(handler=handlers.Add)
- return parser
-
-
-def _set_package_changes_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for package changes subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("package-changes", help="get package changes",
- description="retrieve package changes stored in database",
- epilog="This feature requests package status from the web interface if it is available.",
- formatter_class=_HelpFormatter)
- 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.set_defaults(handler=handlers.Change, action=Action.List, lock=None, quiet=True, report=False, unsafe=True)
- return parser
-
-
-def _set_package_changes_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for package change remove subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("package-changes-remove", help="remove package changes",
- description="remove the package changes stored remotely",
- formatter_class=_HelpFormatter)
- parser.add_argument("package", help="package base")
- parser.set_defaults(handler=handlers.Change, action=Action.Remove, exit_code=False, lock=None, quiet=True,
- report=False, unsafe=True)
- return parser
-
-
-def _set_package_copy_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for package copy subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("package-copy", aliases=["copy"], help="copy package from another repository",
- description="copy package and its metadata from another repository",
- formatter_class=_HelpFormatter)
- parser.add_argument("source", help="source repository name")
- parser.add_argument("package", help="package base", nargs="+")
- parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
- parser.add_argument("--remove", help="remove package from the source repository after", action="store_true")
- parser.set_defaults(handler=handlers.Copy)
- return parser
-
-
-def _set_package_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for package removal subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("package-remove", aliases=["remove"], help="remove package",
- description="remove package from the repository", formatter_class=_HelpFormatter)
- parser.add_argument("package", help="package name or base", nargs="+")
- parser.set_defaults(handler=handlers.Remove)
- return parser
-
-
-def _set_package_status_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for package status subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("package-status", aliases=["status"], help="get package status",
- description="request status of the package",
- epilog="This feature requests package status from the web interface if it is available.",
- formatter_class=_HelpFormatter)
- parser.add_argument("package", help="filter status by package base", nargs="*")
- parser.add_argument("--ahriman", help="get service status itself", action="store_true")
- 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.add_argument("-s", "--status", help="filter packages by status",
- type=BuildStatusEnum, choices=enum_values(BuildStatusEnum))
- parser.set_defaults(handler=handlers.Status, lock=None, quiet=True, report=False, unsafe=True)
- return parser
-
-
-def _set_package_status_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for package status remove subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("package-status-remove", help="remove package status",
- description="remove the package from the status page",
- epilog="Please note that this subcommand does not remove the package itself, it just "
- "clears the status page.",
- formatter_class=_HelpFormatter)
- parser.add_argument("package", help="remove specified packages from status page", nargs="+")
- parser.set_defaults(handler=handlers.StatusUpdate, action=Action.Remove, lock=None, quiet=True, report=False,
- unsafe=True)
- return parser
-
-
-def _set_package_status_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for package status update subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("package-status-update", aliases=["status-update"], help="update package status",
- description="update package status on the status page",
- formatter_class=_HelpFormatter)
- parser.add_argument("package", help="set status for specified packages. "
- "If no packages supplied, service status will be updated",
- nargs="*")
- parser.add_argument("-s", "--status", help="new package build status",
- type=BuildStatusEnum, choices=enum_values(BuildStatusEnum), default=BuildStatusEnum.Success)
- parser.set_defaults(handler=handlers.StatusUpdate, action=Action.Update, lock=None, quiet=True, report=False,
- unsafe=True)
- return parser
-
-
-def _set_patch_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for new single-function patch subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("patch-add", help="add patch for PKGBUILD function",
- description="create or update patched PKGBUILD function or variable",
- epilog="Unlike ``patch-set-add``, this function allows to patch only one PKGBUILD "
- "function, e.g. typing ``ahriman patch-add ahriman pkgver`` it will change the "
- "``pkgver`` inside PKGBUILD, typing ``ahriman patch-add ahriman build()`` "
- "it will change ``build()`` function inside PKGBUILD.",
- formatter_class=_HelpFormatter)
- parser.add_argument("package", help="package base")
- parser.add_argument("variable", help="PKGBUILD variable or function name. If variable is a function, "
- "it must end with ()")
- parser.add_argument("patch", help="path to file which contains function or variable value. If not set, "
- "the value will be read from stdin", type=Path, nargs="?")
- parser.set_defaults(handler=handlers.Patch, action=Action.Update, architecture="", exit_code=False, lock=None,
- report=False, repository="")
- return parser
-
-
-def _set_patch_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for list patches subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("patch-list", help="list patch sets", description="list available patches for the package",
- formatter_class=_HelpFormatter)
- 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("-v", "--variable", help="if set, show only patches for specified PKGBUILD variables",
- action="append")
- parser.set_defaults(handler=handlers.Patch, action=Action.List, architecture="", lock=None, report=False,
- repository="", unsafe=True)
- return parser
-
-
-def _set_patch_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for remove patches subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("patch-remove", help="remove patch set", description="remove patches for the package",
- formatter_class=_HelpFormatter)
- parser.add_argument("package", help="package base")
- parser.add_argument("-v", "--variable", help="should be used for single-function patches in case if you wold like "
- "to remove only specified PKGBUILD variables. In case if not set, "
- "it will remove all patches related to the package",
- action="append")
- parser.set_defaults(handler=handlers.Patch, action=Action.Remove, architecture="", exit_code=False, lock=None,
- report=False, repository="")
- return parser
-
-
-def _set_patch_set_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for new full-diff patch subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("patch-set-add", help="add patch set", description="create or update source patches",
- epilog="In order to add a patch set for the package you will need to:\n\n"
- "1. Clone the AUR package manually.\n"
- "2. Add required changes (e.g. external patches, edit PKGBUILD).\n"
- "3. Run command, e.g. ``ahriman patch-set-add path/to/directory``.\n\n"
- "By default it tracks ``*.patch`` and ``*.diff`` files, but this behavior can be "
- "changed by using ``--track`` option.",
- formatter_class=_HelpFormatter)
- parser.add_argument("package", help="path to directory with changed files for patch addition/update", type=Path)
- parser.add_argument("-t", "--track", help="files which has to be tracked", action="append",
- default=["*.diff", "*.patch"])
- parser.set_defaults(handler=handlers.Patch, action=Action.Update, architecture="", exit_code=False, lock=None,
- report=False, repository="", variable=None)
- return parser
-
-
-def _set_repo_backup_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for repository backup subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("repo-backup", help="backup repository data",
- description="backup repository settings and database",
- formatter_class=_HelpFormatter)
- parser.add_argument("path", help="path of the output archive", type=Path)
- parser.set_defaults(handler=handlers.Backup, architecture="", lock=None, report=False, repository="",
- unsafe=True)
- return parser
-
-
-def _set_repo_check_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for repository check subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("repo-check", aliases=["check"], help="check for updates",
- description="check for packages updates. Same as repo-update --dry-run --no-manual",
- formatter_class=_HelpFormatter)
- parser.add_argument("package", help="filter check by package base", nargs="*")
- parser.add_argument("--changes", help="calculate changes from the latest known commit if available",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("--check-files", help="enable or disable checking of broken dependencies "
- "(e.g. dynamically linked libraries or modules directories)",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
- parser.add_argument("--vcs", help="fetch actual version of VCS packages",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
- "-yy to force refresh even if up to date",
- action="count", default=False)
- parser.set_defaults(handler=handlers.Update, aur=True, dependencies=False, dry_run=True, increment=False,
- local=True, manual=False, username=None)
- return parser
-
-
-def _set_repo_create_keyring_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for create-keyring subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("repo-create-keyring", help="create keyring package",
- description="create package which contains list of trusted keys as set by "
- "configuration. Note, that this action will only create package, the package "
- "itself has to be built manually",
- formatter_class=_HelpFormatter)
- parser.set_defaults(handler=handlers.Triggers, trigger=["ahriman.core.support.KeyringTrigger"])
- return parser
-
-
-def _set_repo_create_mirrorlist_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for create-mirrorlist subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("repo-create-mirrorlist", help="create mirrorlist package",
- description="create package which contains list of available mirrors as set by "
- "configuration. Note, that this action will only create package, the package "
- "itself has to be built manually",
- formatter_class=_HelpFormatter)
- parser.set_defaults(handler=handlers.Triggers, trigger=["ahriman.core.support.MirrorlistTrigger"])
- return parser
-
-
-def _set_repo_daemon_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for daemon subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("repo-daemon", aliases=["daemon"], help="run application as daemon",
- description="start process which periodically will run update process",
- formatter_class=_HelpFormatter)
- parser.add_argument("-i", "--interval", help="interval between runs in seconds", type=int, default=60 * 60 * 12)
- parser.add_argument("--aur", help="enable or disable checking for AUR updates",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("--changes", help="calculate changes from the latest known commit if available. "
- "Only applicable in dry run mode",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("--check-files", help="enable or disable checking of broken dependencies "
- "(e.g. dynamically linked libraries or modules directories)",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("--dependencies", help="process missing package dependencies",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("--dry-run", help="just perform check for updates, same as check command", action="store_true")
- parser.add_argument("--increment", help="increment package release (pkgrel) on duplicate",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("--local", help="enable or disable checking of local packages for updates",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("--manual", help="include or exclude manual updates",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("--partitions", help="instead of updating whole repository, split updates into chunks",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("-u", "--username", help="build as user", default=extract_user())
- parser.add_argument("--vcs", help="fetch actual version of VCS packages",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
- "-yy to force refresh even if up to date",
- action="count", default=False)
- parser.set_defaults(handler=handlers.Daemon, exit_code=False, lock=Path("ahriman-daemon.pid"), package=[])
- return parser
-
-
-def _set_repo_rebuild_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for repository rebuild subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("repo-rebuild", aliases=["rebuild"], help="rebuild repository",
- description="force rebuild whole repository", formatter_class=_HelpFormatter)
- parser.add_argument("--depends-on", help="only rebuild packages that depend on specified packages", action="append")
- parser.add_argument("--dry-run", help="just perform check for packages without rebuild process itself",
- action="store_true")
- parser.add_argument("--from-database",
- help="read packages from database instead of filesystem. This feature in particular is "
- "required in case if you would like to restore repository from another repository "
- "instance. Note, however, that in order to restore packages you need to have original "
- "ahriman instance run with web service and have run repo-update at least once.",
- action="store_true")
- parser.add_argument("--increment", help="increment package release (pkgrel) on duplicate",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
- parser.add_argument("-s", "--status", help="filter packages by status. Requires --from-database to be set",
- type=BuildStatusEnum, choices=enum_values(BuildStatusEnum))
- parser.add_argument("-u", "--username", help="build as user", default=extract_user())
- parser.set_defaults(handler=handlers.Rebuild)
- return parser
-
-
-def _set_repo_remove_unknown_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for remove unknown packages subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("repo-remove-unknown", aliases=["remove-unknown"], help="remove unknown packages",
- description="remove packages which are missing in AUR and do not have local PKGBUILDs",
- formatter_class=_HelpFormatter)
- parser.add_argument("--dry-run", help="just perform check for packages without removal", action="store_true")
- parser.set_defaults(handler=handlers.RemoveUnknown)
- return parser
-
-
-def _set_repo_report_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for report subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("repo-report", aliases=["report"], help="generate report",
- description="generate repository report according to current settings",
- epilog="Create and/or update repository report as configured.",
- formatter_class=_HelpFormatter)
- parser.set_defaults(handler=handlers.Triggers, trigger=["ahriman.core.report.ReportTrigger"])
- return parser
-
-
-def _set_repo_restore_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for repository restore subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("repo-restore", help="restore repository data",
- description="restore settings and database", formatter_class=_HelpFormatter)
- parser.add_argument("path", help="path of the input archive", type=Path)
- parser.add_argument("-o", "--output", help="root path of the extracted files", type=Path, default=Path("/"))
- parser.set_defaults(handler=handlers.Restore, architecture="", lock=None, report=False, repository="",
- unsafe=True)
- return parser
-
-
-def _set_repo_sign_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for sign subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("repo-sign", aliases=["sign"], help="sign packages",
- description="(re-)sign packages and repository database according to current settings",
- epilog="Sign repository and/or packages as configured.",
- formatter_class=_HelpFormatter)
- parser.add_argument("package", help="sign only specified packages", nargs="*")
- parser.set_defaults(handler=handlers.Sign)
- return parser
-
-
-def _set_repo_statistics_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for repository statistics subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("repo-statistics", help="repository statistics",
- description="fetch repository statistics", formatter_class=_HelpFormatter)
- parser.add_argument("package", help="fetch only events for the specified package", nargs="?")
- parser.add_argument("--chart", help="create updates chart and save it to the specified path", type=Path)
- parser.add_argument("-e", "--event", help="event type filter",
- type=EventType, choices=enum_values(EventType), default=EventType.PackageUpdated)
- parser.add_argument("--from-date", help="only fetch events which are newer than the date")
- parser.add_argument("--limit", help="limit response by specified amount of events", type=int, default=-1)
- parser.add_argument("--offset", help="skip specified amount of events", type=int, default=0)
- parser.add_argument("--to-date", help="only fetch events which are older than the date")
- parser.set_defaults(handler=handlers.Statistics, lock=None, quiet=True, report=False, unsafe=True)
- return parser
-
-
-def _set_repo_status_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for repository status update subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("repo-status-update", help="update repository status",
- description="update repository status on the status page",
- formatter_class=_HelpFormatter)
- parser.add_argument("-s", "--status", help="new status",
- type=BuildStatusEnum, choices=enum_values(BuildStatusEnum), default=BuildStatusEnum.Success)
- parser.set_defaults(handler=handlers.StatusUpdate, action=Action.Update, lock=None, package=[], quiet=True,
- report=False, unsafe=True)
- return parser
-
-
-def _set_repo_sync_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for repository sync subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("repo-sync", aliases=["sync"], help="sync repository",
- description="sync repository files to remote server according to current settings",
- epilog="Synchronize the repository to remote services as configured.",
- formatter_class=_HelpFormatter)
- parser.set_defaults(handler=handlers.Triggers, trigger=["ahriman.core.upload.UploadTrigger"])
- return parser
-
-
-def _set_repo_tree_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for repository tree subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("repo-tree", help="dump repository tree",
- description="dump repository tree based on packages dependencies",
- formatter_class=_HelpFormatter)
- parser.add_argument("-p", "--partitions", help="also divide packages by independent partitions",
- type=int, default=1)
- parser.set_defaults(handler=handlers.Structure, lock=None, quiet=True, report=False, unsafe=True)
- return parser
-
-
-def _set_repo_triggers_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for repository triggers subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("repo-triggers", help="run triggers",
- description="run triggers on empty build result as configured by settings",
- formatter_class=_HelpFormatter)
- parser.add_argument("trigger", help="instead of running all triggers as set by configuration, just process "
- "specified ones in order of mention", nargs="*")
- parser.set_defaults(handler=handlers.Triggers)
- return parser
-
-
-def _set_repo_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for repository update subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("repo-update", aliases=["update"], help="update packages",
- description="check for packages updates and run build process if requested",
- formatter_class=_HelpFormatter)
- parser.add_argument("package", help="filter check by package base", nargs="*")
- parser.add_argument("--aur", help="enable or disable checking for AUR updates",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("--changes", help="calculate changes from the latest known commit if available. "
- "Only applicable in dry run mode",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("--check-files", help="enable or disable checking of broken dependencies "
- "(e.g. dynamically linked libraries or modules directories)",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("--dependencies", help="process missing package dependencies",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("--dry-run", help="just perform check for updates, same as check command", action="store_true")
- parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
- parser.add_argument("--increment", help="increment package release (pkgrel) on duplicate",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("--local", help="enable or disable checking of local packages for updates",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("--manual", help="include or exclude manual updates",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("-u", "--username", help="build as user", default=extract_user())
- parser.add_argument("--vcs", help="fetch actual version of VCS packages",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
- "-yy to force refresh even if up to date",
- action="count", default=False)
- parser.set_defaults(handler=handlers.Update)
- return parser
-
-
-def _set_service_clean_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for repository clean subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("service-clean", aliases=["clean", "repo-clean"], help="clean local caches",
- description="remove local caches",
- epilog="The subcommand clears every temporary directories (builds, caches etc). Normally "
- "you should not run this command manually. Also in case if you are going to clear "
- "the chroot directories you will need root privileges.",
- formatter_class=_HelpFormatter)
- parser.add_argument("--cache", help="clear directory with package caches",
- action=argparse.BooleanOptionalAction, default=False)
- parser.add_argument("--chroot", help="clear build chroot", action=argparse.BooleanOptionalAction, default=False)
- parser.add_argument("--manual", help="clear manually added packages queue",
- action=argparse.BooleanOptionalAction, default=False)
- parser.add_argument("--packages", help="clear directory with built packages",
- action=argparse.BooleanOptionalAction, default=False)
- parser.add_argument("--pacman", help="clear directory with pacman local database cache",
- action=argparse.BooleanOptionalAction, default=False)
- parser.set_defaults(handler=handlers.Clean, lock=None, quiet=True, unsafe=True)
- return parser
-
-
-def _set_service_config_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for config subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("service-config", aliases=["config", "repo-config"], help="dump configuration",
- description="dump configuration for the specified architecture",
- formatter_class=_HelpFormatter)
- parser.add_argument("section", help="filter settings by section", nargs="?")
- parser.add_argument("key", help="filter settings by key", nargs="?")
- parser.add_argument("--info", help="show additional information, e.g. configuration files",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("--secure", help="hide passwords and secrets from output",
- action=argparse.BooleanOptionalAction, default=True)
- parser.set_defaults(handler=handlers.Dump, lock=None, quiet=True, report=False, unsafe=True)
- return parser
-
-
-def _set_service_config_validate_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for config validation subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("service-config-validate", aliases=["config-validate", "repo-config-validate"],
- help="validate system configuration",
- description="validate configuration and print found errors",
- formatter_class=_HelpFormatter)
- parser.add_argument("-e", "--exit-code", help="return non-zero exit status if configuration is invalid",
- action="store_true")
- parser.set_defaults(handler=handlers.Validate, lock=None, quiet=True, report=False, unsafe=True)
- return parser
-
-
-def _set_service_key_import_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for key import subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("service-key-import", aliases=["key-import"], help="import PGP key",
- description="import PGP key from public sources to the repository user",
- epilog="By default ahriman runs build process with package sources validation "
- "(in case if signature and keys are available in PKGBUILD). This process will "
- "fail in case if key is not known for build user. This subcommand can be used "
- "in order to import the PGP key to user keychain.",
- formatter_class=_HelpFormatter)
- parser.add_argument("--key-server", help="key server for key import", default="keyserver.ubuntu.com")
- parser.add_argument("key", help="PGP key to import from public server")
- parser.set_defaults(handler=handlers.KeyImport, architecture="", lock=None, report=False, repository="")
- return parser
-
-
-def _set_service_repositories(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for repositories listing
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("service-repositories", help="show repositories",
- description="list all available repositories",
- formatter_class=_HelpFormatter)
- parser.add_argument("--id-only", help="show machine readable identifier instead",
- action=argparse.BooleanOptionalAction, default=False)
- parser.set_defaults(handler=handlers.Repositories, architecture="", lock=None, report=False, repository="",
- unsafe=True)
- return parser
-
-
-def _set_service_run(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for multicommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("service-run", aliases=["run"], help="run multiple commands",
- description="run multiple commands on success run of the previous command",
- epilog="Commands must be quoted by using usual bash rules. Processes will be spawned "
- "under the same user as this command.",
- formatter_class=_HelpFormatter)
- parser.add_argument("command", help="command to be run (quoted) without ``ahriman``", nargs="+")
- parser.set_defaults(handler=handlers.Run, architecture="", lock=None, report=False, repository="",
- unsafe=True, parser=_parser)
- return parser
-
-
-def _set_service_setup_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for setup subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("service-setup", aliases=["init", "repo-init", "repo-setup", "setup"],
- help="initial service configuration",
- description="create initial service configuration, requires root",
- epilog="Create **minimal** configuration for the service according to provided options.",
- formatter_class=_HelpFormatter)
- parser.add_argument("--build-as-user", help="force makepkg user to the specific one")
- parser.add_argument("--from-configuration", help="path to default devtools pacman configuration",
- type=Path, default=Path("/") / "usr" / "share" / "devtools" / "pacman.conf.d" / "extra.conf")
- parser.add_argument("--generate-salt", help="generate salt for user passwords",
- action=argparse.BooleanOptionalAction, default=False)
- parser.add_argument("--makeflags-jobs", help="append MAKEFLAGS variable with parallelism set to number of cores",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("--mirror", help="use the specified explicitly mirror instead of including mirrorlist")
- parser.add_argument("--multilib", help="add or do not multilib repository",
- action=argparse.BooleanOptionalAction, default=True)
- parser.add_argument("--packager", help="packager name and email", required=True)
- parser.add_argument("--server", help="server to be used for devtools. If none set, local files will be used")
- parser.add_argument("--sign-key", help="sign key id")
- parser.add_argument("--sign-target", help="sign options", action="append",
- type=SignSettings.from_option, choices=enum_values(SignSettings))
- parser.add_argument("--web-port", help="port of the web service", type=int)
- parser.add_argument("--web-unix-socket", help="path to unix socket used for interprocess communications", type=Path)
- parser.set_defaults(handler=handlers.Setup, lock=None, quiet=True, report=False, unsafe=True)
- return parser
-
-
-def _set_service_shell_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for shell subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("service-shell", aliases=["shell"], help="invoke python shell",
- description="drop into python shell", formatter_class=_HelpFormatter)
- parser.add_argument("code", help="instead of dropping into shell, just execute the specified code", nargs="?")
- parser.add_argument("-v", "--verbose", help=argparse.SUPPRESS, action="store_true")
- parser.set_defaults(handler=handlers.Shell, lock=None, report=False)
- return parser
-
-
-def _set_service_tree_migrate_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for tree migration subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("service-tree-migrate", help="migrate repository tree",
- description="migrate repository tree between versions",
- formatter_class=_HelpFormatter)
- parser.set_defaults(handler=handlers.TreeMigrate, lock=None, quiet=True, report=False)
- return parser
-
-
-def _set_user_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for create user subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("user-add", help="create or update user",
- description="update user for web services with the given password and role. "
- "In case if password was not entered it will be asked interactively",
- formatter_class=_HelpFormatter)
- parser.add_argument("username", help="username for web service")
- parser.add_argument("--key", help="optional PGP key used by this user. The private key must be imported")
- parser.add_argument("--packager", help="optional packager id used for build process in form of "
- "`Name Surname `")
- parser.add_argument("-p", "--password", help="user password. Blank password will be treated as empty password, "
- "which is in particular must be used for OAuth2 authorization type.")
- parser.add_argument("-R", "--role", help="user access level",
- type=UserAccess, choices=enum_values(UserAccess), default=UserAccess.Read)
- parser.set_defaults(handler=handlers.Users, action=Action.Update, architecture="", exit_code=False, lock=None,
- quiet=True, report=False, repository="")
- return parser
-
-
-def _set_user_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for user list subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("user-list", help="user known users and their access",
- description="list users from the user mapping and their roles",
- formatter_class=_HelpFormatter)
- parser.add_argument("username", help="filter users by username", nargs="?")
- parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
- parser.add_argument("-R", "--role", help="filter users by role", type=UserAccess, choices=enum_values(UserAccess))
- parser.set_defaults(handler=handlers.Users, action=Action.List, architecture="", lock=None, quiet=True,
- report=False, repository="", unsafe=True)
- return parser
-
-
-def _set_user_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for user removal subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("user-remove", help="remove user",
- description="remove user from the user mapping and update the configuration",
- formatter_class=_HelpFormatter)
- parser.add_argument("username", help="username for web service")
- parser.set_defaults(handler=handlers.Users, action=Action.Remove, architecture="", exit_code=False, lock=None,
- quiet=True, report=False, repository="")
- return parser
-
-
-def _set_web_parser(root: SubParserAction) -> argparse.ArgumentParser:
- """
- add parser for web subcommand
-
- Args:
- root(SubParserAction): subparsers for the commands
-
- Returns:
- argparse.ArgumentParser: created argument parser
- """
- parser = root.add_parser("web", help="web server", description="start web server",
- formatter_class=_HelpFormatter)
- parser.set_defaults(handler=handlers.Web, architecture="", lock=Path("ahriman-web.pid"), report=False,
- repository="", parser=_parser)
- return parser
-
-
def run() -> int:
"""
run application instance
@@ -1219,5 +111,5 @@ def run() -> int:
if args.command is None: # in case of empty command we would like to print help message
args_parser.exit(status=2, message=args_parser.format_help())
- handler: handlers.Handler = args.handler
+ handler: Handler = args.handler
return handler.execute(args)
diff --git a/src/ahriman/application/handlers/__init__.py b/src/ahriman/application/handlers/__init__.py
index 23b88a0f..78e01321 100644
--- a/src/ahriman/application/handlers/__init__.py
+++ b/src/ahriman/application/handlers/__init__.py
@@ -17,37 +17,3 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-from ahriman.application.handlers.add import Add
-from ahriman.application.handlers.backup import Backup
-from ahriman.application.handlers.change import Change
-from ahriman.application.handlers.clean import Clean
-from ahriman.application.handlers.copy import Copy
-from ahriman.application.handlers.daemon import Daemon
-from ahriman.application.handlers.dump import Dump
-from ahriman.application.handlers.handler import Handler
-from ahriman.application.handlers.help import Help
-from ahriman.application.handlers.key_import import KeyImport
-from ahriman.application.handlers.patch import Patch
-from ahriman.application.handlers.rebuild import Rebuild
-from ahriman.application.handlers.remove import Remove
-from ahriman.application.handlers.remove_unknown import RemoveUnknown
-from ahriman.application.handlers.repositories import Repositories
-from ahriman.application.handlers.restore import Restore
-from ahriman.application.handlers.run import Run
-from ahriman.application.handlers.search import Search
-from ahriman.application.handlers.service_updates import ServiceUpdates
-from ahriman.application.handlers.setup import Setup
-from ahriman.application.handlers.shell import Shell
-from ahriman.application.handlers.sign import Sign
-from ahriman.application.handlers.statistics import Statistics
-from ahriman.application.handlers.status import Status
-from ahriman.application.handlers.status_update import StatusUpdate
-from ahriman.application.handlers.structure import Structure
-from ahriman.application.handlers.tree_migrate import TreeMigrate
-from ahriman.application.handlers.triggers import Triggers
-from ahriman.application.handlers.unsafe_commands import UnsafeCommands
-from ahriman.application.handlers.update import Update
-from ahriman.application.handlers.users import Users
-from ahriman.application.handlers.validate import Validate
-from ahriman.application.handlers.versions import Versions
-from ahriman.application.handlers.web import Web
diff --git a/src/ahriman/application/handlers/add.py b/src/ahriman/application/handlers/add.py
index 7fe1817b..b4a67519 100644
--- a/src/ahriman/application/handlers/add.py
+++ b/src/ahriman/application/handlers/add.py
@@ -20,8 +20,10 @@
import argparse
from ahriman.application.application import Application
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
+from ahriman.core.utils import enum_values, extract_user
+from ahriman.models.package_source import PackageSource
from ahriman.models.packagers import Packagers
from ahriman.models.pkgbuild_patch import PkgbuildPatch
from ahriman.models.repository_id import RepositoryId
@@ -66,3 +68,49 @@ class Add(Handler):
application.print_updates(packages, log_fn=application.logger.info)
result = application.update(packages, packagers, bump_pkgrel=args.increment)
Add.check_status(args.exit_code, not result.is_empty)
+
+ @staticmethod
+ def _set_package_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for package addition subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("package-add", aliases=["add", "package-update"], help="add package",
+ description="add existing or new package to the build queue",
+ epilog="This subcommand should be used for new package addition. "
+ "It also supports flag --now in case if you would like to build "
+ "the package immediately. You can add new package from one of "
+ "supported sources:\n\n"
+ "1. If it is already built package you can specify the path to the archive.\n"
+ "2. You can also add built packages from the directory (e.g. during the "
+ "migration from another repository source).\n"
+ "3. It is also possible to add package from local PKGBUILD, but in this case "
+ "it will be ignored during the next automatic updates.\n"
+ "4. Ahriman supports downloading archives from remote (e.g. HTTP) sources.\n"
+ "5. Finally you can add package from AUR.")
+ parser.add_argument("package", help="package source (base name, path to local files, remote URL)", nargs="+")
+ parser.add_argument("--changes", help="calculate changes from the latest known commit if available",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--dependencies", help="process missing package dependencies",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty",
+ action="store_true")
+ parser.add_argument("--increment", help="increment package release (pkgrel) version on duplicate",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("-n", "--now", help="run update function after", action="store_true")
+ parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
+ "-yy to force refresh even if up to date",
+ action="count", default=False)
+ parser.add_argument("-s", "--source", help="explicitly specify the package source for this command",
+ type=PackageSource, choices=enum_values(PackageSource), default=PackageSource.Auto)
+ parser.add_argument("-u", "--username", help="build as user", default=extract_user())
+ parser.add_argument("-v", "--variable", help="apply specified makepkg variables to the next build",
+ action="append")
+ return parser
+
+ arguments = [_set_package_add_parser]
diff --git a/src/ahriman/application/handlers/backup.py b/src/ahriman/application/handlers/backup.py
index 17e03d3a..dec50661 100644
--- a/src/ahriman/application/handlers/backup.py
+++ b/src/ahriman/application/handlers/backup.py
@@ -23,7 +23,7 @@ import tarfile
from pathlib import Path
from pwd import getpwuid
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.models.repository_id import RepositoryId
@@ -53,6 +53,23 @@ class Backup(Handler):
for backup_path in backup_paths:
archive.add(backup_path)
+ @staticmethod
+ def _set_repo_backup_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for repository backup subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("repo-backup", help="backup repository data",
+ description="backup repository settings and database")
+ parser.add_argument("path", help="path of the output archive", type=Path)
+ parser.set_defaults(architecture="", lock=None, report=False, repository="", unsafe=True)
+ return parser
+
@staticmethod
def get_paths(configuration: Configuration) -> set[Path]:
"""
@@ -83,3 +100,5 @@ class Backup(Handler):
paths.add(gnupg_home)
return paths
+
+ arguments = [_set_repo_backup_parser]
diff --git a/src/ahriman/application/handlers/change.py b/src/ahriman/application/handlers/change.py
index f2de007f..5dd4999b 100644
--- a/src/ahriman/application/handlers/change.py
+++ b/src/ahriman/application/handlers/change.py
@@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import ChangesPrinter
from ahriman.models.action import Action
@@ -57,3 +57,43 @@ class Change(Handler):
Change.check_status(args.exit_code, not changes.is_empty)
case Action.Remove:
client.package_changes_update(args.package, Changes())
+
+ @staticmethod
+ def _set_package_changes_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for package changes subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("package-changes", help="get package changes",
+ description="retrieve package changes stored in database",
+ epilog="This command requests package status from the web interface "
+ "if it is available.")
+ 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.set_defaults(action=Action.List, lock=None, quiet=True, report=False, unsafe=True)
+ return parser
+
+ @staticmethod
+ def _set_package_changes_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for package change remove subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("package-changes-remove", help="remove package changes",
+ description="remove the package changes stored remotely")
+ parser.add_argument("package", help="package base")
+ parser.set_defaults(action=Action.Remove, exit_code=False, lock=None, quiet=True, report=False, unsafe=True)
+ return parser
+
+ arguments = [_set_package_changes_parser, _set_package_changes_remove_parser]
diff --git a/src/ahriman/application/handlers/clean.py b/src/ahriman/application/handlers/clean.py
index 49b7a56c..95e533a7 100644
--- a/src/ahriman/application/handlers/clean.py
+++ b/src/ahriman/application/handlers/clean.py
@@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId
@@ -46,3 +46,33 @@ class Clean(Handler):
application.on_start()
application.clean(cache=args.cache, chroot=args.chroot, manual=args.manual, packages=args.packages,
pacman=args.pacman)
+
+ @staticmethod
+ def _set_service_clean_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for repository clean subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("service-clean", aliases=["clean", "repo-clean"], help="clean local caches",
+ description="remove local caches",
+ epilog="The subcommand clears every temporary directories (builds, caches etc). "
+ "Normally you should not run this command manually. Also in case if "
+ "you are going to clear the chroot directories you will need root privileges.")
+ parser.add_argument("--cache", help="clear directory with package caches",
+ action=argparse.BooleanOptionalAction, default=False)
+ parser.add_argument("--chroot", help="clear build chroot", action=argparse.BooleanOptionalAction, default=False)
+ parser.add_argument("--manual", help="clear manually added packages queue",
+ action=argparse.BooleanOptionalAction, default=False)
+ parser.add_argument("--packages", help="clear directory with built packages",
+ action=argparse.BooleanOptionalAction, default=False)
+ parser.add_argument("--pacman", help="clear directory with pacman local database cache",
+ action=argparse.BooleanOptionalAction, default=False)
+ parser.set_defaults(lock=None, quiet=True, unsafe=True)
+ return parser
+
+ arguments = [_set_service_clean_parser]
diff --git a/src/ahriman/application/handlers/copy.py b/src/ahriman/application/handlers/copy.py
index 7add09ad..382d5733 100644
--- a/src/ahriman/application/handlers/copy.py
+++ b/src/ahriman/application/handlers/copy.py
@@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package import Package
@@ -67,6 +67,26 @@ class Copy(Handler):
if args.remove:
source_application.remove(args.package)
+ @staticmethod
+ def _set_package_copy_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for package copy subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("package-copy", aliases=["copy"], help="copy package from another repository",
+ description="copy package and its metadata from another repository")
+ parser.add_argument("source", help="source repository name")
+ parser.add_argument("package", help="package base", nargs="+")
+ parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty",
+ action="store_true")
+ parser.add_argument("--remove", help="remove package from the source repository after", action="store_true")
+ return parser
+
@staticmethod
def copy_package(package: Package, application: Application, source_application: Application) -> None:
"""
@@ -93,3 +113,5 @@ class Copy(Handler):
package.base, source_application.reporter.package_dependencies_get(package.base)
)
application.reporter.package_update(package, BuildStatusEnum.Pending)
+
+ arguments = [_set_package_copy_parser]
diff --git a/src/ahriman/application/handlers/daemon.py b/src/ahriman/application/handlers/daemon.py
index 4979ab4c..3fb3ba8d 100644
--- a/src/ahriman/application/handlers/daemon.py
+++ b/src/ahriman/application/handlers/daemon.py
@@ -19,11 +19,14 @@
#
import argparse
+from pathlib import Path
+
from ahriman.application.application import Application
from ahriman.application.application.updates_iterator import FixedUpdatesIterator, UpdatesIterator
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.application.handlers.update import Update
from ahriman.core.configuration import Configuration
+from ahriman.core.utils import extract_user
from ahriman.models.repository_id import RepositoryId
@@ -56,3 +59,48 @@ class Daemon(Handler):
args.package = packages
Update.run(args, repository_id, configuration, report=report)
+
+ @staticmethod
+ def _set_repo_daemon_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for daemon subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("repo-daemon", aliases=["daemon"], help="run application as daemon",
+ description="start process which periodically will run update process")
+ parser.add_argument("-i", "--interval", help="interval between runs in seconds", type=int, default=60 * 60 * 12)
+ parser.add_argument("--aur", help="enable or disable checking for AUR updates",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--changes", help="calculate changes from the latest known commit if available. "
+ "Only applicable in dry run mode",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--check-files", help="enable or disable checking of broken dependencies "
+ "(e.g. dynamically linked libraries or modules directories)",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--dependencies", help="process missing package dependencies",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--dry-run", help="just perform check for updates, same as check command",
+ action="store_true")
+ parser.add_argument("--increment", help="increment package release (pkgrel) on duplicate",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--local", help="enable or disable checking of local packages for updates",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--manual", help="include or exclude manual updates",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--partitions", help="instead of updating whole repository, split updates into chunks",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("-u", "--username", help="build as user", default=extract_user())
+ parser.add_argument("--vcs", help="fetch actual version of VCS packages",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
+ "-yy to force refresh even if up to date",
+ action="count", default=False)
+ parser.set_defaults(exit_code=False, lock=Path("ahriman-daemon.pid"), package=[])
+ return parser
+
+ arguments = [_set_repo_daemon_parser]
diff --git a/src/ahriman/application/handlers/dump.py b/src/ahriman/application/handlers/dump.py
index 1226d6fc..c9e8bc6f 100644
--- a/src/ahriman/application/handlers/dump.py
+++ b/src/ahriman/application/handlers/dump.py
@@ -19,7 +19,7 @@
#
import argparse
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import ConfigurationPathsPrinter, ConfigurationPrinter, StringPrinter
from ahriman.models.repository_id import RepositoryId
@@ -59,3 +59,27 @@ class Dump(Handler):
case section, key: # key only
value = configuration.get(section, key, fallback="")
StringPrinter(value)(verbose=False)
+
+ @staticmethod
+ def _set_service_config_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for config subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("service-config", aliases=["config", "repo-config"], help="dump configuration",
+ description="dump configuration for the specified architecture")
+ parser.add_argument("section", help="filter settings by section", nargs="?")
+ parser.add_argument("key", help="filter settings by key", nargs="?")
+ parser.add_argument("--info", help="show additional information, e.g. configuration files",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--secure", help="hide passwords and secrets from output",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.set_defaults(lock=None, quiet=True, report=False, unsafe=True)
+ return parser
+
+ arguments = [_set_service_config_parser]
diff --git a/src/ahriman/application/handlers/handler.py b/src/ahriman/application/handlers/handler.py
index 615a01a5..723ed61b 100644
--- a/src/ahriman/application/handlers/handler.py
+++ b/src/ahriman/application/handlers/handler.py
@@ -22,6 +22,7 @@ import logging
from collections.abc import Callable, Iterable
from multiprocessing import Pool
+from typing import TypeVar
from ahriman.application.lock import Lock
from ahriman.core.configuration import Configuration
@@ -32,12 +33,21 @@ from ahriman.models.repository_id import RepositoryId
from ahriman.models.repository_paths import RepositoryPaths
+# this workaround is for several things
+# firstly python devs don't think that is it error and asking you for workarounds https://bugs.python.org/issue41592
+# secondly linters don't like when you are importing private members
+# thirdly new mypy doesn't like _SubParsersAction and thinks it is a template
+SubParserAction = TypeVar("SubParserAction", bound="argparse._SubParsersAction[argparse.ArgumentParser]")
+
+
class Handler:
"""
base handler class for command callbacks
Attributes:
ALLOW_MULTI_ARCHITECTURE_RUN(bool): (class attribute) allow running with multiple architectures
+ arguments(list[Callable[[SubParserAction], argparse.ArgumentParser]]): (class attribute) argument parser
+ methods, which will be called to create command line parsers
Examples:
Wrapper for all command line actions, though each derived class implements :func:`run()` method, it usually
@@ -49,6 +59,7 @@ class Handler:
"""
ALLOW_MULTI_ARCHITECTURE_RUN = True
+ arguments: list[Callable[[SubParserAction], argparse.ArgumentParser]]
@classmethod
def call(cls, args: argparse.Namespace, repository_id: RepositoryId) -> bool:
diff --git a/src/ahriman/application/handlers/help.py b/src/ahriman/application/handlers/help.py
index 793564ae..cb4bbb3d 100644
--- a/src/ahriman/application/handlers/help.py
+++ b/src/ahriman/application/handlers/help.py
@@ -19,7 +19,7 @@
#
import argparse
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId
@@ -48,3 +48,22 @@ class Help(Handler):
parser.parse_args(["--help"])
else:
parser.parse_args([args.subcommand, "--help"])
+
+ @staticmethod
+ def _set_help_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for listing help subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("help", help="show help message",
+ description="show help message for application or command and exit")
+ parser.add_argument("subcommand", help="show help message for specific command", nargs="?")
+ parser.set_defaults(architecture="", lock=None, quiet=True, report=False, repository="", unsafe=True)
+ return parser
+
+ arguments = [_set_help_parser]
diff --git a/src/ahriman/application/handlers/key_import.py b/src/ahriman/application/handlers/key_import.py
index 16ae8001..5c48b428 100644
--- a/src/ahriman/application/handlers/key_import.py
+++ b/src/ahriman/application/handlers/key_import.py
@@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId
@@ -46,3 +46,27 @@ class KeyImport(Handler):
"""
application = Application(repository_id, configuration, report=report)
application.repository.sign.key_import(args.key_server, args.key)
+
+ @staticmethod
+ def _set_service_key_import_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for key import subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("service-key-import", aliases=["key-import"], help="import PGP key",
+ description="import PGP key from public sources to the repository user",
+ epilog="By default ahriman runs build process with package sources validation "
+ "(in case if signature and keys are available in PKGBUILD). This process will "
+ "fail in case if key is not known for build user. This subcommand can be used "
+ "in order to import the PGP key to user keychain.")
+ parser.add_argument("--key-server", help="key server for key import", default="keyserver.ubuntu.com")
+ parser.add_argument("key", help="PGP key to import from public server")
+ parser.set_defaults(architecture="", lock=None, report=False, repository="")
+ return parser
+
+ arguments = [_set_service_key_import_parser]
diff --git a/src/ahriman/application/handlers/patch.py b/src/ahriman/application/handlers/patch.py
index d24a7753..124fa795 100644
--- a/src/ahriman/application/handlers/patch.py
+++ b/src/ahriman/application/handlers/patch.py
@@ -23,7 +23,7 @@ import sys
from pathlib import Path
from ahriman.application.application import Application
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.build_tools.sources import Sources
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import PatchPrinter
@@ -67,6 +67,100 @@ class Patch(Handler):
case Action.Remove:
Patch.patch_set_remove(application, args.package, args.variable)
+ @staticmethod
+ def _set_patch_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for new single-function patch subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("patch-add", help="add patch for PKGBUILD function",
+ description="create or update patched PKGBUILD function or variable",
+ epilog="Unlike ``patch-set-add``, this function allows to patch only one PKGBUILD "
+ "function, e.g. typing ``ahriman patch-add ahriman pkgver`` it will change the "
+ "``pkgver`` inside PKGBUILD, typing ``ahriman patch-add ahriman build()`` "
+ "it will change ``build()`` function inside PKGBUILD.")
+ parser.add_argument("package", help="package base")
+ parser.add_argument("variable", help="PKGBUILD variable or function name. If variable is a function, "
+ "it must end with ()")
+ parser.add_argument("patch", help="path to file which contains function or variable value. If not set, "
+ "the value will be read from stdin", type=Path, nargs="?")
+ parser.set_defaults(action=Action.Update, architecture="", exit_code=False, lock=None, report=False,
+ repository="")
+ return parser
+
+ @staticmethod
+ def _set_patch_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for list patches subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("patch-list", help="list patch sets",
+ description="list available patches 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("-v", "--variable", help="if set, show only patches for specified PKGBUILD variables",
+ action="append")
+ parser.set_defaults(action=Action.List, architecture="", lock=None, report=False, repository="", unsafe=True)
+ return parser
+
+ @staticmethod
+ def _set_patch_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for remove patches subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("patch-remove", help="remove patch set", description="remove patches for the package")
+ parser.add_argument("package", help="package base")
+ parser.add_argument("-v", "--variable",
+ help="should be used for single-function patches in case if you wold like "
+ "to remove only specified PKGBUILD variables. In case if not set, "
+ "it will remove all patches related to the package",
+ action="append")
+ parser.set_defaults(action=Action.Remove, architecture="", exit_code=False, lock=None, report=False,
+ repository="")
+ return parser
+
+ @staticmethod
+ def _set_patch_set_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for new full-diff patch subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("patch-set-add", help="add patch set", description="create or update source patches",
+ epilog="In order to add a patch set for the package you will need to:\n\n"
+ "1. Clone the AUR package manually.\n"
+ "2. Add required changes (e.g. external patches, edit PKGBUILD).\n"
+ "3. Run command, e.g. ``ahriman patch-set-add path/to/directory``.\n\n"
+ "By default it tracks ``*.patch`` and ``*.diff`` files, but this behavior "
+ "can be changed by using ``--track`` option.")
+ parser.add_argument("package", help="path to directory with changed files for patch addition/update", type=Path)
+ parser.add_argument("-t", "--track", help="files which has to be tracked", action="append",
+ default=["*.diff", "*.patch"])
+ parser.set_defaults(action=Action.Update, architecture="", exit_code=False, lock=None, report=False,
+ repository="", variable=None)
+ return parser
+
@staticmethod
def patch_create_from_diff(sources_dir: Path, architecture: str, track: list[str]) -> tuple[str, PkgbuildPatch]:
"""
@@ -155,3 +249,10 @@ class Patch(Handler):
application.reporter.package_patches_remove(package_base, variable)
else:
application.reporter.package_patches_remove(package_base, None) # just pass as is
+
+ arguments = [
+ _set_patch_add_parser,
+ _set_patch_list_parser,
+ _set_patch_remove_parser,
+ _set_patch_set_add_parser,
+ ]
diff --git a/src/ahriman/application/handlers/rebuild.py b/src/ahriman/application/handlers/rebuild.py
index 40f20f34..ad5dfa43 100644
--- a/src/ahriman/application/handlers/rebuild.py
+++ b/src/ahriman/application/handlers/rebuild.py
@@ -20,8 +20,9 @@
import argparse
from ahriman.application.application import Application
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
+from ahriman.core.utils import enum_values, extract_user
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package import Package
from ahriman.models.packagers import Packagers
@@ -59,6 +60,38 @@ class Rebuild(Handler):
result = application.update(packages, Packagers(args.username), bump_pkgrel=args.increment)
Rebuild.check_status(args.exit_code, not result.is_empty)
+ @staticmethod
+ def _set_repo_rebuild_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for repository rebuild subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("repo-rebuild", aliases=["rebuild"], help="rebuild repository",
+ description="force rebuild whole repository")
+ parser.add_argument("--depends-on", help="only rebuild packages that depend on specified packages",
+ action="append")
+ parser.add_argument("--dry-run", help="just perform check for packages without rebuild process itself",
+ action="store_true")
+ parser.add_argument("--from-database",
+ help="read packages from database instead of filesystem. This feature in particular is "
+ "required in case if you would like to restore repository from another repository "
+ "instance. Note, however, that in order to restore packages you need to have original "
+ "ahriman instance run with web service and have run repo-update at least once.",
+ action="store_true")
+ parser.add_argument("--increment", help="increment package release (pkgrel) on duplicate",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty",
+ action="store_true")
+ parser.add_argument("-s", "--status", help="filter packages by status. Requires --from-database to be set",
+ type=BuildStatusEnum, choices=enum_values(BuildStatusEnum))
+ parser.add_argument("-u", "--username", help="build as user", default=extract_user())
+ return parser
+
@staticmethod
def extract_packages(application: Application, status: BuildStatusEnum | None, *,
from_database: bool) -> list[Package]:
@@ -81,3 +114,5 @@ class Rebuild(Handler):
]
return application.repository.packages()
+
+ arguments = [_set_repo_rebuild_parser]
diff --git a/src/ahriman/application/handlers/remove.py b/src/ahriman/application/handlers/remove.py
index 033f34ca..8a553da0 100644
--- a/src/ahriman/application/handlers/remove.py
+++ b/src/ahriman/application/handlers/remove.py
@@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId
@@ -45,3 +45,21 @@ class Remove(Handler):
application = Application(repository_id, configuration, report=report)
application.on_start()
application.remove(args.package)
+
+ @staticmethod
+ def _set_package_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for package removal subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("package-remove", aliases=["remove"], help="remove package",
+ description="remove package from the repository")
+ parser.add_argument("package", help="package name or base", nargs="+")
+ return parser
+
+ arguments = [_set_package_remove_parser]
diff --git a/src/ahriman/application/handlers/remove_unknown.py b/src/ahriman/application/handlers/remove_unknown.py
index d062416e..200c0be5 100644
--- a/src/ahriman/application/handlers/remove_unknown.py
+++ b/src/ahriman/application/handlers/remove_unknown.py
@@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import StringPrinter
from ahriman.models.repository_id import RepositoryId
@@ -53,3 +53,21 @@ class RemoveUnknown(Handler):
return
application.remove(unknown_packages)
+
+ @staticmethod
+ def _set_repo_remove_unknown_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for remove unknown packages subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("repo-remove-unknown", aliases=["remove-unknown"], help="remove unknown packages",
+ description="remove packages which are missing in AUR and do not have local PKGBUILDs")
+ parser.add_argument("--dry-run", help="just perform check for packages without removal", action="store_true")
+ return parser
+
+ arguments = [_set_repo_remove_unknown_parser]
diff --git a/src/ahriman/application/handlers/repositories.py b/src/ahriman/application/handlers/repositories.py
index 049d777e..fbc790c0 100644
--- a/src/ahriman/application/handlers/repositories.py
+++ b/src/ahriman/application/handlers/repositories.py
@@ -19,7 +19,7 @@
#
import argparse
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import RepositoryPrinter
from ahriman.models.repository_id import RepositoryId
@@ -52,3 +52,23 @@ class Repositories(Handler):
)
for repository in cls.repositories_extract(dummy_args):
RepositoryPrinter(repository)(verbose=not args.id_only)
+
+ @staticmethod
+ def _set_service_repositories(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for repositories listing
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("service-repositories", help="show repositories",
+ description="list all available repositories")
+ parser.add_argument("--id-only", help="show machine readable identifier instead",
+ action=argparse.BooleanOptionalAction, default=False)
+ parser.set_defaults(architecture="", lock=None, report=False, repository="", unsafe=True)
+ return parser
+
+ arguments = [_set_service_repositories]
diff --git a/src/ahriman/application/handlers/restore.py b/src/ahriman/application/handlers/restore.py
index 969d67fc..94c4dd95 100644
--- a/src/ahriman/application/handlers/restore.py
+++ b/src/ahriman/application/handlers/restore.py
@@ -20,7 +20,9 @@
import argparse
import tarfile
-from ahriman.application.handlers.handler import Handler
+from pathlib import Path
+
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId
@@ -46,3 +48,23 @@ class Restore(Handler):
"""
with tarfile.open(args.path) as archive:
archive.extractall(path=args.output) # nosec
+
+ @staticmethod
+ def _set_repo_restore_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for repository restore subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("repo-restore", help="restore repository data",
+ description="restore settings and database")
+ parser.add_argument("path", help="path of the input archive", type=Path)
+ parser.add_argument("-o", "--output", help="root path of the extracted files", type=Path, default=Path("/"))
+ parser.set_defaults(architecture="", lock=None, report=False, repository="", unsafe=True)
+ return parser
+
+ arguments = [_set_repo_restore_parser]
diff --git a/src/ahriman/application/handlers/run.py b/src/ahriman/application/handlers/run.py
index 051c608a..c244c0e1 100644
--- a/src/ahriman/application/handlers/run.py
+++ b/src/ahriman/application/handlers/run.py
@@ -20,7 +20,7 @@
import argparse
import shlex
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId
@@ -49,6 +49,25 @@ class Run(Handler):
status = Run.run_command(shlex.split(command), parser)
Run.check_status(True, status)
+ @staticmethod
+ def _set_service_run(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for multicommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("service-run", aliases=["run"], help="run multiple commands",
+ description="run multiple commands on success run of the previous command",
+ epilog="Commands must be quoted by using usual bash rules. Processes will be spawned "
+ "under the same user as this command.")
+ parser.add_argument("command", help="command to be run (quoted) without ``ahriman``", nargs="+")
+ parser.set_defaults(architecture="", lock=None, report=False, repository="", unsafe=True)
+ return parser
+
@staticmethod
def run_command(command: list[str], parser: argparse.ArgumentParser) -> bool:
"""
@@ -64,3 +83,5 @@ class Run(Handler):
args = parser.parse_args(command)
handler: Handler = args.handler
return handler.execute(args) == 0
+
+ arguments = [_set_service_run]
diff --git a/src/ahriman/application/handlers/search.py b/src/ahriman/application/handlers/search.py
index 5df8a02c..875d1423 100644
--- a/src/ahriman/application/handlers/search.py
+++ b/src/ahriman/application/handlers/search.py
@@ -22,7 +22,7 @@ import argparse
from collections.abc import Callable, Iterable
from dataclasses import fields
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.alpm.remote import AUR, Official
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import OptionError
@@ -68,6 +68,33 @@ class Search(Handler):
for package in Search.sort(packages_list, args.sort_by):
AurPrinter(package)(verbose=args.info)
+ @staticmethod
+ def _set_aur_search_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for AUR search subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("aur-search", aliases=["search"], help="search for package",
+ description="search for package in AUR using API")
+ parser.add_argument("search",
+ help="search terms, can be specified multiple times, the result will match all terms",
+ nargs="+")
+ 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.add_argument("--sort-by",
+ help="sort field by this field. In case if two packages have the same value of "
+ "the specified field, they will be always sorted by name",
+ default="name", choices=sorted(Search.SORT_FIELDS))
+ parser.set_defaults(architecture="", lock=None, quiet=True, report=False, repository="", unsafe=True)
+ return parser
+
@staticmethod
def sort(packages: Iterable[AURPackage], sort_by: str) -> list[AURPackage]:
"""
@@ -90,3 +117,5 @@ class Search(Handler):
comparator: Callable[[AURPackage], tuple[str, str]] =\
lambda package: (getattr(package, sort_by), package.name)
return sorted(packages, key=comparator)
+
+ arguments = [_set_aur_search_parser]
diff --git a/src/ahriman/application/handlers/service_updates.py b/src/ahriman/application/handlers/service_updates.py
index db2582aa..3e073818 100644
--- a/src/ahriman/application/handlers/service_updates.py
+++ b/src/ahriman/application/handlers/service_updates.py
@@ -20,7 +20,7 @@
import argparse
from ahriman import __version__
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import UpdatePrinter
from ahriman.models.package import Package
@@ -58,3 +58,23 @@ class ServiceUpdates(Handler):
UpdatePrinter(remote, local_version)(verbose=True, separator=" -> ")
ServiceUpdates.check_status(args.exit_code, same_version)
+
+ @staticmethod
+ def _set_help_updates_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for service update check subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("help-updates", help="check for service updates",
+ description="request AUR for current version and compare with current service version")
+ parser.add_argument("-e", "--exit-code", help="return non-zero exit code if updates available",
+ action="store_true")
+ parser.set_defaults(architecture="", lock=None, quiet=True, report=False, repository="", unsafe=True)
+ return parser
+
+ arguments = [_set_help_updates_parser]
diff --git a/src/ahriman/application/handlers/setup.py b/src/ahriman/application/handlers/setup.py
index fd038ef5..44ecc845 100644
--- a/src/ahriman/application/handlers/setup.py
+++ b/src/ahriman/application/handlers/setup.py
@@ -24,11 +24,13 @@ from pwd import getpwuid
from urllib.parse import quote_plus as urlencode
from ahriman.application.application import Application
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import MissingArchitectureError
+from ahriman.core.utils import enum_values
from ahriman.models.repository_id import RepositoryId
from ahriman.models.repository_paths import RepositoryPaths
+from ahriman.models.sign_settings import SignSettings
from ahriman.models.user import User
@@ -80,6 +82,44 @@ class Setup(Handler):
# lazy database sync
application.repository.pacman.handle # pylint: disable=pointless-statement
+ @staticmethod
+ def _set_service_setup_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for setup subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("service-setup", aliases=["init", "repo-init", "repo-setup", "setup"],
+ help="initial service configuration",
+ description="create initial service configuration, requires root",
+ epilog="Create minimal configuration for the service according to provided options.")
+ parser.add_argument("--build-as-user", help="force makepkg user to the specific one")
+ parser.add_argument("--from-configuration", help="path to default devtools pacman configuration",
+ type=Path,
+ default=Path("/") / "usr" / "share" / "devtools" / "pacman.conf.d" / "extra.conf")
+ parser.add_argument("--generate-salt", help="generate salt for user passwords",
+ action=argparse.BooleanOptionalAction, default=False)
+ parser.add_argument("--makeflags-jobs",
+ help="append MAKEFLAGS variable with parallelism set to number of cores",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--mirror", help="use the specified explicitly mirror instead of including mirrorlist")
+ parser.add_argument("--multilib", help="add or do not multilib repository",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--packager", help="packager name and email", required=True)
+ parser.add_argument("--server", help="server to be used for devtools. If none set, local files will be used")
+ parser.add_argument("--sign-key", help="sign key id")
+ parser.add_argument("--sign-target", help="sign options", action="append",
+ type=SignSettings.from_option, choices=enum_values(SignSettings))
+ parser.add_argument("--web-port", help="port of the web service", type=int)
+ parser.add_argument("--web-unix-socket", help="path to unix socket used for interprocess communications",
+ type=Path)
+ parser.set_defaults(lock=None, quiet=True, report=False, unsafe=True)
+ return parser
+
@staticmethod
def build_command(root: Path, repository_id: RepositoryId) -> Path:
"""
@@ -240,3 +280,5 @@ class Setup(Handler):
command.unlink(missing_ok=True)
command.symlink_to(Setup.ARCHBUILD_COMMAND_PATH)
paths.chown(command) # we would like to keep owner inside ahriman's home
+
+ arguments = [_set_service_setup_parser]
diff --git a/src/ahriman/application/handlers/shell.py b/src/ahriman/application/handlers/shell.py
index c7ae94c6..750f3a09 100644
--- a/src/ahriman/application/handlers/shell.py
+++ b/src/ahriman/application/handlers/shell.py
@@ -23,7 +23,7 @@ import sys
from pathlib import Path
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import StringPrinter
from ahriman.models.repository_id import RepositoryId
@@ -63,3 +63,23 @@ class Shell(Handler):
code.interact(local=local_variables)
else:
code.InteractiveConsole(locals=local_variables).runcode(args.code)
+
+ @staticmethod
+ def _set_service_shell_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for shell subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("service-shell", aliases=["shell"], help="invoke python shell",
+ description="drop into python shell")
+ parser.add_argument("code", help="instead of dropping into shell, just execute the specified code", nargs="?")
+ parser.add_argument("-v", "--verbose", help=argparse.SUPPRESS, action="store_true")
+ parser.set_defaults(lock=None, report=False)
+ return parser
+
+ arguments = [_set_service_shell_parser]
diff --git a/src/ahriman/application/handlers/sign.py b/src/ahriman/application/handlers/sign.py
index 50af427b..5cfc31cc 100644
--- a/src/ahriman/application/handlers/sign.py
+++ b/src/ahriman/application/handlers/sign.py
@@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId
@@ -43,3 +43,22 @@ class Sign(Handler):
report(bool): force enable or disable reporting
"""
Application(repository_id, configuration, report=report).sign(args.package)
+
+ @staticmethod
+ def _set_repo_sign_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for sign subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("repo-sign", aliases=["sign"], help="sign packages",
+ description="(re-)sign packages and repository database according to current settings",
+ epilog="Sign repository and/or packages as configured.")
+ parser.add_argument("package", help="sign only specified packages", nargs="*")
+ return parser
+
+ arguments = [_set_repo_sign_parser]
diff --git a/src/ahriman/application/handlers/statistics.py b/src/ahriman/application/handlers/statistics.py
index aef8320c..c538b621 100644
--- a/src/ahriman/application/handlers/statistics.py
+++ b/src/ahriman/application/handlers/statistics.py
@@ -25,11 +25,11 @@ from collections.abc import Callable
from pathlib import Path
from ahriman.application.application import Application
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import EventStatsPrinter, PackageStatsPrinter
-from ahriman.core.utils import pretty_datetime
-from ahriman.models.event import Event
+from ahriman.core.utils import enum_values, pretty_datetime
+from ahriman.models.event import Event, EventType
from ahriman.models.repository_id import RepositoryId
@@ -68,6 +68,30 @@ class Statistics(Handler):
case _:
Statistics.stats_for_package(args.event, events, args.chart)
+ @staticmethod
+ def _set_repo_statistics_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for repository statistics subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("repo-statistics", help="repository statistics",
+ description="fetch repository statistics")
+ parser.add_argument("package", help="fetch only events for the specified package", nargs="?")
+ parser.add_argument("--chart", help="create updates chart and save it to the specified path", type=Path)
+ parser.add_argument("-e", "--event", help="event type filter",
+ type=EventType, choices=enum_values(EventType), default=EventType.PackageUpdated)
+ parser.add_argument("--from-date", help="only fetch events which are newer than the date")
+ parser.add_argument("--limit", help="limit response by specified amount of events", type=int, default=-1)
+ parser.add_argument("--offset", help="skip specified amount of events", type=int, default=0)
+ parser.add_argument("--to-date", help="only fetch events which are older than the date")
+ parser.set_defaults(lock=None, quiet=True, report=False, unsafe=True)
+ return parser
+
@staticmethod
def event_stats(event_type: str, events: list[Event]) -> None:
"""
@@ -168,3 +192,5 @@ class Statistics(Handler):
# chart if enabled
if chart_path is not None:
Statistics.plot_packages(event_type, by_object_id, chart_path)
+
+ arguments = [_set_repo_statistics_parser]
diff --git a/src/ahriman/application/handlers/status.py b/src/ahriman/application/handlers/status.py
index ddbfca3d..2d007b05 100644
--- a/src/ahriman/application/handlers/status.py
+++ b/src/ahriman/application/handlers/status.py
@@ -22,10 +22,11 @@ import argparse
from collections.abc import Callable
from ahriman.application.application import Application
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import PackagePrinter, StatusPrinter
-from ahriman.models.build_status import BuildStatus
+from ahriman.core.utils import enum_values
+from ahriman.models.build_status import BuildStatus, BuildStatusEnum
from ahriman.models.package import Package
from ahriman.models.repository_id import RepositoryId
@@ -68,3 +69,31 @@ class Status(Handler):
lambda item: args.status is None or item[1].status == args.status
for package, package_status in sorted(filter(filter_fn, packages), key=comparator):
PackagePrinter(package, package_status)(verbose=args.info)
+
+ @staticmethod
+ def _set_package_status_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for package status subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("package-status", aliases=["status"], help="get package status",
+ description="request status of the package",
+ epilog="This command requests package status from the web interface "
+ "if it is available.")
+ parser.add_argument("package", help="filter status by package base", nargs="*")
+ parser.add_argument("--ahriman", help="get service status itself", action="store_true")
+ 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.add_argument("-s", "--status", help="filter packages by status",
+ type=BuildStatusEnum, choices=enum_values(BuildStatusEnum))
+ parser.set_defaults(lock=None, quiet=True, report=False, unsafe=True)
+ return parser
+
+ arguments = [_set_package_status_parser]
diff --git a/src/ahriman/application/handlers/status_update.py b/src/ahriman/application/handlers/status_update.py
index 085060fe..d46103be 100644
--- a/src/ahriman/application/handlers/status_update.py
+++ b/src/ahriman/application/handlers/status_update.py
@@ -20,9 +20,11 @@
import argparse
from ahriman.application.application import Application
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
+from ahriman.core.utils import enum_values
from ahriman.models.action import Action
+from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.repository_id import RepositoryId
@@ -59,3 +61,67 @@ class StatusUpdate(Handler):
case Action.Remove:
for package in args.package:
client.package_remove(package)
+
+ @staticmethod
+ def _set_package_status_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for package status remove subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("package-status-remove", help="remove package status",
+ description="remove the package from the status page",
+ epilog="Please note that this subcommand does not remove the package itself, it just "
+ "clears the status page.")
+ parser.add_argument("package", help="remove specified packages from status page", nargs="+")
+ parser.set_defaults(action=Action.Remove, lock=None, quiet=True, report=False, unsafe=True)
+ return parser
+
+ @staticmethod
+ def _set_package_status_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for package status update subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("package-status-update", aliases=["status-update"], help="update package status",
+ description="update package status on the status page")
+ parser.add_argument("package", help="set status for specified packages. "
+ "If no packages supplied, service status will be updated",
+ nargs="*")
+ parser.add_argument("-s", "--status", help="new package build status",
+ type=BuildStatusEnum, choices=enum_values(BuildStatusEnum), default=BuildStatusEnum.Success)
+ parser.set_defaults(action=Action.Update, lock=None, quiet=True, report=False, unsafe=True)
+ return parser
+
+ @staticmethod
+ def _set_repo_status_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for repository status update subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("repo-status-update", help="update repository status",
+ description="update repository status on the status page")
+ parser.add_argument("-s", "--status", help="new status",
+ type=BuildStatusEnum, choices=enum_values(BuildStatusEnum), default=BuildStatusEnum.Success)
+ parser.set_defaults(action=Action.Update, lock=None, package=[], quiet=True, report=False, unsafe=True)
+ return parser
+
+ arguments = [
+ _set_package_status_remove_parser,
+ _set_package_status_update_parser,
+ _set_repo_status_update_parser,
+ ]
diff --git a/src/ahriman/application/handlers/structure.py b/src/ahriman/application/handlers/structure.py
index efb2d11e..e546a785 100644
--- a/src/ahriman/application/handlers/structure.py
+++ b/src/ahriman/application/handlers/structure.py
@@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import StringPrinter, TreePrinter
from ahriman.core.tree import Tree
@@ -58,3 +58,23 @@ class Structure(Handler):
# empty line
StringPrinter("")(verbose=False)
+
+ @staticmethod
+ def _set_repo_tree_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for repository tree subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("repo-tree", help="dump repository tree",
+ description="dump repository tree based on packages dependencies")
+ parser.add_argument("-p", "--partitions", help="also divide packages by independent partitions",
+ type=int, default=1)
+ parser.set_defaults(lock=None, quiet=True, report=False, unsafe=True)
+ return parser
+
+ arguments = [_set_repo_tree_parser]
diff --git a/src/ahriman/application/handlers/tree_migrate.py b/src/ahriman/application/handlers/tree_migrate.py
index e29598fd..1984e132 100644
--- a/src/ahriman/application/handlers/tree_migrate.py
+++ b/src/ahriman/application/handlers/tree_migrate.py
@@ -19,7 +19,7 @@
#
import argparse
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId
from ahriman.models.repository_paths import RepositoryPaths
@@ -50,6 +50,22 @@ class TreeMigrate(Handler):
# perform migration
TreeMigrate.tree_move(current_tree, target_tree)
+ @staticmethod
+ def _set_service_tree_migrate_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for tree migration subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("service-tree-migrate", help="migrate repository tree",
+ description="migrate repository tree between versions")
+ parser.set_defaults(lock=None, quiet=True, report=False)
+ return parser
+
@staticmethod
def tree_move(from_tree: RepositoryPaths, to_tree: RepositoryPaths) -> None:
"""
@@ -66,3 +82,5 @@ class TreeMigrate(Handler):
RepositoryPaths.repository,
):
attribute.fget(from_tree).rename(attribute.fget(to_tree)) # type: ignore[attr-defined]
+
+ arguments = [_set_service_tree_migrate_parser]
diff --git a/src/ahriman/application/handlers/triggers.py b/src/ahriman/application/handlers/triggers.py
index f3739d90..023a53d9 100644
--- a/src/ahriman/application/handlers/triggers.py
+++ b/src/ahriman/application/handlers/triggers.py
@@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId
from ahriman.models.result import Result
@@ -49,3 +49,98 @@ class Triggers(Handler):
loader.triggers = [loader.load_trigger(trigger, repository_id, configuration) for trigger in args.trigger]
application.on_start()
application.on_result(Result())
+
+ @staticmethod
+ def _set_repo_create_keyring_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for create-keyring subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("repo-create-keyring", help="create keyring package",
+ description="create package which contains list of trusted keys as set by "
+ "configuration. Note, that this action will only create package, "
+ "the package itself has to be built manually")
+ parser.set_defaults(trigger=["ahriman.core.support.KeyringTrigger"])
+ return parser
+
+ @staticmethod
+ def _set_repo_create_mirrorlist_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for create-mirrorlist subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("repo-create-mirrorlist", help="create mirrorlist package",
+ description="create package which contains list of available mirrors as set by "
+ "configuration. Note, that this action will only create package, "
+ "the package itself has to be built manually")
+ parser.set_defaults(trigger=["ahriman.core.support.MirrorlistTrigger"])
+ return parser
+
+ @staticmethod
+ def _set_repo_report_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for report subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("repo-report", aliases=["report"], help="generate report",
+ description="generate repository report according to current settings",
+ epilog="Create and/or update repository report as configured.")
+ parser.set_defaults(trigger=["ahriman.core.report.ReportTrigger"])
+ return parser
+
+ @staticmethod
+ def _set_repo_sync_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for repository sync subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("repo-sync", aliases=["sync"], help="sync repository",
+ description="sync repository files to remote server according to current settings",
+ epilog="Synchronize the repository to remote services as configured.")
+ parser.set_defaults(trigger=["ahriman.core.upload.UploadTrigger"])
+ return parser
+
+ @staticmethod
+ def _set_repo_triggers_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for repository triggers subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("repo-triggers", help="run triggers",
+ description="run triggers on empty build result as configured by settings")
+ parser.add_argument("trigger", help="instead of running all triggers as set by configuration, just process "
+ "specified ones in order of mention", nargs="*")
+ return parser
+
+ arguments = [
+ _set_repo_create_keyring_parser,
+ _set_repo_create_mirrorlist_parser,
+ _set_repo_report_parser,
+ _set_repo_sync_parser,
+ _set_repo_triggers_parser,
+ ]
diff --git a/src/ahriman/application/handlers/unsafe_commands.py b/src/ahriman/application/handlers/unsafe_commands.py
index 11a6347d..7f6da703 100644
--- a/src/ahriman/application/handlers/unsafe_commands.py
+++ b/src/ahriman/application/handlers/unsafe_commands.py
@@ -19,7 +19,7 @@
#
import argparse
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import StringPrinter
from ahriman.models.repository_id import RepositoryId
@@ -52,6 +52,25 @@ class UnsafeCommands(Handler):
for command in unsafe_commands:
StringPrinter(command)(verbose=True)
+ @staticmethod
+ def _set_help_commands_unsafe_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for listing unsafe commands
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("help-commands-unsafe", help="list unsafe commands",
+ description="list unsafe commands as defined in default args")
+ parser.add_argument("subcommand",
+ help="instead of showing commands, just test command line for unsafe subcommand "
+ "and return 0 in case if command is safe and 1 otherwise", nargs="*")
+ parser.set_defaults(architecture="", lock=None, quiet=True, report=False, repository="", unsafe=True)
+ return parser
+
@staticmethod
def check_unsafe(command: list[str], unsafe_commands: list[str], parser: argparse.ArgumentParser) -> None:
"""
@@ -81,3 +100,5 @@ class UnsafeCommands(Handler):
subparser = next((action for action in parser._actions if isinstance(action, argparse._SubParsersAction)), None)
actions = subparser.choices if subparser is not None else {}
return sorted(action_name for action_name, action in actions.items() if action.get_default("unsafe"))
+
+ arguments = [_set_help_commands_unsafe_parser]
diff --git a/src/ahriman/application/handlers/update.py b/src/ahriman/application/handlers/update.py
index dafcac61..1f965c2d 100644
--- a/src/ahriman/application/handlers/update.py
+++ b/src/ahriman/application/handlers/update.py
@@ -22,8 +22,9 @@ import argparse
from collections.abc import Callable
from ahriman.application.application import Application
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
+from ahriman.core.utils import extract_user
from ahriman.models.packagers import Packagers
from ahriman.models.repository_id import RepositoryId
@@ -64,6 +65,78 @@ class Update(Handler):
result = application.update(packages, packagers, bump_pkgrel=args.increment)
Update.check_status(args.exit_code, not result.is_empty)
+ @staticmethod
+ def _set_repo_check_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for repository check subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("repo-check", aliases=["check"], help="check for updates",
+ description="check for packages updates. Same as repo-update --dry-run --no-manual")
+ parser.add_argument("package", help="filter check by package base", nargs="*")
+ parser.add_argument("--changes", help="calculate changes from the latest known commit if available",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--check-files", help="enable or disable checking of broken dependencies "
+ "(e.g. dynamically linked libraries or modules directories)",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty",
+ action="store_true")
+ parser.add_argument("--vcs", help="fetch actual version of VCS packages",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
+ "-yy to force refresh even if up to date",
+ action="count", default=False)
+ parser.set_defaults(aur=True, dependencies=False, dry_run=True, increment=False, local=True, manual=False,
+ username=None)
+ return parser
+
+ @staticmethod
+ def _set_repo_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for repository update subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("repo-update", aliases=["update"], help="update packages",
+ description="check for packages updates and run build process if requested")
+ parser.add_argument("package", help="filter check by package base", nargs="*")
+ parser.add_argument("--aur", help="enable or disable checking for AUR updates",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--changes", help="calculate changes from the latest known commit if available. "
+ "Only applicable in dry run mode",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--check-files", help="enable or disable checking of broken dependencies "
+ "(e.g. dynamically linked libraries or modules directories)",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--dependencies", help="process missing package dependencies",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--dry-run", help="just perform check for updates, same as check command",
+ action="store_true")
+ parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty",
+ action="store_true")
+ parser.add_argument("--increment", help="increment package release (pkgrel) on duplicate",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--local", help="enable or disable checking of local packages for updates",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("--manual", help="include or exclude manual updates",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("-u", "--username", help="build as user", default=extract_user())
+ parser.add_argument("--vcs", help="fetch actual version of VCS packages",
+ action=argparse.BooleanOptionalAction, default=True)
+ parser.add_argument("-y", "--refresh", help="download fresh package databases from the mirror before actions, "
+ "-yy to force refresh even if up to date",
+ action="count", default=False)
+ return parser
+
@staticmethod
def log_fn(application: Application, dry_run: bool) -> Callable[[str], None]:
"""
@@ -79,3 +152,8 @@ class Update(Handler):
def inner(line: str) -> None:
return print(line) if dry_run else application.logger.info(line) # pylint: disable=bad-builtin
return inner
+
+ arguments = [
+ _set_repo_check_parser,
+ _set_repo_update_parser,
+ ]
diff --git a/src/ahriman/application/handlers/users.py b/src/ahriman/application/handlers/users.py
index c34d6201..53587edb 100644
--- a/src/ahriman/application/handlers/users.py
+++ b/src/ahriman/application/handlers/users.py
@@ -20,14 +20,16 @@
import argparse
import getpass
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.exceptions import PasswordError
from ahriman.core.formatters import UserPrinter
+from ahriman.core.utils import enum_values
from ahriman.models.action import Action
from ahriman.models.repository_id import RepositoryId
from ahriman.models.user import User
+from ahriman.models.user_access import UserAccess
class Users(Handler):
@@ -65,6 +67,73 @@ class Users(Handler):
case Action.Remove:
database.user_remove(args.username)
+ @staticmethod
+ def _set_user_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for create user subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("user-add", help="create or update user",
+ description="update user for web services with the given password and role. "
+ "In case if password was not entered it will be asked interactively")
+ parser.add_argument("username", help="username for web service")
+ parser.add_argument("--key", help="optional PGP key used by this user. The private key must be imported")
+ parser.add_argument("--packager", help="optional packager id used for build process in form of "
+ "`Name Surname `")
+ parser.add_argument(
+ "-p", "--password", help="user password. Blank password will be treated as empty password, "
+ "which is in particular must be used for OAuth2 authorization type.")
+ parser.add_argument("-R", "--role", help="user access level",
+ type=UserAccess, choices=enum_values(UserAccess), default=UserAccess.Read)
+ parser.set_defaults(action=Action.Update, architecture="", exit_code=False, lock=None, quiet=True,
+ report=False, repository="")
+ return parser
+
+ @staticmethod
+ def _set_user_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for user list subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("user-list", help="user known users and their access",
+ description="list users from the user mapping and their roles")
+ parser.add_argument("username", help="filter users by username", nargs="?")
+ parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty",
+ action="store_true")
+ parser.add_argument("-R", "--role", help="filter users by role", type=UserAccess,
+ choices=enum_values(UserAccess))
+ parser.set_defaults(action=Action.List, architecture="", lock=None, quiet=True, report=False, repository="",
+ unsafe=True)
+ return parser
+
+ @staticmethod
+ def _set_user_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for user removal subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("user-remove", help="remove user",
+ description="remove user from the user mapping and update the configuration")
+ parser.add_argument("username", help="username for web service")
+ parser.set_defaults(action=Action.Remove, architecture="", exit_code=False, lock=None, quiet=True,
+ report=False, repository="")
+ return parser
+
@staticmethod
def user_create(args: argparse.Namespace) -> User:
"""
@@ -92,3 +161,9 @@ class Users(Handler):
return User(username=args.username, password=password, access=args.role,
packager_id=args.packager, key=args.key)
+
+ arguments = [
+ _set_user_add_parser,
+ _set_user_list_parser,
+ _set_user_remove_parser,
+ ]
diff --git a/src/ahriman/application/handlers/validate.py b/src/ahriman/application/handlers/validate.py
index 34694a51..336893f5 100644
--- a/src/ahriman/application/handlers/validate.py
+++ b/src/ahriman/application/handlers/validate.py
@@ -22,7 +22,7 @@ import copy
from typing import Any
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.core.configuration.schema import CONFIGURATION_SCHEMA, ConfigurationSchema
from ahriman.core.exceptions import ExtensionError
@@ -63,6 +63,25 @@ class Validate(Handler):
# as we reach this part it means that we always have errors
Validate.check_status(args.exit_code, False)
+ @staticmethod
+ def _set_service_config_validate_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for config validation subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("service-config-validate", aliases=["config-validate", "repo-config-validate"],
+ help="validate system configuration",
+ description="validate configuration and print found errors")
+ parser.add_argument("-e", "--exit-code", help="return non-zero exit status if configuration is invalid",
+ action="store_true")
+ parser.set_defaults(lock=None, quiet=True, report=False, unsafe=True)
+ return parser
+
@staticmethod
def schema(repository_id: RepositoryId, configuration: Configuration) -> ConfigurationSchema:
"""
@@ -136,3 +155,5 @@ class Validate(Handler):
Validate.schema_merge(value, schema[key])
return schema
+
+ arguments = [_set_service_config_validate_parser]
diff --git a/src/ahriman/application/handlers/versions.py b/src/ahriman/application/handlers/versions.py
index a187734e..75379cc1 100644
--- a/src/ahriman/application/handlers/versions.py
+++ b/src/ahriman/application/handlers/versions.py
@@ -25,7 +25,7 @@ from collections.abc import Generator
from importlib import metadata
from ahriman import __version__
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import VersionPrinter
from ahriman.models.repository_id import RepositoryId
@@ -59,6 +59,22 @@ class Versions(Handler):
packages = Versions.package_dependencies("ahriman")
VersionPrinter("Installed packages", dict(packages))(verbose=False, separator=" ")
+ @staticmethod
+ def _set_help_version_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for version subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("help-version", aliases=["version"], help="application version",
+ description="print application and its dependencies versions")
+ parser.set_defaults(architecture="", lock=None, quiet=True, report=False, repository="", unsafe=True)
+ return parser
+
@staticmethod
def package_dependencies(root: str) -> Generator[tuple[str, str], None, None]:
"""
@@ -96,3 +112,5 @@ class Versions(Handler):
yield distribution.name, distribution.version
except metadata.PackageNotFoundError:
continue
+
+ arguments = [_set_help_version_parser]
diff --git a/src/ahriman/application/handlers/web.py b/src/ahriman/application/handlers/web.py
index 5a8b71a6..552eed6c 100644
--- a/src/ahriman/application/handlers/web.py
+++ b/src/ahriman/application/handlers/web.py
@@ -20,8 +20,9 @@
import argparse
from collections.abc import Generator
+from pathlib import Path
-from ahriman.application.handlers.handler import Handler
+from ahriman.application.handlers.handler import Handler, SubParserAction
from ahriman.core.configuration import Configuration
from ahriman.core.spawn import Spawn
from ahriman.core.triggers import TriggerLoader
@@ -71,6 +72,21 @@ class Web(Handler):
spawner.stop()
spawner.join()
+ @staticmethod
+ def _set_web_parser(root: SubParserAction) -> argparse.ArgumentParser:
+ """
+ add parser for web subcommand
+
+ Args:
+ root(SubParserAction): subparsers for the commands
+
+ Returns:
+ argparse.ArgumentParser: created argument parser
+ """
+ parser = root.add_parser("web", help="web server", description="start web server")
+ parser.set_defaults(architecture="", lock=Path("ahriman-web.pid"), report=False, repository="")
+ return parser
+
@staticmethod
def extract_arguments(args: argparse.Namespace, configuration: Configuration) -> Generator[str, None, None]:
"""
@@ -100,3 +116,5 @@ class Web(Handler):
# arguments from configuration
if (wait_timeout := configuration.getint("web", "wait_timeout", fallback=None)) is not None:
yield from ["--wait-timeout", str(wait_timeout)]
+
+ arguments = [_set_web_parser]
diff --git a/src/ahriman/core/auth/helpers.py b/src/ahriman/core/auth/helpers.py
index 70a4484f..42d2882e 100644
--- a/src/ahriman/core/auth/helpers.py
+++ b/src/ahriman/core/auth/helpers.py
@@ -17,14 +17,14 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-from typing import Any
-
try:
import aiohttp_security
_has_aiohttp_security = True
except ImportError:
_has_aiohttp_security = False
+from typing import Any
+
__all__ = ["authorized_userid", "check_authorized", "forget", "remember"]
diff --git a/src/ahriman/core/log/journal_handler.py b/src/ahriman/core/log/journal_handler.py
index 6641ccc7..2cb9b8bb 100644
--- a/src/ahriman/core/log/journal_handler.py
+++ b/src/ahriman/core/log/journal_handler.py
@@ -17,7 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
-from logging import NullHandler # pylint: disable=imports-out-of-order
+# pylint: disable=imports-out-of-order
+from logging import NullHandler
from typing import Any
diff --git a/src/ahriman/core/module_loader.py b/src/ahriman/core/module_loader.py
new file mode 100644
index 00000000..1f870504
--- /dev/null
+++ b/src/ahriman/core/module_loader.py
@@ -0,0 +1,78 @@
+#
+# Copyright (c) 2021-2024 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 .
+#
+import inspect
+
+from collections.abc import Generator
+from importlib import import_module
+from pathlib import Path
+from pkgutil import ModuleInfo, walk_packages
+from types import ModuleType
+from typing import Any, TypeGuard, TypeVar
+
+
+__all__ = ["implementations"]
+
+
+T = TypeVar("T")
+
+
+def _modules(module_root: Path, prefix: str) -> Generator[ModuleInfo, None, None]:
+ """
+ extract available modules from package
+
+ Args:
+ module_root(Path): module root path
+ prefix(str): modules package prefix
+
+ Yields:
+ ModuleInfo: module information each available module
+ """
+ prefix = f"{prefix}." if prefix else ""
+ for module_info in walk_packages([str(module_root)], prefix):
+ if module_info.ispkg:
+ yield from _modules(module_root / module_info.name, prefix)
+ else:
+ yield module_info
+
+
+def implementations(root_module: ModuleType, base_class: type[T]) -> Generator[type[T], None, None]:
+ """
+ extract implementations of the ``base_class`` from the module
+
+ Args:
+ root_module(ModuleType): root module
+ base_class(type[T]): base class type
+
+ Yields:
+ type[T]: found implementations
+ """
+ def is_base_class(clazz: Any) -> TypeGuard[type[T]]:
+ return inspect.isclass(clazz) \
+ and issubclass(clazz, base_class) and clazz != base_class \
+ and clazz.__module__ == module.__name__
+
+ prefix = root_module.__name__
+
+ for module_root in root_module.__path__:
+ for module_info in _modules(Path(module_root), prefix):
+ module = import_module(module_info.name)
+
+ for _, attribute in inspect.getmembers(module, is_base_class):
+ yield attribute
diff --git a/src/ahriman/web/routes.py b/src/ahriman/web/routes.py
index 3729ca29..8e96b811 100644
--- a/src/ahriman/web/routes.py
+++ b/src/ahriman/web/routes.py
@@ -18,93 +18,35 @@
# along with this program. If not, see .
#
from aiohttp.web import Application, View
-from collections.abc import Generator
-from importlib.machinery import SourceFileLoader
-from pathlib import Path
-from pkgutil import ModuleInfo, iter_modules
-from types import ModuleType
-from typing import Any, Type, TypeGuard
+
+import ahriman.web.views
from ahriman.core.configuration import Configuration
+from ahriman.core.module_loader import implementations
from ahriman.web.views.base import BaseView
__all__ = ["setup_routes"]
-def _dynamic_routes(module_root: Path, configuration: Configuration) -> dict[str, Type[View]]:
+def _dynamic_routes(configuration: Configuration) -> dict[str, type[View]]:
"""
extract dynamic routes based on views
Args:
- module_root(Path): root module path with views
configuration(Configuration): configuration instance
Returns:
- dict[str, Type[View]]: map of the route to its view
+ dict[str, type[View]]: map of the route to its view
"""
- def is_base_view(clazz: Any) -> TypeGuard[Type[BaseView]]:
- return isinstance(clazz, type) and issubclass(clazz, BaseView)
-
- routes: dict[str, Type[View]] = {}
- for module_info in _modules(module_root):
- module = _module(module_info)
-
- for attribute_name in dir(module):
- view = getattr(module, attribute_name)
- if not is_base_view(view):
- continue
-
- view_routes = view.routes(configuration)
- routes.update([(route, view) for route in view_routes])
+ routes: dict[str, type[View]] = {}
+ for view in implementations(ahriman.web.views, BaseView):
+ view_routes = view.routes(configuration)
+ routes.update([(route, view) for route in view_routes])
return routes
-def _module(module_info: ModuleInfo) -> ModuleType:
- """
- load module from its info
-
- Args:
- module_info(ModuleInfo): module info descriptor
-
- Returns:
- ModuleType: loaded module
-
- Raises:
- ValueError: if loader is not an instance of :class:`importlib.machinery.SourceFileLoader`
- """
- module_spec = module_info.module_finder.find_spec(module_info.name, None)
- if module_spec is None:
- raise ValueError(f"Module specification of {module_info.name} is empty")
-
- loader = module_spec.loader
- if not isinstance(loader, SourceFileLoader):
- raise ValueError(f"Module {module_info.name} loader is not an instance of SourceFileLoader")
-
- module = ModuleType(loader.name)
- loader.exec_module(module)
-
- return module
-
-
-def _modules(module_root: Path) -> Generator[ModuleInfo, None, None]:
- """
- extract available modules from package
-
- Args:
- module_root(Path): module root path
-
- Yields:
- ModuleInfo: module information each available module
- """
- for module_info in iter_modules([str(module_root)]):
- if module_info.ispkg:
- yield from _modules(module_root / module_info.name)
- else:
- yield module_info
-
-
def setup_routes(application: Application, configuration: Configuration) -> None:
"""
setup all defined routes
@@ -115,6 +57,5 @@ def setup_routes(application: Application, configuration: Configuration) -> None
"""
application.router.add_static("/static", configuration.getpath("web", "static_path"), follow_symlinks=True)
- views_root = Path(__file__).parent / "views"
- for route, view in _dynamic_routes(views_root, configuration).items():
+ for route, view in _dynamic_routes(configuration).items():
application.router.add_view(route, view)
diff --git a/tests/ahriman/application/handlers/test_handler.py b/tests/ahriman/application/handlers/test_handler.py
index a43b6d43..063447b1 100644
--- a/tests/ahriman/application/handlers/test_handler.py
+++ b/tests/ahriman/application/handlers/test_handler.py
@@ -4,7 +4,7 @@ import pytest
from pathlib import Path
from pytest_mock import MockerFixture
-from ahriman.application.handlers import Handler
+from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import ExitCode, MissingArchitectureError, MultipleArchitecturesError
from ahriman.models.log_handler import LogHandler
@@ -19,7 +19,7 @@ def test_call(args: argparse.Namespace, configuration: Configuration, mocker: Mo
args.log_handler = LogHandler.Console
args.quiet = False
args.report = False
- mocker.patch("ahriman.application.handlers.Handler.run")
+ mocker.patch("ahriman.application.handlers.handler.Handler.run")
configuration_mock = mocker.patch("ahriman.core.configuration.Configuration.from_path", return_value=configuration)
log_handler_mock = mocker.patch("ahriman.core.log.log_loader.LogLoader.handler", return_value=args.log_handler)
log_load_mock = mocker.patch("ahriman.core.log.log_loader.LogLoader.load")
@@ -76,7 +76,7 @@ def test_execute(args: argparse.Namespace, mocker: MockerFixture) -> None:
RepositoryId("i686", "aur"),
RepositoryId("x86_64", "aur"),
]
- mocker.patch("ahriman.application.handlers.Handler.repositories_extract", return_value=ids)
+ mocker.patch("ahriman.application.handlers.handler.Handler.repositories_extract", return_value=ids)
starmap_mock = mocker.patch("multiprocessing.pool.Pool.starmap")
Handler.execute(args)
@@ -88,7 +88,7 @@ def test_execute_multiple_not_supported(args: argparse.Namespace, mocker: Mocker
must raise an exception if multiple architectures are not supported by the handler
"""
args.command = "web"
- mocker.patch("ahriman.application.handlers.Handler.repositories_extract", return_value=[
+ mocker.patch("ahriman.application.handlers.handler.Handler.repositories_extract", return_value=[
RepositoryId("i686", "aur"),
RepositoryId("x86_64", "aur"),
])
@@ -102,7 +102,7 @@ def test_execute_single(args: argparse.Namespace, mocker: MockerFixture) -> None
"""
must run execution in current process if only one architecture supplied
"""
- mocker.patch("ahriman.application.handlers.Handler.repositories_extract", return_value=[
+ mocker.patch("ahriman.application.handlers.handler.Handler.repositories_extract", return_value=[
RepositoryId("x86_64", "aur"),
])
starmap_mock = mocker.patch("multiprocessing.pool.Pool.starmap")
diff --git a/tests/ahriman/application/handlers/test_handler_add.py b/tests/ahriman/application/handlers/test_handler_add.py
index 2db6903d..f9507ab0 100644
--- a/tests/ahriman/application/handlers/test_handler_add.py
+++ b/tests/ahriman/application/handlers/test_handler_add.py
@@ -3,7 +3,7 @@ import pytest
from pytest_mock import MockerFixture
-from ahriman.application.handlers import Add
+from ahriman.application.handlers.add import Add
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
@@ -82,7 +82,7 @@ def test_run_with_updates(args: argparse.Namespace, configuration: Configuration
mocker.patch("ahriman.application.application.Application.add")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.update", return_value=result)
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
dependencies_mock = mocker.patch("ahriman.application.application.Application.with_dependencies",
@@ -113,7 +113,7 @@ def test_run_no_changes(args: argparse.Namespace, configuration: Configuration,
mocker.patch("ahriman.application.application.Application.add")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.application.Application.update")
- mocker.patch("ahriman.application.handlers.Handler.check_status")
+ mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
mocker.patch("ahriman.application.application.Application.updates")
mocker.patch("ahriman.application.application.Application.with_dependencies")
mocker.patch("ahriman.application.application.Application.print_updates")
@@ -138,7 +138,7 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
mocker.patch("ahriman.application.application.Application.with_dependencies")
mocker.patch("ahriman.application.application.Application.updates")
mocker.patch("ahriman.application.application.Application.print_updates")
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Add.run(args, repository_id, configuration, report=False)
diff --git a/tests/ahriman/application/handlers/test_handler_backup.py b/tests/ahriman/application/handlers/test_handler_backup.py
index 19b90760..6a9b6d2c 100644
--- a/tests/ahriman/application/handlers/test_handler_backup.py
+++ b/tests/ahriman/application/handlers/test_handler_backup.py
@@ -4,7 +4,7 @@ from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import MagicMock
-from ahriman.application.handlers import Backup
+from ahriman.application.handlers.backup import Backup
from ahriman.core.configuration import Configuration
from ahriman.models.repository_paths import RepositoryPaths
@@ -28,7 +28,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
must run command
"""
args = _default_args(args)
- mocker.patch("ahriman.application.handlers.Backup.get_paths", return_value=[Path("path")])
+ mocker.patch("ahriman.application.handlers.backup.Backup.get_paths", return_value=[Path("path")])
tarfile = MagicMock()
add_mock = tarfile.__enter__.return_value = MagicMock()
mocker.patch("ahriman.application.handlers.backup.tarfile.open", return_value=tarfile)
diff --git a/tests/ahriman/application/handlers/test_handler_change.py b/tests/ahriman/application/handlers/test_handler_change.py
index f96217d6..9e17456f 100644
--- a/tests/ahriman/application/handlers/test_handler_change.py
+++ b/tests/ahriman/application/handlers/test_handler_change.py
@@ -3,7 +3,7 @@ import pytest
from pytest_mock import MockerFixture
-from ahriman.application.handlers import Change
+from ahriman.application.handlers.change import Change
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
@@ -36,7 +36,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_get",
return_value=Changes("sha", "change"))
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ 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()
@@ -55,7 +55,7 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
args.exit_code = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.status.local_client.LocalClient.package_changes_get", return_value=Changes())
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Change.run(args, repository_id, configuration, report=False)
diff --git a/tests/ahriman/application/handlers/test_handler_clean.py b/tests/ahriman/application/handlers/test_handler_clean.py
index 196c4138..8f4f0896 100644
--- a/tests/ahriman/application/handlers/test_handler_clean.py
+++ b/tests/ahriman/application/handlers/test_handler_clean.py
@@ -2,7 +2,7 @@ import argparse
from pytest_mock import MockerFixture
-from ahriman.application.handlers import Clean
+from ahriman.application.handlers.clean import Clean
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
diff --git a/tests/ahriman/application/handlers/test_handler_copy.py b/tests/ahriman/application/handlers/test_handler_copy.py
index c364d90c..d9278ab4 100644
--- a/tests/ahriman/application/handlers/test_handler_copy.py
+++ b/tests/ahriman/application/handlers/test_handler_copy.py
@@ -4,7 +4,7 @@ import pytest
from pytest_mock import MockerFixture
from ahriman.application.application import Application
-from ahriman.application.handlers import Copy
+from ahriman.application.handlers.copy import Copy
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.build_status import BuildStatusEnum
@@ -37,7 +37,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman])
- application_mock = mocker.patch("ahriman.application.handlers.Copy.copy_package")
+ application_mock = mocker.patch("ahriman.application.handlers.copy.Copy.copy_package")
update_mock = mocker.patch("ahriman.application.application.Application.update")
remove_mock = mocker.patch("ahriman.application.application.Application.remove")
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
@@ -59,7 +59,7 @@ def test_run_remove(args: argparse.Namespace, configuration: Configuration, repo
args.remove = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[package_ahriman])
- mocker.patch("ahriman.application.handlers.Copy.copy_package")
+ mocker.patch("ahriman.application.handlers.copy.Copy.copy_package")
mocker.patch("ahriman.application.application.Application.update")
remove_mock = mocker.patch("ahriman.application.application.Application.remove")
@@ -77,7 +77,7 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
args.exit_code = True
mocker.patch("ahriman.core.repository.Repository.packages", return_value=[])
mocker.patch("ahriman.application.application.Application.update")
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Copy.run(args, repository_id, configuration, report=False)
diff --git a/tests/ahriman/application/handlers/test_handler_daemon.py b/tests/ahriman/application/handlers/test_handler_daemon.py
index ee897bea..79abccd2 100644
--- a/tests/ahriman/application/handlers/test_handler_daemon.py
+++ b/tests/ahriman/application/handlers/test_handler_daemon.py
@@ -2,7 +2,7 @@ import argparse
from pytest_mock import MockerFixture
-from ahriman.application.handlers import Daemon
+from ahriman.application.handlers.daemon import Daemon
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
@@ -31,7 +31,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, package_ahr
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- run_mock = mocker.patch("ahriman.application.handlers.Update.run")
+ run_mock = mocker.patch("ahriman.application.handlers.update.Update.run")
iter_mock = mocker.patch("ahriman.application.application.updates_iterator.UpdatesIterator.__iter__",
return_value=iter([[package_ahriman.base]]))
@@ -50,7 +50,7 @@ def test_run_no_partitions(args: argparse.Namespace, configuration: Configuratio
args = _default_args(args)
args.partitions = False
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- run_mock = mocker.patch("ahriman.application.handlers.Update.run")
+ run_mock = mocker.patch("ahriman.application.handlers.update.Update.run")
iter_mock = mocker.patch("ahriman.application.application.updates_iterator.UpdatesIterator.__iter__",
return_value=iter([[]]))
@@ -67,7 +67,7 @@ def test_run_no_updates(args: argparse.Namespace, configuration: Configuration,
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- run_mock = mocker.patch("ahriman.application.handlers.Update.run")
+ run_mock = mocker.patch("ahriman.application.handlers.update.Update.run")
iter_mock = mocker.patch("ahriman.application.application.updates_iterator.UpdatesIterator.__iter__",
return_value=iter([[package_ahriman.base], None]))
diff --git a/tests/ahriman/application/handlers/test_handler_dump.py b/tests/ahriman/application/handlers/test_handler_dump.py
index dcb5f1c1..03541947 100644
--- a/tests/ahriman/application/handlers/test_handler_dump.py
+++ b/tests/ahriman/application/handlers/test_handler_dump.py
@@ -3,7 +3,7 @@ import pytest
from pytest_mock import MockerFixture
-from ahriman.application.handlers import Dump
+from ahriman.application.handlers.dump import Dump
from ahriman.core.configuration import Configuration
diff --git a/tests/ahriman/application/handlers/test_handler_help.py b/tests/ahriman/application/handlers/test_handler_help.py
index bb615a0c..73ea5b55 100644
--- a/tests/ahriman/application/handlers/test_handler_help.py
+++ b/tests/ahriman/application/handlers/test_handler_help.py
@@ -3,7 +3,7 @@ import argparse
from pytest_mock import MockerFixture
from ahriman.application.ahriman import _parser
-from ahriman.application.handlers import Help
+from ahriman.application.handlers.help import Help
from ahriman.core.configuration import Configuration
diff --git a/tests/ahriman/application/handlers/test_handler_key_import.py b/tests/ahriman/application/handlers/test_handler_key_import.py
index dc8e9fd3..f133289b 100644
--- a/tests/ahriman/application/handlers/test_handler_key_import.py
+++ b/tests/ahriman/application/handlers/test_handler_key_import.py
@@ -2,7 +2,7 @@ import argparse
from pytest_mock import MockerFixture
-from ahriman.application.handlers import KeyImport
+from ahriman.application.handlers.key_import import KeyImport
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
diff --git a/tests/ahriman/application/handlers/test_handler_patch.py b/tests/ahriman/application/handlers/test_handler_patch.py
index 59307cdd..46aabd7f 100644
--- a/tests/ahriman/application/handlers/test_handler_patch.py
+++ b/tests/ahriman/application/handlers/test_handler_patch.py
@@ -6,7 +6,7 @@ from pathlib import Path
from pytest_mock import MockerFixture
from ahriman.application.application import Application
-from ahriman.application.handlers import Patch
+from ahriman.application.handlers.patch import Patch
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.action import Action
@@ -40,9 +40,9 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
args = _default_args(args)
args.action = Action.Update
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- patch_mock = mocker.patch("ahriman.application.handlers.Patch.patch_create_from_diff",
+ patch_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_create_from_diff",
return_value=(args.package, PkgbuildPatch(None, "patch")))
- application_mock = mocker.patch("ahriman.application.handlers.Patch.patch_set_create")
+ application_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_set_create")
_, repository_id = configuration.check_loaded()
Patch.run(args, repository_id, configuration, report=False)
@@ -61,8 +61,9 @@ def test_run_function(args: argparse.Namespace, configuration: Configuration, re
args.variable = "version"
patch = PkgbuildPatch(args.variable, args.patch)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- patch_mock = mocker.patch("ahriman.application.handlers.Patch.patch_create_from_function", return_value=patch)
- application_mock = mocker.patch("ahriman.application.handlers.Patch.patch_set_create")
+ patch_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_create_from_function",
+ return_value=patch)
+ application_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_set_create")
_, repository_id = configuration.check_loaded()
Patch.run(args, repository_id, configuration, report=False)
@@ -79,7 +80,7 @@ def test_run_list(args: argparse.Namespace, configuration: Configuration, reposi
args.action = Action.List
args.variable = ["version"]
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- application_mock = mocker.patch("ahriman.application.handlers.Patch.patch_set_list")
+ application_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_set_list")
_, repository_id = configuration.check_loaded()
Patch.run(args, repository_id, configuration, report=False)
@@ -95,7 +96,7 @@ def test_run_remove(args: argparse.Namespace, configuration: Configuration, repo
args.action = Action.Remove
args.variable = ["version"]
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- application_mock = mocker.patch("ahriman.application.handlers.Patch.patch_set_remove")
+ application_mock = mocker.patch("ahriman.application.handlers.patch.Patch.patch_set_remove")
_, repository_id = configuration.check_loaded()
Patch.run(args, repository_id, configuration, report=False)
@@ -163,7 +164,7 @@ def test_patch_set_list(application: Application, mocker: MockerFixture) -> None
get_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_patches_get",
return_value=[PkgbuildPatch(None, "patch"), PkgbuildPatch("version", "value")])
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
Patch.patch_set_list(application, "ahriman", ["version"], False)
get_mock.assert_called_once_with("ahriman", None)
@@ -178,7 +179,7 @@ def test_patch_set_list_all(application: Application, mocker: MockerFixture) ->
get_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_patches_get",
return_value=[PkgbuildPatch(None, "patch")])
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
Patch.patch_set_list(application, "ahriman", None, False)
get_mock.assert_called_once_with("ahriman", None)
@@ -191,7 +192,7 @@ def test_patch_set_list_empty_exception(application: Application, mocker: Mocker
must raise ExitCode exception on empty patch list
"""
mocker.patch("ahriman.core.status.local_client.LocalClient.package_patches_get", return_value={})
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
Patch.patch_set_list(application, "ahriman", [], True)
check_mock.assert_called_once_with(True, [])
diff --git a/tests/ahriman/application/handlers/test_handler_rebuild.py b/tests/ahriman/application/handlers/test_handler_rebuild.py
index c0d95012..2a10b889 100644
--- a/tests/ahriman/application/handlers/test_handler_rebuild.py
+++ b/tests/ahriman/application/handlers/test_handler_rebuild.py
@@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.application.application import Application
-from ahriman.application.handlers import Rebuild
+from ahriman.application.handlers.rebuild import Rebuild
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
@@ -43,11 +43,12 @@ def test_run(args: argparse.Namespace, package_ahriman: Package, configuration:
result = Result()
result.add_updated(package_ahriman)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- extract_mock = mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[package_ahriman])
+ extract_mock = mocker.patch("ahriman.application.handlers.rebuild.Rebuild.extract_packages",
+ return_value=[package_ahriman])
application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on",
return_value=[package_ahriman])
application_mock = mocker.patch("ahriman.application.application.Application.update", return_value=result)
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
on_start_mock = mocker.patch("ahriman.application.application.Application.on_start")
_, repository_id = configuration.check_loaded()
@@ -70,7 +71,7 @@ def test_run_extract_packages(args: argparse.Namespace, configuration: Configura
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.application.Application.add")
mocker.patch("ahriman.application.application.Application.print_updates")
- extract_mock = mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[])
+ extract_mock = mocker.patch("ahriman.application.handlers.rebuild.Rebuild.extract_packages", return_value=[])
_, repository_id = configuration.check_loaded()
Rebuild.run(args, repository_id, configuration, report=False)
@@ -85,9 +86,9 @@ def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, rep
args = _default_args(args)
args.dry_run = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[package_ahriman])
+ mocker.patch("ahriman.application.handlers.rebuild.Rebuild.extract_packages", return_value=[package_ahriman])
application_mock = mocker.patch("ahriman.application.application.Application.update")
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
print_mock = mocker.patch("ahriman.application.application.Application.print_updates")
_, repository_id = configuration.check_loaded()
@@ -106,7 +107,7 @@ def test_run_filter(args: argparse.Namespace, configuration: Configuration, repo
args.depends_on = ["python-aur"]
mocker.patch("ahriman.application.application.Application.update")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[])
+ mocker.patch("ahriman.application.handlers.rebuild.Rebuild.extract_packages", return_value=[])
application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on")
_, repository_id = configuration.check_loaded()
@@ -122,7 +123,7 @@ def test_run_without_filter(args: argparse.Namespace, configuration: Configurati
args = _default_args(args)
mocker.patch("ahriman.application.application.Application.update")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[])
+ mocker.patch("ahriman.application.handlers.rebuild.Rebuild.extract_packages", return_value=[])
application_packages_mock = mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on")
_, repository_id = configuration.check_loaded()
@@ -139,10 +140,10 @@ def test_run_update_empty_exception(args: argparse.Namespace, configuration: Con
args.exit_code = True
args.dry_run = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- mocker.patch("ahriman.application.handlers.Rebuild.extract_packages")
+ mocker.patch("ahriman.application.handlers.rebuild.Rebuild.extract_packages")
mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on", return_value=[])
mocker.patch("ahriman.application.application.Application.print_updates")
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Rebuild.run(args, repository_id, configuration, report=False)
@@ -157,10 +158,10 @@ def test_run_build_empty_exception(args: argparse.Namespace, configuration: Conf
args = _default_args(args)
args.exit_code = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- mocker.patch("ahriman.application.handlers.Rebuild.extract_packages")
+ mocker.patch("ahriman.application.handlers.rebuild.Rebuild.extract_packages")
mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on", return_value=[package_ahriman])
mocker.patch("ahriman.application.application.Application.update", return_value=Result())
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Rebuild.run(args, repository_id, configuration, report=False)
diff --git a/tests/ahriman/application/handlers/test_handler_remove.py b/tests/ahriman/application/handlers/test_handler_remove.py
index fba69bed..240105cd 100644
--- a/tests/ahriman/application/handlers/test_handler_remove.py
+++ b/tests/ahriman/application/handlers/test_handler_remove.py
@@ -2,7 +2,7 @@ import argparse
from pytest_mock import MockerFixture
-from ahriman.application.handlers import Remove
+from ahriman.application.handlers.remove import Remove
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
diff --git a/tests/ahriman/application/handlers/test_handler_remove_unknown.py b/tests/ahriman/application/handlers/test_handler_remove_unknown.py
index 0bb9910e..4b4062f4 100644
--- a/tests/ahriman/application/handlers/test_handler_remove_unknown.py
+++ b/tests/ahriman/application/handlers/test_handler_remove_unknown.py
@@ -3,7 +3,7 @@ import pytest
from pytest_mock import MockerFixture
-from ahriman.application.handlers import RemoveUnknown
+from ahriman.application.handlers.remove_unknown import RemoveUnknown
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
diff --git a/tests/ahriman/application/handlers/test_handler_repositories.py b/tests/ahriman/application/handlers/test_handler_repositories.py
index fb4f29ed..84ba72e2 100644
--- a/tests/ahriman/application/handlers/test_handler_repositories.py
+++ b/tests/ahriman/application/handlers/test_handler_repositories.py
@@ -3,7 +3,7 @@ import pytest
from pytest_mock import MockerFixture
-from ahriman.application.handlers import Repositories
+from ahriman.application.handlers.repositories import Repositories
from ahriman.core.configuration import Configuration
@@ -29,7 +29,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
args = _default_args(args)
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
- application_mock = mocker.patch("ahriman.application.handlers.Handler.repositories_extract",
+ application_mock = mocker.patch("ahriman.application.handlers.handler.Handler.repositories_extract",
return_value=[repository_id])
Repositories.run(args, repository_id, configuration, report=False)
diff --git a/tests/ahriman/application/handlers/test_handler_restore.py b/tests/ahriman/application/handlers/test_handler_restore.py
index ee2697fb..9ccb0d42 100644
--- a/tests/ahriman/application/handlers/test_handler_restore.py
+++ b/tests/ahriman/application/handlers/test_handler_restore.py
@@ -4,7 +4,7 @@ from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import MagicMock
-from ahriman.application.handlers import Restore
+from ahriman.application.handlers.restore import Restore
from ahriman.core.configuration import Configuration
diff --git a/tests/ahriman/application/handlers/test_handler_run.py b/tests/ahriman/application/handlers/test_handler_run.py
index f49beb0b..48165b90 100644
--- a/tests/ahriman/application/handlers/test_handler_run.py
+++ b/tests/ahriman/application/handlers/test_handler_run.py
@@ -4,7 +4,7 @@ import pytest
from pytest_mock import MockerFixture
from ahriman.application.ahriman import _parser
-from ahriman.application.handlers import Run
+from ahriman.application.handlers.run import Run
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import ExitCode
@@ -29,7 +29,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
must run command
"""
args = _default_args(args)
- application_mock = mocker.patch("ahriman.application.handlers.Run.run_command")
+ application_mock = mocker.patch("ahriman.application.handlers.run.Run.run_command")
_, repository_id = configuration.check_loaded()
Run.run(args, repository_id, configuration, report=False)
@@ -42,7 +42,7 @@ def test_run_failed(args: argparse.Namespace, configuration: Configuration, mock
"""
args = _default_args(args)
args.command = ["help", "config"]
- application_mock = mocker.patch("ahriman.application.handlers.Run.run_command", return_value=False)
+ application_mock = mocker.patch("ahriman.application.handlers.run.Run.run_command", return_value=False)
_, repository_id = configuration.check_loaded()
with pytest.raises(ExitCode):
@@ -54,8 +54,13 @@ def test_run_command(mocker: MockerFixture) -> None:
"""
must correctly run external command
"""
- execute_mock = mocker.patch("ahriman.application.handlers.Help.execute")
- Run.run_command(["help"], _parser())
+ # because of dynamic load we need to patch exact instance of the object
+ parser = _parser()
+ subparser = next((action for action in parser._actions if isinstance(action, argparse._SubParsersAction)), None)
+ action = subparser.choices["help"]
+ execute_mock = mocker.patch.object(action.get_default("handler"), "execute")
+
+ Run.run_command(["help"], parser)
execute_mock.assert_called_once_with(pytest.helpers.anyvar(int))
diff --git a/tests/ahriman/application/handlers/test_handler_search.py b/tests/ahriman/application/handlers/test_handler_search.py
index 25e9873a..e04ab28b 100644
--- a/tests/ahriman/application/handlers/test_handler_search.py
+++ b/tests/ahriman/application/handlers/test_handler_search.py
@@ -5,7 +5,7 @@ import pytest
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
-from ahriman.application.handlers import Search
+from ahriman.application.handlers.search import Search
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import OptionError
from ahriman.core.repository import Repository
@@ -39,7 +39,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
aur_search_mock = mocker.patch("ahriman.core.alpm.remote.AUR.multisearch", return_value=[aur_package_ahriman])
official_search_mock = mocker.patch("ahriman.core.alpm.remote.Official.multisearch",
return_value=[aur_package_ahriman])
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ 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()
@@ -64,7 +64,7 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
mocker.patch("ahriman.core.alpm.remote.Official.multisearch", return_value=[])
mocker.patch("ahriman.core.formatters.Printer.print")
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Search.run(args, repository_id, configuration, report=False)
@@ -80,7 +80,7 @@ def test_run_sort(args: argparse.Namespace, configuration: Configuration, reposi
mocker.patch("ahriman.core.alpm.remote.AUR.multisearch", return_value=[aur_package_ahriman])
mocker.patch("ahriman.core.alpm.remote.Official.multisearch", return_value=[])
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- sort_mock = mocker.patch("ahriman.application.handlers.Search.sort")
+ sort_mock = mocker.patch("ahriman.application.handlers.search.Search.sort")
_, repository_id = configuration.check_loaded()
Search.run(args, repository_id, configuration, report=False)
@@ -100,7 +100,7 @@ def test_run_sort_by(args: argparse.Namespace, configuration: Configuration, rep
mocker.patch("ahriman.core.alpm.remote.AUR.multisearch", return_value=[aur_package_ahriman])
mocker.patch("ahriman.core.alpm.remote.Official.multisearch", return_value=[])
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- sort_mock = mocker.patch("ahriman.application.handlers.Search.sort")
+ sort_mock = mocker.patch("ahriman.application.handlers.search.Search.sort")
_, repository_id = configuration.check_loaded()
Search.run(args, repository_id, configuration, report=False)
diff --git a/tests/ahriman/application/handlers/test_handler_service_updates.py b/tests/ahriman/application/handlers/test_handler_service_updates.py
index c0449cde..7c18ddbc 100644
--- a/tests/ahriman/application/handlers/test_handler_service_updates.py
+++ b/tests/ahriman/application/handlers/test_handler_service_updates.py
@@ -4,7 +4,7 @@ import pytest
from pytest_mock import MockerFixture
from ahriman import __version__
-from ahriman.application.handlers import ServiceUpdates
+from ahriman.application.handlers.service_updates import ServiceUpdates
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
@@ -34,7 +34,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
package_mock = mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
application_mock = mocker.patch("ahriman.core.formatters.Printer.print")
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
ServiceUpdates.run(args, repository_id, configuration, report=False)
@@ -53,7 +53,7 @@ def test_run_skip(args: argparse.Namespace, configuration: Configuration, reposi
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman)
application_mock = mocker.patch("ahriman.core.formatters.Printer.print")
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
ServiceUpdates.run(args, repository_id, configuration, report=False)
diff --git a/tests/ahriman/application/handlers/test_handler_setup.py b/tests/ahriman/application/handlers/test_handler_setup.py
index 656ae6e5..3a292acc 100644
--- a/tests/ahriman/application/handlers/test_handler_setup.py
+++ b/tests/ahriman/application/handlers/test_handler_setup.py
@@ -7,7 +7,7 @@ from typing import Any
from unittest.mock import call as MockCall
from urllib.parse import quote_plus as urlencode
-from ahriman.application.handlers import Setup
+from ahriman.application.handlers.setup import Setup
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import MissingArchitectureError
from ahriman.core.repository import Repository
@@ -50,11 +50,11 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
"""
args = _default_args(args)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- ahriman_configuration_mock = mocker.patch("ahriman.application.handlers.Setup.configuration_create_ahriman")
- devtools_configuration_mock = mocker.patch("ahriman.application.handlers.Setup.configuration_create_devtools")
- makepkg_configuration_mock = mocker.patch("ahriman.application.handlers.Setup.configuration_create_makepkg")
- sudo_configuration_mock = mocker.patch("ahriman.application.handlers.Setup.configuration_create_sudo")
- executable_mock = mocker.patch("ahriman.application.handlers.Setup.executable_create")
+ ahriman_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_ahriman")
+ devtools_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_devtools")
+ makepkg_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_makepkg")
+ sudo_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_sudo")
+ executable_mock = mocker.patch("ahriman.application.handlers.setup.Setup.executable_create")
init_mock = mocker.patch("ahriman.core.alpm.repo.Repo.init")
_, repository_id = configuration.check_loaded()
@@ -95,12 +95,12 @@ def test_run_with_server(args: argparse.Namespace, configuration: Configuration,
args = _default_args(args)
args.server = "server"
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- mocker.patch("ahriman.application.handlers.Setup.configuration_create_ahriman")
- mocker.patch("ahriman.application.handlers.Setup.configuration_create_makepkg")
- mocker.patch("ahriman.application.handlers.Setup.configuration_create_sudo")
- mocker.patch("ahriman.application.handlers.Setup.executable_create")
+ mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_ahriman")
+ mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_makepkg")
+ mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_sudo")
+ mocker.patch("ahriman.application.handlers.setup.Setup.executable_create")
mocker.patch("ahriman.core.alpm.repo.Repo.init")
- devtools_configuration_mock = mocker.patch("ahriman.application.handlers.Setup.configuration_create_devtools")
+ devtools_configuration_mock = mocker.patch("ahriman.application.handlers.setup.Setup.configuration_create_devtools")
_, repository_id = configuration.check_loaded()
Setup.run(args, repository_id, configuration, report=False)
diff --git a/tests/ahriman/application/handlers/test_handler_shell.py b/tests/ahriman/application/handlers/test_handler_shell.py
index af46976a..105e3df6 100644
--- a/tests/ahriman/application/handlers/test_handler_shell.py
+++ b/tests/ahriman/application/handlers/test_handler_shell.py
@@ -3,7 +3,7 @@ import pytest
from pytest_mock import MockerFixture
-from ahriman.application.handlers import Shell
+from ahriman.application.handlers.shell import Shell
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
diff --git a/tests/ahriman/application/handlers/test_handler_sign.py b/tests/ahriman/application/handlers/test_handler_sign.py
index 9e1fa579..566d48f6 100644
--- a/tests/ahriman/application/handlers/test_handler_sign.py
+++ b/tests/ahriman/application/handlers/test_handler_sign.py
@@ -2,7 +2,7 @@ import argparse
from pytest_mock import MockerFixture
-from ahriman.application.handlers import Sign
+from ahriman.application.handlers.sign import Sign
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
diff --git a/tests/ahriman/application/handlers/test_handler_statistics.py b/tests/ahriman/application/handlers/test_handler_statistics.py
index 9d4dede9..111684c2 100644
--- a/tests/ahriman/application/handlers/test_handler_statistics.py
+++ b/tests/ahriman/application/handlers/test_handler_statistics.py
@@ -5,7 +5,7 @@ from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
-from ahriman.application.handlers import Statistics
+from ahriman.application.handlers.statistics import Statistics
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.core.utils import pretty_datetime, utcnow
@@ -42,7 +42,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
events = [Event("1", "1"), Event("2", "2")]
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
events_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.event_get", return_value=events)
- application_mock = mocker.patch("ahriman.application.handlers.Statistics.stats_per_package")
+ application_mock = mocker.patch("ahriman.application.handlers.statistics.Statistics.stats_per_package")
_, repository_id = configuration.check_loaded()
Statistics.run(args, repository_id, configuration, report=False)
@@ -60,7 +60,7 @@ def test_run_for_package(args: argparse.Namespace, configuration: Configuration,
events = [Event("1", "1"), Event("2", "2")]
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
events_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.event_get", return_value=events)
- application_mock = mocker.patch("ahriman.application.handlers.Statistics.stats_for_package")
+ application_mock = mocker.patch("ahriman.application.handlers.statistics.Statistics.stats_for_package")
_, repository_id = configuration.check_loaded()
Statistics.run(args, repository_id, configuration, report=False)
@@ -77,7 +77,7 @@ def test_run_convert_from_date(args: argparse.Namespace, configuration: Configur
date = utcnow()
args.from_date = date.isoformat()
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- mocker.patch("ahriman.application.handlers.Statistics.stats_per_package")
+ mocker.patch("ahriman.application.handlers.statistics.Statistics.stats_per_package")
events_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.event_get", return_value=[])
_, repository_id = configuration.check_loaded()
@@ -94,7 +94,7 @@ def test_run_convert_to_date(args: argparse.Namespace, configuration: Configurat
date = utcnow()
args.to_date = date.isoformat()
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
- mocker.patch("ahriman.application.handlers.Statistics.stats_per_package")
+ mocker.patch("ahriman.application.handlers.statistics.Statistics.stats_per_package")
events_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.event_get", return_value=[])
_, repository_id = configuration.check_loaded()
@@ -147,8 +147,8 @@ def test_stats_for_package(mocker: MockerFixture) -> None:
must print statistics for the package
"""
events = [Event("event", "1"), Event("event", "1")]
- events_mock = mocker.patch("ahriman.application.handlers.Statistics.event_stats")
- chart_plot = mocker.patch("ahriman.application.handlers.Statistics.plot_times")
+ events_mock = mocker.patch("ahriman.application.handlers.statistics.Statistics.event_stats")
+ chart_plot = mocker.patch("ahriman.application.handlers.statistics.Statistics.plot_times")
Statistics.stats_for_package("event", events, None)
events_mock.assert_called_once_with("event", events)
@@ -161,8 +161,8 @@ def test_stats_for_package_with_chart(mocker: MockerFixture) -> None:
"""
local = Path("local")
events = [Event("event", "1"), Event("event", "1")]
- mocker.patch("ahriman.application.handlers.Statistics.event_stats")
- chart_plot = mocker.patch("ahriman.application.handlers.Statistics.plot_times")
+ mocker.patch("ahriman.application.handlers.statistics.Statistics.event_stats")
+ chart_plot = mocker.patch("ahriman.application.handlers.statistics.Statistics.plot_times")
Statistics.stats_for_package("event", events, local)
chart_plot.assert_called_once_with("event", events, local)
@@ -174,8 +174,8 @@ def test_stats_per_package(mocker: MockerFixture) -> None:
"""
events = [Event("event", "1"), Event("event", "2"), Event("event", "1")]
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
- events_mock = mocker.patch("ahriman.application.handlers.Statistics.event_stats")
- chart_plot = mocker.patch("ahriman.application.handlers.Statistics.plot_packages")
+ events_mock = mocker.patch("ahriman.application.handlers.statistics.Statistics.event_stats")
+ chart_plot = mocker.patch("ahriman.application.handlers.statistics.Statistics.plot_packages")
Statistics.stats_per_package("event", events, None)
print_mock.assert_has_calls([
@@ -192,8 +192,8 @@ def test_stats_per_package_with_chart(mocker: MockerFixture) -> None:
"""
local = Path("local")
events = [Event("event", "1"), Event("event", "2"), Event("event", "1")]
- mocker.patch("ahriman.application.handlers.Statistics.event_stats")
- chart_plot = mocker.patch("ahriman.application.handlers.Statistics.plot_packages")
+ mocker.patch("ahriman.application.handlers.statistics.Statistics.event_stats")
+ chart_plot = mocker.patch("ahriman.application.handlers.statistics.Statistics.plot_packages")
Statistics.stats_per_package("event", events, local)
chart_plot.assert_called_once_with("event", {"1": 2, "2": 1}, local)
diff --git a/tests/ahriman/application/handlers/test_handler_status.py b/tests/ahriman/application/handlers/test_handler_status.py
index 4ad84e25..a2ca0c57 100644
--- a/tests/ahriman/application/handlers/test_handler_status.py
+++ b/tests/ahriman/application/handlers/test_handler_status.py
@@ -4,7 +4,7 @@ import pytest
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
-from ahriman.application.handlers import Status
+from ahriman.application.handlers.status import Status
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
@@ -43,7 +43,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.core.status.Client.status_get")
packages_mock = mocker.patch("ahriman.core.status.local_client.LocalClient.package_get", return_value=packages)
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ 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()
@@ -67,7 +67,7 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.core.status.Client.status_get")
mocker.patch("ahriman.core.status.local_client.LocalClient.package_get", return_value=[])
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Status.run(args, repository_id, configuration, report=False)
diff --git a/tests/ahriman/application/handlers/test_handler_status_update.py b/tests/ahriman/application/handlers/test_handler_status_update.py
index 289032e4..49e3bf21 100644
--- a/tests/ahriman/application/handlers/test_handler_status_update.py
+++ b/tests/ahriman/application/handlers/test_handler_status_update.py
@@ -2,7 +2,7 @@ import argparse
from pytest_mock import MockerFixture
-from ahriman.application.handlers import StatusUpdate
+from ahriman.application.handlers.status_update import StatusUpdate
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.repository import Repository
diff --git a/tests/ahriman/application/handlers/test_handler_structure.py b/tests/ahriman/application/handlers/test_handler_structure.py
index 5a09e9f3..c3316e6b 100644
--- a/tests/ahriman/application/handlers/test_handler_structure.py
+++ b/tests/ahriman/application/handlers/test_handler_structure.py
@@ -4,7 +4,7 @@ import pytest
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
-from ahriman.application.handlers import Structure
+from ahriman.application.handlers.structure import Structure
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
diff --git a/tests/ahriman/application/handlers/test_handler_tree_migrate.py b/tests/ahriman/application/handlers/test_handler_tree_migrate.py
index 60b731d2..af45aaba 100644
--- a/tests/ahriman/application/handlers/test_handler_tree_migrate.py
+++ b/tests/ahriman/application/handlers/test_handler_tree_migrate.py
@@ -4,7 +4,7 @@ from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
-from ahriman.application.handlers import TreeMigrate
+from ahriman.application.handlers.tree_migrate import TreeMigrate
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId
from ahriman.models.repository_paths import RepositoryPaths
@@ -15,7 +15,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
must run command
"""
tree_create_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.tree_create")
- application_mock = mocker.patch("ahriman.application.handlers.TreeMigrate.tree_move")
+ application_mock = mocker.patch("ahriman.application.handlers.tree_migrate.TreeMigrate.tree_move")
_, repository_id = configuration.check_loaded()
old_paths = configuration.repository_paths
new_paths = RepositoryPaths(old_paths.root, old_paths.repository_id, _force_current_tree=True)
diff --git a/tests/ahriman/application/handlers/test_handler_triggers.py b/tests/ahriman/application/handlers/test_handler_triggers.py
index fcc4220d..47a41169 100644
--- a/tests/ahriman/application/handlers/test_handler_triggers.py
+++ b/tests/ahriman/application/handlers/test_handler_triggers.py
@@ -2,7 +2,7 @@ import argparse
from pytest_mock import MockerFixture
-from ahriman.application.handlers import Triggers
+from ahriman.application.handlers.triggers import Triggers
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
diff --git a/tests/ahriman/application/handlers/test_handler_unsafe_commands.py b/tests/ahriman/application/handlers/test_handler_unsafe_commands.py
index 5fdc9f5a..617bb5fe 100644
--- a/tests/ahriman/application/handlers/test_handler_unsafe_commands.py
+++ b/tests/ahriman/application/handlers/test_handler_unsafe_commands.py
@@ -4,7 +4,7 @@ import pytest
from pytest_mock import MockerFixture
from ahriman.application.ahriman import _parser
-from ahriman.application.handlers import UnsafeCommands
+from ahriman.application.handlers.unsafe_commands import UnsafeCommands
from ahriman.core.configuration import Configuration
@@ -28,7 +28,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
must run command
"""
args = _default_args(args)
- commands_mock = mocker.patch("ahriman.application.handlers.UnsafeCommands.get_unsafe_commands",
+ commands_mock = mocker.patch("ahriman.application.handlers.unsafe_commands.UnsafeCommands.get_unsafe_commands",
return_value=["command"])
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
@@ -44,9 +44,9 @@ def test_run_check(args: argparse.Namespace, configuration: Configuration, mocke
"""
args = _default_args(args)
args.subcommand = ["clean"]
- commands_mock = mocker.patch("ahriman.application.handlers.UnsafeCommands.get_unsafe_commands",
+ commands_mock = mocker.patch("ahriman.application.handlers.unsafe_commands.UnsafeCommands.get_unsafe_commands",
return_value=["command"])
- check_mock = mocker.patch("ahriman.application.handlers.UnsafeCommands.check_unsafe")
+ check_mock = mocker.patch("ahriman.application.handlers.unsafe_commands.UnsafeCommands.check_unsafe")
_, repository_id = configuration.check_loaded()
UnsafeCommands.run(args, repository_id, configuration, report=False)
@@ -58,7 +58,7 @@ def test_check_unsafe(mocker: MockerFixture) -> None:
"""
must check if command is unsafe
"""
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
UnsafeCommands.check_unsafe(["service-clean"], ["service-clean"], _parser())
check_mock.assert_called_once_with(True, False)
@@ -67,7 +67,7 @@ def test_check_unsafe_safe(mocker: MockerFixture) -> None:
"""
must check if command is safe
"""
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
UnsafeCommands.check_unsafe(["package-status"], ["service-clean"], _parser())
check_mock.assert_called_once_with(True, True)
diff --git a/tests/ahriman/application/handlers/test_handler_update.py b/tests/ahriman/application/handlers/test_handler_update.py
index 8cf49132..428ddbde 100644
--- a/tests/ahriman/application/handlers/test_handler_update.py
+++ b/tests/ahriman/application/handlers/test_handler_update.py
@@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
from ahriman.application.application import Application
-from ahriman.application.handlers import Update
+from ahriman.application.handlers.update import Update
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.package import Package
@@ -49,7 +49,7 @@ def test_run(args: argparse.Namespace, package_ahriman: Package, configuration:
result.add_updated(package_ahriman)
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.update", return_value=result)
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
dependencies_mock = mocker.patch("ahriman.application.application.Application.with_dependencies",
return_value=[package_ahriman])
updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
@@ -81,7 +81,7 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
args.dry_run = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.application.Application.updates", return_value=[])
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Update.run(args, repository_id, configuration, report=False)
@@ -101,7 +101,7 @@ def test_run_update_empty_exception(args: argparse.Namespace, package_ahriman: P
mocker.patch("ahriman.application.application.Application.with_dependencies", return_value=[package_ahriman])
mocker.patch("ahriman.application.application.Application.print_updates")
mocker.patch("ahriman.application.application.Application.changes")
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Update.run(args, repository_id, configuration, report=False)
@@ -117,7 +117,7 @@ def test_run_dry_run(args: argparse.Namespace, package_ahriman: Package, configu
args.dry_run = True
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
application_mock = mocker.patch("ahriman.application.application.Application.update")
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman])
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
@@ -140,7 +140,7 @@ def test_run_no_changes(args: argparse.Namespace, configuration: Configuration,
args.changes = False
mocker.patch("ahriman.core.repository.Repository.load", return_value=repository)
mocker.patch("ahriman.application.application.Application.update")
- mocker.patch("ahriman.application.handlers.Handler.check_status")
+ mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
mocker.patch("ahriman.application.application.Application.updates")
changes_mock = mocker.patch("ahriman.application.application.Application.changes")
diff --git a/tests/ahriman/application/handlers/test_handler_users.py b/tests/ahriman/application/handlers/test_handler_users.py
index 607d4459..8e60ce59 100644
--- a/tests/ahriman/application/handlers/test_handler_users.py
+++ b/tests/ahriman/application/handlers/test_handler_users.py
@@ -4,7 +4,7 @@ import pytest
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
-from ahriman.application.handlers import Users
+from ahriman.application.handlers.users import Users
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.exceptions import PasswordError
@@ -42,7 +42,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, database: S
packager_id=args.packager, key=args.key)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("ahriman.models.user.User.hash_password", return_value=user)
- create_user_mock = mocker.patch("ahriman.application.handlers.Users.user_create", return_value=user)
+ create_user_mock = mocker.patch("ahriman.application.handlers.users.Users.user_create", return_value=user)
update_mock = mocker.patch("ahriman.core.database.SQLite.user_update")
_, repository_id = configuration.check_loaded()
@@ -60,7 +60,7 @@ def test_run_empty_salt(args: argparse.Namespace, configuration: Configuration,
user = User(username=args.username, password=args.password, access=args.role,
packager_id=args.packager, key=args.key)
mocker.patch("ahriman.models.user.User.hash_password", return_value=user)
- create_user_mock = mocker.patch("ahriman.application.handlers.Users.user_create", return_value=user)
+ create_user_mock = mocker.patch("ahriman.application.handlers.users.Users.user_create", return_value=user)
update_mock = mocker.patch("ahriman.core.database.SQLite.user_update")
_, repository_id = configuration.check_loaded()
@@ -80,7 +80,7 @@ def test_run_empty_salt_without_password(args: argparse.Namespace, configuration
packager_id=args.packager, key=args.key)
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("ahriman.models.user.User.hash_password", return_value=user)
- create_user_mock = mocker.patch("ahriman.application.handlers.Users.user_create", return_value=user)
+ create_user_mock = mocker.patch("ahriman.application.handlers.users.Users.user_create", return_value=user)
update_mock = mocker.patch("ahriman.core.database.SQLite.user_update")
_, repository_id = configuration.check_loaded()
@@ -97,7 +97,7 @@ def test_run_list(args: argparse.Namespace, configuration: Configuration, databa
args = _default_args(args)
args.action = Action.List
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
list_mock = mocker.patch("ahriman.core.database.SQLite.user_list", return_value=[user])
_, repository_id = configuration.check_loaded()
@@ -116,7 +116,7 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
args.exit_code = True
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("ahriman.core.database.SQLite.user_list", return_value=[])
- check_mock = mocker.patch("ahriman.application.handlers.Handler.check_status")
+ check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_status")
_, repository_id = configuration.check_loaded()
Users.run(args, repository_id, configuration, report=False)
diff --git a/tests/ahriman/application/handlers/test_handler_validate.py b/tests/ahriman/application/handlers/test_handler_validate.py
index 8525fdc4..7d2bea99 100644
--- a/tests/ahriman/application/handlers/test_handler_validate.py
+++ b/tests/ahriman/application/handlers/test_handler_validate.py
@@ -4,7 +4,7 @@ import pytest
from pytest_mock import MockerFixture
-from ahriman.application.handlers import Validate
+from ahriman.application.handlers.validate import Validate
from ahriman.core.configuration import Configuration
from ahriman.core.configuration.schema import CONFIGURATION_SCHEMA
from ahriman.core.configuration.validator import Validator
diff --git a/tests/ahriman/application/handlers/test_handler_versions.py b/tests/ahriman/application/handlers/test_handler_versions.py
index be64b9c2..ec1c7b51 100644
--- a/tests/ahriman/application/handlers/test_handler_versions.py
+++ b/tests/ahriman/application/handlers/test_handler_versions.py
@@ -4,7 +4,7 @@ import pytest
from pytest_mock import MockerFixture
from unittest.mock import call as MockCall
-from ahriman.application.handlers import Versions
+from ahriman.application.handlers.versions import Versions
from ahriman.core.configuration import Configuration
@@ -12,7 +12,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
"""
must run command
"""
- application_mock = mocker.patch("ahriman.application.handlers.Versions.package_dependencies")
+ application_mock = mocker.patch("ahriman.application.handlers.versions.Versions.package_dependencies")
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
_, repository_id = configuration.check_loaded()
diff --git a/tests/ahriman/application/handlers/test_handler_web.py b/tests/ahriman/application/handlers/test_handler_web.py
index fe2861c2..e99703bb 100644
--- a/tests/ahriman/application/handlers/test_handler_web.py
+++ b/tests/ahriman/application/handlers/test_handler_web.py
@@ -3,7 +3,7 @@ import pytest
from pytest_mock import MockerFixture
-from ahriman.application.handlers import Web
+from ahriman.application.handlers.web import Web
from ahriman.core.configuration import Configuration
from ahriman.core.repository import Repository
from ahriman.models.log_handler import LogHandler
@@ -43,7 +43,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
stop_mock = mocker.patch("ahriman.core.spawn.Spawn.stop")
join_mock = mocker.patch("ahriman.core.spawn.Spawn.join")
_, repository_id = configuration.check_loaded()
- mocker.patch("ahriman.application.handlers.Handler.repositories_extract", return_value=[repository_id])
+ mocker.patch("ahriman.application.handlers.handler.Handler.repositories_extract", return_value=[repository_id])
Web.run(args, repository_id, configuration, report=False)
setup_mock.assert_called_once_with(configuration, pytest.helpers.anyvar(int), [repository_id])
diff --git a/tests/ahriman/application/test_ahriman.py b/tests/ahriman/application/test_ahriman.py
index b5b523b7..140a37be 100644
--- a/tests/ahriman/application/test_ahriman.py
+++ b/tests/ahriman/application/test_ahriman.py
@@ -5,7 +5,7 @@ from pathlib import Path
from pytest_mock import MockerFixture
from ahriman.application import ahriman
-from ahriman.application.handlers import Handler
+from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.models.action import Action
from ahriman.models.build_status import BuildStatusEnum
diff --git a/tests/ahriman/core/test_module_loader.py b/tests/ahriman/core/test_module_loader.py
new file mode 100644
index 00000000..fd9d5a65
--- /dev/null
+++ b/tests/ahriman/core/test_module_loader.py
@@ -0,0 +1,25 @@
+import ahriman.web.views
+
+from pathlib import Path
+
+from ahriman.core.module_loader import _modules, implementations
+from ahriman.web.views.base import BaseView
+
+
+def test_implementations() -> None:
+ """
+ must load implementations from the package
+ """
+ routes = list(implementations(ahriman.web.views, BaseView))
+ assert routes
+ assert all(isinstance(view, type) for view in routes)
+ assert all(issubclass(view, BaseView) for view in routes)
+
+
+def test_modules() -> None:
+ """
+ must load modules
+ """
+ modules = list(_modules(Path(__file__).parent.parent, "ahriman.web.views"))
+ assert modules
+ assert all(not module.ispkg for module in modules)
diff --git a/tests/ahriman/web/test_routes.py b/tests/ahriman/web/test_routes.py
index 7d8ce98c..8d5c1253 100644
--- a/tests/ahriman/web/test_routes.py
+++ b/tests/ahriman/web/test_routes.py
@@ -1,14 +1,9 @@
-import pytest
-
from aiohttp.web import Application
-from importlib.machinery import ModuleSpec
from pathlib import Path
-from pytest_mock import MockerFixture
-from types import ModuleType
from ahriman.core.configuration import Configuration
from ahriman.core.utils import walk
-from ahriman.web.routes import _dynamic_routes, _module, _modules, setup_routes
+from ahriman.web.routes import _dynamic_routes, setup_routes
def test_dynamic_routes(resource_path_root: Path, configuration: Configuration) -> None:
@@ -22,54 +17,11 @@ def test_dynamic_routes(resource_path_root: Path, configuration: Configuration)
if file.suffix == ".py" and file.name not in ("__init__.py", "base.py", "status_view_guard.py")
]
- routes = _dynamic_routes(views_root, configuration)
+ routes = _dynamic_routes(configuration)
assert all(isinstance(view, type) for view in routes.values())
assert len(set(routes.values())) == len(expected_views)
-def test_module(mocker: MockerFixture) -> None:
- """
- must load module
- """
- exec_mock = mocker.patch("importlib.machinery.SourceFileLoader.exec_module")
- module_info = next(_modules(Path(__file__).parent))
-
- module = _module(module_info)
- assert isinstance(module, ModuleType)
- exec_mock.assert_called_once_with(pytest.helpers.anyvar(int))
-
-
-def test_module_no_spec(mocker: MockerFixture) -> None:
- """
- must raise ValueError if spec is not available
- """
- mocker.patch("importlib.machinery.FileFinder.find_spec", return_value=None)
- module_info = next(_modules(Path(__file__).parent))
-
- with pytest.raises(ValueError):
- _module(module_info)
-
-
-def test_module_no_loader(mocker: MockerFixture) -> None:
- """
- must raise ValueError if loader is not available
- """
- mocker.patch("importlib.machinery.FileFinder.find_spec", return_value=ModuleSpec("name", None))
- module_info = next(_modules(Path(__file__).parent))
-
- with pytest.raises(ValueError):
- _module(module_info)
-
-
-def test_modules() -> None:
- """
- must load modules
- """
- modules = list(_modules(Path(__file__).parent.parent))
- assert modules
- assert all(not module.ispkg for module in modules)
-
-
def test_setup_routes(application: Application, configuration: Configuration) -> None:
"""
must generate non-empty list of routes
diff --git a/tests/ahriman/web/views/test_view_static.py b/tests/ahriman/web/views/test_view_static.py
index 8d25e3b6..bcbe3759 100644
--- a/tests/ahriman/web/views/test_view_static.py
+++ b/tests/ahriman/web/views/test_view_static.py
@@ -1,6 +1,7 @@
import pytest
from aiohttp.test_utils import TestClient
+from pytest_mock import MockerFixture
from ahriman.models.user_access import UserAccess
from ahriman.web.views.static import StaticView
@@ -31,12 +32,12 @@ async def test_get(client_with_auth: TestClient) -> None:
assert response.headers["Location"] == "/static/favicon.ico"
-async def test_get_not_found(client_with_auth: TestClient) -> None:
+async def test_get_not_found(client_with_auth: TestClient, mocker: MockerFixture) -> None:
"""
must raise not found if path is invalid
"""
- for route in client_with_auth.app.router.routes():
- if hasattr(route.handler, "ROUTES"):
- route.handler.ROUTES = []
+ print([route.handler for route in client_with_auth.app.router.routes()])
+ static_route = next(route for route in client_with_auth.app.router.routes() if route.handler == StaticView)
+ mocker.patch.object(static_route.handler, "ROUTES", [])
response = await client_with_auth.get("/favicon.ico", allow_redirects=False)
assert response.status == 404