Repository: conan-io/conan
Pull request: To check what to do with version build metadata and prereleases
Changelog: Bugfix: Fix version precendence for metadata version ranges.
Docs: Omit

I suspect more things are still needed


Problem statement:
[bug] semver fails to resolve metadata field correctly
### Environment details

* Operating System+version: Ubuntu 22.04
* Compiler+version: N/A
* Conan version: 2.0.17
* Python version: 3.10.8


### Steps to reproduce

1. Have a package with a version number followed by a "metadata (i.e. preceded by +)" field containing a number. For example: ```1.0.0+2```. NOTE: There cannot be a "larger" version available, such as ```1.0.0+3``` if you want to reproduce this.
2. in a conanfile, require ```>=``` this version. For example: ```self.requires("package/[>=1.0.0+2]")```
3. Try to resolve dependencies. Conan will fail with: ```ERROR: Package 'package/[>=1.0.0+2]' not resolved: Version range '>=1.0.0+2' from requirement 'package/[>=1.0.0+2]' [etc, etc...] ```

I can verify that this should not be the case by running:
```
>>> from conans.model.version import Version
>>> Version("1.0.0+2") >= Version("1.0.0+2")
True
>>> Version("1.0.0+2") >= Version("1.0.0+3")
False
```

Note also that if I set the metadata number to one LESS than the exisiting version, it will be able to find it! If I set ```self.requires("package/[>=1.0.0+1]")``` it will be able to find version ```1.0.0+2```.

This seems like a bug to me?

FYI: we use this field to hold  "build number" in case we run another build without changing the version. It increments after each build. It's useful to us to be able to specify that we should not use a build which is older than some build number.

### Logs

_No response_

Hints from issue discussion:

Commit message: To think about what to do with build metadata and rpereleases

Patch to review:
diff --git a/conans/model/version_range.py b/conans/model/version_range.py
index 4360758a109..f12d6a80a8a 100644
--- a/conans/model/version_range.py
+++ b/conans/model/version_range.py
@@ -12,7 +12,7 @@ def __init__(self, operator, version):
         self.display_version = version
 
         value = str(version)
-        if (operator == ">=" or operator == "<") and "-" not in value:
+        if (operator == ">=" or operator == "<") and "-" not in value and not version.build:
             value += "-"
         self.version = Version(value)
 
diff --git a/conans/test/unittests/model/version/test_version_range.py b/conans/test/unittests/model/version/test_version_range.py
index 3d45847a245..c4cd6402325 100644
--- a/conans/test/unittests/model/version/test_version_range.py
+++ b/conans/test/unittests/model/version/test_version_range.py
@@ -37,7 +37,9 @@
     ['~1.1.2',  [[['>=', '1.1.2-'], ['<', '1.2.0-']]], ["1.1.3"],  ["1.1.3-alpha1", "1.2.0-alpha1"]],
     ['>=2.0-pre.0 <3', [[['>=', '2.0-pre.0'], ['<', '3-']]], ["2.1"], ["2.0-pre.1","2.0-alpha.1"]],
     # Or explicitly
-    ['>=2.0-pre.0, include_prerelease', [[['>=', '2.0-pre.0']]], ["2.1", "2.0-pre.1"], ["1.5"]]
+    ['>=2.0-pre.0, include_prerelease', [[['>=', '2.0-pre.0']]], ["2.1", "2.0-pre.1"], ["1.5"]],
+    # Build metadata
+    ['>=1.0.0+2', [[['>=', '1.0.0+2']]], ["1.0.0+2", "1.0.0+3"], ["1.0.0+1"]],
 ]
 
 


Code review comments:
diff_hunk: @@ -37,7 +37,9 @@
     ['~1.1.2',  [[['>=', '1.1.2-'], ['<', '1.2.0-']]], ["1.1.3"],  ["1.1.3-alpha1", "1.2.0-alpha1"]],
     ['>=2.0-pre.0 <3', [[['>=', '2.0-pre.0'], ['<', '3-']]], ["2.1"], ["2.0-pre.1","2.0-alpha.1"]],
     # Or explicitly
-    ['>=2.0-pre.0, include_prerelease', [[['>=', '2.0-pre.0']]], ["2.1", "2.0-pre.1"], ["1.5"]]
+    ['>=2.0-pre.0, include_prerelease', [[['>=', '2.0-pre.0']]], ["2.1", "2.0-pre.1"], ["1.5"]],
+    # Build metadata
+    ['>=1.0.0+2', [[['>=', '1.0.0+2']]], ["1.0.0+2", "1.0.0+3"], ["1.0.0+1"]],
path: conans/test/unittests/model/version/test_version_range.py
text: @user1:
We need to add cases of pre-releases for this. Is ``1.0.1-pre.1`` valid if prereleases=True?
What if the condition is ``>=1.0.0-pre.1+2``? Just rough examples, just the idea, not exact cases.

diff_hunk: @@ -12,7 +12,7 @@ def __init__(self, operator, version):
         self.display_version = version
 
         value = str(version)
-        if (operator == ">=" or operator == "<") and "-" not in value:
+        if (operator == ">=" or operator == "<") and "-" not in value and not version.build:
path: conans/model/version_range.py
text: @user1:
Might be that the full solution is to insert ``-``, so ``1.0.0+b.2`` becomes ``1.0.0-+b.2`` (empty prerelease). Just a possibility.

@author:
Let's talk about this some time next week when we have a tad of free time :)