Repository: conan-io/conan
Pull request: Fix user/channel when searching patterns in a local-recipes-index
Changelog: Fix: Fix user/channel when searching patterns in a local-recipes-index.
Docs: Omit

Some minor edits to the code still need to be made, but I think this is a better approach than the current one

Closes https://github.com/conan-io/conan/issues/17403

Problem statement:
[question] Install latest version (or version range) from local recipes index
### What is your question?

In my CI, I use a `local-recipes-index` remote, from which I want to build the latest version of my packages. If I have a "normal" remote, I can do that with
```
$ conan install --requires pkg/[] -b missing
```
However, that doesn't seem to work with a local index:
```
$ conan list -r local-recipes *
Found 1 pkg/version recipes matching * in local-detectors
local-recipes
  attribute-processing-api
    attribute-processing-api/2.3.1@user/stable
$ conan install --requires attribute-processing-api/[]@user/stable -r local-recipes
[...]
======== Computing dependency graph ========
Graph root
    cli
ERROR: Package 'attribute-processing-api/[]@user/stable' not resolved: Version range '' from requirement 'attribute-processing-api/[]@user/stable' required by 'None' could not be resolved.
```

It seems, version ranges in general are not supported for local recipe indexes, even though `conan list` can determine all the versions in the remote.

Is this expected behavior? If yes, how would I go and build all the latest versions of my packages in that case. I don't really want to specify the explicit version in multiple places as that would be quite error-prone/easy to forget.





### Have you read the CONTRIBUTING guide?

- [X] I've read the CONTRIBUTING guide

Hints from issue discussion:
@AbrilRBS I just saw your PR and just wanted to clarify that the issue has nothing to do with an "empty" version range. If I use `[*]` or `[>2]` (or another version range), the behavior is the same.
Hi @fschoenm thanks for your question.

A few things to note here, as locally I'm not able to reproduce with a similar case in Conan 2.9. I've tried a very similar setup to yours locally, and I can't get this error to pop up

- An empty version range works as a deprecated feature, ideally you'd use `[*]`.
- You're running `--requires attribute-processing-api/[]@user/stable`, but in the reduced logs I see `attribute-processing-api/[]@trs/stable`, a different user/channel. What's your whole dependency graph like?

@AbrilRBS 

> You're running --requires attribute-processing-api/[]@user/stable, but in the reduced logs I see attribute-processing-api/[]@trs/stable, a different user/channel. What's your whole dependency graph like?

Sorry, that was a copy & paste mistake. The user/channel is the same everywhere. There also are no other dependencies.

Are you saying, you can install version ranges from a local recipe index? Have you tried packages with a user/channel? Because that seems to be what breaks it for me. If I try with a local conan-center-index clone, it works but as soon as I add user/channel to a recipe, it fails:

```
$ conan list -r local-cci zstd*
Found 4 pkg/version recipes matching zstd* in local-cci
local-cci
  zstd
    zstd/1.5.0@oss/stable
    zstd/1.5.2@oss/stable
    zstd/1.5.5@oss/stable
    zstd/1.5.6@oss/stable
$ conan install --requires 'zstd/[*]@oss/stable' -r local-cci --envs-generation false
[...]
======== Computing dependency graph ========
Graph root
    cli
ERROR: Package 'zstd/[*]@oss/stable' not resolved: Version range '*' from requirement 'zstd/[*]@oss/stable' required by 'None' could not be resolved.
```
Yes, I see - Trying it now again I'm in fact able to reproduce this with an empty conan cache, thanks for the report :)

Commit message: Try to ignore user/channel when searching patterns in a local recipe index

Patch to review:
diff --git a/conans/client/rest_client_local_recipe_index.py b/conans/client/rest_client_local_recipe_index.py
index 8615781bdfe..54ca55a6bb9 100644
--- a/conans/client/rest_client_local_recipe_index.py
+++ b/conans/client/rest_client_local_recipe_index.py
@@ -213,6 +213,14 @@ def get_recipes_references(self, pattern):
                 raise ConanException(f"Corrupted repo, folder {r} without 'config.yml'")
             versions = config_yml["versions"]
             for v in versions:
+                try:
+                    pattern_ref = RecipeReference.loads(pattern)
+                    # We don't care about user/channel, _search_remote_recipes is checking for those at a later point,
+                    # just add all candidates for now
+                    pattern = f"{pattern_ref.name}/{pattern_ref.version}"
+                except:
+                    # pattern = pattern
+                    pass
                 # TODO: Check the search pattern is the same as remotes and cache
                 ref = f"{r}/{v}"
                 if not fnmatch(ref, pattern):
diff --git a/test/integration/remote/test_local_recipes_index.py b/test/integration/remote/test_local_recipes_index.py
index 31103c0145b..0b2e97392f4 100644
--- a/test/integration/remote/test_local_recipes_index.py
+++ b/test/integration/remote/test_local_recipes_index.py
@@ -360,3 +360,32 @@ def test_install(self, c3i_pyrequires_folder):
         c.run("install --requires=pkg/1.0 --build missing -vvv")
         assert "pyreq/1.0#a0d63ca853edefa33582a24a1bb3c75f - Downloaded (local)" in c.out
         assert "pkg/1.0: Created package" in c.out
+
+class TestUserChannel:
+    @pytest.fixture(scope="class")
+    def c3i_user_channel_folder(self):
+        folder = temp_folder()
+        recipes_folder = os.path.join(folder, "recipes")
+        config = textwrap.dedent("""
+                versions:
+                  "1.0":
+                    folder: all
+                  "2.0":
+                    folder: other
+                """)
+        pkg = str(GenConanfile("pkg").with_class_attribute("user='myuser'")\
+                                    .with_class_attribute("channel='mychannel'"))
+        save_files(recipes_folder,
+                   {"pkg/config.yml": config,
+                    "pkg/all/conanfile.py": pkg,
+                    "pkg/other/conanfile.py": str(GenConanfile("pkg"))})
+        return folder
+
+    def test_user_channel_requirement(self, c3i_user_channel_folder):
+        tc = TestClient(light=True)
+        tc.run(f"remote add local '{c3i_user_channel_folder}'")
+        tc.run("graph info --requires=pkg/[*]@myuser/mychannel")
+        assert "Version range '*' from requirement 'pkg/[*]@myuser/mychannel'" not in tc.out
+        assert "pkg/1.0@myuser/mychannel#0b23a5938afb0457079e41aac8991595 - Downloaded (local)" in tc.out
+
+    # TODO: Think of other cases this might be failing


Code review comments:
diff_hunk: @@ -213,6 +213,14 @@ def get_recipes_references(self, pattern):
                 raise ConanException(f"Corrupted repo, folder {r} without 'config.yml'")
             versions = config_yml["versions"]
             for v in versions:
+                try:
+                    pattern_ref = RecipeReference.loads(pattern)
+                    # We don't care about user/channel, _search_remote_recipes is checking for those at a later point,
+                    # just add all candidates for now
+                    pattern = f"{pattern_ref.name}/{pattern_ref.version}"
+                except:
+                    # pattern = pattern
path: conans/client/rest_client_local_recipe_index.py
text: @author:
I dont think this can actually happen, pattern is already str of a RecipeReference afaik from https://github.com/conan-io/conan/blob/2.10.0/conans/client/graph/range_resolver.py#L55

@user1:
It is fine to exclude the user/channel for this initial check, but then it has to be re-checked again after the actual ``conanfile`` has been loaded and to match the real user/channel defined by recipe?

@author:
As far as I can tell, this is being done in `_search_remote_recipes` https://github.com/conan-io/conan/blob/develop2/conans/client/graph/range_resolver.py#L80 as it expects that the search has ignored the user/channel

@user1:
There it contains a:
```
# TODO: This is still necessary to filter user/channel, until search_recipes is fixed
```
So basically trying this to return the right thing and not rely on RangeResolved to fix it later?