diff --git a/docs/ahriman.1 b/docs/ahriman.1 index fe53b379..1b6b985b 100644 --- a/docs/ahriman.1 +++ b/docs/ahriman.1 @@ -3,7 +3,7 @@ ahriman .SH SYNOPSIS .B ahriman -[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--report | --no-report] [-q] [--unsafe] [-V] {aur-search,search,help,help-commands-unsafe,help-version,version,package-add,add,package-update,package-remove,remove,package-status,status,package-status-remove,package-status-update,status-update,patch-add,patch-list,patch-remove,patch-set-add,repo-backup,repo-check,check,repo-daemon,daemon,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-report,report,repo-restore,repo-sign,sign,repo-status-update,repo-sync,sync,repo-tree,repo-triggers,repo-update,update,service-clean,clean,repo-clean,service-config,config,repo-config,service-config-validate,config-validate,repo-config-validate,service-key-import,key-import,service-setup,init,repo-init,repo-setup,setup,service-shell,shell,user-add,user-list,user-remove,web} ... +[-h] [-a ARCHITECTURE] [-c CONFIGURATION] [--force] [-l LOCK] [--report | --no-report] [-q] [--unsafe] [-V] {aur-search,search,help,help-commands-unsafe,help-updates,help-version,version,package-add,add,package-update,package-remove,remove,package-status,status,package-status-remove,package-status-update,status-update,patch-add,patch-list,patch-remove,patch-set-add,repo-backup,repo-check,check,repo-daemon,daemon,repo-rebuild,rebuild,repo-remove-unknown,remove-unknown,repo-report,report,repo-restore,repo-sign,sign,repo-status-update,repo-sync,sync,repo-tree,repo-triggers,repo-update,update,service-clean,clean,repo-clean,service-config,config,repo-config,service-config-validate,config-validate,repo-config-validate,service-key-import,key-import,service-setup,init,repo-init,repo-setup,setup,service-shell,shell,user-add,user-list,user-remove,web} ... .SH DESCRIPTION ArcH linux ReposItory MANager @@ -52,6 +52,9 @@ show help message \fBahriman\fR \fI\,help\-commands\-unsafe\/\fR list unsafe commands .TP +\fBahriman\fR \fI\,help\-updates\/\fR +check for service updates +.TP \fBahriman\fR \fI\,help\-version\/\fR application version .TP @@ -196,6 +199,16 @@ list unsafe commands as defined in default args instead of showing commands, just test command line for unsafe subcommand and return 0 in case if command is safe and 1 otherwise +.SH COMMAND \fI\,'ahriman help\-updates'\/\fR +usage: ahriman help\-updates [\-h] [\-e] + +request AUR for current version and compare with current service version + +.SH OPTIONS \fI\,'ahriman help\-updates'\/\fR +.TP +\fB\-e\fR, \fB\-\-exit\-code\fR +return non\-zero exit code if updates available + .SH COMMAND \fI\,'ahriman help\-version'\/\fR usage: ahriman help\-version [\-h] diff --git a/docs/ahriman.application.handlers.rst b/docs/ahriman.application.handlers.rst index 952f912c..7f6b617f 100644 --- a/docs/ahriman.application.handlers.rst +++ b/docs/ahriman.application.handlers.rst @@ -116,6 +116,14 @@ ahriman.application.handlers.search module :no-undoc-members: :show-inheritance: +ahriman.application.handlers.service\_updates module +---------------------------------------------------- + +.. automodule:: ahriman.application.handlers.service_updates + :members: + :no-undoc-members: + :show-inheritance: + ahriman.application.handlers.setup module ----------------------------------------- diff --git a/docs/completions/bash/_ahriman b/docs/completions/bash/_ahriman index 39ddad34..25d2f6df 100644 --- a/docs/completions/bash/_ahriman +++ b/docs/completions/bash/_ahriman @@ -1,12 +1,13 @@ # AUTOMATICALLY GENERATED by `shtab` -_shtab_ahriman_subparsers=('aur-search' 'search' 'help' 'help-commands-unsafe' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'user-add' 'user-list' 'user-remove' 'web') +_shtab_ahriman_subparsers=('aur-search' 'search' 'help' 'help-commands-unsafe' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'user-add' 'user-list' 'user-remove' 'web') _shtab_ahriman_option_strings=('-h' '--help' '-a' '--architecture' '-c' '--configuration' '--force' '-l' '--lock' '--report' '--no-report' '-q' '--quiet' '--unsafe' '-V' '--version') _shtab_ahriman_aur_search_option_strings=('-h' '--help' '-e' '--exit-code' '--info' '--no-info' '--sort-by') _shtab_ahriman_search_option_strings=('-h' '--help' '-e' '--exit-code' '--info' '--no-info' '--sort-by') _shtab_ahriman_help_option_strings=('-h' '--help') _shtab_ahriman_help_commands_unsafe_option_strings=('-h' '--help' '--command') +_shtab_ahriman_help_updates_option_strings=('-h' '--help' '-e' '--exit-code') _shtab_ahriman_help_version_option_strings=('-h' '--help') _shtab_ahriman_version_option_strings=('-h' '--help') _shtab_ahriman_package_add_option_strings=('-h' '--help' '-e' '--exit-code' '-n' '--now' '-y' '--refresh' '-s' '--source' '--without-dependencies') @@ -69,7 +70,7 @@ _shtab_ahriman_web_option_strings=('-h' '--help') -_shtab_ahriman_pos_0_choices=('aur-search' 'search' 'help' 'help-commands-unsafe' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'user-add' 'user-list' 'user-remove' 'web') +_shtab_ahriman_pos_0_choices=('aur-search' 'search' 'help' 'help-commands-unsafe' 'help-updates' 'help-version' 'version' 'package-add' 'add' 'package-update' 'package-remove' 'remove' 'package-status' 'status' 'package-status-remove' 'package-status-update' 'status-update' 'patch-add' 'patch-list' 'patch-remove' 'patch-set-add' 'repo-backup' 'repo-check' 'check' 'repo-daemon' 'daemon' 'repo-rebuild' 'rebuild' 'repo-remove-unknown' 'remove-unknown' 'repo-report' 'report' 'repo-restore' 'repo-sign' 'sign' 'repo-status-update' 'repo-sync' 'sync' 'repo-tree' 'repo-triggers' 'repo-update' 'update' 'service-clean' 'clean' 'repo-clean' 'service-config' 'config' 'repo-config' 'service-config-validate' 'config-validate' 'repo-config-validate' 'service-key-import' 'key-import' 'service-setup' 'init' 'repo-init' 'repo-setup' 'setup' 'service-shell' 'shell' 'user-add' 'user-list' 'user-remove' 'web') _shtab_ahriman_aur_search___sort_by_choices=('description' 'first_submitted' 'id' 'last_modified' 'maintainer' 'name' 'num_votes' 'out_of_date' 'package_base' 'package_base_id' 'popularity' 'repository' 'url' 'url_path' 'version') _shtab_ahriman_search___sort_by_choices=('description' 'first_submitted' 'id' 'last_modified' 'maintainer' 'name' 'num_votes' 'out_of_date' 'package_base' 'package_base_id' 'popularity' 'repository' 'url' 'url_path' 'version') _shtab_ahriman_package_add__s_choices=('auto' 'archive' 'aur' 'directory' 'local' 'remote' 'repository') @@ -127,6 +128,10 @@ _shtab_ahriman_help__h_nargs=0 _shtab_ahriman_help___help_nargs=0 _shtab_ahriman_help_commands_unsafe__h_nargs=0 _shtab_ahriman_help_commands_unsafe___help_nargs=0 +_shtab_ahriman_help_updates__h_nargs=0 +_shtab_ahriman_help_updates___help_nargs=0 +_shtab_ahriman_help_updates__e_nargs=0 +_shtab_ahriman_help_updates___exit_code_nargs=0 _shtab_ahriman_help_version__h_nargs=0 _shtab_ahriman_help_version___help_nargs=0 _shtab_ahriman_version__h_nargs=0 diff --git a/docs/completions/zsh/_ahriman b/docs/completions/zsh/_ahriman index 7d29a16e..d98e5d95 100644 --- a/docs/completions/zsh/_ahriman +++ b/docs/completions/zsh/_ahriman @@ -14,6 +14,7 @@ _shtab_ahriman_commands() { "daemon:start process which periodically will run update process" "help:show help message for application or command and exit" "help-commands-unsafe:list unsafe commands as defined in default args" + "help-updates:request AUR for current version and compare with current service version" "help-version:print application and its dependencies versions" "init:create initial service configuration, requires root" "key-import:import PGP key from public sources to the repository user" @@ -148,6 +149,11 @@ _shtab_ahriman_help_commands_unsafe_options=( "--command[instead of showing commands, just test command line for unsafe subcommand and return 0 in case if command is safe and 1 otherwise]:command:" ) +_shtab_ahriman_help_updates_options=( + "(- : *)"{-h,--help}"[show this help message and exit]" + {-e,--exit-code}"[return non-zero exit code if updates available]" +) + _shtab_ahriman_help_version_options=( "(- : *)"{-h,--help}"[show this help message and exit]" ) @@ -555,6 +561,7 @@ _shtab_ahriman() { daemon) _arguments -C $_shtab_ahriman_daemon_options ;; help) _arguments -C $_shtab_ahriman_help_options ;; help-commands-unsafe) _arguments -C $_shtab_ahriman_help_commands_unsafe_options ;; + help-updates) _arguments -C $_shtab_ahriman_help_updates_options ;; help-version) _arguments -C $_shtab_ahriman_help_version_options ;; init) _arguments -C $_shtab_ahriman_init_options ;; key-import) _arguments -C $_shtab_ahriman_key_import_options ;; diff --git a/src/ahriman/application/ahriman.py b/src/ahriman/application/ahriman.py index 27365671..56b0bcc7 100644 --- a/src/ahriman/application/ahriman.py +++ b/src/ahriman/application/ahriman.py @@ -86,6 +86,7 @@ def _parser() -> argparse.ArgumentParser: _set_aur_search_parser(subparsers) _set_help_parser(subparsers) _set_help_commands_unsafe_parser(subparsers) + _set_help_updates_parser(subparsers) _set_help_version_parser(subparsers) _set_package_add_parser(subparsers) _set_package_remove_parser(subparsers) @@ -161,8 +162,8 @@ def _set_help_parser(root: SubParserAction) -> argparse.ArgumentParser: description="show help message for application or command and exit", formatter_class=_formatter) 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=_parser) + parser.set_defaults(handler=handlers.Help, architecture=[""], lock=None, report=False, quiet=True, unsafe=True, + parser=_parser) return parser @@ -185,6 +186,25 @@ def _set_help_commands_unsafe_parser(root: SubParserAction) -> argparse.Argument return parser +def _set_help_updates_parser(root: SubParserAction) -> argparse.ArgumentParser: + """ + add parser for service update check subcommand + + Args: + root(SubParserAction): subparsers for the commands + + Returns: + argparse.ArgumentParser: created argument parser + """ + parser = root.add_parser("help-updates", help="check for service updates", + description="request AUR for current version and compare with current service version", + formatter_class=_formatter) + 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, + unsafe=True) + return parser + + def _set_help_version_parser(root: SubParserAction) -> argparse.ArgumentParser: """ add parser for version subcommand @@ -197,8 +217,7 @@ def _set_help_version_parser(root: SubParserAction) -> argparse.ArgumentParser: """ parser = root.add_parser("help-version", aliases=["version"], help="application version", 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, quiet=True, unsafe=True) return parser diff --git a/src/ahriman/application/handlers/__init__.py b/src/ahriman/application/handlers/__init__.py index 2b19f62e..980c1d1c 100644 --- a/src/ahriman/application/handlers/__init__.py +++ b/src/ahriman/application/handlers/__init__.py @@ -32,6 +32,7 @@ from ahriman.application.handlers.remove import Remove from ahriman.application.handlers.remove_unknown import RemoveUnknown from ahriman.application.handlers.restore import Restore from ahriman.application.handlers.search import Search +from ahriman.application.handlers.service_updates import ServiceUpdates from ahriman.application.handlers.setup import Setup from ahriman.application.handlers.shell import Shell from ahriman.application.handlers.sign import Sign diff --git a/src/ahriman/application/handlers/service_updates.py b/src/ahriman/application/handlers/service_updates.py new file mode 100644 index 00000000..8aa1f0e3 --- /dev/null +++ b/src/ahriman/application/handlers/service_updates.py @@ -0,0 +1,65 @@ +# +# Copyright (c) 2021-2023 ahriman team. +# +# This file is part of ahriman +# (see https://github.com/arcan1s/ahriman). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +import argparse + +from typing import Type + +from ahriman import version +from ahriman.application.application import Application +from ahriman.application.handlers import Handler +from ahriman.core.configuration import Configuration +from ahriman.core.formatters import UpdatePrinter +from ahriman.models.package import Package + + +class ServiceUpdates(Handler): + """ + service updates handler + """ + + ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture" + + @classmethod + def run(cls: Type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *, + report: bool, unsafe: bool) -> None: + """ + callback for command line + + Args: + args(argparse.Namespace): command line args + architecture(str): repository architecture + configuration(Configuration): configuration instance + report(bool): force enable or disable reporting + unsafe(bool): if set no user check will be performed before path creation + """ + application = Application(architecture, configuration, report=report, unsafe=unsafe) + + remote = Package.from_aur("ahriman", application.repository.pacman) + release = remote.version.rsplit("-", 1)[-1] # we don't store pkgrel locally, so we just append it + local_version = f"{version.__version__}-{release}" + + # technically we would like to compare versions, but it is fine to raise an exception in case if locally + # installed package is newer than in AUR + same_version = remote.version == local_version + if same_version: + return + + UpdatePrinter(remote, local_version).print(verbose=True, separator=" -> ") + ServiceUpdates.check_if_empty(args.exit_code, not same_version) diff --git a/tests/ahriman/application/handlers/test_handler_service_updates.py b/tests/ahriman/application/handlers/test_handler_service_updates.py new file mode 100644 index 00000000..80811466 --- /dev/null +++ b/tests/ahriman/application/handlers/test_handler_service_updates.py @@ -0,0 +1,58 @@ +import argparse + +from pytest_mock import MockerFixture + +from ahriman import version +from ahriman.application.handlers import ServiceUpdates +from ahriman.core.configuration import Configuration +from ahriman.core.repository import Repository +from ahriman.models.package import Package + + +def _default_args(args: argparse.Namespace) -> argparse.Namespace: + """ + default arguments for these test cases + + Args: + args(argparse.Namespace): command line arguments fixture + + Returns: + argparse.Namespace: generated arguments for these test cases + """ + args.exit_code = False + return args + + +def test_run(args: argparse.Namespace, configuration: Configuration, repository: Repository, + package_ahriman: Package, mocker: MockerFixture) -> None: + """ + must run command + """ + package_ahriman.version = "0.0.0" + args = _default_args(args) + mocker.patch("ahriman.core.repository.Repository.load", return_value=repository) + package_mock = mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman) + application_mock = mocker.patch("ahriman.core.formatters.Printer.print") + check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty") + + ServiceUpdates.run(args, "x86_64", configuration, report=False, unsafe=False) + package_mock.assert_called_once_with(package_ahriman.base, repository.pacman) + application_mock.assert_called_once_with(verbose=True, separator=" -> ") + check_mock.assert_called_once_with(args.exit_code, True) + + +def test_run_skip(args: argparse.Namespace, configuration: Configuration, repository: Repository, + package_ahriman: Package, mocker: MockerFixture) -> None: + """ + must do not perform any actions if package is up-to-date + """ + package_ahriman.version = f"{version.__version__}-1" + args = _default_args(args) + mocker.patch("ahriman.core.repository.Repository.load", return_value=repository) + mocker.patch("ahriman.models.package.Package.from_aur", return_value=package_ahriman) + application_mock = mocker.patch("ahriman.core.formatters.Printer.print") + check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty") + + ServiceUpdates.run(args, "x86_64", configuration, report=False, unsafe=False) + application_mock.assert_not_called() + check_mock.assert_not_called() diff --git a/tests/ahriman/application/test_ahriman.py b/tests/ahriman/application/test_ahriman.py index 653b6c62..b92e6dc2 100644 --- a/tests/ahriman/application/test_ahriman.py +++ b/tests/ahriman/application/test_ahriman.py @@ -107,6 +107,26 @@ def test_subparsers_help_commands_unsafe_architecture(parser: argparse.ArgumentP assert args.architecture == [""] +def test_subparsers_help_updates(parser: argparse.ArgumentParser) -> None: + """ + help-updates command must imply architecture list, lock, report, quiet and unsafe + """ + args = parser.parse_args(["help-updates"]) + assert args.architecture == [""] + assert args.lock is None + assert not args.report + assert args.quiet + assert args.unsafe + + +def test_subparsers_help_updates_architecture(parser: argparse.ArgumentParser) -> None: + """ + help-updates command must correctly parse architecture list + """ + args = parser.parse_args(["-a", "x86_64", "help-updates"]) + assert args.architecture == [""] + + def test_subparsers_help_version(parser: argparse.ArgumentParser) -> None: """ help-version command must imply architecture, lock, report, quiet and unsafe