Repository: conan-io/conan
Pull request #17877 (2025-03-02 20:29:39)

fix user conflicts with version ranges
Changelog: Bugfix: Solve conflict not raised when version-ranges have different user
Docs: Omit

Close https://github.com/conan-io/conan/issues/17875


memsharded: CI failure is unrelated, will be fixed once ``develop2`` is fixed

Patch:
diff --git a/conans/client/graph/graph_builder.py b/conans/client/graph/graph_builder.py
index 51ac37afa..af016a0e8 100644
--- a/conans/client/graph/graph_builder.py
+++ b/conans/client/graph/graph_builder.py
@@ -137,7 +137,9 @@ class DepsGraphBuilder(object):
         version_range = require.version_range
         prev_version_range = prev_require.version_range if prev_node is None else None
         if version_range:
-            # TODO: Check user/channel conflicts first
+            if require.ref.user != prev_require.ref.user or \
+                    require.ref.channel != prev_require.ref.channel:
+                raise GraphConflictError(node, require, prev_node, prev_require, base_previous)
             if prev_version_range is not None:
                 # It it is not conflicting, but range can be incompatible, restrict range
                 restricted_version_range = version_range.intersection(prev_version_range)
@@ -149,9 +151,10 @@ class DepsGraphBuilder(object):
                     require.ref = prev_ref
                 else:
                     raise GraphConflictError(node, require, prev_node, prev_require, base_previous)
-
         elif prev_version_range is not None:
-            # TODO: Check user/channel conflicts first
+            if require.ref.user != prev_require.ref.user or \
+                    require.ref.channel != prev_require.ref.channel:
+                raise GraphConflictError(node, require, prev_node, prev_require, base_previous)
             if not prev_version_range.contains(require.ref.version, resolve_prereleases):
                 raise GraphConflictError(node, require, prev_node, prev_require, base_previous)
         else:


Test patch:
diff --git a/test/integration/graph/conflict_diamond_test.py b/test/integration/graph/conflict_diamond_test.py
index f0383157c..6f24d54e9 100644
--- a/test/integration/graph/conflict_diamond_test.py
+++ b/test/integration/graph/conflict_diamond_test.py
@@ -1,3 +1,5 @@
+import pytest
+
 from conan.test.assets.genconanfile import GenConanfile
 from conan.test.utils.tools import TestClient
 
@@ -68,3 +70,33 @@ class TestConflictDiamondTest:
         c.run("install --requires=engine/1.0  --requires=ai/1.0", assert_error=True)
         assert "Conflict between math/1.0.1 and math/1.0 in the graph"
         assert "Conflict originates from ai/1.0"
+
+
+@pytest.mark.parametrize("version_range", [True, False])
+def test_conflict_user(version_range):
+    # https://github.com/conan-io/conan/issues/17875
+    v = "[^1.0]" if version_range else "1.0"
+    c = TestClient(light=True)
+    c.save({"dep/conanfile.py": GenConanfile("dep", "1.0"),
+            "pkg/conanfile.py": GenConanfile("pkg", "1.0").with_requires(f"dep/{v}@user1"),
+            "app/conanfile.py": GenConanfile("app", "1.0").with_requires(f"pkg/{v}@user1",
+                                                                         f"dep/{v}@user2")})
+    c.run("create dep --user=user1")
+    c.run("create dep --user=user2")
+    c.run("create pkg --user=user1")
+    c.run("install app", assert_error=True)
+    assert f"Version conflict: Conflict between dep/{v}@user1 and dep/{v}@user2" in c.out
+
+
+def test_conflict_user_order():
+    # https://github.com/conan-io/conan/issues/17875
+    c = TestClient(light=True)
+    c.save({"dep/conanfile.py": GenConanfile("dep", "1.0"),
+            "pkg/conanfile.py": GenConanfile("pkg", "1.0").with_requires("dep/1.0@user1"),
+            "app/conanfile.py": GenConanfile("app", "1.0").with_requires("pkg/1.0@user1",
+                                                                         "dep/[>=1.0]@user2")})
+    c.run("create dep --user=user1")
+    c.run("create dep --user=user2")
+    c.run("create pkg --user=user1")
+    c.run("install app", assert_error=True)
+    assert "ERROR: Version conflict: Conflict between dep/1.0@user1 and dep/[>=1.0]@user2" in c.out