diff --git a/examples/extensions/commands/ci_test_example.py b/examples/extensions/commands/ci_test_example.py index 13899f14..f0fcd568 100644 --- a/examples/extensions/commands/ci_test_example.py +++ b/examples/extensions/commands/ci_test_example.py @@ -1,7 +1,9 @@ import os +import warnings +import json from test.examples_tools import run, tmp_dir - +from conan import conan_version non_deterministic_conanfile = """\ from datetime import datetime @@ -54,11 +56,24 @@ def install_clean_command(): f.write(non_deterministic_conanfile.format(name="clean_other", comment="# Changing RREV")) run("conan create .") # different RREV (this is the latest one) -# 3. Run "conan clean" command: Cleaning all the non-latest RREVs (and its packages) and PREVs -output = run("conan clean --force") -assert "Removed package revision: clean_hello/1.0#" in output # removing earlier PREV from clean_hello -assert "Removed recipe revision: clean_other/1.0#" in output # removing earlier RREV from clean_other -# Now, it should have removed nothing -output = run("conan clean --force") -assert "Removed recipe revision: clean_other/1.0#" not in output -assert "Removed package revision: clean_hello/1.0#" not in output +if conan_version >= "2.21.0-dev": + output = run("conan list '*/*#*:*#*' --format=json ") + all_packages = json.loads("\n".join(output.splitlines()[1:])) + output = run("conan list '*/*#latest:*#latest' --format=json") + latest_packages = json.loads("\n".join(output.splitlines()[1:])) + if all_packages == latest_packages: + warnings.warn("Skipping 'conan clean' test because there are no old revisions to clean.") + # 3. Run "conan clean" command: Cleaning all the non-latest RREVs (and its packages) and PREVs + output = run("conan clean --force") + assert "Removed package revision: clean_hello/1.0#" in output # removing earlier PREV from clean_hello + assert "Removed recipe revision: clean_other/1.0#" in output # removing earlier RREV from clean_other + # Now, it should have removed nothing + output = run("conan clean --force") + assert "Removed recipe revision: clean_other/1.0#" not in output + assert "Removed package revision: clean_hello/1.0#" not in output + # Make sure latest revisions are still there + output = run("conan list '*/*#*:*#*' --format=json") + listed_after = json.loads("\n".join(output.splitlines()[1:])) + assert latest_packages == listed_after +else: + warnings.warn("Skipping 'conan clean' test because it requires Conan 2.21 due new API list.") diff --git a/examples/extensions/commands/clean/cmd_clean.py b/examples/extensions/commands/clean/cmd_clean.py index fb22e14a..af50acd7 100644 --- a/examples/extensions/commands/clean/cmd_clean.py +++ b/examples/extensions/commands/clean/cmd_clean.py @@ -1,4 +1,5 @@ from conan.api.conan_api import ConanAPI +from conan.api.model import PackagesList, ListPattern from conan.api.input import UserInput from conan.api.output import ConanOutput, Color from conan.cli.command import OnceArgument, conan_command @@ -27,26 +28,30 @@ def confirmation(message): remote = conan_api.remotes.get(args.remote) if args.remote else None output_remote = remote or "Local cache" - # Getting all the recipes - recipes = conan_api.search.recipes("*/*", remote=remote) - if recipes and not confirmation("Do you want to remove all the recipes revisions and their packages ones, " + # List all recipes revisions and all their packages revisions as well + pkg_list = conan_api.list.select(ListPattern("*/*#*:*#*", rrev=None, prev=None), remote=remote) + if pkg_list and not confirmation("Do you want to remove all the recipes revisions and their packages ones, " "except the latest package revision from the latest recipe one?"): + out.writeln("Aborted") return - for recipe in recipes: - out.writeln(f"{str(recipe)}", fg=recipe_color) - all_rrevs = conan_api.list.recipe_revisions(recipe, remote=remote) - latest_rrev = all_rrevs[0] if all_rrevs else None - for rrev in all_rrevs: - if rrev != latest_rrev: - conan_api.remove.recipe(rrev, remote=remote) - out.writeln(f"Removed recipe revision: {rrev.repr_notime()} " - f"and all its package revisions [{output_remote}]", fg=removed_color) + + # Split the package list into based on their recipe reference + for sub_pkg_list in pkg_list.split(): + latest = max(sub_pkg_list.items(), key=lambda item: item[0])[0] + out.writeln(f"Keeping recipe revision: {latest.repr_notime()} " + f"and its latest package revisions [{output_remote}]", fg=recipe_color) + for rref, packages in sub_pkg_list.items(): + # For the latest recipe revision, keep the latest package revision only + if latest == rref: + # Get the latest package timestamp for each package_id + latest_pref_list = [max([p for p in packages if p.package_id == pkg_id], key=lambda p: p.timestamp) + for pkg_id in {p.package_id for p in packages}] + for pref in packages: + if pref not in latest_pref_list: + conan_api.remove.package(pref, remote=remote) + out.writeln(f"Removed package revision: {pref.repr_notime()} [{output_remote}]", fg=removed_color) else: - packages = conan_api.list.packages_configurations(rrev, remote=remote) - for package_ref in packages: - all_prevs = conan_api.list.package_revisions(package_ref, remote=remote) - latest_prev = all_prevs[0] if all_prevs else None - for prev in all_prevs: - if prev != latest_prev: - conan_api.remove.package(prev, remote=remote) - out.writeln(f"Removed package revision: {prev.repr_notime()} [{output_remote}]", fg=removed_color) + # Otherwise, remove all outdated recipe revisions and their packages + conan_api.remove.recipe(rref, remote=remote) + out.writeln(f"Removed recipe revision: {rref.repr_notime()} " + f"and all its package revisions [{output_remote}]", fg=removed_color)