diff --git a/conan/cli/commands/remove.py b/conan/cli/commands/remove.py index 6909e6fe605..a109a4b7494 100644 --- a/conan/cli/commands/remove.py +++ b/conan/cli/commands/remove.py @@ -62,6 +62,7 @@ def remove(conan_api: ConanAPI, parser, *args): ui = UserInput(conan_api.config.get("core:non_interactive")) remote = conan_api.remotes.get(args.remote) if args.remote else None + cache_name = "Local Cache" if not remote else remote.name def confirmation(message): return args.confirm or ui.request_boolean(message) @@ -69,7 +70,7 @@ def confirmation(message): if args.list: listfile = make_abs_path(args.list) multi_package_list = MultiPackagesList.load(listfile) - package_list = multi_package_list["Local Cache" if not remote else remote.name] + package_list = multi_package_list[cache_name] refs_to_remove = package_list.refs() if not refs_to_remove: # the package list might contain only refs, no revs ConanOutput().warning("Nothing to remove, package list do not contain recipe revisions") @@ -79,16 +80,36 @@ def confirmation(message): raise ConanException('--package-query supplied but the pattern does not match packages') package_list = conan_api.list.select(ref_pattern, args.package_query, remote) multi_package_list = MultiPackagesList() - multi_package_list.add("Local Cache" if not remote else remote.name, package_list) + multi_package_list.add(cache_name, package_list) + # TODO: This iteration and removal of not-confirmed is ugly and complicated, improve it for ref, ref_bundle in package_list.refs().items(): - if ref_bundle.get("packages") is None: + ref_dict = package_list.recipes[str(ref)]["revisions"] + packages = ref_bundle.get("packages") + if packages is None: if confirmation(f"Remove the recipe and all the packages of '{ref.repr_notime()}'?"): conan_api.remove.recipe(ref, remote=remote) + else: + ref_dict.pop(ref.revision) + if not ref_dict: + package_list.recipes.pop(str(ref)) continue - for pref, _ in package_list.prefs(ref, ref_bundle).items(): + prefs = package_list.prefs(ref, ref_bundle) + if not prefs: + ConanOutput().info(f"No binaries to remove for '{ref.repr_notime()}'") + ref_dict.pop(ref.revision) + if not ref_dict: + package_list.recipes.pop(str(ref)) + continue + + for pref, _ in prefs.items(): if confirmation(f"Remove the package '{pref.repr_notime()}'?"): conan_api.remove.package(pref, remote=remote) + else: + pref_dict = packages[pref.package_id]["revisions"] + pref_dict.pop(pref.revision) + if not pref_dict: + packages.pop(pref.package_id) return { "results": multi_package_list.serialize(), diff --git a/conans/test/integration/command_v2/test_combined_pkglist_flows.py b/conans/test/integration/command_v2/test_combined_pkglist_flows.py index 117ae7e3aab..150dccc71a1 100644 --- a/conans/test/integration/command_v2/test_combined_pkglist_flows.py +++ b/conans/test/integration/command_v2/test_combined_pkglist_flows.py @@ -231,16 +231,26 @@ def test_remove_all(self, client, remote): assert "There are no matching recipe references" in client.out @pytest.mark.parametrize("remote", [False, True]) - def test_remove_packages(self, client, remote): + def test_remove_packages_no_revisions(self, client, remote): # It is necessary to do *#* for actually removing something remote = "-r=default" if remote else "" client.run(f"list *#*:* {remote} --format=json", redirect_stdout="pkglist.json") client.run(f"remove --list=pkglist.json {remote} -c") + assert "No binaries to remove for 'zli/1.0.0#f034dc90894493961d92dd32a9ee3b78'" in client.out + assert "No binaries to remove for 'zlib/1.0.0@user/channel" \ + "#ffd4bc45820ddb320ab224685b9ba3fb" in client.out + + @pytest.mark.parametrize("remote", [False, True]) + def test_remove_packages(self, client, remote): + # It is necessary to do *#* for actually removing something + remote = "-r=default" if remote else "" + client.run(f"list *#*:*#* {remote} --format=json", redirect_stdout="pkglist.json") + client.run(f"remove --list=pkglist.json {remote} -c") + assert "Removed recipe and all binaries" not in client.out - assert "zli/1.0.0#f034dc90894493961d92dd32a9ee3b78:" \ - " Removed binaries" in client.out - assert "zlib/1.0.0@user/channel#ffd4bc45820ddb320ab224685b9ba3fb:" \ - " Removed binaries" in client.out + assert "zli/1.0.0#f034dc90894493961d92dd32a9ee3b78: Removed binaries" in client.out + assert "zlib/1.0.0@user/channel#ffd4bc45820ddb320ab224685b9ba3fb: " \ + "Removed binaries" in client.out client.run(f"list *:* {remote}") assert "zli/1.0.0" in client.out assert "zlib/1.0.0@user/channel" in client.out