Repository: conan-io/conan
Pull request #13173 (2023-02-20 13:13:58)

possible fix for conan search pkg/*@
Changelog: Bugfix: possible fix for ``conan search pkg/*@`` and ``conan upload pkg/*@``, to filter packages wo user/channel
Docs: Omit

Close https://github.com/conan-io/conan/issues/13170





Patch:
diff --git a/conans/client/cache/cache.py b/conans/client/cache/cache.py
index b827edb16..156580103 100644
--- a/conans/client/cache/cache.py
+++ b/conans/client/cache/cache.py
@@ -5,6 +5,7 @@ from collections import OrderedDict
 
 from jinja2 import Environment, select_autoescape, FileSystemLoader, ChoiceLoader, Template
 
+from conan import conan_version
 from conans.assets.templates import dict_loader
 from conans.client.cache.editable import EditablePackages
 from conans.client.cache.remote_registry import RemoteRegistry
@@ -173,7 +174,8 @@ class ClientCache(object):
                 distro = None
                 if platform.system() in ["Linux", "FreeBSD"]:
                     import distro
-                content = Template(text).render({"platform": platform, "os": os, "distro": distro})
+                content = Template(text).render({"platform": platform, "os": os, "distro": distro,
+                                                 "conan_version": conan_version})
                 self._new_config.loads(content)
         return self._new_config
 
diff --git a/conans/client/profile_loader.py b/conans/client/profile_loader.py
index b484f68af..669df4f1e 100644
--- a/conans/client/profile_loader.py
+++ b/conans/client/profile_loader.py
@@ -4,6 +4,7 @@ from collections import OrderedDict, defaultdict
 
 from jinja2 import Environment, FileSystemLoader
 
+from conan import conan_version
 from conan.tools.env.environment import ProfileEnvironment
 from conans.errors import ConanException, ConanV2Exception
 from conans.model.conf import ConfDefinition
@@ -124,7 +125,8 @@ def read_profile(profile_name, cwd, default_folder):
         base_path = os.path.dirname(profile_path)
         context = {"platform": platform,
                    "os": os,
-                   "profile_dir": base_path}
+                   "profile_dir": base_path,
+                   "conan_version": conan_version}
         rtemplate = Environment(loader=FileSystemLoader(base_path)).from_string(text)
         text = rtemplate.render(context)
 
diff --git a/conans/client/rest/rest_client_v1.py b/conans/client/rest/rest_client_v1.py
index 03ac6e1a1..b284a3e93 100644
--- a/conans/client/rest/rest_client_v1.py
+++ b/conans/client/rest/rest_client_v1.py
@@ -210,6 +210,8 @@ class RestV1Methods(RestCommonMethods):
     def get_recipe(self, ref, dest_folder):
         urls = self._get_recipe_urls(ref)
         urls.pop(EXPORT_SOURCES_TGZ_NAME, None)
+        accepted_files = ["conanfile.py", "conan_export.tgz", "conanmanifest.txt"]
+        urls = {f: url for f, url in urls.items() if any(f.startswith(m) for m in accepted_files)}
         check_compressed_files(EXPORT_TGZ_NAME, urls)
         md5s = self.get_recipe_snapshot(ref) if self._config.download_cache else None
         zipped_files = self._download_files_to_folder(urls, dest_folder, md5s)
@@ -234,6 +236,8 @@ class RestV1Methods(RestCommonMethods):
 
     def get_package(self, pref, dest_folder):
         urls = self._get_package_urls(pref)
+        accepted_files = ["conaninfo.txt", "conan_package.tgz", "conanmanifest.txt"]
+        urls = {f: url for f, url in urls.items() if any(f.startswith(m) for m in accepted_files)}
         check_compressed_files(PACKAGE_TGZ_NAME, urls)
         md5s = self.get_package_snapshot(pref) if self._config.download_cache else None
         zipped_files = self._download_files_to_folder(urls, dest_folder, md5s)
diff --git a/conans/client/rest/rest_client_v2.py b/conans/client/rest/rest_client_v2.py
index 7ee31663b..d17a4f4fe 100644
--- a/conans/client/rest/rest_client_v2.py
+++ b/conans/client/rest/rest_client_v2.py
@@ -89,8 +89,8 @@ class RestV2Methods(RestCommonMethods):
         data = self._get_file_list_json(url)
         files = data["files"]
         check_compressed_files(EXPORT_TGZ_NAME, files)
-        if EXPORT_SOURCES_TGZ_NAME in files:
-            files.remove(EXPORT_SOURCES_TGZ_NAME)
+        accepted_files = ["conanfile.py", "conan_export.tgz", "conanmanifest.txt"]
+        files = [f for f in files if any(f.startswith(m) for m in accepted_files)]
 
         # If we didn't indicated reference, server got the latest, use absolute now, it's safer
         urls = {fn: self.router.recipe_file(ref, fn) for fn in files}
@@ -122,6 +122,8 @@ class RestV2Methods(RestCommonMethods):
         url = self.router.package_snapshot(pref)
         data = self._get_file_list_json(url)
         files = data["files"]
+        accepted_files = ["conaninfo.txt", "conan_package.tgz", "conanmanifest.txt"]
+        files = [f for f in files if any(f.startswith(m) for m in accepted_files)]
         check_compressed_files(PACKAGE_TGZ_NAME, files)
         # If we didn't indicated reference, server got the latest, use absolute now, it's safer
         urls = {fn: self.router.package_file(pref, fn) for fn in files}
diff --git a/conans/model/ref.py b/conans/model/ref.py
index 8c58ee349..ae57366b0 100644
--- a/conans/model/ref.py
+++ b/conans/model/ref.py
@@ -79,7 +79,7 @@ def check_valid_ref(reference, strict_mode=True):
                 return False
             if "*" in reference:
                 ref = ConanFileReference.loads(reference, validate=True)
-                if "*" in ref.name or "*" in ref.user or "*" in ref.channel:
+                if "*" in ref.name or "*" in (ref.user or "") or "*" in (ref.channel or ""):
                     return False
                 if str(ref.version).startswith("["):  # It is a version range
                     return True
diff --git a/conans/search/search.py b/conans/search/search.py
index 71508ef7e..ecf2d6a31 100644
--- a/conans/search/search.py
+++ b/conans/search/search.py
@@ -94,13 +94,19 @@ def _evaluate(prop_name, prop_value, conan_vars_info):
 
 def search_recipes(cache, pattern=None, ignorecase=True):
     # Conan references in main storage
+    no_user_channel = False
     if pattern:
         if isinstance(pattern, ConanFileReference):
             pattern = repr(pattern)
+        if pattern.endswith("@"):  # packages without user/channel:
+            no_user_channel = True
+            pattern = pattern[:-1]
         pattern = translate(pattern)
         pattern = re.compile(pattern, re.IGNORECASE) if ignorecase else re.compile(pattern)
 
     refs = cache.all_refs()
+    if no_user_channel:
+        refs = [r for r in refs if r.user is None and r.channel is None]
     refs.extend(cache.editable_packages.edited_refs.keys())
     if pattern:
         refs = [r for r in refs if _partial_match(pattern, repr(r))]


Test patch:
diff --git a/conans/test/integration/command/search_test.py b/conans/test/integration/command/search_test.py
index 71e276fe8..4382e2ec3 100644
--- a/conans/test/integration/command/search_test.py
+++ b/conans/test/integration/command/search_test.py
@@ -1646,3 +1646,23 @@ def test_error_revision_search():
     c.run("config set general.revisions_enabled=1")
     c.run("search mozilla/1.0.0@ -r=default", assert_error=True)
     assert "ERROR: Recipe not found: 'mozilla/1.0.0'. [Remote: default]" in c.out
+
+
+def test_no_user_channel_error():
+    # https://github.com/conan-io/conan/issues/13170
+    c = TestClient(default_server_user=True)
+    c.save({"conanfile.py": GenConanfile("pkg")})
+    c.run("export . 1.0@")
+    c.run("export . 1.0@user/channel")
+    c.run("search *")
+    assert "pkg/1.0" in str(c.out).splitlines()
+    assert "pkg/1.0@user/channel" in c.out
+    c.run("search pkg/*@")
+    assert "pkg/1.0" in c.out
+    assert "user/channel" not in c.out
+
+    # The same underlying logic is used in upload
+    c.run("upload pkg/*@ -r=default -c")
+    assert "Uploading pkg/1.0 to remote 'default'" in c.out
+    assert "user/channel" not in c.out
+
diff --git a/conans/test/integration/configuration/conf/test_conf.py b/conans/test/integration/configuration/conf/test_conf.py
index 9310a156b..539eb6150 100644
--- a/conans/test/integration/configuration/conf/test_conf.py
+++ b/conans/test/integration/configuration/conf/test_conf.py
@@ -3,9 +3,9 @@ import platform
 import textwrap
 
 import pytest
-import six
 from mock import patch
 
+from conan import conan_version
 from conans.errors import ConanException
 from conans.util.files import save
 from conans.test.utils.tools import TestClient
@@ -180,10 +180,12 @@ def test_composition_conan_conf_different_data_types_by_cli_arg(client):
 def test_jinja_global_conf(client):
     save(client.cache.new_config_path, "user.mycompany:parallel = {{os.cpu_count()/2}}\n"
                                        "user.mycompany:other = {{platform.system()}}\n"
-                                       "user.mycompany:dist = {{distro.id() if distro else '42'}}\n")
+                                       "user.mycompany:dist = {{distro.id() if distro else '42'}}\n"
+                                       "user.conan:version = {{conan_version}}-{{conan_version>0.1}}")
     client.run("install .")
     assert "user.mycompany:parallel={}".format(os.cpu_count()/2) in client.out
     assert "user.mycompany:other={}".format(platform.system()) in client.out
+    assert f"user.conan:version={conan_version}-True" in client.out
     if platform.system() == "Linux":
         import distro
         assert "user.mycompany:dist={}".format(distro.id()) in client.out
diff --git a/conans/test/integration/configuration/test_profile_jinja.py b/conans/test/integration/configuration/test_profile_jinja.py
index 30aa9ee84..da1a72209 100644
--- a/conans/test/integration/configuration/test_profile_jinja.py
+++ b/conans/test/integration/configuration/test_profile_jinja.py
@@ -1,6 +1,7 @@
 import platform
 import textwrap
 
+from conan import conan_version
 from conans.client.tools import environment_append
 from conans.test.assets.genconanfile import GenConanfile
 from conans.test.utils.tools import TestClient
@@ -71,3 +72,19 @@ def test_profile_template_profile_dir():
                  "anysubfolder/toolchain.cmake": "MyToolchainCMake!!!"})
     client.run("install . -pr=anysubfolder/profile1.jinja")
     assert "conanfile.py: CONTENT: MyToolchainCMake!!!" in client.out
+
+
+def test_profile_version():
+    client = TestClient()
+    tpl1 = textwrap.dedent("""
+        [options]
+        *:myoption={{conan_version}}
+        *:myoption2={{conan_version<13 and conan_version>1.0}}
+        """)
+
+    client.save({"conanfile.py": GenConanfile(),
+                 "profile1.jinja": tpl1})
+    client.run("install . -pr=profile1.jinja")
+    assert f"*:myoption={conan_version}" in client.out
+    assert "*:myoption2=True" in client.out
+
diff --git a/conans/test/integration/export_sources_test.py b/conans/test/integration/export_sources_test.py
index 84982a63b..5f1e43aa9 100644
--- a/conans/test/integration/export_sources_test.py
+++ b/conans/test/integration/export_sources_test.py
@@ -143,7 +143,7 @@ class ExportsSourcesTest(unittest.TestCase):
         self.assertEqual(scan_folder(export_src_folder or self.export_sources_folder),
                          sorted(expected_src_exports))
 
-    def _check_export_installed_folder(self, mode, updated=False):
+    def _check_export_installed_folder(self, mode):
         """ Just installed, no EXPORT_SOURCES_DIR is present
         """
         if mode == "exports_sources":
@@ -156,8 +156,6 @@ class ExportsSourcesTest(unittest.TestCase):
             expected_exports = ['conanfile.py', 'conanmanifest.txt', "src/data.txt"]
         if mode == "overlap":
             expected_exports = ['conanfile.py', 'conanmanifest.txt', "src/data.txt", "src/hello.h"]
-        if updated:
-            expected_exports.append("license.txt")
 
         self.assertEqual(scan_folder(self.export_folder), sorted(expected_exports))
         self.assertFalse(os.path.exists(self.export_sources_folder))
@@ -376,15 +374,3 @@ class ExportsSourcesTest(unittest.TestCase):
         self.client.run("install Hello/0.1@lasote/testing --update")
         self.assertIn("Hello/0.1@lasote/testing: Already installed!", self.client.out)
         self._check_export_installed_folder(mode)
-
-        rev = self.server.server_store.get_last_revision(self.ref)
-        ref = self.ref.copy_with_rev(rev.revision)
-        server_path = self.server.server_store.export(ref)
-        save(os.path.join(server_path, "license.txt"), "mylicense")
-        manifest = FileTreeManifest.load(server_path)
-        manifest.time += 1
-        manifest.file_sums["license.txt"] = md5sum(os.path.join(server_path, "license.txt"))
-        manifest.save(server_path)
-
-        self.client.run("install Hello/0.1@lasote/testing --update")
-        self._check_export_installed_folder(mode, updated=True)
diff --git a/conans/test/integration/remote/rest_api_test.py b/conans/test/integration/remote/rest_api_test.py
index a37c6052a..3590c730a 100644
--- a/conans/test/integration/remote/rest_api_test.py
+++ b/conans/test/integration/remote/rest_api_test.py
@@ -135,7 +135,7 @@ class RestApiTest(unittest.TestCase):
         # Get the package
         tmp_dir = temp_folder()
         self.api.get_package(pref, tmp_dir)
-        self.assertIn("hello.cpp", os.listdir(tmp_dir))
+        self.assertNotIn("hello.cpp", os.listdir(tmp_dir))
 
     def test_get_package_info(self):
         # Upload a conans
diff --git a/conans/test/unittests/util/xz_test.py b/conans/test/unittests/util/xz_test.py
index c31b0bcff..683c05a0d 100644
--- a/conans/test/unittests/util/xz_test.py
+++ b/conans/test/unittests/util/xz_test.py
@@ -21,66 +21,6 @@ from conans.util.files import load, save_files
 class XZTest(TestCase):
     output = TestBufferConanOutput()
 
-    def test_error_xz(self):
-        server = TestServer()
-        ref = ConanFileReference.loads("Pkg/0.1@user/channel")
-        ref = ref.copy_with_rev(DEFAULT_REVISION_V1)
-        export = server.server_store.export(ref)
-        server.server_store.update_last_revision(ref)
-        save_files(export, {"conanfile.py": "#",
-                            "conanmanifest.txt": "#",
-                            "conan_export.txz": "#"})
-        client = TestClient(servers={"default": server},
-                            users={"default": [("lasote", "mypass")]})
-        client.run("install Pkg/0.1@user/channel", assert_error=True)
-        self.assertIn("ERROR: This Conan version is not prepared to handle "
-                      "'conan_export.txz' file format", client.out)
-
-    def test_error_sources_xz(self):
-        server = TestServer()
-        ref = ConanFileReference.loads("Pkg/0.1@user/channel")
-        ref = ref.copy_with_rev(DEFAULT_REVISION_V1)
-        client = TestClient(servers={"default": server},
-                            users={"default": [("lasote", "mypass")]})
-        server.server_store.update_last_revision(ref)
-        export = server.server_store.export(ref)
-        conanfile = """from conans import ConanFile
-class Pkg(ConanFile):
-    exports_sources = "*"
-"""
-        save_files(export, {"conanfile.py": conanfile,
-                            "conanmanifest.txt": "1",
-                            "conan_sources.txz": "#"})
-        client.run("install Pkg/0.1@user/channel --build", assert_error=True)
-        self.assertIn("ERROR: This Conan version is not prepared to handle "
-                      "'conan_sources.txz' file format", client.out)
-
-    def test_error_package_xz(self):
-        server = TestServer()
-        ref = ConanFileReference.loads("Pkg/0.1@user/channel")
-        ref = ref.copy_with_rev(DEFAULT_REVISION_V1)
-        client = TestClient(servers={"default": server},
-                            users={"default": [("lasote", "mypass")]})
-        server.server_store.update_last_revision(ref)
-        export = server.server_store.export(ref)  # *1 the path can't be known before upload a revision
-        conanfile = """from conans import ConanFile
-class Pkg(ConanFile):
-    exports_sources = "*"
-"""
-        save_files(export, {"conanfile.py": conanfile,
-                            "conanmanifest.txt": "1"})
-        pref = PackageReference(ref, NO_SETTINGS_PACKAGE_ID, DEFAULT_REVISION_V1)
-        server.server_store.update_last_package_revision(pref.copy_with_revs(DEFAULT_REVISION_V1,
-                                                                             DEFAULT_REVISION_V1))
-
-        package = server.server_store.package(pref)
-        save_files(package, {"conaninfo.txt": "#",
-                             "conanmanifest.txt": "1",
-                             "conan_package.txz": "#"})
-        client.run("install Pkg/0.1@user/channel", assert_error=True)
-        self.assertIn("ERROR: This Conan version is not prepared to handle "
-                      "'conan_package.txz' file format", client.out)
-
     @pytest.mark.skipif(not six.PY3, reason="only Py3")
     def test(self):
         tmp_dir = temp_folder()