rewrite repository definition logic

This commit is contained in:
Evgenii Alekseev 2023-08-31 03:49:40 +03:00
parent b6a9284b92
commit b7dc4c7ef4
45 changed files with 668 additions and 241 deletions

View File

@ -157,7 +157,8 @@ def _set_aur_search_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("--sort-by", help="sort field by this field. In case if two packages have the same value of " 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", "the specified field, they will be always sorted by name",
default="name", choices=sorted(handlers.Search.SORT_FIELDS)) default="name", choices=sorted(handlers.Search.SORT_FIELDS))
parser.set_defaults(handler=handlers.Search, architecture=[""], lock=None, report=False, quiet=True, unsafe=True) parser.set_defaults(handler=handlers.Search, architecture=[""], lock=None, report=False, repository=[""],
quiet=True, unsafe=True)
return parser return parser
@ -175,8 +176,8 @@ def _set_help_parser(root: SubParserAction) -> argparse.ArgumentParser:
description="show help message for application or command and exit", description="show help message for application or command and exit",
formatter_class=_formatter) formatter_class=_formatter)
parser.add_argument("command", help="show help message for specific command", nargs="?") parser.add_argument("command", help="show help message for specific command", nargs="?")
parser.set_defaults(handler=handlers.Help, architecture=[""], lock=None, report=False, quiet=True, unsafe=True, parser.set_defaults(handler=handlers.Help, architecture=[""], lock=None, report=False, repository=[""], quiet=True,
parser=_parser) unsafe=True, parser=_parser)
return parser return parser
@ -194,8 +195,8 @@ def _set_help_commands_unsafe_parser(root: SubParserAction) -> argparse.Argument
description="list unsafe commands as defined in default args", formatter_class=_formatter) description="list unsafe commands as defined in default args", formatter_class=_formatter)
parser.add_argument("command", help="instead of showing commands, just test command line for unsafe subcommand " parser.add_argument("command", 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="*") "and return 0 in case if command is safe and 1 otherwise", nargs="*")
parser.set_defaults(handler=handlers.UnsafeCommands, architecture=[""], lock=None, report=False, quiet=True, parser.set_defaults(handler=handlers.UnsafeCommands, architecture=[""], lock=None, report=False, repository=[""],
unsafe=True, parser=_parser) quiet=True, unsafe=True, parser=_parser)
return parser return parser
@ -213,8 +214,8 @@ def _set_help_updates_parser(root: SubParserAction) -> argparse.ArgumentParser:
description="request AUR for current version and compare with current service version", description="request AUR for current version and compare with current service version",
formatter_class=_formatter) formatter_class=_formatter)
parser.add_argument("-e", "--exit-code", help="return non-zero exit code if updates available", action="store_true") 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, report=False, quiet=True, parser.set_defaults(handler=handlers.ServiceUpdates, architecture=[""], lock=None, report=False, repository=[""],
unsafe=True) quiet=True, unsafe=True)
return parser return parser
@ -230,7 +231,8 @@ def _set_help_version_parser(root: SubParserAction) -> argparse.ArgumentParser:
""" """
parser = root.add_parser("help-version", aliases=["version"], help="application version", parser = root.add_parser("help-version", aliases=["version"], help="application version",
description="print application and its dependencies versions", formatter_class=_formatter) description="print application and its dependencies versions", formatter_class=_formatter)
parser.set_defaults(handler=handlers.Versions, architecture=[""], lock=None, report=False, quiet=True, unsafe=True) parser.set_defaults(handler=handlers.Versions, architecture=[""], lock=None, report=False, repository=[""],
quiet=True, unsafe=True)
return parser return parser
@ -381,7 +383,8 @@ def _set_patch_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
"it must end with ()") "it must end with ()")
parser.add_argument("patch", help="path to file which contains function or variable value. If not set, " 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="?") "the value will be read from stdin", type=Path, nargs="?")
parser.set_defaults(handler=handlers.Patch, action=Action.Update, architecture=[""], lock=None, report=False) parser.set_defaults(handler=handlers.Patch, action=Action.Update, architecture=[""], lock=None, report=False,
repository=[""])
return parser return parser
@ -402,7 +405,7 @@ def _set_patch_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("-v", "--variable", help="if set, show only patches for specified PKGBUILD variables", parser.add_argument("-v", "--variable", help="if set, show only patches for specified PKGBUILD variables",
action="append") action="append")
parser.set_defaults(handler=handlers.Patch, action=Action.List, architecture=[""], lock=None, report=False, parser.set_defaults(handler=handlers.Patch, action=Action.List, architecture=[""], lock=None, report=False,
unsafe=True) repository=[""], unsafe=True)
return parser return parser
@ -423,7 +426,8 @@ def _set_patch_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
"to remove only specified PKGBUILD variables. In case if not set, " "to remove only specified PKGBUILD variables. In case if not set, "
"it will remove all patches related to the package", "it will remove all patches related to the package",
action="append") action="append")
parser.set_defaults(handler=handlers.Patch, action=Action.Remove, architecture=[""], lock=None, report=False) parser.set_defaults(handler=handlers.Patch, action=Action.Remove, architecture=[""], lock=None, report=False,
repository=[""])
return parser return parser
@ -448,7 +452,7 @@ def _set_patch_set_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("-t", "--track", help="files which has to be tracked", action="append", parser.add_argument("-t", "--track", help="files which has to be tracked", action="append",
default=["*.diff", "*.patch"]) default=["*.diff", "*.patch"])
parser.set_defaults(handler=handlers.Patch, action=Action.Update, architecture=[""], lock=None, report=False, parser.set_defaults(handler=handlers.Patch, action=Action.Update, architecture=[""], lock=None, report=False,
variable=None) repository=[""], variable=None)
return parser return parser
@ -465,7 +469,8 @@ def _set_repo_backup_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser = root.add_parser("repo-backup", help="backup repository data", parser = root.add_parser("repo-backup", help="backup repository data",
description="backup repository settings and database", formatter_class=_formatter) description="backup repository settings and database", formatter_class=_formatter)
parser.add_argument("path", help="path of the output archive", type=Path) parser.add_argument("path", help="path of the output archive", type=Path)
parser.set_defaults(handler=handlers.Backup, architecture=[""], lock=None, report=False, unsafe=True) parser.set_defaults(handler=handlers.Backup, architecture=[""], lock=None, report=False, repository=[""],
unsafe=True)
return parser return parser
@ -644,7 +649,8 @@ def _set_repo_restore_parser(root: SubParserAction) -> argparse.ArgumentParser:
description="restore settings and database", formatter_class=_formatter) description="restore settings and database", formatter_class=_formatter)
parser.add_argument("path", help="path of the input archive", type=Path) 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.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, unsafe=True) parser.set_defaults(handler=handlers.Restore, architecture=[""], lock=None, report=False, repository=[""],
unsafe=True)
return parser return parser
@ -681,7 +687,7 @@ def _set_repo_status_update_parser(root: SubParserAction) -> argparse.ArgumentPa
description="update repository status on the status page", formatter_class=_formatter) description="update repository status on the status page", formatter_class=_formatter)
parser.add_argument("-s", "--status", help="new status", parser.add_argument("-s", "--status", help="new status",
type=BuildStatusEnum, choices=enum_values(BuildStatusEnum), default=BuildStatusEnum.Success) type=BuildStatusEnum, choices=enum_values(BuildStatusEnum), default=BuildStatusEnum.Success)
parser.set_defaults(handler=handlers.StatusUpdate, action=Action.Update, lock=None, report=False, package=[], parser.set_defaults(handler=handlers.StatusUpdate, action=Action.Update, lock=None, package=[], report=False,
quiet=True, unsafe=True) quiet=True, unsafe=True)
return parser return parser
@ -865,7 +871,7 @@ def _set_service_key_import_parser(root: SubParserAction) -> argparse.ArgumentPa
formatter_class=_formatter) formatter_class=_formatter)
parser.add_argument("--key-server", help="key server for key import", default="keyserver.ubuntu.com") 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.add_argument("key", help="PGP key to import from public server")
parser.set_defaults(handler=handlers.KeyImport, architecture=[""], lock=None, report=False) parser.set_defaults(handler=handlers.KeyImport, architecture=[""], lock=None, report=False, repository=[""])
return parser return parser
@ -946,7 +952,7 @@ def _set_user_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("-R", "--role", help="user access level", parser.add_argument("-R", "--role", help="user access level",
type=UserAccess, choices=enum_values(UserAccess), default=UserAccess.Read) type=UserAccess, choices=enum_values(UserAccess), default=UserAccess.Read)
parser.set_defaults(handler=handlers.Users, action=Action.Update, architecture=[""], lock=None, report=False, parser.set_defaults(handler=handlers.Users, action=Action.Update, architecture=[""], lock=None, report=False,
quiet=True) repository=[""], quiet=True)
return parser return parser
@ -967,7 +973,7 @@ def _set_user_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", 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("-R", "--role", help="filter users by role", type=UserAccess, choices=enum_values(UserAccess)) 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, report=False, parser.set_defaults(handler=handlers.Users, action=Action.List, architecture=[""], lock=None, report=False,
quiet=True, unsafe=True) repository=[""], quiet=True, unsafe=True)
return parser return parser
@ -986,7 +992,7 @@ def _set_user_remove_parser(root: SubParserAction) -> argparse.ArgumentParser:
formatter_class=_formatter) formatter_class=_formatter)
parser.add_argument("username", help="username for web service") parser.add_argument("username", help="username for web service")
parser.set_defaults(handler=handlers.Users, action=Action.Remove, architecture=[""], lock=None, report=False, parser.set_defaults(handler=handlers.Users, action=Action.Remove, architecture=[""], lock=None, report=False,
quiet=True) repository=[""], quiet=True)
return parser return parser

View File

@ -34,7 +34,7 @@ class Backup(Handler):
backup packages handler backup packages handler
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_MULTI_ARCHITECTURE_RUN = False # system-wide action
@classmethod @classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *, def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,

View File

@ -30,7 +30,7 @@ class Dump(Handler):
dump configuration handler dump configuration handler
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = False ALLOW_MULTI_ARCHITECTURE_RUN = False # conflicting io
@classmethod @classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *, def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,

View File

@ -35,7 +35,6 @@ class Handler:
base handler class for command callbacks base handler class for command callbacks
Attributes: Attributes:
ALLOW_AUTO_ARCHITECTURE_RUN(bool): (class attribute) allow defining architecture from existing repositories
ALLOW_MULTI_ARCHITECTURE_RUN(bool): (class attribute) allow running with multiple architectures ALLOW_MULTI_ARCHITECTURE_RUN(bool): (class attribute) allow running with multiple architectures
Examples: Examples:
@ -47,7 +46,6 @@ class Handler:
>>> Add.execute(args) >>> Add.execute(args)
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = True
ALLOW_MULTI_ARCHITECTURE_RUN = True ALLOW_MULTI_ARCHITECTURE_RUN = True
@classmethod @classmethod
@ -121,32 +119,35 @@ class Handler:
Raises: Raises:
MissingArchitectureError: if no architecture set and automatic detection is not allowed or failed MissingArchitectureError: if no architecture set and automatic detection is not allowed or failed
""" """
if not cls.ALLOW_AUTO_ARCHITECTURE_RUN and args.architecture is None:
# for some parsers (e.g. config) we need to run with specific architecture
# for those cases architecture must be set explicitly
raise MissingArchitectureError(args.command)
configuration = Configuration() configuration = Configuration()
configuration.load(args.configuration) configuration.load(args.configuration)
name = configuration.get("repository", "name", fallback="") # will only be used for legacy mode # pylint, wtf???
if args.architecture: # architecture is specified explicitly
repositories = args.repository or [name] # fallback for legacy mode
return sorted(
set(
RepositoryId(architecture, repository)
for architecture in args.architecture
for repository in repositories
)
)
# wtf???
root = configuration.getpath("repository", "root") # pylint: disable=assignment-from-no-return root = configuration.getpath("repository", "root") # pylint: disable=assignment-from-no-return
architectures = RepositoryPaths.known_architectures(root, name)
if not architectures: # well we did not find anything # extract repository names first
names = args.repository
if names is None: # try to read file system first
names = RepositoryPaths.known_repositories(root)
if not names: # try to read configuration now
names = [configuration.get("repository", "name")]
# extract architecture names
if (architectures := args.architecture) is not None:
repositories = set(
RepositoryId(architecture, name)
for name in names
for architecture in architectures
)
else: # try to read from file system
repositories = set(
RepositoryId(architecture, name)
for name in names
for architecture in RepositoryPaths.known_architectures(root, name)
)
if not repositories:
raise MissingArchitectureError(args.command) raise MissingArchitectureError(args.command)
return sorted(architectures) return sorted(repositories)
@classmethod @classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *, def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,

View File

@ -29,7 +29,7 @@ class Help(Handler):
help handler help handler
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_MULTI_ARCHITECTURE_RUN = False # system-wide action
@classmethod @classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *, def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,

View File

@ -30,7 +30,7 @@ class KeyImport(Handler):
key import packages handler key import packages handler
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_MULTI_ARCHITECTURE_RUN = False # system-wide action
@classmethod @classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *, def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,

View File

@ -38,6 +38,8 @@ class Patch(Handler):
patch control handler patch control handler
""" """
ALLOW_MULTI_ARCHITECTURE_RUN = False # system-wide action
@classmethod @classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *, def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,
report: bool) -> None: report: bool) -> None:

View File

@ -31,7 +31,7 @@ class Restore(Handler):
restore packages handler restore packages handler
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_MULTI_ARCHITECTURE_RUN = False # system-wide action
@classmethod @classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *, def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,

View File

@ -40,7 +40,7 @@ class Search(Handler):
SORT_FIELDS(set[str]): (class attribute) allowed fields to sort the package list SORT_FIELDS(set[str]): (class attribute) allowed fields to sort the package list
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_MULTI_ARCHITECTURE_RUN = False # system-wide action
SORT_FIELDS = { SORT_FIELDS = {
field.name field.name
for field in fields(AURPackage) for field in fields(AURPackage)

View File

@ -33,7 +33,7 @@ class ServiceUpdates(Handler):
service updates handler service updates handler
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_MULTI_ARCHITECTURE_RUN = False # system-wide action
@classmethod @classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *, def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,

View File

@ -25,6 +25,7 @@ from pwd import getpwuid
from ahriman.application.application import Application from ahriman.application.application import Application
from ahriman.application.handlers import Handler from ahriman.application.handlers import Handler
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import MissingArchitectureError
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId
from ahriman.models.repository_paths import RepositoryPaths from ahriman.models.repository_paths import RepositoryPaths
from ahriman.models.user import User from ahriman.models.user import User
@ -40,7 +41,7 @@ class Setup(Handler):
SUDOERS_DIR_PATH(Path): (class attribute) path to sudoers.d includes directory SUDOERS_DIR_PATH(Path): (class attribute) path to sudoers.d includes directory
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = False ALLOW_MULTI_ARCHITECTURE_RUN = False # conflicting io
ARCHBUILD_COMMAND_PATH = Path("/usr") / "bin" / "archbuild" ARCHBUILD_COMMAND_PATH = Path("/usr") / "bin" / "archbuild"
MIRRORLIST_PATH = Path("/etc") / "pacman.d" / "mirrorlist" MIRRORLIST_PATH = Path("/etc") / "pacman.d" / "mirrorlist"
@ -58,6 +59,10 @@ class Setup(Handler):
configuration(Configuration): configuration instance configuration(Configuration): configuration instance
report(bool): force enable or disable reporting report(bool): force enable or disable reporting
""" """
# special check for args to avoid auto definition for setup command
if args.architecture is None or args.repository is None:
raise MissingArchitectureError(args.command)
Setup.configuration_create_ahriman(args, repository_id, configuration) Setup.configuration_create_ahriman(args, repository_id, configuration)
configuration.reload() configuration.reload()

View File

@ -34,7 +34,7 @@ class Shell(Handler):
python shell handler python shell handler
""" """
ALLOW_MULTI_ARCHITECTURE_RUN = False ALLOW_MULTI_ARCHITECTURE_RUN = False # conflicting io
@classmethod @classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *, def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,

View File

@ -35,7 +35,7 @@ class Status(Handler):
package status handler package status handler
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = False ALLOW_MULTI_ARCHITECTURE_RUN = False # conflicting io
@classmethod @classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *, def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,

View File

@ -31,7 +31,7 @@ class StatusUpdate(Handler):
status update handler status update handler
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = False ALLOW_MULTI_ARCHITECTURE_RUN = False # conflicting io
@classmethod @classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *, def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,

View File

@ -32,7 +32,7 @@ class Structure(Handler):
dump repository structure handler dump repository structure handler
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = False ALLOW_MULTI_ARCHITECTURE_RUN = False # conflicting io
@classmethod @classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *, def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,

View File

@ -30,7 +30,7 @@ class UnsafeCommands(Handler):
unsafe command help parser unsafe command help parser
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_MULTI_ARCHITECTURE_RUN = False # system-wide action
@classmethod @classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *, def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,

View File

@ -35,7 +35,7 @@ class Users(Handler):
user management handler user management handler
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_MULTI_ARCHITECTURE_RUN = False # system-wide action
@classmethod @classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *, def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,

View File

@ -37,7 +37,7 @@ class Validate(Handler):
configuration validator handler configuration validator handler
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = False ALLOW_MULTI_ARCHITECTURE_RUN = False # conflicting io
@classmethod @classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *, def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,

View File

@ -39,7 +39,7 @@ class Versions(Handler):
PEP423_PACKAGE_NAME(str): (class attribute) special regex for valid PEP423 package name PEP423_PACKAGE_NAME(str): (class attribute) special regex for valid PEP423 package name
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" ALLOW_MULTI_ARCHITECTURE_RUN = False # system-wide action
PEP423_PACKAGE_NAME = re.compile(r"^[A-Za-z0-9._-]+") PEP423_PACKAGE_NAME = re.compile(r"^[A-Za-z0-9._-]+")
@classmethod @classmethod

View File

@ -32,7 +32,6 @@ class Web(Handler):
web server handler web server handler
""" """
ALLOW_AUTO_ARCHITECTURE_RUN = False
ALLOW_MULTI_ARCHITECTURE_RUN = False # required to be able to spawn external processes ALLOW_MULTI_ARCHITECTURE_RUN = False # required to be able to spawn external processes
@classmethod @classmethod

View File

@ -165,7 +165,7 @@ class MissingArchitectureError(ValueError):
Args: Args:
command(str): command name which throws exception command(str): command name which throws exception
""" """
ValueError.__init__(self, f"Architecture required for subcommand {command}, but missing") ValueError.__init__(self, f"Architecture/repository required for subcommand {command}, but missing")
class MultipleArchitecturesError(ValueError): class MultipleArchitecturesError(ValueError):
@ -180,7 +180,7 @@ class MultipleArchitecturesError(ValueError):
Args: Args:
command(str): command name which throws exception command(str): command name which throws exception
""" """
ValueError.__init__(self, f"Multiple architectures are not supported by subcommand {command}") ValueError.__init__(self, f"Multiple architectures/repositories are not supported by subcommand {command}")
class OptionError(ValueError): class OptionError(ValueError):

View File

@ -20,8 +20,6 @@
from dataclasses import dataclass from dataclasses import dataclass
from typing import Any from typing import Any
from ahriman.core.exceptions import InitializeError
@dataclass(frozen=True) @dataclass(frozen=True)
class RepositoryId: class RepositoryId:
@ -36,16 +34,6 @@ class RepositoryId:
architecture: str architecture: str
name: str name: str
def __post_init__(self) -> None:
"""
check that name is set
Raises:
InitializeError: in case if name is not set
"""
if not self.name:
raise InitializeError("Repository name is not set")
def __lt__(self, other: Any) -> bool: def __lt__(self, other: Any) -> bool:
""" """
comparison operator for sorting comparison operator for sorting

View File

@ -140,31 +140,57 @@ class RepositoryPaths:
""" """
return self.owner(self.root) return self.owner(self.root)
# TODO see https://github.com/python/mypy/issues/12534, remove type: ignore after release
# pylint: disable=protected-access
@classmethod @classmethod
def known_architectures(cls, root: Path, name: str) -> set[RepositoryId]: def known_architectures(cls, root: Path, name: str = "") -> set[str]: # type: ignore[return]
""" """
get known architectures get known architecture names
Args: Args:
root(Path): repository root root(Path): repository root
name(str): repository name from configuration name(str, optional): repository name (Default value = "")
Returns: Returns:
set[RepositoryId]: list of tuple of repository name and architectures for which tree is created set[str]: list of repository architectures for which there is created tree
""" """
def walk(repository_path: Path, repository_name: str) -> Generator[RepositoryId, None, None]: def walk(repository_dir: Path) -> Generator[str, None, None]:
for architecture in filter(lambda path: path.is_dir(), repository_path.iterdir()): for architecture in filter(lambda path: path.is_dir(), repository_dir.iterdir()):
yield RepositoryId(architecture.name, repository_name) yield architecture.name
instance = cls(root, RepositoryId("", ""))
match (instance._repository_root / name):
case full_tree if full_tree.is_dir():
return set(walk(full_tree)) # actually works for legacy too in case if name is set to empty string
case _ if instance._repository_root.is_dir():
return set(walk(instance._repository_root)) # legacy only tree
case _:
return set() # no tree detected at all
def walk_root(paths: RepositoryPaths) -> Generator[RepositoryId, None, None]:
# pylint: disable=protected-access # pylint: disable=protected-access
for repository in filter(lambda path: path.is_dir(), paths._repository_root.iterdir()): @classmethod
print(repository) def known_repositories(cls, root: Path) -> set[str]:
yield from walk(repository, repository.name) """
get known repository names
instance = cls(root, RepositoryId("", root.name)) # suppress initialization error Args:
# try to get list per repository first and then fallback to old schema if nothing found root(Path): repository root
return set(walk_root(instance)) or set(walk(instance._repository_root, name))
Returns:
set[str]: list of repository names for which there is created tree. Returns empty set in case if repository
is loaded in legacy mode
"""
# simply walk through the root. In case if there are subdirectories, emit the name
def walk(paths: RepositoryPaths) -> Generator[str, None, None]:
for repository in filter(lambda path: path.is_dir(), paths._repository_root.iterdir()):
if any(path.is_dir() for path in repository.iterdir()):
yield repository.name
instance = cls(root, RepositoryId("", ""))
if not instance._repository_root.is_dir():
return set() # no tree created
return set(walk(instance))
@staticmethod @staticmethod
def owner(path: Path) -> tuple[int, int]: def owner(path: Path) -> tuple[int, int]:

View File

@ -117,14 +117,68 @@ def test_run(args: argparse.Namespace, configuration: Configuration) -> None:
def test_repositories_extract(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None: def test_repositories_extract(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
""" """
must generate list of available architectures must generate list of available repositories based on flags
""" """
args.architecture = ["arch"]
args.configuration = configuration.path args.configuration = configuration.path
_, repository_id = configuration.check_loaded() args.repository = ["repo"]
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures") known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories")
Handler.repositories_extract(args) assert Handler.repositories_extract(args) == [RepositoryId("arch", "repo")]
known_architectures_mock.assert_called_once_with(configuration.getpath("repository", "root"), repository_id.name) known_architectures_mock.assert_not_called()
known_repositories_mock.assert_not_called()
def test_repositories_extract_repository(args: argparse.Namespace, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
must generate list of available repositories based on flags and tree
"""
args.architecture = ["arch"]
args.configuration = configuration.path
args.repository = None
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories",
return_value={"repo"})
assert Handler.repositories_extract(args) == [RepositoryId("arch", "repo")]
known_architectures_mock.assert_not_called()
known_repositories_mock.assert_called_once_with(configuration.repository_paths.root)
def test_repositories_extract_repository_legacy(args: argparse.Namespace, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
must generate list of available repositories based on flags and tree
"""
args.architecture = ["arch"]
args.configuration = configuration.path
args.repository = None
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures")
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories",
return_value={"repo"})
assert Handler.repositories_extract(args) == [RepositoryId("arch", "repo")]
known_architectures_mock.assert_not_called()
known_repositories_mock.assert_called_once_with(configuration.repository_paths.root)
def test_repositories_extract_architecture(args: argparse.Namespace, configuration: Configuration,
mocker: MockerFixture) -> None:
"""
must read repository name from config
"""
args.architecture = None
args.configuration = configuration.path
args.repository = ["repo"]
known_architectures_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures",
return_value={"arch"})
known_repositories_mock = mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories")
assert Handler.repositories_extract(args) == [RepositoryId("arch", "repo")]
known_architectures_mock.assert_called_once_with(configuration.repository_paths.root, "repo")
known_repositories_mock.assert_not_called()
def test_repositories_extract_empty(args: argparse.Namespace, configuration: Configuration, def test_repositories_extract_empty(args: argparse.Namespace, configuration: Configuration,
@ -133,52 +187,16 @@ def test_repositories_extract_empty(args: argparse.Namespace, configuration: Con
must raise exception if no available architectures found must raise exception if no available architectures found
""" """
args.command = "config" args.command = "config"
args.architecture = None
args.configuration = configuration.path args.configuration = configuration.path
args.repository = None
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures", return_value=set()) mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_architectures", return_value=set())
mocker.patch("ahriman.models.repository_paths.RepositoryPaths.known_repositories", return_value=set())
with pytest.raises(MissingArchitectureError): with pytest.raises(MissingArchitectureError):
Handler.repositories_extract(args) Handler.repositories_extract(args)
def test_repositories_extract_exception(args: argparse.Namespace, mocker: MockerFixture) -> None:
"""
must raise exception on missing architectures
"""
args.command = "config"
mocker.patch.object(Handler, "ALLOW_AUTO_ARCHITECTURE_RUN", False)
with pytest.raises(MissingArchitectureError):
Handler.repositories_extract(args)
def test_repositories_extract_specified(args: argparse.Namespace, configuration: Configuration) -> None:
"""
must return architecture list if it has been specified
"""
args.configuration = configuration.path
args.architecture = ["i686", "x86_64"]
args.repository = []
_, repository_id = configuration.check_loaded()
ids = [RepositoryId(architecture, repository_id.name) for architecture in args.architecture]
assert Handler.repositories_extract(args) == sorted(set(ids))
def test_repositories_extract_specified_with_repository(args: argparse.Namespace, configuration: Configuration) -> None:
"""
must return architecture list if it has been specified together with repositories
"""
args.configuration = configuration.path
args.architecture = ["i686", "x86_64"]
args.repository = ["repo1", "repo2"]
ids = [
RepositoryId(architecture, name)
for architecture in args.architecture
for name in args.repository
]
assert Handler.repositories_extract(args) == sorted(set(ids))
def test_check_if_empty() -> None: def test_check_if_empty() -> None:
""" """
must raise exception in case if predicate is True and enabled must raise exception in case if predicate is True and enabled

View File

@ -56,8 +56,8 @@ def test_get_paths(configuration: Configuration, mocker: MockerFixture) -> None:
assert all(path.exists() for path in paths if path.name not in (".gnupg", "cache")) assert all(path.exists() for path in paths if path.name not in (".gnupg", "cache"))
def test_disallow_auto_architecture_run() -> None: def test_disallow_multi_architecture_run() -> None:
""" """
must not allow multi architecture run must not allow multi architecture run
""" """
assert not Backup.ALLOW_AUTO_ARCHITECTURE_RUN assert not Backup.ALLOW_MULTI_ARCHITECTURE_RUN

View File

@ -35,8 +35,8 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
print_mock.assert_called() print_mock.assert_called()
def test_disallow_auto_architecture_run() -> None: def test_disallow_multi_architecture_run() -> None:
""" """
must not allow multi architecture run must not allow multi architecture run
""" """
assert not Dump.ALLOW_AUTO_ARCHITECTURE_RUN assert not Dump.ALLOW_MULTI_ARCHITECTURE_RUN

View File

@ -47,8 +47,8 @@ def test_run_command(args: argparse.Namespace, configuration: Configuration, moc
parse_mock.assert_called_once_with(["aur-search", "--help"]) parse_mock.assert_called_once_with(["aur-search", "--help"])
def test_disallow_auto_architecture_run() -> None: def test_disallow_multi_architecture_run() -> None:
""" """
must not allow multi architecture run must not allow multi architecture run
""" """
assert not Help.ALLOW_AUTO_ARCHITECTURE_RUN assert not Help.ALLOW_MULTI_ARCHITECTURE_RUN

View File

@ -36,8 +36,8 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
application_mock.assert_called_once_with(args.key_server, args.key) application_mock.assert_called_once_with(args.key_server, args.key)
def test_disallow_auto_architecture_run() -> None: def test_disallow_multi_architecture_run() -> None:
""" """
must not allow multi architecture run must not allow multi architecture run
""" """
assert not KeyImport.ALLOW_AUTO_ARCHITECTURE_RUN assert not KeyImport.ALLOW_MULTI_ARCHITECTURE_RUN

View File

@ -190,3 +190,10 @@ def test_patch_set_remove(application: Application, package_ahriman: Package, mo
remove_mock = mocker.patch("ahriman.core.database.SQLite.patches_remove") remove_mock = mocker.patch("ahriman.core.database.SQLite.patches_remove")
Patch.patch_set_remove(application, package_ahriman.base, ["version"]) Patch.patch_set_remove(application, package_ahriman.base, ["version"])
remove_mock.assert_called_once_with(package_ahriman.base, ["version"]) remove_mock.assert_called_once_with(package_ahriman.base, ["version"])
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Patch.ALLOW_MULTI_ARCHITECTURE_RUN

View File

@ -37,8 +37,8 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
extract_mock.extractall.assert_called_once_with(path=args.output) extract_mock.extractall.assert_called_once_with(path=args.output)
def test_disallow_auto_architecture_run() -> None: def test_disallow_multi_architecture_run() -> None:
""" """
must not allow multi architecture run must not allow multi architecture run
""" """
assert not Restore.ALLOW_AUTO_ARCHITECTURE_RUN assert not Restore.ALLOW_MULTI_ARCHITECTURE_RUN

View File

@ -128,11 +128,11 @@ def test_sort_exception(aur_package_ahriman: AURPackage) -> None:
Search.sort([aur_package_ahriman], "random_field") Search.sort([aur_package_ahriman], "random_field")
def test_disallow_auto_architecture_run() -> None: def test_disallow_multi_architecture_run() -> None:
""" """
must not allow multi architecture run must not allow multi architecture run
""" """
assert not Search.ALLOW_AUTO_ARCHITECTURE_RUN assert not Search.ALLOW_MULTI_ARCHITECTURE_RUN
def test_sort_fields(aur_package_ahriman: AURPackage) -> None: def test_sort_fields(aur_package_ahriman: AURPackage) -> None:

View File

@ -58,3 +58,10 @@ def test_run_skip(args: argparse.Namespace, configuration: Configuration, reposi
ServiceUpdates.run(args, repository_id, configuration, report=False) ServiceUpdates.run(args, repository_id, configuration, report=False)
application_mock.assert_not_called() application_mock.assert_not_called()
check_mock.assert_not_called() check_mock.assert_not_called()
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not ServiceUpdates.ALLOW_MULTI_ARCHITECTURE_RUN

View File

@ -8,6 +8,7 @@ from unittest.mock import call as MockCall
from ahriman.application.handlers import Setup from ahriman.application.handlers import Setup
from ahriman.core.configuration import Configuration from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import MissingArchitectureError
from ahriman.core.repository import Repository from ahriman.core.repository import Repository
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId
from ahriman.models.repository_paths import RepositoryPaths from ahriman.models.repository_paths import RepositoryPaths
@ -24,6 +25,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
Returns: Returns:
argparse.Namespace: generated arguments for these test cases argparse.Namespace: generated arguments for these test cases
""" """
args.architecture = ["x86_64"]
args.build_as_user = "ahriman" args.build_as_user = "ahriman"
args.from_configuration = Path("/usr/share/devtools/pacman.conf.d/extra.conf") args.from_configuration = Path("/usr/share/devtools/pacman.conf.d/extra.conf")
args.generate_salt = True args.generate_salt = True
@ -31,6 +33,7 @@ def _default_args(args: argparse.Namespace) -> argparse.Namespace:
args.mirror = "mirror" args.mirror = "mirror"
args.multilib = True args.multilib = True
args.packager = "John Doe <john@doe.com>" args.packager = "John Doe <john@doe.com>"
args.repository = ["aur-clone"]
args.server = None args.server = None
args.sign_key = "key" args.sign_key = "key"
args.sign_target = [SignSettings.Packages] args.sign_target = [SignSettings.Packages]
@ -64,6 +67,25 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
init_mock.assert_called_once_with() init_mock.assert_called_once_with()
def test_run_no_architecture_or_repository(configuration: Configuration) -> None:
"""
must raise MissingArchitectureError if either architecture or repository are not supplied
"""
_, repository_id = configuration.check_loaded()
args = argparse.Namespace(architecture=None, command="service-setup", repository=None)
with pytest.raises(MissingArchitectureError):
Setup.run(args, repository_id, configuration, report=False)
args = argparse.Namespace(architecture=[repository_id.architecture], command="service-setup", repository=None)
with pytest.raises(MissingArchitectureError):
Setup.run(args, repository_id, configuration, report=False)
args = argparse.Namespace(architecture=None, command="service-setup", repository=[repository_id.name])
with pytest.raises(MissingArchitectureError):
Setup.run(args, repository_id, configuration, report=False)
def test_run_with_server(args: argparse.Namespace, configuration: Configuration, repository: Repository, def test_run_with_server(args: argparse.Namespace, configuration: Configuration, repository: Repository,
mocker: MockerFixture) -> None: mocker: MockerFixture) -> None:
""" """
@ -251,8 +273,8 @@ def test_executable_create(configuration: Configuration, repository_paths: Repos
unlink_mock.assert_called_once_with(missing_ok=True) unlink_mock.assert_called_once_with(missing_ok=True)
def test_disallow_auto_architecture_run() -> None: def test_disallow_multi_architecture_run() -> None:
""" """
must not allow multi architecture run must not allow multi architecture run
""" """
assert not Setup.ALLOW_AUTO_ARCHITECTURE_RUN assert not Setup.ALLOW_MULTI_ARCHITECTURE_RUN

View File

@ -69,3 +69,10 @@ def test_run_verbose(args: argparse.Namespace, configuration: Configuration, rep
application_mock.assert_called_once_with(local=pytest.helpers.anyvar(int)) application_mock.assert_called_once_with(local=pytest.helpers.anyvar(int))
read_mock.assert_called_once_with(encoding="utf8") read_mock.assert_called_once_with(encoding="utf8")
print_mock.assert_called_once_with(verbose=False) print_mock.assert_called_once_with(verbose=False)
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Shell.ALLOW_MULTI_ARCHITECTURE_RUN

View File

@ -133,8 +133,8 @@ def test_imply_with_report(args: argparse.Namespace, configuration: Configuratio
load_mock.assert_called_once_with(repository_id, configuration, database, report=True, refresh_pacman_database=0) load_mock.assert_called_once_with(repository_id, configuration, database, report=True, refresh_pacman_database=0)
def test_disallow_auto_architecture_run() -> None: def test_disallow_multi_architecture_run() -> None:
""" """
must not allow multi architecture run must not allow multi architecture run
""" """
assert not Status.ALLOW_AUTO_ARCHITECTURE_RUN assert not Status.ALLOW_MULTI_ARCHITECTURE_RUN

View File

@ -86,8 +86,8 @@ def test_imply_with_report(args: argparse.Namespace, configuration: Configuratio
load_mock.assert_called_once_with(repository_id, configuration, database, report=True, refresh_pacman_database=0) load_mock.assert_called_once_with(repository_id, configuration, database, report=True, refresh_pacman_database=0)
def test_disallow_auto_architecture_run() -> None: def test_disallow_multi_architecture_run() -> None:
""" """
must not allow multi architecture run must not allow multi architecture run
""" """
assert not StatusUpdate.ALLOW_AUTO_ARCHITECTURE_RUN assert not StatusUpdate.ALLOW_MULTI_ARCHITECTURE_RUN

View File

@ -46,8 +46,8 @@ def test_run(args: argparse.Namespace, configuration: Configuration, repository:
]) ])
def test_disallow_auto_architecture_run() -> None: def test_disallow_multi_architecture_run() -> None:
""" """
must not allow multi architecture run must not allow multi architecture run
""" """
assert not Structure.ALLOW_AUTO_ARCHITECTURE_RUN assert not Structure.ALLOW_MULTI_ARCHITECTURE_RUN

View File

@ -83,8 +83,8 @@ def test_get_unsafe_commands() -> None:
assert subparser.choices[command].get_default("unsafe") assert subparser.choices[command].get_default("unsafe")
def test_disallow_auto_architecture_run() -> None: def test_disallow_multi_architecture_run() -> None:
""" """
must not allow multi architecture run must not allow multi architecture run
""" """
assert not UnsafeCommands.ALLOW_AUTO_ARCHITECTURE_RUN assert not UnsafeCommands.ALLOW_MULTI_ARCHITECTURE_RUN

View File

@ -175,8 +175,8 @@ def test_user_create_getpass_exception(args: argparse.Namespace, mocker: MockerF
Users.user_create(args) Users.user_create(args)
def test_disallow_auto_architecture_run() -> None: def test_disallow_multi_architecture_run() -> None:
""" """
must not allow multi architecture run must not allow multi architecture run
""" """
assert not Users.ALLOW_AUTO_ARCHITECTURE_RUN assert not Users.ALLOW_MULTI_ARCHITECTURE_RUN

View File

@ -115,8 +115,8 @@ def test_schema_merge() -> None:
assert key in merged["gitremote"]["schema"] assert key in merged["gitremote"]["schema"]
def test_disallow_auto_architecture_run() -> None: def test_disallow_multi_architecture_run() -> None:
""" """
must not allow multi architecture run must not allow multi architecture run
""" """
assert not Validate.ALLOW_AUTO_ARCHITECTURE_RUN assert not Validate.ALLOW_MULTI_ARCHITECTURE_RUN

View File

@ -37,3 +37,10 @@ def test_package_dependencies_missing() -> None:
assert packages assert packages
assert packages.get("pyalpm") is not None assert packages.get("pyalpm") is not None
assert packages.get("Sphinx") is None assert packages.get("Sphinx") is None
def test_disallow_multi_architecture_run() -> None:
"""
must not allow multi architecture run
"""
assert not Versions.ALLOW_MULTI_ARCHITECTURE_RUN

View File

@ -116,13 +116,6 @@ def test_extract_arguments_full(parser: argparse.ArgumentParser, configuration:
] ]
def test_disallow_auto_architecture_run() -> None:
"""
must not allow auto architecture run
"""
assert not Web.ALLOW_AUTO_ARCHITECTURE_RUN
def test_disallow_multi_architecture_run() -> None: def test_disallow_multi_architecture_run() -> None:
""" """
must not allow multi architecture run must not allow multi architecture run

View File

@ -18,16 +18,16 @@ def test_parser(parser: argparse.ArgumentParser) -> None:
""" """
must parse valid command line must parse valid command line
""" """
parser.parse_args(["-a", "x86_64", "service-config"]) parser.parse_args(["-a", "x86_64", "-r", "repo", "service-config"])
def test_parser_option_configuration(parser: argparse.ArgumentParser) -> None: def test_parser_option_configuration(parser: argparse.ArgumentParser) -> None:
""" """
must convert configuration option to Path instance must convert configuration option to Path instance
""" """
args = parser.parse_args(["-a", "x86_64", "service-config"]) args = parser.parse_args(["-a", "x86_64", "-r", "repo", "service-config"])
assert isinstance(args.configuration, Path) assert isinstance(args.configuration, Path)
args = parser.parse_args(["-a", "x86_64", "-c", "ahriman.ini", "service-config"]) args = parser.parse_args(["-a", "x86_64", "-c", "ahriman.ini", "-r", "repo", "service-config"])
assert isinstance(args.configuration, Path) assert isinstance(args.configuration, Path)
@ -93,12 +93,13 @@ def test_parser_option_repository_multiple(parser: argparse.ArgumentParser) -> N
def test_subparsers_aur_search(parser: argparse.ArgumentParser) -> None: def test_subparsers_aur_search(parser: argparse.ArgumentParser) -> None:
""" """
aur-search command must imply architecture list, lock, report, quiet and unsafe aur-search command must imply architecture list, lock, report, repository, quiet and unsafe
""" """
args = parser.parse_args(["aur-search", "ahriman"]) args = parser.parse_args(["aur-search", "ahriman"])
assert args.architecture == [""] assert args.architecture == [""]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == [""]
assert args.quiet assert args.quiet
assert args.unsafe assert args.unsafe
@ -111,14 +112,23 @@ def test_subparsers_aur_search_option_architecture(parser: argparse.ArgumentPars
assert args.architecture == [""] assert args.architecture == [""]
def test_subparsers_aur_search_option_repository(parser: argparse.ArgumentParser) -> None:
"""
aur-search command must correctly parse repository list
"""
args = parser.parse_args(["-r", "repo", "aur-search", "ahriman"])
assert args.repository == [""]
def test_subparsers_help(parser: argparse.ArgumentParser) -> None: def test_subparsers_help(parser: argparse.ArgumentParser) -> None:
""" """
help command must imply architecture list, lock, report, quiet, unsafe and parser help command must imply architecture list, lock, report, repository, quiet, unsafe and parser
""" """
args = parser.parse_args(["help"]) args = parser.parse_args(["help"])
assert args.architecture == [""] assert args.architecture == [""]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == [""]
assert args.quiet assert args.quiet
assert args.unsafe assert args.unsafe
assert args.parser is not None and args.parser() assert args.parser is not None and args.parser()
@ -132,14 +142,23 @@ def test_subparsers_help_option_architecture(parser: argparse.ArgumentParser) ->
assert args.architecture == [""] assert args.architecture == [""]
def test_subparsers_help_option_repository(parser: argparse.ArgumentParser) -> None:
"""
help command must correctly parse repository list
"""
args = parser.parse_args(["-r", "repo", "help"])
assert args.repository == [""]
def test_subparsers_help_commands_unsafe(parser: argparse.ArgumentParser) -> None: def test_subparsers_help_commands_unsafe(parser: argparse.ArgumentParser) -> None:
""" """
help-commands-unsafe command must imply architecture list, lock, report, quiet, unsafe and parser help-commands-unsafe command must imply architecture list, lock, report, repository, quiet, unsafe and parser
""" """
args = parser.parse_args(["help-commands-unsafe"]) args = parser.parse_args(["help-commands-unsafe"])
assert args.architecture == [""] assert args.architecture == [""]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == [""]
assert args.quiet assert args.quiet
assert args.unsafe assert args.unsafe
assert args.parser is not None and args.parser() assert args.parser is not None and args.parser()
@ -153,14 +172,23 @@ def test_subparsers_help_commands_unsafe_option_architecture(parser: argparse.Ar
assert args.architecture == [""] assert args.architecture == [""]
def test_subparsers_help_commands_unsafe_option_repository(parser: argparse.ArgumentParser) -> None:
"""
help-commands-unsafe command must correctly parse repository list
"""
args = parser.parse_args(["-r", "repo", "help-commands-unsafe"])
assert args.repository == [""]
def test_subparsers_help_updates(parser: argparse.ArgumentParser) -> None: def test_subparsers_help_updates(parser: argparse.ArgumentParser) -> None:
""" """
help-updates command must imply architecture list, lock, report, quiet and unsafe help-updates command must imply architecture list, lock, report, repository, quiet and unsafe
""" """
args = parser.parse_args(["help-updates"]) args = parser.parse_args(["help-updates"])
assert args.architecture == [""] assert args.architecture == [""]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == [""]
assert args.quiet assert args.quiet
assert args.unsafe assert args.unsafe
@ -173,14 +201,23 @@ def test_subparsers_help_updates_option_architecture(parser: argparse.ArgumentPa
assert args.architecture == [""] assert args.architecture == [""]
def test_subparsers_help_updates_option_repository(parser: argparse.ArgumentParser) -> None:
"""
help-updates command must correctly parse repository list
"""
args = parser.parse_args(["-r", "repo", "help-updates"])
assert args.repository == [""]
def test_subparsers_help_version(parser: argparse.ArgumentParser) -> None: def test_subparsers_help_version(parser: argparse.ArgumentParser) -> None:
""" """
help-version command must imply architecture, lock, report, quiet and unsafe help-version command must imply architecture, lock, report, repository, quiet and unsafe
""" """
args = parser.parse_args(["help-version"]) args = parser.parse_args(["help-version"])
assert args.architecture == [""] assert args.architecture == [""]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == [""]
assert args.quiet assert args.quiet
assert args.unsafe assert args.unsafe
@ -193,6 +230,14 @@ def test_subparsers_help_version_option_architecture(parser: argparse.ArgumentPa
assert args.architecture == [""] assert args.architecture == [""]
def test_subparsers_help_version_option_repository(parser: argparse.ArgumentParser) -> None:
"""
help-version command must correctly parse repository list
"""
args = parser.parse_args(["-r", "repo", "help-version"])
assert args.repository == [""]
def test_subparsers_package_add_option_architecture(parser: argparse.ArgumentParser) -> None: def test_subparsers_package_add_option_architecture(parser: argparse.ArgumentParser) -> None:
""" """
package-add command must correctly parse architecture list package-add command must correctly parse architecture list
@ -203,6 +248,16 @@ def test_subparsers_package_add_option_architecture(parser: argparse.ArgumentPar
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
def test_subparsers_package_add_option_repository(parser: argparse.ArgumentParser) -> None:
"""
package-add command must correctly parse repository list
"""
args = parser.parse_args(["package-add", "ahriman"])
assert args.repository is None
args = parser.parse_args(["-r", "repo", "package-add", "ahriman"])
assert args.repository == ["repo"]
def test_subparsers_package_add_option_refresh(parser: argparse.ArgumentParser) -> None: def test_subparsers_package_add_option_refresh(parser: argparse.ArgumentParser) -> None:
""" """
package-add command must count refresh options package-add command must count refresh options
@ -225,14 +280,25 @@ def test_subparsers_package_remove_option_architecture(parser: argparse.Argument
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
def test_subparsers_package_remove_option_repository(parser: argparse.ArgumentParser) -> None:
"""
package-remove command must correctly parse repository list
"""
args = parser.parse_args(["package-remove", "ahriman"])
assert args.repository is None
args = parser.parse_args(["-r", "repo", "package-remove", "ahriman"])
assert args.repository == ["repo"]
def test_subparsers_package_status(parser: argparse.ArgumentParser) -> None: def test_subparsers_package_status(parser: argparse.ArgumentParser) -> None:
""" """
package-status command must imply lock, report, quiet and unsafe package-status command must imply lock, report, quiet and unsafe
""" """
args = parser.parse_args(["-a", "x86_64", "package-status"]) args = parser.parse_args(["-a", "x86_64", "-r", "repo", "package-status"])
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == ["repo"]
assert args.quiet assert args.quiet
assert args.unsafe assert args.unsafe
@ -241,11 +307,12 @@ def test_subparsers_package_status_remove(parser: argparse.ArgumentParser) -> No
""" """
package-status-remove command must imply action, lock, report, quiet and unsafe package-status-remove command must imply action, lock, report, quiet and unsafe
""" """
args = parser.parse_args(["-a", "x86_64", "package-status-remove", "ahriman"]) args = parser.parse_args(["-a", "x86_64", "-r", "repo", "package-status-remove", "ahriman"])
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
assert args.action == Action.Remove assert args.action == Action.Remove
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == ["repo"]
assert args.quiet assert args.quiet
assert args.unsafe assert args.unsafe
@ -254,11 +321,12 @@ def test_subparsers_package_status_update(parser: argparse.ArgumentParser) -> No
""" """
package-status-update command must imply action, lock, report, quiet and unsafe package-status-update command must imply action, lock, report, quiet and unsafe
""" """
args = parser.parse_args(["-a", "x86_64", "package-status-update"]) args = parser.parse_args(["-a", "x86_64", "-r", "repo", "package-status-update"])
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
assert args.action == Action.Update assert args.action == Action.Update
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == ["repo"]
assert args.quiet assert args.quiet
assert args.unsafe assert args.unsafe
@ -267,21 +335,22 @@ def test_subparsers_package_status_update_option_status(parser: argparse.Argumen
""" """
package-status-update command must convert status option to buildstatusenum instance package-status-update command must convert status option to buildstatusenum instance
""" """
args = parser.parse_args(["-a", "x86_64", "package-status-update"]) args = parser.parse_args(["-a", "x86_64", "-r", "repo", "package-status-update"])
assert isinstance(args.status, BuildStatusEnum) assert isinstance(args.status, BuildStatusEnum)
args = parser.parse_args(["-a", "x86_64", "package-status-update", "--status", "failed"]) args = parser.parse_args(["-a", "x86_64", "-r", "repo", "package-status-update", "--status", "failed"])
assert isinstance(args.status, BuildStatusEnum) assert isinstance(args.status, BuildStatusEnum)
def test_subparsers_patch_add(parser: argparse.ArgumentParser) -> None: def test_subparsers_patch_add(parser: argparse.ArgumentParser) -> None:
""" """
patch-add command must imply action, architecture list, lock and report patch-add command must imply action, architecture list, lock, report and repository
""" """
args = parser.parse_args(["patch-add", "ahriman", "version"]) args = parser.parse_args(["patch-add", "ahriman", "version"])
assert args.action == Action.Update assert args.action == Action.Update
assert args.architecture == [""] assert args.architecture == [""]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == [""]
def test_subparsers_patch_add_option_architecture(parser: argparse.ArgumentParser) -> None: def test_subparsers_patch_add_option_architecture(parser: argparse.ArgumentParser) -> None:
@ -292,15 +361,24 @@ def test_subparsers_patch_add_option_architecture(parser: argparse.ArgumentParse
assert args.architecture == [""] assert args.architecture == [""]
def test_subparsers_patch_add_option_repository(parser: argparse.ArgumentParser) -> None:
"""
patch-add command must correctly parse repository list
"""
args = parser.parse_args(["-r", "repo", "patch-add", "ahriman", "version"])
assert args.repository == [""]
def test_subparsers_patch_list(parser: argparse.ArgumentParser) -> None: def test_subparsers_patch_list(parser: argparse.ArgumentParser) -> None:
""" """
patch-list command must imply action, architecture list, lock, report and unsafe patch-list command must imply action, architecture list, lock, report, repository and unsafe
""" """
args = parser.parse_args(["patch-list", "ahriman"]) args = parser.parse_args(["patch-list", "ahriman"])
assert args.action == Action.List assert args.action == Action.List
assert args.architecture == [""] assert args.architecture == [""]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == [""]
assert args.unsafe assert args.unsafe
@ -312,6 +390,14 @@ def test_subparsers_patch_list_option_architecture(parser: argparse.ArgumentPars
assert args.architecture == [""] assert args.architecture == [""]
def test_subparsers_patch_list_option_repository(parser: argparse.ArgumentParser) -> None:
"""
patch-list command must correctly parse repository list
"""
args = parser.parse_args(["-r", "repo", "patch-list", "ahriman"])
assert args.repository == [""]
def test_subparsers_patch_list_option_variable_empty(parser: argparse.ArgumentParser) -> None: def test_subparsers_patch_list_option_variable_empty(parser: argparse.ArgumentParser) -> None:
""" """
patch-list command must accept empty variable list as None patch-list command must accept empty variable list as None
@ -330,13 +416,14 @@ def test_subparsers_patch_list_option_variable_multiple(parser: argparse.Argumen
def test_subparsers_patch_remove(parser: argparse.ArgumentParser) -> None: def test_subparsers_patch_remove(parser: argparse.ArgumentParser) -> None:
""" """
patch-remove command must imply action, architecture list, lock and report patch-remove command must imply action, architecture list, lock, report and repository
""" """
args = parser.parse_args(["patch-remove", "ahriman"]) args = parser.parse_args(["patch-remove", "ahriman"])
assert args.action == Action.Remove assert args.action == Action.Remove
assert args.architecture == [""] assert args.architecture == [""]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == [""]
def test_subparsers_patch_remove_option_architecture(parser: argparse.ArgumentParser) -> None: def test_subparsers_patch_remove_option_architecture(parser: argparse.ArgumentParser) -> None:
@ -347,6 +434,14 @@ def test_subparsers_patch_remove_option_architecture(parser: argparse.ArgumentPa
assert args.architecture == [""] assert args.architecture == [""]
def test_subparsers_patch_remove_option_repository(parser: argparse.ArgumentParser) -> None:
"""
patch-remove command must correctly parse repository list
"""
args = parser.parse_args(["-r", "repo", "patch-remove", "ahriman"])
assert args.repository == [""]
def test_subparsers_patch_remove_option_variable_empty(parser: argparse.ArgumentParser) -> None: def test_subparsers_patch_remove_option_variable_empty(parser: argparse.ArgumentParser) -> None:
""" """
patch-remove command must accept empty variable list as None patch-remove command must accept empty variable list as None
@ -365,13 +460,14 @@ def test_subparsers_patch_remove_option_variable_multiple(parser: argparse.Argum
def test_subparsers_patch_set_add(parser: argparse.ArgumentParser) -> None: def test_subparsers_patch_set_add(parser: argparse.ArgumentParser) -> None:
""" """
patch-set-add command must imply action, architecture list, lock, report and variable patch-set-add command must imply action, architecture list, lock, report, repository and variable
""" """
args = parser.parse_args(["patch-set-add", "ahriman"]) args = parser.parse_args(["patch-set-add", "ahriman"])
assert args.action == Action.Update assert args.action == Action.Update
assert args.architecture == [""] assert args.architecture == [""]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == [""]
assert args.variable is None assert args.variable is None
@ -391,6 +487,14 @@ def test_subparsers_patch_set_add_option_package(parser: argparse.ArgumentParser
assert isinstance(args.package, Path) assert isinstance(args.package, Path)
def test_subparsers_patch_set_add_option_repository(parser: argparse.ArgumentParser) -> None:
"""
patch-set-add command must correctly parse repository list
"""
args = parser.parse_args(["-r", "repo", "patch-set-add", "ahriman"])
assert args.repository == [""]
def test_subparsers_patch_set_add_option_track(parser: argparse.ArgumentParser) -> None: def test_subparsers_patch_set_add_option_track(parser: argparse.ArgumentParser) -> None:
""" """
patch-set-add command must correctly parse track files patterns patch-set-add command must correctly parse track files patterns
@ -401,12 +505,13 @@ def test_subparsers_patch_set_add_option_track(parser: argparse.ArgumentParser)
def test_subparsers_repo_backup(parser: argparse.ArgumentParser) -> None: def test_subparsers_repo_backup(parser: argparse.ArgumentParser) -> None:
""" """
repo-backup command must imply architecture list, lock, report and unsafe repo-backup command must imply architecture list, lock, report, repository and unsafe
""" """
args = parser.parse_args(["repo-backup", "output.zip"]) args = parser.parse_args(["repo-backup", "output.zip"])
assert args.architecture == [""] assert args.architecture == [""]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == [""]
assert args.unsafe assert args.unsafe
@ -418,6 +523,14 @@ def test_subparsers_repo_backup_option_architecture(parser: argparse.ArgumentPar
assert args.architecture == [""] assert args.architecture == [""]
def test_subparsers_repo_backup_option_repository(parser: argparse.ArgumentParser) -> None:
"""
repo-backup command must correctly parse repository list
"""
args = parser.parse_args(["-r", "repo", "repo-backup", "output.zip"])
assert args.repository == [""]
def test_subparsers_repo_check(parser: argparse.ArgumentParser) -> None: def test_subparsers_repo_check(parser: argparse.ArgumentParser) -> None:
""" """
repo-check command must imply dependencies, dry-run, aur, manual and username repo-check command must imply dependencies, dry-run, aur, manual and username
@ -440,6 +553,16 @@ def test_subparsers_repo_check_option_architecture(parser: argparse.ArgumentPars
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
def test_subparsers_repo_check_option_repository(parser: argparse.ArgumentParser) -> None:
"""
repo-check command must correctly parse architecture list
"""
args = parser.parse_args(["repo-check"])
assert args.repository is None
args = parser.parse_args(["-r", "repo", "repo-check"])
assert args.repository == ["repo"]
def test_subparsers_repo_check_option_refresh(parser: argparse.ArgumentParser) -> None: def test_subparsers_repo_check_option_refresh(parser: argparse.ArgumentParser) -> None:
""" """
repo-check command must count refresh options repo-check command must count refresh options
@ -470,6 +593,16 @@ def test_subparsers_repo_create_keyring_option_architecture(parser: argparse.Arg
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
def test_subparsers_repo_create_keyring_option_repository(parser: argparse.ArgumentParser) -> None:
"""
repo-create-keyring command must correctly parse repository list
"""
args = parser.parse_args(["repo-create-keyring"])
assert args.repository is None
args = parser.parse_args(["-r", "repo", "repo-create-keyring"])
assert args.repository == ["repo"]
def test_subparsers_repo_create_mirrorlist(parser: argparse.ArgumentParser) -> None: def test_subparsers_repo_create_mirrorlist(parser: argparse.ArgumentParser) -> None:
""" """
repo-create-mirrorlist command must imply trigger repo-create-mirrorlist command must imply trigger
@ -488,6 +621,16 @@ def test_subparsers_repo_create_mirrorlist_option_architecture(parser: argparse.
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
def test_subparsers_repo_create_mirrorlist_option_repository(parser: argparse.ArgumentParser) -> None:
"""
repo-create-mirrorlist command must correctly parse repository list
"""
args = parser.parse_args(["repo-create-mirrorlist"])
assert args.repository is None
args = parser.parse_args(["-r", "repo", "repo-create-mirrorlist"])
assert args.repository == ["repo"]
def test_subparsers_repo_daemon(parser: argparse.ArgumentParser) -> None: def test_subparsers_repo_daemon(parser: argparse.ArgumentParser) -> None:
""" """
repo-daemon command must imply dry run, exit code and package repo-daemon command must imply dry run, exit code and package
@ -530,6 +673,16 @@ def test_subparsers_repo_rebuild_option_architecture(parser: argparse.ArgumentPa
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
def test_subparsers_repo_rebuild_option_repository(parser: argparse.ArgumentParser) -> None:
"""
repo-rebuild command must correctly parse repository list
"""
args = parser.parse_args(["repo-rebuild"])
assert args.repository is None
args = parser.parse_args(["-r", "repo", "repo-rebuild"])
assert args.repository == ["repo"]
def test_subparsers_repo_rebuild_option_depends_on_empty(parser: argparse.ArgumentParser) -> None: def test_subparsers_repo_rebuild_option_depends_on_empty(parser: argparse.ArgumentParser) -> None:
""" """
repo-rebuild command must accept empty depends-on list as None repo-rebuild command must accept empty depends-on list as None
@ -550,7 +703,7 @@ def test_subparsers_repo_rebuild_option_status(parser: argparse.ArgumentParser)
""" """
repo-rebuild command must convert status option to BuildStatusEnum instance repo-rebuild command must convert status option to BuildStatusEnum instance
""" """
args = parser.parse_args(["-a", "x86_64", "repo-rebuild", "--status", "failed"]) args = parser.parse_args(["-a", "x86_64", "-r", "repo", "repo-rebuild", "--status", "failed"])
assert isinstance(args.status, BuildStatusEnum) assert isinstance(args.status, BuildStatusEnum)
@ -564,6 +717,16 @@ def test_subparsers_repo_remove_unknown_option_architecture(parser: argparse.Arg
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
def test_subparsers_repo_remove_unknown_option_repository(parser: argparse.ArgumentParser) -> None:
"""
repo-remove-unknown command must correctly parse repository list
"""
args = parser.parse_args(["repo-remove-unknown"])
assert args.repository is None
args = parser.parse_args(["-r", "repo", "repo-remove-unknown"])
assert args.repository == ["repo"]
def test_subparsers_repo_report(parser: argparse.ArgumentParser) -> None: def test_subparsers_repo_report(parser: argparse.ArgumentParser) -> None:
""" """
repo-report command must imply trigger repo-report command must imply trigger
@ -582,14 +745,25 @@ def test_subparsers_repo_report_option_architecture(parser: argparse.ArgumentPar
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
def test_subparsers_repo_report_option_repository(parser: argparse.ArgumentParser) -> None:
"""
repo-report command must correctly parse repository list
"""
args = parser.parse_args(["repo-report"])
assert args.repository is None
args = parser.parse_args(["-r", "repo", "repo-report"])
assert args.repository == ["repo"]
def test_subparsers_repo_restore(parser: argparse.ArgumentParser) -> None: def test_subparsers_repo_restore(parser: argparse.ArgumentParser) -> None:
""" """
repo-restore command must imply architecture list, lock, report and unsafe repo-restore command must imply architecture list, lock, report, repository and unsafe
""" """
args = parser.parse_args(["repo-restore", "output.zip"]) args = parser.parse_args(["repo-restore", "output.zip"])
assert args.architecture == [""] assert args.architecture == [""]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == [""]
assert args.unsafe assert args.unsafe
@ -601,6 +775,14 @@ def test_subparsers_repo_restore_option_architecture(parser: argparse.ArgumentPa
assert args.architecture == [""] assert args.architecture == [""]
def test_subparsers_repo_restore_option_repository(parser: argparse.ArgumentParser) -> None:
"""
repo-restore command must correctly parse repository list
"""
args = parser.parse_args(["-r", "repo", "repo-restore", "output.zip"])
assert args.repository == [""]
def test_subparsers_repo_sign_option_architecture(parser: argparse.ArgumentParser) -> None: def test_subparsers_repo_sign_option_architecture(parser: argparse.ArgumentParser) -> None:
""" """
repo-sign command must correctly parse architecture list repo-sign command must correctly parse architecture list
@ -611,15 +793,26 @@ def test_subparsers_repo_sign_option_architecture(parser: argparse.ArgumentParse
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
def test_subparsers_repo_sign_option_repository(parser: argparse.ArgumentParser) -> None:
"""
repo-sign command must correctly parse repository list
"""
args = parser.parse_args(["repo-sign"])
assert args.repository is None
args = parser.parse_args(["-r", "repo", "repo-sign"])
assert args.repository == ["repo"]
def test_subparsers_repo_status_update(parser: argparse.ArgumentParser) -> None: def test_subparsers_repo_status_update(parser: argparse.ArgumentParser) -> None:
""" """
re[p-status-update command must imply action, lock, report, package, quiet and unsafe re[p-status-update command must imply action, lock, report, package, quiet and unsafe
""" """
args = parser.parse_args(["-a", "x86_64", "package-status-update"]) args = parser.parse_args(["-a", "x86_64", "-r", "repo", "package-status-update"])
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
assert args.action == Action.Update assert args.action == Action.Update
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == ["repo"]
assert not args.package assert not args.package
assert args.quiet assert args.quiet
assert args.unsafe assert args.unsafe
@ -629,9 +822,9 @@ def test_subparsers_repo_status_update_option_status(parser: argparse.ArgumentPa
""" """
repo-status-update command must convert status option to BuildStatusEnum instance repo-status-update command must convert status option to BuildStatusEnum instance
""" """
args = parser.parse_args(["-a", "x86_64", "repo-status-update"]) args = parser.parse_args(["-a", "x86_64", "-r", "repo", "repo-status-update"])
assert isinstance(args.status, BuildStatusEnum) assert isinstance(args.status, BuildStatusEnum)
args = parser.parse_args(["-a", "x86_64", "repo-status-update", "--status", "failed"]) args = parser.parse_args(["-a", "x86_64", "-r", "repo", "repo-status-update", "--status", "failed"])
assert isinstance(args.status, BuildStatusEnum) assert isinstance(args.status, BuildStatusEnum)
@ -653,6 +846,16 @@ def test_subparsers_repo_sync_option_architecture(parser: argparse.ArgumentParse
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
def test_subparsers_repo_sync_option_repository(parser: argparse.ArgumentParser) -> None:
"""
repo-sync command must correctly parse repository list
"""
args = parser.parse_args(["repo-sync"])
assert args.repository is None
args = parser.parse_args(["-r", "repo", "repo-sync"])
assert args.repository == ["repo"]
def test_subparsers_repo_tree(parser: argparse.ArgumentParser) -> None: def test_subparsers_repo_tree(parser: argparse.ArgumentParser) -> None:
""" """
repo-tree command must imply lock, report, quiet and unsafe repo-tree command must imply lock, report, quiet and unsafe
@ -674,6 +877,16 @@ def test_subparsers_repo_tree_option_architecture(parser: argparse.ArgumentParse
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
def test_subparsers_repo_tree_option_repository(parser: argparse.ArgumentParser) -> None:
"""
repo-tree command must correctly parse repository list
"""
args = parser.parse_args(["repo-tree"])
assert args.repository is None
args = parser.parse_args(["-r", "repo", "repo-tree"])
assert args.repository == ["repo"]
def test_subparsers_repo_tree_option_partitions(parser: argparse.ArgumentParser) -> None: def test_subparsers_repo_tree_option_partitions(parser: argparse.ArgumentParser) -> None:
""" """
must convert partitions option to int instance must convert partitions option to int instance
@ -694,6 +907,16 @@ def test_subparsers_repo_triggers_option_architecture(parser: argparse.ArgumentP
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
def test_subparsers_repo_triggers_option_repository(parser: argparse.ArgumentParser) -> None:
"""
repo-triggers command must correctly parse repository list
"""
args = parser.parse_args(["repo-triggers"])
assert args.repository is None
args = parser.parse_args(["-r", "repo", "repo-triggers"])
assert args.repository == ["repo"]
def test_subparsers_repo_update_option_architecture(parser: argparse.ArgumentParser) -> None: def test_subparsers_repo_update_option_architecture(parser: argparse.ArgumentParser) -> None:
""" """
repo-update command must correctly parse architecture list repo-update command must correctly parse architecture list
@ -704,6 +927,16 @@ def test_subparsers_repo_update_option_architecture(parser: argparse.ArgumentPar
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
def test_subparsers_repo_update_option_repository(parser: argparse.ArgumentParser) -> None:
"""
repo-update command must correctly parse repository list
"""
args = parser.parse_args(["repo-update"])
assert args.repository is None
args = parser.parse_args(["-r", "repo", "repo-update"])
assert args.repository == ["repo"]
def test_subparsers_repo_update_option_refresh(parser: argparse.ArgumentParser) -> None: def test_subparsers_repo_update_option_refresh(parser: argparse.ArgumentParser) -> None:
""" """
repo-update command must count refresh options repo-update command must count refresh options
@ -735,14 +968,25 @@ def test_subparsers_service_clean_option_architecture(parser: argparse.ArgumentP
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
def test_subparsers_service_clean_option_repository(parser: argparse.ArgumentParser) -> None:
"""
service-clean command must correctly parse repository list
"""
args = parser.parse_args(["service-clean"])
assert args.repository is None
args = parser.parse_args(["-r", "repo", "service-clean"])
assert args.repository == ["repo"]
def test_subparsers_service_config(parser: argparse.ArgumentParser) -> None: def test_subparsers_service_config(parser: argparse.ArgumentParser) -> None:
""" """
service-config command must imply lock, report, quiet and unsafe service-config command must imply lock, report, quiet and unsafe
""" """
args = parser.parse_args(["-a", "x86_64", "service-config"]) args = parser.parse_args(["-a", "x86_64", "-r", "repo", "service-config"])
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == ["repo"]
assert args.quiet assert args.quiet
assert args.unsafe assert args.unsafe
@ -751,22 +995,24 @@ def test_subparsers_service_config_validate(parser: argparse.ArgumentParser) ->
""" """
service-config-validate command must imply lock, report, quiet and unsafe service-config-validate command must imply lock, report, quiet and unsafe
""" """
args = parser.parse_args(["-a", "x86_64", "service-config-validate"]) args = parser.parse_args(["-a", "x86_64", "-r", "repo", "service-config-validate"])
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == ["repo"]
assert args.quiet assert args.quiet
assert args.unsafe assert args.unsafe
def test_subparsers_service_key_import(parser: argparse.ArgumentParser) -> None: def test_subparsers_service_key_import(parser: argparse.ArgumentParser) -> None:
""" """
service-key-import command must imply architecture list, lock and report service-key-import command must imply architecture list, lock, report and repository
""" """
args = parser.parse_args(["service-key-import", "key"]) args = parser.parse_args(["service-key-import", "key"])
assert args.architecture == [""] assert args.architecture == [""]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == [""]
def test_subparsers_service_key_import_option_architecture(parser: argparse.ArgumentParser) -> None: def test_subparsers_service_key_import_option_architecture(parser: argparse.ArgumentParser) -> None:
@ -777,14 +1023,23 @@ def test_subparsers_service_key_import_option_architecture(parser: argparse.Argu
assert args.architecture == [""] assert args.architecture == [""]
def test_subparsers_service_key_import_option_repository(parser: argparse.ArgumentParser) -> None:
"""
service-key-import command must correctly parse repository list
"""
args = parser.parse_args(["-r", "repo", "service-key-import", "key"])
assert args.repository == [""]
def test_subparsers_service_setup(parser: argparse.ArgumentParser) -> None: def test_subparsers_service_setup(parser: argparse.ArgumentParser) -> None:
""" """
service-setup command must imply lock, report, quiet and unsafe service-setup command must imply lock, report, quiet and unsafe
""" """
args = parser.parse_args(["-a", "x86_64", "service-setup", "--packager", "John Doe <john@doe.com>"]) args = parser.parse_args(["-a", "x86_64", "-r", "repo", "service-setup", "--packager", "John Doe <john@doe.com>"])
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == ["repo"]
assert args.quiet assert args.quiet
assert args.unsafe assert args.unsafe
@ -793,9 +1048,9 @@ def test_subparsers_service_setup_option_from_configuration(parser: argparse.Arg
""" """
service-setup command must convert from-configuration option to path instance service-setup command must convert from-configuration option to path instance
""" """
args = parser.parse_args(["-a", "x86_64", "service-setup", "--packager", "John Doe <john@doe.com>"]) args = parser.parse_args(["-a", "x86_64", "-r", "repo", "service-setup", "--packager", "John Doe <john@doe.com>"])
assert isinstance(args.from_configuration, Path) assert isinstance(args.from_configuration, Path)
args = parser.parse_args(["-a", "x86_64", "service-setup", "--packager", "John Doe <john@doe.com>", args = parser.parse_args(["-a", "x86_64", "-r", "repo", "service-setup", "--packager", "John Doe <john@doe.com>",
"--from-configuration", "path"]) "--from-configuration", "path"])
assert isinstance(args.from_configuration, Path) assert isinstance(args.from_configuration, Path)
@ -804,7 +1059,7 @@ def test_subparsers_service_setup_option_sign_target(parser: argparse.ArgumentPa
""" """
service-setup command must convert sign-target option to SignSettings instance service-setup command must convert sign-target option to SignSettings instance
""" """
args = parser.parse_args(["-a", "x86_64", "service-setup", "--packager", "John Doe <john@doe.com>", args = parser.parse_args(["-a", "x86_64", "-r", "repo", "service-setup", "--packager", "John Doe <john@doe.com>",
"--sign-target", "packages"]) "--sign-target", "packages"])
assert args.sign_target assert args.sign_target
assert all(isinstance(target, SignSettings) for target in args.sign_target) assert all(isinstance(target, SignSettings) for target in args.sign_target)
@ -814,7 +1069,7 @@ def test_subparsers_service_setup_option_sign_target_empty(parser: argparse.Argu
""" """
service-setup command must accept empty sign-target list as None service-setup command must accept empty sign-target list as None
""" """
args = parser.parse_args(["-a", "x86_64", "service-setup", "--packager", "John Doe <john@doe.com>"]) args = parser.parse_args(["-a", "x86_64", "-r", "repo", "service-setup", "--packager", "John Doe <john@doe.com>"])
assert args.sign_target is None assert args.sign_target is None
@ -822,7 +1077,7 @@ def test_subparsers_service_setup_option_sign_target_multiple(parser: argparse.A
""" """
service-setup command must accept multiple sign-target service-setup command must accept multiple sign-target
""" """
args = parser.parse_args(["-a", "x86_64", "service-setup", "--packager", "John Doe <john@doe.com>", args = parser.parse_args(["-a", "x86_64", "-r", "repo", "service-setup", "--packager", "John Doe <john@doe.com>",
"--sign-target", "packages", "--sign-target", "repository"]) "--sign-target", "packages", "--sign-target", "repository"])
assert args.sign_target == [SignSettings.Packages, SignSettings.Repository] assert args.sign_target == [SignSettings.Packages, SignSettings.Repository]
@ -838,13 +1093,14 @@ def test_subparsers_service_shell(parser: argparse.ArgumentParser) -> None:
def test_subparsers_user_add(parser: argparse.ArgumentParser) -> None: def test_subparsers_user_add(parser: argparse.ArgumentParser) -> None:
""" """
user-add command must imply action, architecture, lock, report and quiet user-add command must imply action, architecture, lock, report, repository and quiet
""" """
args = parser.parse_args(["user-add", "username"]) args = parser.parse_args(["user-add", "username"])
assert args.action == Action.Update assert args.action == Action.Update
assert args.architecture == [""] assert args.architecture == [""]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == [""]
assert args.quiet assert args.quiet
@ -856,6 +1112,14 @@ def test_subparsers_user_add_option_architecture(parser: argparse.ArgumentParser
assert args.architecture == [""] assert args.architecture == [""]
def test_subparsers_user_add_option_repository(parser: argparse.ArgumentParser) -> None:
"""
user-add command must correctly parse repository list
"""
args = parser.parse_args(["-r", "repo", "user-add", "username"])
assert args.repository == [""]
def test_subparsers_user_add_option_role(parser: argparse.ArgumentParser) -> None: def test_subparsers_user_add_option_role(parser: argparse.ArgumentParser) -> None:
""" """
user-add command must convert role option to UserAccess instance user-add command must convert role option to UserAccess instance
@ -868,13 +1132,14 @@ def test_subparsers_user_add_option_role(parser: argparse.ArgumentParser) -> Non
def test_subparsers_user_list(parser: argparse.ArgumentParser) -> None: def test_subparsers_user_list(parser: argparse.ArgumentParser) -> None:
""" """
user-list command must imply action, architecture, lock, report, quiet and unsafe user-list command must imply action, architecture, lock, report, repository, quiet and unsafe
""" """
args = parser.parse_args(["user-list"]) args = parser.parse_args(["user-list"])
assert args.action == Action.List assert args.action == Action.List
assert args.architecture == [""] assert args.architecture == [""]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == [""]
assert args.quiet assert args.quiet
assert args.unsafe assert args.unsafe
@ -887,6 +1152,14 @@ def test_subparsers_user_list_option_architecture(parser: argparse.ArgumentParse
assert args.architecture == [""] assert args.architecture == [""]
def test_subparsers_user_list_option_repository(parser: argparse.ArgumentParser) -> None:
"""
user-list command must correctly parse repository list
"""
args = parser.parse_args(["-r", "repo", "user-list"])
assert args.repository == [""]
def test_subparsers_user_list_option_role(parser: argparse.ArgumentParser) -> None: def test_subparsers_user_list_option_role(parser: argparse.ArgumentParser) -> None:
""" """
user-list command must convert role option to UserAccess instance user-list command must convert role option to UserAccess instance
@ -897,13 +1170,14 @@ def test_subparsers_user_list_option_role(parser: argparse.ArgumentParser) -> No
def test_subparsers_user_remove(parser: argparse.ArgumentParser) -> None: def test_subparsers_user_remove(parser: argparse.ArgumentParser) -> None:
""" """
user-remove command must imply action, architecture, lock, report and quiet user-remove command must imply action, architecture, lock, report, repository and quiet
""" """
args = parser.parse_args(["user-remove", "username"]) args = parser.parse_args(["user-remove", "username"])
assert args.action == Action.Remove assert args.action == Action.Remove
assert args.architecture == [""] assert args.architecture == [""]
assert args.lock is None assert args.lock is None
assert not args.report assert not args.report
assert args.repository == [""]
assert args.quiet assert args.quiet
@ -915,13 +1189,22 @@ def test_subparsers_user_remove_option_architecture(parser: argparse.ArgumentPar
assert args.architecture == [""] assert args.architecture == [""]
def test_subparsers_user_remove_option_repository(parser: argparse.ArgumentParser) -> None:
"""
user-remove command must correctly parse repository list
"""
args = parser.parse_args(["-r", "repo", "user-remove", "username"])
assert args.repository == [""]
def test_subparsers_web(parser: argparse.ArgumentParser) -> None: def test_subparsers_web(parser: argparse.ArgumentParser) -> None:
""" """
web command must imply report and parser web command must imply report and parser
""" """
args = parser.parse_args(["-a", "x86_64", "web"]) args = parser.parse_args(["-a", "x86_64", "-r", "repo", "web"])
assert args.architecture == ["x86_64"] assert args.architecture == ["x86_64"]
assert not args.report assert not args.report
assert args.repository == ["repo"]
assert args.parser is not None and args.parser() assert args.parser is not None and args.parser()

View File

@ -1,19 +1,8 @@
import pytest import pytest
from ahriman.core.exceptions import InitializeError
from ahriman.models.repository_id import RepositoryId from ahriman.models.repository_id import RepositoryId
def test_post_init() -> None:
"""
must raise InitializeError if name is empty
"""
RepositoryId("x86_64", "aur-clone")
with pytest.raises(InitializeError):
RepositoryId("x86_64", "")
def test_lt() -> None: def test_lt() -> None:
""" """
must correctly compare instances must correctly compare instances

View File

@ -51,61 +51,127 @@ def test_known_architectures(repository_paths: RepositoryPaths, mocker: MockerFi
""" """
must list available directory paths /repository/repo/arch must list available directory paths /repository/repo/arch
""" """
def iterdir(root: Path) -> list[Path]: is_dir_mock = mocker.patch("pathlib.Path.is_dir", autospec=True, return_value=True)
if root == repository_paths._repository_root: iterdir_mock = mocker.patch("pathlib.Path.iterdir", autospec=True, return_value=[Path("i686"), Path("x86_64")])
assert repository_paths.known_architectures(repository_paths.root, repository_paths.repository_id.name) == {
"i686",
"x86_64",
}
iterdir_mock.assert_called_once_with(repository_paths._repository_root / repository_paths.repository_id.name)
is_dir_mock.assert_has_calls([
MockCall(repository_paths._repository_root / repository_paths.repository_id.name),
MockCall(Path("i686")),
MockCall(Path("x86_64")),
])
def test_known_architectures_legacy(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
"""
must list available directory paths /repository/arch
"""
def is_dir(path: Path) -> bool:
return path.name != repository_paths.repository_id.name
is_dir_mock = mocker.patch("pathlib.Path.is_dir", autospec=True, side_effect=is_dir)
iterdir_mock = mocker.patch("pathlib.Path.iterdir", autospec=True, return_value=[Path("i686"), Path("x86_64")])
assert repository_paths.known_architectures(repository_paths.root, repository_paths.repository_id.name) == {
"i686",
"x86_64",
}
iterdir_mock.assert_called_once_with(repository_paths._repository_root)
is_dir_mock.assert_has_calls([
MockCall(repository_paths._repository_root / repository_paths.repository_id.name),
MockCall(repository_paths._repository_root),
MockCall(Path("i686")),
MockCall(Path("x86_64")),
])
def test_known_architectures_legacy_backward(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
"""
must list available directory paths /repository/arch in backward compatibility mode
"""
def is_dir(path: Path) -> bool:
return path.name != repository_paths.repository_id.name
is_dir_mock = mocker.patch("pathlib.Path.is_dir", autospec=True, side_effect=is_dir)
iterdir_mock = mocker.patch("pathlib.Path.iterdir", autospec=True, return_value=[Path("i686"), Path("x86_64")])
assert repository_paths.known_architectures(repository_paths.root) == {"i686", "x86_64"}
iterdir_mock.assert_called_once_with(repository_paths._repository_root)
is_dir_mock.assert_has_calls([
MockCall(repository_paths._repository_root),
MockCall(Path("i686")),
MockCall(Path("x86_64")),
])
def test_known_architectures_empty(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
"""
must return empty architectures if tree is not available
"""
mocker.patch("pathlib.Path.is_dir", return_value=False)
iterdir_mock = mocker.patch("pathlib.Path.iterdir")
# new style
assert not repository_paths.known_architectures(repository_paths.root, repository_paths.repository_id.name)
# legacy mode
assert not repository_paths.known_architectures(repository_paths.root)
iterdir_mock.assert_not_called()
def test_known_repositories(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
"""
must list available directory paths /repository/repo
"""
def iterdir(path: Path) -> list[Path]:
if path == repository_paths._repository_root:
return [Path("repo1"), Path("repo2")] return [Path("repo1"), Path("repo2")]
if root == Path("repo1"):
return [Path("i686"), Path("x86_64")]
return [Path("x86_64")] return [Path("x86_64")]
is_dir_mock = mocker.patch("pathlib.Path.is_dir", autospec=True, return_value=True) is_dir_mock = mocker.patch("pathlib.Path.is_dir", autospec=True, return_value=True)
iterdir_mock = mocker.patch("pathlib.Path.iterdir", autospec=True, side_effect=iterdir) iterdir_mock = mocker.patch("pathlib.Path.iterdir", autospec=True, side_effect=iterdir)
assert repository_paths.known_architectures(repository_paths.root, "") == { assert repository_paths.known_repositories(repository_paths.root) == {"repo1", "repo2"}
RepositoryId("i686", "repo1"),
RepositoryId("x86_64", "repo1"),
RepositoryId("x86_64", "repo2"),
}
iterdir_mock.assert_has_calls([ iterdir_mock.assert_has_calls([
MockCall(repository_paths._repository_root), MockCall(repository_paths._repository_root),
MockCall(Path("repo1")), MockCall(Path("repo1")),
MockCall(Path("repo2")), MockCall(Path("repo2")),
]) ])
is_dir_mock.assert_has_calls([ is_dir_mock.assert_has_calls([
MockCall(repository_paths._repository_root),
MockCall(Path("repo1")), MockCall(Path("repo1")),
MockCall(Path("i686")),
MockCall(Path("x86_64")), MockCall(Path("x86_64")),
MockCall(Path("repo2")), MockCall(Path("repo2")),
MockCall(Path("x86_64")), MockCall(Path("x86_64")),
]) ])
def test_known_architectures_legacy(repository_id: RepositoryId, repository_paths: RepositoryPaths, def test_known_repositories_legacy(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
mocker: MockerFixture) -> None:
""" """
must correctly define legacy tree /repository/arch must return empty repository list for legacy tree
""" """
def iterdir(root: Path) -> list[Path]: def is_dir(path: Path) -> bool:
if root == repository_paths._repository_root: return path == repository_paths._repository_root
return [Path("i686"), Path("x86_64")]
return []
is_dir_mock = mocker.patch("pathlib.Path.is_dir", autospec=True, return_value=True) mocker.patch("pathlib.Path.is_dir", autospec=True, side_effect=is_dir)
iterdir_mock = mocker.patch("pathlib.Path.iterdir", autospec=True, side_effect=iterdir) iterdir_mock = mocker.patch("pathlib.Path.iterdir", autospec=True, return_value=[Path("i686"), Path("x86_64")])
assert repository_paths.known_architectures(repository_paths.root, repository_id.name) == { assert not repository_paths.known_repositories(repository_paths.root)
RepositoryId("i686", repository_id.name), iterdir_mock.assert_called_once_with(repository_paths._repository_root)
RepositoryId("x86_64", repository_id.name),
}
iterdir_mock.assert_has_calls([ def test_known_repositories_empty(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
MockCall(repository_paths._repository_root), """
MockCall(Path("i686")), must return empty repositories if tree is not available
MockCall(Path("x86_64")), """
]) mocker.patch("pathlib.Path.is_dir", return_value=False)
is_dir_mock.assert_has_calls([ iterdir_mock = mocker.patch("pathlib.Path.iterdir")
MockCall(Path("i686")),
MockCall(Path("x86_64")), assert not repository_paths.known_repositories(repository_paths.root)
]) iterdir_mock.assert_not_called()
def test_owner(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None: def test_owner(repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
@ -207,6 +273,7 @@ def test_tree_create(repository_paths: RepositoryPaths, mocker: MockerFixture) -
and not prop.endswith("_for") and not prop.endswith("_for")
and prop not in ("chown", and prop not in ("chown",
"known_architectures", "known_architectures",
"known_repositories",
"owner", "owner",
"repository_id", "repository_id",
"root", "root",