diff --git a/docs/releases.rst b/docs/releases.rst index 3c691f316f..0715c90195 100644 --- a/docs/releases.rst +++ b/docs/releases.rst @@ -5,6 +5,13 @@ ====================== +tmt-1.41.0 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Add the ``--workdir-root`` option for the ``tmt clean images`` +command so that users can specify the directory they want. + + tmt-1.40.0 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tmt/base.py b/tmt/base.py index dcd66954ad..43d39be9b2 100644 --- a/tmt/base.py +++ b/tmt/base.py @@ -3952,7 +3952,8 @@ def images(self) -> bool: successful = True for method in tmt.steps.provision.ProvisionPlugin.methods(): # FIXME: ignore[union-attr]: https://github.com/teemtee/tmt/issues/1599 - if not method.class_.clean_images(self, self.is_dry_run): # type: ignore[union-attr] + if not method.class_.clean_images( # type: ignore[union-attr] + self, self.is_dry_run, self.workdir_root): successful = False return successful diff --git a/tmt/cli/_root.py b/tmt/cli/_root.py index a037770c04..5e21c5346d 100644 --- a/tmt/cli/_root.py +++ b/tmt/cli/_root.py @@ -1700,9 +1700,10 @@ def clean_guests( # inference. See Context and ContextObjects above. @clean.command(name='images') # type: ignore[arg-type] @pass_context +@workdir_root_options @verbosity_options @dry_options -def clean_images(context: Context, **kwargs: Any) -> None: +def clean_images(context: Context, _workdir_root: Optional[str], **kwargs: Any) -> None: """ Remove images of supported provision methods. @@ -1714,12 +1715,17 @@ def clean_images(context: Context, **kwargs: Any) -> None: # cleaned, similarly to guests. assert context.obj.clean_logger is not None # narrow type + workdir_root = Path(_workdir_root) if _workdir_root is not None else None + if workdir_root and not workdir_root.exists(): + raise tmt.utils.GeneralError(f"Path '{workdir_root}' doesn't exist.") + clean_obj = tmt.Clean( logger=context.obj.clean_logger .descend(logger_name='clean-images', extra_shift=0) .apply_verbosity_options(**kwargs), parent=context.obj.clean, - cli_invocation=CliInvocation.from_context(context)) + cli_invocation=CliInvocation.from_context(context), + workdir_root=effective_workdir_root(workdir_root)) context.obj.clean_partials["images"].append(clean_obj.images) # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tmt/steps/provision/__init__.py b/tmt/steps/provision/__init__.py index ac0767d800..0656e52def 100644 --- a/tmt/steps/provision/__init__.py +++ b/tmt/steps/provision/__init__.py @@ -2368,7 +2368,7 @@ def options(cls, how: Optional[str] = None) -> list[tmt.options.ClickOptionDecor return super().options(how) + cls._guest_class.options(how) @classmethod - def clean_images(cls, clean: 'tmt.base.Clean', dry: bool) -> bool: + def clean_images(cls, clean: 'tmt.base.Clean', dry: bool, workdir_root: Path) -> bool: """ Remove the images of one particular plugin """ return True diff --git a/tmt/steps/provision/testcloud.py b/tmt/steps/provision/testcloud.py index 4120b6e6aa..21fcd89f8d 100644 --- a/tmt/steps/provision/testcloud.py +++ b/tmt/steps/provision/testcloud.py @@ -1220,15 +1220,17 @@ def _print_local_images(self) -> None: click.echo(f"{TESTCLOUD_IMAGES / filename}") @classmethod - def clean_images(cls, clean: 'tmt.base.Clean', dry: bool) -> bool: + def clean_images(cls, clean: 'tmt.base.Clean', dry: bool, workdir_root: Path) -> bool: """ Remove the testcloud images """ + testcloud_images = workdir_root / 'testcloud/images' + clean.info('testcloud', shift=1, color='green') - if not TESTCLOUD_IMAGES.exists(): + if not testcloud_images.exists(): clean.warn( - f"Directory '{TESTCLOUD_IMAGES}' does not exist.", shift=2) + f"Directory '{testcloud_images}' does not exist.", shift=2) return True successful = True - for image in TESTCLOUD_IMAGES.iterdir(): + for image in testcloud_images.iterdir(): if dry: clean.verbose(f"Would remove '{image}'.", shift=2) else: