From 2df13a9fe732b5f570a161777e2c6ee071f3b701 Mon Sep 17 00:00:00 2001 From: skycastlelily Date: Thu, 5 Dec 2024 18:07:27 +0800 Subject: [PATCH] Implement the `--keep` option for `tmt clean` (#3183) (#3183) --- docs/releases.rst | 6 +++--- tests/clean/basic/test.sh | 20 ++++++++++++++++---- tmt/base.py | 6 ++---- tmt/cli/_root.py | 14 ++++++++++---- 4 files changed, 31 insertions(+), 15 deletions(-) diff --git a/docs/releases.rst b/docs/releases.rst index 1f4a8e19c2..b869455141 100644 --- a/docs/releases.rst +++ b/docs/releases.rst @@ -11,9 +11,9 @@ tmt-1.40.0 Add the ``--workdir-root`` option for the ``tmt clean`` command so that users can specify the directory they want to clean. -Add the ``--keep`` option for the ``tmt clean guests`` command. -Users can now choose to keep the selected number of latest guests -and clean the rest to release the resources. +Add the ``--keep`` option for the ``tmt clean guests`` and ``tmt clean`` +commands. Users can now choose to keep the selected number of latest guests, +and maybe also runs, clean the rest to release the resources. tmt-1.39.0 diff --git a/tests/clean/basic/test.sh b/tests/clean/basic/test.sh index 1bd0d7dc0b..61bcf90e0f 100755 --- a/tests/clean/basic/test.sh +++ b/tests/clean/basic/test.sh @@ -9,7 +9,7 @@ rlJournalStart rlPhaseEnd rlPhaseStartTest "Create a couple of runs" - for id in {001..004}; do + for id in {001..005}; do rlRun "tmt --feeling-safe run --id clean-$id" rlAssertExists "$root/clean-$id" done @@ -20,7 +20,8 @@ rlJournalStart rlAssertExists "$root/clean-001" rlAssertExists "$root/clean-002" rlAssertExists "$root/clean-003" - rlAssertNotExists "$root/clean-004" + rlAssertExists "$root/clean-004" + rlAssertNotExists "$root/clean-005" rlPhaseEnd rlPhaseStartTest "Remove selected (full path)" @@ -28,7 +29,8 @@ rlJournalStart rlAssertNotExists "$root/clean-001" rlAssertExists "$root/clean-002" rlAssertExists "$root/clean-003" - rlAssertNotExists "$root/clean-004" + rlAssertExists "$root/clean-004" + rlAssertNotExists "$root/clean-005" rlPhaseEnd rlPhaseStartTest "Remove selected (name)" @@ -36,7 +38,17 @@ rlJournalStart rlAssertNotExists "$root/clean-001" rlAssertNotExists "$root/clean-002" rlAssertExists "$root/clean-003" - rlAssertNotExists "$root/clean-004" + rlAssertExists "$root/clean-004" + rlAssertNotExists "$root/clean-005" + rlPhaseEnd + + rlPhaseStartTest "Skip latest guests and runs" + rlRun "tmt clean -v --keep 1" + rlAssertNotExists "$root/clean-001" + rlAssertNotExists "$root/clean-002" + rlAssertNotExists "$root/clean-003" + rlAssertExists "$root/clean-004" + rlAssertNotExists "$root/clean-005" rlPhaseEnd rlPhaseStartTest "Create a couple of runs in non-default root workdir" diff --git a/tmt/base.py b/tmt/base.py index a0985a7761..05d1b89f46 100644 --- a/tmt/base.py +++ b/tmt/base.py @@ -3997,7 +3997,7 @@ def _stop_running_guests(self, run: Run) -> bool: self.cli_invocation.options['quiet'] = quiet return successful - def guests(self, run_ids: tuple[str, ...]) -> bool: + def guests(self, run_ids: tuple[str, ...], keep: Optional[int]) -> bool: """ Clean guests of runs """ self.info('guests', color='blue') self.verbose('workdir root', self.workdir_root) @@ -4009,7 +4009,6 @@ def guests(self, run_ids: tuple[str, ...]) -> bool: successful = True assert self._cli_context_object is not None # narrow type all_workdirs = list(tmt.utils.generate_runs(self.workdir_root, run_ids)) - keep = self.opt('keep') if keep is not None: # Sort by modify time of the workdirs to keep the newest guests all_workdirs.sort( @@ -4038,7 +4037,7 @@ def _clean_workdir(self, path: Path) -> bool: return False return True - def runs(self, id_: tuple[str, ...]) -> bool: + def runs(self, id_: tuple[str, ...], keep: Optional[int]) -> bool: """ Clean workdirs of runs """ self.info('runs', color='blue') self.verbose('workdir root', self.workdir_root) @@ -4050,7 +4049,6 @@ def runs(self, id_: tuple[str, ...]) -> bool: assert last_run.workdir is not None # narrow type return self._clean_workdir(last_run.workdir) all_workdirs = list(tmt.utils.generate_runs(self.workdir_root, id_)) - keep = self.opt('keep') if keep is not None: # Sort by modify time of the workdirs and keep the newest workdirs all_workdirs.sort( diff --git a/tmt/cli/_root.py b/tmt/cli/_root.py index 7021e43563..936ca22b0c 100644 --- a/tmt/cli/_root.py +++ b/tmt/cli/_root.py @@ -1636,6 +1636,9 @@ def _construct_trying_provision_options(params: Any) -> dict[str, Any]: @option( '-i', '--id', 'id_', metavar="ID", multiple=True, help='Identifier (name or directory path) of the run to be cleaned.') +@option( + '-k', '--keep', type=int, default=None, + help='The number of latest workdirs to keep, clean the rest.') @option( '-s', '--skip', choices=CLEAN_RESOURCES, help='The resources which should be kept on the disk.', multiple=True) @@ -1645,6 +1648,7 @@ def _construct_trying_provision_options(params: Any) -> dict[str, Any]: def clean(context: Context, last: bool, id_: tuple[str, ...], + keep: Optional[int], skip: list[str], _workdir_root: Optional[str], **kwargs: Any) -> None: @@ -1691,9 +1695,9 @@ def clean(context: Context, cli_invocation=CliInvocation.from_context(context), workdir_root=workdir_root) if workdir_root.exists(): - if 'guests' not in skip and not clean_obj.guests(id_): + if 'guests' not in skip and not clean_obj.guests(id_, keep): exit_code = 1 - if 'runs' not in skip and not clean_obj.runs(id_): + if 'runs' not in skip and not clean_obj.runs(id_, keep): exit_code = 1 else: clean_obj.warn( @@ -1774,7 +1778,8 @@ def clean_runs( workdir_root=effective_workdir_root(workdir_root)) context.obj.clean_partials["runs"].append( lambda: clean_obj.runs( - (context.parent and context.parent.params.get('id_', [])) or id_)) + (context.parent and context.parent.params.get('id_', [])) or id_, + (context.parent and context.parent.params.get('keep', [])) or keep)) @clean.command(name='guests') @@ -1822,7 +1827,8 @@ def clean_guests( workdir_root=effective_workdir_root(workdir_root)) context.obj.clean_partials["guests"].append( lambda: clean_obj.guests( - (context.parent and context.parent.params.get('id_', [])) or id_)) + (context.parent and context.parent.params.get('id_', [])) or id_, + (context.parent and context.parent.params.get('keep', [])) or keep)) # ignore[arg-type]: click code expects click.Context, but we use our own type for better type