diff --git a/src/ahriman/application/application/application.py b/src/ahriman/application/application/application.py index 3c453ca8..fdf8c63e 100644 --- a/src/ahriman/application/application/application.py +++ b/src/ahriman/application/application/application.py @@ -17,10 +17,12 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -from collections.abc import Iterable +from collections.abc import Callable, Iterable from ahriman.application.application.application_packages import ApplicationPackages from ahriman.application.application.application_repository import ApplicationRepository +from ahriman.core.formatters import UpdatePrinter +from ahriman.core.tree import Tree from ahriman.models.package import Package from ahriman.models.result import Result @@ -89,6 +91,22 @@ class Application(ApplicationPackages, ApplicationRepository): """ self.repository.triggers.on_stop() + def print_updates(self, packages: list[Package], *, log_fn: Callable[[str], None]) -> None: + """ + print list of packages to be built. This method will build dependency tree and print updates accordingly + + Args: + packages(list[Package]): package list to be printed + log_fn(Callable[[str], None]): logger function to log updates + """ + local_versions = {package.base: package.version for package in self.repository.packages()} + + tree = Tree.resolve(packages) + for level in tree: + for package in level: + UpdatePrinter(package, local_versions.get(package.base)).print( + verbose=True, log_fn=log_fn, separator=" -> ") + def with_dependencies(self, packages: list[Package], *, process_dependencies: bool) -> list[Package]: """ add missing dependencies to list of packages @@ -126,5 +144,8 @@ class Application(ApplicationPackages, ApplicationRepository): for package_name, username in missing.items(): package = Package.from_aur(package_name, self.repository.pacman, username) with_dependencies[package.base] = package + # register package in local database + self.database.remote_update(package) + self.repository.reporter.set_unknown(package) return list(with_dependencies.values()) diff --git a/src/ahriman/application/application/application_repository.py b/src/ahriman/application/application/application_repository.py index 6d67d5ae..827faeaa 100644 --- a/src/ahriman/application/application/application_repository.py +++ b/src/ahriman/application/application/application_repository.py @@ -17,12 +17,11 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # -from collections.abc import Callable, Iterable +from collections.abc import Iterable from pathlib import Path from ahriman.application.application.application_properties import ApplicationProperties from ahriman.core.build_tools.sources import Sources -from ahriman.core.formatters import UpdatePrinter from ahriman.core.tree import Tree from ahriman.models.package import Package from ahriman.models.packagers import Packagers @@ -158,7 +157,7 @@ class ApplicationRepository(ApplicationProperties): return build_result def updates(self, filter_packages: Iterable[str], *, - aur: bool, local: bool, manual: bool, vcs: bool, log_fn: Callable[[str], None]) -> list[Package]: + aur: bool, local: bool, manual: bool, vcs: bool) -> list[Package]: """ get list of packages to run update process @@ -168,7 +167,6 @@ class ApplicationRepository(ApplicationProperties): local(bool): enable or disable checking of local packages for updates manual(bool): include or exclude manual updates vcs(bool): enable or disable checking of VCS packages - log_fn(Callable[[str], None]): logger function to log updates Returns: list[Package]: list of out-of-dated packages @@ -182,14 +180,4 @@ class ApplicationRepository(ApplicationProperties): if manual: updates.update({package.base: package for package in self.repository.updates_manual()}) - local_versions = {package.base: package.version for package in self.repository.packages()} - updated_packages = [package for _, package in sorted(updates.items())] - - # reorder updates according to the dependency tree - tree = Tree.resolve(updated_packages) - for level in tree: - for package in level: - UpdatePrinter(package, local_versions.get(package.base)).print( - verbose=True, log_fn=log_fn, separator=" -> ") - - return updated_packages + return [package for _, package in sorted(updates.items())] diff --git a/src/ahriman/application/handlers/add.py b/src/ahriman/application/handlers/add.py index 95a79c92..dcca843b 100644 --- a/src/ahriman/application/handlers/add.py +++ b/src/ahriman/application/handlers/add.py @@ -50,10 +50,10 @@ class Add(Handler): if not args.now: return - packages = application.updates(args.package, aur=False, local=False, manual=True, vcs=False, - log_fn=application.logger.info) + packages = application.updates(args.package, aur=False, local=False, manual=True, vcs=False) packages = application.with_dependencies(packages, process_dependencies=args.dependencies) packagers = Packagers(args.username, {package.base: package.packager for package in packages}) + application.print_updates(packages, log_fn=application.logger.info) result = application.update(packages, packagers) Add.check_if_empty(args.exit_code, result.is_empty) diff --git a/src/ahriman/application/handlers/rebuild.py b/src/ahriman/application/handlers/rebuild.py index e00ac5ee..735a0134 100644 --- a/src/ahriman/application/handlers/rebuild.py +++ b/src/ahriman/application/handlers/rebuild.py @@ -22,7 +22,6 @@ import argparse 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.build_status import BuildStatusEnum from ahriman.models.package import Package @@ -53,8 +52,7 @@ class Rebuild(Handler): Rebuild.check_if_empty(args.exit_code, not updates) if args.dry_run: - for package in updates: - UpdatePrinter(package, package.version).print(verbose=True) + application.print_updates(updates, log_fn=print) return result = application.update(updates, args.username) diff --git a/src/ahriman/application/handlers/update.py b/src/ahriman/application/handlers/update.py index 77f2cb79..3ffd0cbb 100644 --- a/src/ahriman/application/handlers/update.py +++ b/src/ahriman/application/handlers/update.py @@ -48,8 +48,7 @@ class Update(Handler): application = Application(architecture, configuration, report=report, unsafe=unsafe, refresh_pacman_database=args.refresh) application.on_start() - packages = application.updates(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs, - log_fn=Update.log_fn(application, args.dry_run)) + packages = application.updates(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs) Update.check_if_empty(args.exit_code, not packages) if args.dry_run: return @@ -57,6 +56,7 @@ class Update(Handler): packages = application.with_dependencies(packages, process_dependencies=args.dependencies) packagers = Packagers(args.username, {package.base: package.packager for package in packages}) + application.print_updates(packages, log_fn=application.logger.info) result = application.update(packages, packagers) Update.check_if_empty(args.exit_code, result.is_empty) diff --git a/tests/ahriman/application/application/test_application.py b/tests/ahriman/application/application/test_application.py index 2cf922ed..c6275df9 100644 --- a/tests/ahriman/application/application/test_application.py +++ b/tests/ahriman/application/application/test_application.py @@ -2,6 +2,7 @@ from pytest_mock import MockerFixture from unittest.mock import MagicMock, call as MockCall from ahriman.application.application import Application +from ahriman.core.tree import Leaf, Tree from ahriman.models.package import Package from ahriman.models.result import Result @@ -47,6 +48,19 @@ def test_on_stop(application: Application, mocker: MockerFixture) -> None: triggers_mock.assert_called_once_with() +def test_print_updates(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: + """ + must print updates + """ + tree = Tree([Leaf(package_ahriman)]) + mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[package_ahriman]) + mocker.patch("ahriman.core.tree.Tree.resolve", return_value=tree.levels()) + print_mock = mocker.patch("ahriman.core.formatters.Printer.print") + + application.print_updates([package_ahriman], log_fn=print) + print_mock.assert_called_once_with(verbose=True, log_fn=print, separator=" -> ") + + def test_with_dependencies(application: Application, package_ahriman: Package, package_python_schedule: Package, mocker: MockerFixture) -> None: """ @@ -75,6 +89,8 @@ def test_with_dependencies(application: Application, package_ahriman: Package, p package_mock = mocker.patch("ahriman.models.package.Package.from_aur", side_effect=lambda *args: packages[args[0]]) packages_mock = mocker.patch("ahriman.application.application.Application._known_packages", return_value={"devtools", "python-build", "python-pytest"}) + update_remote_mock = mocker.patch("ahriman.core.database.SQLite.remote_update") + status_client_mock = mocker.patch("ahriman.core.status.client.Client.set_unknown") result = application.with_dependencies([package_ahriman], process_dependencies=True) assert {package.base: package for package in result} == packages @@ -85,6 +101,17 @@ def test_with_dependencies(application: Application, package_ahriman: Package, p ], any_order=True) packages_mock.assert_called_once_with() + update_remote_mock.assert_has_calls([ + MockCall(package_python_schedule), + MockCall(packages["python"]), + MockCall(packages["python-installer"]), + ], any_order=True) + status_client_mock.assert_has_calls([ + MockCall(package_python_schedule), + MockCall(packages["python"]), + MockCall(packages["python-installer"]), + ], any_order=True) + def test_with_dependencies_skip(application: Application, package_ahriman: Package, mocker: MockerFixture) -> None: """ diff --git a/tests/ahriman/application/application/test_application_repository.py b/tests/ahriman/application/application/test_application_repository.py index 2bc613a2..8cc77552 100644 --- a/tests/ahriman/application/application/test_application_repository.py +++ b/tests/ahriman/application/application/test_application_repository.py @@ -197,16 +197,12 @@ def test_updates_all(application_repository: ApplicationRepository, package_ahri """ must get updates for all """ - tree = Tree([Leaf(package_ahriman)]) - - mocker.patch("ahriman.core.tree.Tree.resolve", return_value=tree.levels()) - mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[]) updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur", return_value=[package_ahriman]) updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local") updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") - application_repository.updates([], aur=True, local=True, manual=True, vcs=True, log_fn=print) + application_repository.updates([], aur=True, local=True, manual=True, vcs=True) updates_aur_mock.assert_called_once_with([], vcs=True) updates_local_mock.assert_called_once_with(vcs=True) updates_manual_mock.assert_called_once_with() @@ -216,12 +212,11 @@ def test_updates_disabled(application_repository: ApplicationRepository, mocker: """ must get updates without anything """ - mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[]) updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur") updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local") updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") - application_repository.updates([], aur=False, local=False, manual=False, vcs=True, log_fn=print) + application_repository.updates([], aur=False, local=False, manual=False, vcs=True) updates_aur_mock.assert_not_called() updates_local_mock.assert_not_called() updates_manual_mock.assert_not_called() @@ -231,12 +226,11 @@ def test_updates_no_aur(application_repository: ApplicationRepository, mocker: M """ must get updates without aur """ - mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[]) updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur") updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local") updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") - application_repository.updates([], aur=False, local=True, manual=True, vcs=True, log_fn=print) + application_repository.updates([], aur=False, local=True, manual=True, vcs=True) updates_aur_mock.assert_not_called() updates_local_mock.assert_called_once_with(vcs=True) updates_manual_mock.assert_called_once_with() @@ -246,12 +240,11 @@ def test_updates_no_local(application_repository: ApplicationRepository, mocker: """ must get updates without local packages """ - mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[]) updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur") updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local") updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") - application_repository.updates([], aur=True, local=False, manual=True, vcs=True, log_fn=print) + application_repository.updates([], aur=True, local=False, manual=True, vcs=True) updates_aur_mock.assert_called_once_with([], vcs=True) updates_local_mock.assert_not_called() updates_manual_mock.assert_called_once_with() @@ -261,12 +254,11 @@ def test_updates_no_manual(application_repository: ApplicationRepository, mocker """ must get updates without manual """ - mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[]) updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur") updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local") updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") - application_repository.updates([], aur=True, local=True, manual=False, vcs=True, log_fn=print) + application_repository.updates([], aur=True, local=True, manual=False, vcs=True) updates_aur_mock.assert_called_once_with([], vcs=True) updates_local_mock.assert_called_once_with(vcs=True) updates_manual_mock.assert_not_called() @@ -276,12 +268,11 @@ def test_updates_no_vcs(application_repository: ApplicationRepository, mocker: M """ must get updates without VCS """ - mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[]) updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur") updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local") updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") - application_repository.updates([], aur=True, local=True, manual=True, vcs=False, log_fn=print) + application_repository.updates([], aur=True, local=True, manual=True, vcs=False) updates_aur_mock.assert_called_once_with([], vcs=False) updates_local_mock.assert_called_once_with(vcs=False) updates_manual_mock.assert_called_once_with() @@ -291,12 +282,11 @@ def test_updates_with_filter(application_repository: ApplicationRepository, mock """ must get updates with filter """ - mocker.patch("ahriman.core.repository.repository.Repository.packages", return_value=[]) updates_aur_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_aur") updates_local_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_local") updates_manual_mock = mocker.patch("ahriman.core.repository.update_handler.UpdateHandler.updates_manual") - application_repository.updates(["filter"], aur=True, local=True, manual=True, vcs=True, log_fn=print) + application_repository.updates(["filter"], aur=True, local=True, manual=True, vcs=True) updates_aur_mock.assert_called_once_with(["filter"], vcs=True) updates_local_mock.assert_called_once_with(vcs=True) updates_manual_mock.assert_called_once_with() diff --git a/tests/ahriman/application/handlers/test_handler_add.py b/tests/ahriman/application/handlers/test_handler_add.py index 5e10e930..0dca4650 100644 --- a/tests/ahriman/application/handlers/test_handler_add.py +++ b/tests/ahriman/application/handlers/test_handler_add.py @@ -65,14 +65,15 @@ def test_run_with_updates(args: argparse.Namespace, configuration: Configuration updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman]) dependencies_mock = mocker.patch("ahriman.application.application.Application.with_dependencies", return_value=[package_ahriman]) + print_mock = mocker.patch("ahriman.application.application.Application.print_updates") Add.run(args, "x86_64", configuration, report=False, unsafe=False) - updates_mock.assert_called_once_with(args.package, aur=False, local=False, manual=True, vcs=False, - log_fn=pytest.helpers.anyvar(int)) + updates_mock.assert_called_once_with(args.package, aur=False, local=False, manual=True, vcs=False) application_mock.assert_called_once_with([package_ahriman], Packagers(args.username, {package_ahriman.base: "packager"})) dependencies_mock.assert_called_once_with([package_ahriman], process_dependencies=args.dependencies) check_mock.assert_called_once_with(False, False) + print_mock.assert_called_once_with([package_ahriman], log_fn=pytest.helpers.anyvar(int)) def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository, @@ -88,6 +89,7 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat mocker.patch("ahriman.application.application.Application.update", return_value=Result()) mocker.patch("ahriman.application.application.Application.with_dependencies") mocker.patch("ahriman.application.application.Application.updates") + mocker.patch("ahriman.application.application.Application.print_updates") check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty") Add.run(args, "x86_64", configuration, report=False, unsafe=False) diff --git a/tests/ahriman/application/handlers/test_handler_rebuild.py b/tests/ahriman/application/handlers/test_handler_rebuild.py index 1918cb66..6a3b84c3 100644 --- a/tests/ahriman/application/handlers/test_handler_rebuild.py +++ b/tests/ahriman/application/handlers/test_handler_rebuild.py @@ -66,6 +66,7 @@ def test_run_extract_packages(args: argparse.Namespace, configuration: Configura args.dry_run = True mocker.patch("ahriman.core.repository.Repository.load", return_value=repository) mocker.patch("ahriman.application.application.Application.add") + mocker.patch("ahriman.application.application.Application.print_updates") extract_mock = mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[]) Rebuild.run(args, "x86_64", configuration, report=False, unsafe=False) @@ -83,10 +84,12 @@ def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, rep mocker.patch("ahriman.application.handlers.Rebuild.extract_packages", return_value=[package_ahriman]) application_mock = mocker.patch("ahriman.application.application.Application.update") check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty") + print_mock = mocker.patch("ahriman.application.application.Application.print_updates") Rebuild.run(args, "x86_64", configuration, report=False, unsafe=False) application_mock.assert_not_called() check_mock.assert_called_once_with(False, False) + print_mock.assert_called_once_with([package_ahriman], log_fn=pytest.helpers.anyvar(int)) def test_run_filter(args: argparse.Namespace, configuration: Configuration, repository: Repository, @@ -131,6 +134,7 @@ def test_run_update_empty_exception(args: argparse.Namespace, configuration: Con mocker.patch("ahriman.core.repository.Repository.load", return_value=repository) mocker.patch("ahriman.application.handlers.Rebuild.extract_packages") mocker.patch("ahriman.core.repository.repository.Repository.packages_depend_on", return_value=[]) + mocker.patch("ahriman.application.application.Application.print_updates") check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty") Rebuild.run(args, "x86_64", configuration, report=False, unsafe=False) diff --git a/tests/ahriman/application/handlers/test_handler_update.py b/tests/ahriman/application/handlers/test_handler_update.py index 8ce3898f..496fac11 100644 --- a/tests/ahriman/application/handlers/test_handler_update.py +++ b/tests/ahriman/application/handlers/test_handler_update.py @@ -51,15 +51,16 @@ def test_run(args: argparse.Namespace, package_ahriman: Package, configuration: return_value=[package_ahriman]) updates_mock = mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman]) on_start_mock = mocker.patch("ahriman.application.application.Application.on_start") + print_mock = mocker.patch("ahriman.application.application.Application.print_updates") Update.run(args, "x86_64", configuration, report=False, unsafe=False) application_mock.assert_called_once_with([package_ahriman], Packagers(args.username, {package_ahriman.base: "packager"})) - updates_mock.assert_called_once_with(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs, - log_fn=pytest.helpers.anyvar(int)) + updates_mock.assert_called_once_with(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs) dependencies_mock.assert_called_once_with([package_ahriman], process_dependencies=args.dependencies) check_mock.assert_has_calls([MockCall(False, False), MockCall(False, False)]) on_start_mock.assert_called_once_with() + print_mock.assert_called_once_with([package_ahriman], log_fn=pytest.helpers.anyvar(int)) def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, repository: Repository, @@ -89,6 +90,7 @@ def test_run_update_empty_exception(args: argparse.Namespace, package_ahriman: P mocker.patch("ahriman.application.application.Application.update", return_value=Result()) mocker.patch("ahriman.application.application.Application.updates", return_value=[package_ahriman]) mocker.patch("ahriman.application.application.Application.with_dependencies", return_value=[package_ahriman]) + mocker.patch("ahriman.application.application.Application.print_updates") check_mock = mocker.patch("ahriman.application.handlers.Handler.check_if_empty") Update.run(args, "x86_64", configuration, report=False, unsafe=False) @@ -108,8 +110,7 @@ def test_run_dry_run(args: argparse.Namespace, configuration: Configuration, rep updates_mock = mocker.patch("ahriman.application.application.Application.updates") Update.run(args, "x86_64", configuration, report=False, unsafe=False) - updates_mock.assert_called_once_with(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs, - log_fn=pytest.helpers.anyvar(int)) + updates_mock.assert_called_once_with(args.package, aur=args.aur, local=args.local, manual=args.manual, vcs=args.vcs) application_mock.assert_not_called() check_mock.assert_called_once_with(False, pytest.helpers.anyvar(int))