Repository: conan-io/conan

Issue:
[bug] conan download pkg/version@user/channel#randomrev downloads latest
Latest 1.20, Windows 10, python 3.7

With revisions disable this doesn't fail (it should). And worse, it creates an entry with revision ``randomrev`` in the cache.

Also, it seems that it is not possible to ``conan download`` a package with a given package revision.


Discussion:
It is important to follow up here, this could be problematic or show underlying problems, for example if using lockfiles, like the locked version is not the one being retrieved.

Resolving patch:
diff --git a/conans/client/command.py b/conans/client/command.py
--- a/conans/client/command.py
+++ b/conans/client/command.py
@@ -403,8 +403,12 @@ def download(self, *args):
         else:
             reference = repr(pref.ref)
             if pref.ref.user is None:
-                reference += "@"
-            packages_list = [pref.id]
+                if pref.ref.revision:
+                    reference = "%s/%s@#%s" % (pref.ref.name, pref.ref.version, pref.ref.revision)
+                else:
+                    reference += "@"
+            pkgref = "{}#{}".format(pref.id, pref.revision) if pref.revision else pref.id
+            packages_list = [pkgref]
             if args.package:
                 raise ConanException("Use a full package reference (preferred) or the `--package`"
                                      " command argument, but not both.")
diff --git a/conans/client/conan_api.py b/conans/client/conan_api.py
--- a/conans/client/conan_api.py
+++ b/conans/client/conan_api.py
@@ -430,6 +430,9 @@ def download(self, reference, remote_name=None, packages=None, recipe=False):
         # Install packages without settings (fixed ids or all)
         if check_valid_ref(reference):
             ref = ConanFileReference.loads(reference)
+            if ref.revision and not self.app.config.revisions_enabled:
+                raise ConanException("Revisions not enabled in the client, specify a "
+                                     "reference without revision")
             if packages and ref.revision is None:
                 for package_id in packages:
                     if "#" in package_id:


Test patch:
diff --git a/conans/test/functional/command/download_test.py b/conans/test/functional/command/download_test.py
--- a/conans/test/functional/command/download_test.py
+++ b/conans/test/functional/command/download_test.py
@@ -5,6 +5,7 @@
 from conans.model.ref import ConanFileReference
 from conans.test.utils.tools import (TestClient, TestServer, NO_SETTINGS_PACKAGE_ID, TurboTestClient,
                                      GenConanfile)
+from conans.util.env_reader import get_env
 from conans.util.files import load
 
 
@@ -220,3 +221,75 @@ def no_user_channel_test(self):
         client.run("download pkg/1.0@")
         self.assertIn("pkg/1.0: Downloading pkg/1.0:%s" % NO_SETTINGS_PACKAGE_ID, client.out)
         self.assertIn("pkg/1.0: Package installed %s" % NO_SETTINGS_PACKAGE_ID, client.out)
+
+    @unittest.skipIf(get_env("TESTING_REVISIONS_ENABLED", False), "No sense with revs")
+    def download_revs_disabled_with_rrev_test(self):
+        # https://github.com/conan-io/conan/issues/6106
+        client = TestClient(revisions_enabled=False)
+        client.run("download pkg/1.0@user/channel#fakerevision", assert_error=True)
+        self.assertIn(
+            "ERROR: Revisions not enabled in the client, specify a reference without revision",
+            client.out)
+
+    @unittest.skipUnless(get_env("TESTING_REVISIONS_ENABLED", False), "Only revisions")
+    def download_revs_enabled_with_fake_rrev_test(self):
+        client = TestClient(default_server_user=True, revisions_enabled=True)
+        client.save({"conanfile.py": GenConanfile()})
+        client.run("create . pkg/1.0@user/channel")
+        client.run("upload * --all --confirm")
+        client.run("remove * -f")
+        client.run("download pkg/1.0@user/channel#fakerevision", assert_error=True)
+        self.assertIn("ERROR: Recipe not found: 'pkg/1.0@user/channel'", client.out)
+
+    @unittest.skipUnless(get_env("TESTING_REVISIONS_ENABLED", False), "Only revisions")
+    def download_revs_enabled_with_rrev_test(self):
+        ref = ConanFileReference.loads("pkg/1.0@user/channel")
+        client = TurboTestClient(default_server_user=True, revisions_enabled=True)
+        pref = client.create(ref, conanfile=GenConanfile())
+        client.run("upload pkg/1.0@user/channel --all --confirm")
+        # create new revision from recipe
+        client.create(ref, conanfile=GenConanfile().with_build_msg("new revision"))
+        client.run("upload pkg/1.0@user/channel --all --confirm")
+        client.run("remove * -f")
+        client.run("download pkg/1.0@user/channel#{}".format(pref.ref.revision))
+        self.assertIn("pkg/1.0@user/channel: Package installed {}".format(pref.id), client.out)
+        search_result = client.search("pkg/1.0@user/channel --revisions")[0]
+        self.assertIn(pref.ref.revision, search_result["revision"])
+
+    @unittest.skipUnless(get_env("TESTING_REVISIONS_ENABLED", False), "Only revisions")
+    def download_revs_enabled_with_rrev_no_user_channel_test(self):
+        ref = ConanFileReference.loads("pkg/1.0@")
+        servers = {"default": TestServer([("*/*@*/*", "*")], [("*/*@*/*", "*")],
+                                         users={"user": "password"})}
+        client = TurboTestClient(servers=servers, revisions_enabled=True,
+                                 users={"default": [("user", "password")]})
+        pref = client.create(ref, conanfile=GenConanfile())
+        client.run("upload pkg/1.0@ --all --confirm")
+        # create new revision from recipe
+        client.create(ref, conanfile=GenConanfile().with_build_msg("new revision"))
+        client.run("upload pkg/1.0@ --all --confirm")
+        client.run("remove * -f")
+        client.run("download pkg/1.0@#{}".format(pref.ref.revision))
+        self.assertIn("pkg/1.0: Package installed {}".format(pref.id), client.out)
+        search_result = client.search("pkg/1.0@ --revisions")[0]
+        self.assertIn(pref.ref.revision, search_result["revision"])
+
+    @unittest.skipUnless(get_env("TESTING_REVISIONS_ENABLED", False), "Only revisions")
+    def download_revs_enabled_with_prev_test(self):
+        # https://github.com/conan-io/conan/issues/6106
+        ref = ConanFileReference.loads("pkg/1.0@user/channel")
+        client = TurboTestClient(default_server_user=True, revisions_enabled=True)
+        pref = client.create(ref, conanfile=GenConanfile())
+        client.run("upload pkg/1.0@user/channel --all --confirm")
+        client.create(ref, conanfile=GenConanfile().with_build_msg("new revision"))
+        client.run("upload pkg/1.0@user/channel --all --confirm")
+        client.run("remove * -f")
+        client.run("download pkg/1.0@user/channel#{}:{}#{}".format(pref.ref.revision,
+                                                                   pref.id,
+                                                                   pref.revision))
+        self.assertIn("pkg/1.0@user/channel: Package installed {}".format(pref.id), client.out)
+        search_result = client.search("pkg/1.0@user/channel --revisions")[0]
+        self.assertIn(pref.ref.revision, search_result["revision"])
+        search_result = client.search(
+            "pkg/1.0@user/channel#{}:{} --revisions".format(pref.ref.revision, pref.id))[0]
+        self.assertIn(pref.revision, search_result["revision"])
diff --git a/conans/test/utils/tools.py b/conans/test/utils/tools.py
--- a/conans/test/utils/tools.py
+++ b/conans/test/utils/tools.py
@@ -1236,7 +1236,8 @@ def export(self, ref, conanfile=GenConanfile(), args=None, assert_error=False):
     def create(self, ref, conanfile=GenConanfile(), args=None, assert_error=False):
         if conanfile:
             self.save({"conanfile.py": conanfile})
-        self.run("create . {} {} --json {}".format(ref.full_str(),
+        full_str = "{}@".format(ref.full_str()) if not ref.user else ref.full_str()
+        self.run("create . {} {} --json {}".format(full_str,
                                                    args or "", self.tmp_json_name),
                  assert_error=assert_error)
         rrev = self.cache.package_layout(ref).recipe_revision()