add configurable exit codes to some commands (#55)

This commit is contained in:
2022-04-01 18:30:11 +03:00
committed by GitHub
parent a132b1544a
commit d5503b22ba
22 changed files with 279 additions and 28 deletions

View File

@ -112,6 +112,7 @@ def _set_aur_search_parser(root: SubParserAction) -> argparse.ArgumentParser:
description="search for package in AUR using API", formatter_class=_formatter)
parser.add_argument("search", help="search terms, can be specified multiple times, result will match all terms",
nargs="+")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("-i", "--info", help="show additional package information", action="store_true")
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",
@ -189,6 +190,7 @@ def _set_package_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
"5) and finally you can add package from AUR.",
formatter_class=_formatter)
parser.add_argument("package", help="package source (base name, path to local files, remote URL)", nargs="+")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("-n", "--now", help="run update function after", action="store_true")
parser.add_argument("-s", "--source", help="explicitly specify the package source for this command",
type=PackageSource, choices=PackageSource, default=PackageSource.Auto)
@ -222,6 +224,7 @@ def _set_package_status_parser(root: SubParserAction) -> argparse.ArgumentParser
formatter_class=_formatter)
parser.add_argument("package", help="filter status by package base", nargs="*")
parser.add_argument("--ahriman", help="get service status itself", action="store_true")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("-i", "--info", help="show additional package information", action="store_true")
parser.add_argument("-s", "--status", help="filter packages by status",
type=BuildStatusEnum, choices=BuildStatusEnum)
@ -293,6 +296,7 @@ def _set_patch_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser = root.add_parser("patch-list", help="list patch sets",
description="list available patches for the package", formatter_class=_formatter)
parser.add_argument("package", help="package base", nargs="?")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.set_defaults(handler=handlers.Patch, action=Action.List, architecture=[""], lock=None, no_report=True)
return parser
@ -320,6 +324,7 @@ def _set_repo_check_parser(root: SubParserAction) -> argparse.ArgumentParser:
description="check for packages updates. Same as update --dry-run --no-manual",
formatter_class=_formatter)
parser.add_argument("package", help="filter check by package base", nargs="*")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("--no-vcs", help="do not check VCS packages", action="store_true")
parser.set_defaults(handler=handlers.Update, dry_run=True, no_aur=False, no_local=False, no_manual=True)
return parser
@ -369,6 +374,7 @@ def _set_repo_rebuild_parser(root: SubParserAction) -> argparse.ArgumentParser:
parser.add_argument("--depends-on", help="only rebuild packages that depend on specified package", action="append")
parser.add_argument("--dry-run", help="just perform check for packages without rebuild process itself",
action="store_true")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.set_defaults(handler=handlers.Rebuild)
return parser
@ -484,6 +490,7 @@ def _set_repo_update_parser(root: SubParserAction) -> argparse.ArgumentParser:
formatter_class=_formatter)
parser.add_argument("package", help="filter check by package base", nargs="*")
parser.add_argument("--dry-run", help="just perform check for updates, same as check command", action="store_true")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("--no-aur", help="do not check for AUR updates. Implies --no-vcs", action="store_true")
parser.add_argument("--no-local", help="do not check local packages for updates", action="store_true")
parser.add_argument("--no-manual", help="do not include manual updates", action="store_true")
@ -524,6 +531,7 @@ def _set_user_list_parser(root: SubParserAction) -> argparse.ArgumentParser:
description="list users from the user mapping and their roles",
formatter_class=_formatter)
parser.add_argument("username", help="filter users by username", nargs="?")
parser.add_argument("-e", "--exit-code", help="return non-zero exit status if result is empty", action="store_true")
parser.add_argument("-r", "--role", help="filter users by role", type=UserAccess, choices=UserAccess)
parser.set_defaults(handler=handlers.User, action=Action.List, architecture=[""], lock=None, no_report=True, # nosec
password="", quiet=True, unsafe=True)

View File

@ -132,7 +132,7 @@ class Repository(Properties):
result.extend(unknown_aur(package)) # local package not found
return result
def update(self, updates: Iterable[Package]) -> None:
def update(self, updates: Iterable[Package]) -> Result:
"""
run package updates
:param updates: list of packages to update
@ -144,8 +144,9 @@ class Repository(Properties):
self._finalize(result.merge(update_result))
# process built packages
build_result = Result()
packages = self.repository.packages_built()
process_update(packages, Result())
process_update(packages, build_result)
# process manual packages
tree = Tree.load(updates, self.database)
@ -155,6 +156,8 @@ class Repository(Properties):
packages = self.repository.packages_built()
process_update(packages, build_result)
return build_result
def updates(self, filter_packages: Iterable[str], no_aur: bool, no_local: bool, no_manual: bool, no_vcs: bool,
log_fn: Callable[[str], None]) -> List[Package]:
"""

View File

@ -48,4 +48,5 @@ class Add(Handler):
return
packages = application.updates(args.package, True, True, False, True, application.logger.info)
application.update(packages)
result = application.update(packages)
Add.check_if_empty(args.exit_code, result.is_empty)

View File

@ -119,3 +119,13 @@ class Handler:
:param unsafe: if set no user check will be performed before path creation
"""
raise NotImplementedError
@staticmethod
def check_if_empty(enabled: bool, predicate: bool) -> None:
"""
check condition and flag and raise ExitCode exception in case if it is enabled and condition match
:param enabled: if False no check will be performed
:param predicate: indicates condition on which exception should be thrown
"""
if enabled and predicate:
raise ExitCode()

View File

@ -51,7 +51,7 @@ class Patch(Handler):
application = Application(architecture, configuration, no_report, unsafe)
if args.action == Action.List:
Patch.patch_set_list(application, args.package)
Patch.patch_set_list(application, args.package, args.exit_code)
elif args.action == Action.Remove:
Patch.patch_set_remove(application, args.package)
elif args.action == Action.Update:
@ -71,13 +71,16 @@ class Patch(Handler):
application.database.patches_insert(package.base, patch)
@staticmethod
def patch_set_list(application: Application, package_base: Optional[str]) -> None:
def patch_set_list(application: Application, package_base: Optional[str], exit_code: bool) -> None:
"""
list patches available for the package base
:param application: application instance
:param package_base: package base
:param exit_code: raise ExitCode on empty search result
"""
patches = application.database.patches_list(package_base)
Patch.check_if_empty(exit_code, not patches)
for base, patch in patches.items():
content = base if package_base is None else patch
StringPrinter(content).print(verbose=True)

View File

@ -47,9 +47,12 @@ class Rebuild(Handler):
application = Application(architecture, configuration, no_report, unsafe)
updates = application.repository.packages_depends_on(depends_on)
Rebuild.check_if_empty(args.exit_code, not updates)
if args.dry_run:
for package in updates:
UpdatePrinter(package, package.version).print(verbose=True)
return
application.update(updates)
result = application.update(updates)
Rebuild.check_if_empty(args.exit_code, result.is_empty)

View File

@ -45,6 +45,7 @@ class RemoveUnknown(Handler):
"""
application = Application(architecture, configuration, no_report, unsafe)
unknown_packages = application.unknown()
if args.dry_run:
for package in sorted(unknown_packages):
StringPrinter(package).print(args.info)

View File

@ -37,8 +37,7 @@ class Search(Handler):
"""
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
# later we will have to remove some fields from here (lists)
SORT_FIELDS = {pair.name for pair in fields(AURPackage)}
SORT_FIELDS = {field.name for field in fields(AURPackage) if field.default_factory is not list}
@classmethod
def run(cls: Type[Handler], args: argparse.Namespace, architecture: str,
@ -52,6 +51,7 @@ class Search(Handler):
:param unsafe: if set no user check will be performed before path creation
"""
packages_list = AUR.multisearch(*args.search)
Search.check_if_empty(args.exit_code, not packages_list)
for package in Search.sort(packages_list, args.sort_by):
AurPrinter(package).print(args.info)

View File

@ -60,6 +60,8 @@ class Status(Handler):
else:
packages = client.get(None)
Status.check_if_empty(args.exit_code, not packages)
comparator: Callable[[Tuple[Package, BuildStatus]], str] = lambda item: item[0].base
filter_fn: Callable[[Tuple[Package, BuildStatus]], bool] =\
lambda item: args.status is None or item[1].status == args.status

View File

@ -45,10 +45,12 @@ class Update(Handler):
application = Application(architecture, configuration, no_report, unsafe)
packages = application.updates(args.package, args.no_aur, args.no_local, args.no_manual, args.no_vcs,
Update.log_fn(application, args.dry_run))
Update.check_if_empty(args.exit_code, not packages)
if args.dry_run:
return
application.update(packages)
result = application.update(packages)
Update.check_if_empty(args.exit_code, result.is_empty)
@staticmethod
def log_fn(application: Application, dry_run: bool) -> Callable[[str], None]:

View File

@ -60,8 +60,10 @@ class User(Handler):
User.configuration_create(auth_configuration, user, salt, args.as_service, args.secure)
database.user_update(user.hash_password(salt))
elif args.action == Action.List:
for found_user in database.user_list(args.username, args.access):
UserPrinter(found_user).print(verbose=True)
users = database.user_list(args.username, args.role)
User.check_if_empty(args.exit_code, not users)
for user in users:
UserPrinter(user).print(verbose=True)
elif args.action == Action.Remove:
database.user_remove(args.username)

View File

@ -48,6 +48,13 @@ class Result:
"""
return list(self._failed.values())
@property
def is_empty(self) -> bool:
"""
:return: True in case if success list is empty and False otherwise
"""
return not bool(self._success)
@property
def success(self) -> List[Package]:
"""