From a25e561d3d1bf05bbd009ddc89b41ae2aa5159cf Mon Sep 17 00:00:00 2001 From: Athrey Vinay Date: Wed, 8 Apr 2026 18:48:55 +0100 Subject: [PATCH 1/5] remove redundant install_* methods --- tmt/package_managers/__init__.py | 20 ------------------- tmt/package_managers/apk.py | 14 +++---------- tmt/package_managers/bootc.py | 32 +----------------------------- tmt/package_managers/rpm_ostree.py | 12 ++++++----- tmt/steps/prepare/install.py | 4 ++-- 5 files changed, 13 insertions(+), 69 deletions(-) diff --git a/tmt/package_managers/__init__.py b/tmt/package_managers/__init__.py index 2732297ee5..aa1784432b 100644 --- a/tmt/package_managers/__init__.py +++ b/tmt/package_managers/__init__.py @@ -640,16 +640,6 @@ def create_repository(self, directory: Path) -> CommandOutput: """ return self.guest.execute(self.engine.create_repository(directory)) - def install_from_repository( - self, - *installables: Installable, - options: Optional[Options] = None, - ) -> CommandOutput: - """ - Install packages from a repository - """ - return self.install(*installables, options=options) - def install_local( self, *installables: Installable, @@ -660,16 +650,6 @@ def install_local( """ return self.install(*installables, options=options) - def install_from_url( - self, - *installables: Installable, - options: Optional[Options] = None, - ) -> CommandOutput: - """ - Install packages stored on a remote URL - """ - return self.install(*installables, options=options) - def enable_copr(self, *repositories: str) -> None: """ Enable requested copr repositories diff --git a/tmt/package_managers/apk.py b/tmt/package_managers/apk.py index 63a37b6b18..fe72e1604f 100644 --- a/tmt/package_managers/apk.py +++ b/tmt/package_managers/apk.py @@ -74,7 +74,9 @@ def _reduce_to_packages(self, *installables: Installable) -> ReducedPackages: packages.append(self.path_to_package(installable)) else: - raise GeneralError(f"Package specification '{installable}' is not supported.") + raise tmt.utils.PrepareError( + "Package manager 'apk' does not support installing from a remote URL." + ) return packages @@ -206,13 +208,3 @@ def install_local( options.allow_untrusted = True options.check_first = False return self.install(*installables, options=options) - - def install_from_url( - self, - *installables: Installable, - options: Optional[Options] = None, - ) -> CommandOutput: - raise tmt.utils.PrepareError( - f'Package manager "{self.guest.facts.package_manager}" ' - 'does not support installing from a remote URL.' - ) diff --git a/tmt/package_managers/bootc.py b/tmt/package_managers/bootc.py index b2287dc718..2a553be6f0 100644 --- a/tmt/package_managers/bootc.py +++ b/tmt/package_managers/bootc.py @@ -311,7 +311,6 @@ def install( if missing_installables: self.engine.install(*missing_installables, options=options) - return self.build_container() or CommandOutput(stdout=None, stderr=None) return CommandOutput(stdout=None, stderr=None) @@ -322,7 +321,7 @@ def reinstall( ) -> CommandOutput: self.engine.reinstall(*installables, options=options) - return self.build_container() or CommandOutput(stdout=None, stderr=None) + return CommandOutput(stdout=None, stderr=None) def install_debuginfo( self, @@ -332,35 +331,6 @@ def install_debuginfo( self.engine.install_debuginfo(*installables, options=options) return CommandOutput(stdout=None, stderr=None) - def install_from_repository( - self, - *installables: Installable, - options: Optional[Options] = None, - ) -> CommandOutput: - - # Check presence to avoid unnecessary container rebuilds - presence = self.check_presence(*installables) - - missing_installables = {i for i, present in presence.items() if not present} - if missing_installables: - self.engine.install(*missing_installables, options=options) - - return CommandOutput(stdout=None, stderr=None) - - def install_from_url( - self, - *installables: Installable, - options: Optional[Options] = None, - ) -> CommandOutput: - - presence = self.check_presence(*installables) - - missing_installables = {i for i, present in presence.items() if not present} - if missing_installables: - self.engine.install(*missing_installables, options=options) - - return CommandOutput(stdout=None, stderr=None) - def install_local( self, *installables: Installable, diff --git a/tmt/package_managers/rpm_ostree.py b/tmt/package_managers/rpm_ostree.py index e2d2fcfafd..50719d99fe 100644 --- a/tmt/package_managers/rpm_ostree.py +++ b/tmt/package_managers/rpm_ostree.py @@ -218,8 +218,10 @@ def sort_packages( required: list[Installable] = [] recommended: list[Installable] = [] - for installable in installables: - if all(self.check_presence(installable).values()): + presence = self.check_presence(*installables) + + for installable, present in presence.items(): + if present: continue if options.skip_missing: recommended.append(installable) @@ -228,7 +230,7 @@ def sort_packages( return required, recommended - def install_from_repository( + def install( self, *installables: Installable, options: Optional[Options] = None, @@ -239,13 +241,13 @@ def install_from_repository( for package in recommended: self.info('package', str(package), 'green') try: - self.install(package) + super().install(package) except RunError as error: self.debug(f"Package installation failed: {error}") self.warn(f"Unable to install recommended package '{package}'.") if required: - return self.install(*required) + return super().install(*required) return CommandOutput(stdout=None, stderr=None) diff --git a/tmt/steps/prepare/install.py b/tmt/steps/prepare/install.py index 1f580ab24b..e1ecee1e60 100644 --- a/tmt/steps/prepare/install.py +++ b/tmt/steps/prepare/install.py @@ -274,7 +274,7 @@ def _install( if self.remote_packages: install_outputs.append( - guest.package_manager.install_from_url( + guest.package_manager.install( *self._list_installables('remote package', *self.remote_packages), options=options, ) @@ -282,7 +282,7 @@ def _install( if self.packages: install_outputs.append( - guest.package_manager.install_from_repository( + guest.package_manager.install( *self._list_installables('package', *self.packages), options=options, ) From c211ce0335b352b0c343ab36e3d4b34914ca2652 Mon Sep 17 00:00:00 2001 From: Athrey Vinay Date: Tue, 14 Apr 2026 22:03:03 +0100 Subject: [PATCH 2/5] address gemini comments... --- tmt/package_managers/apk.py | 3 ++- tmt/package_managers/rpm_ostree.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tmt/package_managers/apk.py b/tmt/package_managers/apk.py index fe72e1604f..705540f7c3 100644 --- a/tmt/package_managers/apk.py +++ b/tmt/package_managers/apk.py @@ -75,7 +75,8 @@ def _reduce_to_packages(self, *installables: Installable) -> ReducedPackages: else: raise tmt.utils.PrepareError( - "Package manager 'apk' does not support installing from a remote URL." + f"Package manager 'apk' does not support installing from a remote " + f"URL '{installable}'." ) return packages diff --git a/tmt/package_managers/rpm_ostree.py b/tmt/package_managers/rpm_ostree.py index 50719d99fe..537b14c94d 100644 --- a/tmt/package_managers/rpm_ostree.py +++ b/tmt/package_managers/rpm_ostree.py @@ -241,7 +241,7 @@ def install( for package in recommended: self.info('package', str(package), 'green') try: - super().install(package) + super().install(package, options=options) except RunError as error: self.debug(f"Package installation failed: {error}") self.warn(f"Unable to install recommended package '{package}'.") From e39d0d633b14d0c65d438ab4a3c3855f9efc08bb Mon Sep 17 00:00:00 2001 From: Athrey Vinay Date: Tue, 5 May 2026 09:35:59 +0100 Subject: [PATCH 3/5] pass options to the required path --- tmt/package_managers/rpm_ostree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tmt/package_managers/rpm_ostree.py b/tmt/package_managers/rpm_ostree.py index 537b14c94d..0d936b8369 100644 --- a/tmt/package_managers/rpm_ostree.py +++ b/tmt/package_managers/rpm_ostree.py @@ -247,7 +247,7 @@ def install( self.warn(f"Unable to install recommended package '{package}'.") if required: - return super().install(*required) + return super().install(*required, options=options) return CommandOutput(stdout=None, stderr=None) From 33b1286cc67d62e3bb72871b66fd8a6f2d90c2c6 Mon Sep 17 00:00:00 2001 From: Athrey Vinay Date: Tue, 5 May 2026 13:30:59 +0100 Subject: [PATCH 4/5] add build container in _assert_rsync --- tmt/guest/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tmt/guest/__init__.py b/tmt/guest/__init__.py index cfe42bb3e3..eba82a5068 100644 --- a/tmt/guest/__init__.py +++ b/tmt/guest/__init__.py @@ -3532,6 +3532,7 @@ def _assert_rsync(self) -> None: # this is needed for example on fresh Debian installs. self.package_manager.refresh_metadata() self.package_manager.install(Package('rsync')) + self.package_manager.finalize_installation() except Exception as exc: raise tmt.utils.GeneralError( From ba5f5991ec88ae27a754dd158278267b63ac6143 Mon Sep 17 00:00:00 2001 From: Athrey Vinay Date: Wed, 13 May 2026 13:46:32 +0100 Subject: [PATCH 5/5] finalize installation at call sites --- tmt/guest/__init__.py | 1 + tmt/steps/prepare/distgit.py | 1 + tmt/steps/prepare/feature/crb.py | 1 + 3 files changed, 3 insertions(+) diff --git a/tmt/guest/__init__.py b/tmt/guest/__init__.py index eba82a5068..36b19de7f1 100644 --- a/tmt/guest/__init__.py +++ b/tmt/guest/__init__.py @@ -3346,6 +3346,7 @@ def setup(self) -> None: return if not self.facts.is_superuser and self.become: self.package_manager.install(FileSystemPath('/usr/bin/setfacl')) + self.package_manager.finalize_installation() workdir_root = effective_workdir_root() self.execute( ShellScript( diff --git a/tmt/steps/prepare/distgit.py b/tmt/steps/prepare/distgit.py index d565bddb53..d029e8dcd6 100644 --- a/tmt/steps/prepare/distgit.py +++ b/tmt/steps/prepare/distgit.py @@ -213,6 +213,7 @@ def go( # Install required packages for rpm-build to work guest.package_manager.install(*explicit_requires) + guest.package_manager.finalize_installation() source_dir = self.data.source_dir assert source_dir diff --git a/tmt/steps/prepare/feature/crb.py b/tmt/steps/prepare/feature/crb.py index 7575078690..28eb9cdaaf 100644 --- a/tmt/steps/prepare/feature/crb.py +++ b/tmt/steps/prepare/feature/crb.py @@ -60,6 +60,7 @@ def _manage_repo(cls, guest: Guest, logger: tmt.log.Logger, action: str) -> None return guest.package_manager.install(Package("dnf-command(config-manager)")) + guest.package_manager.finalize_installation() logger.info(f"{action.capitalize()} CRB repository.")