Repository: conan-io/conan

Issue:
[feature] 'conan info' to return the package revision
Currently, the `conan info` command returns the recipe revision, but information about package revision is not available:

```json
{
        "revision": "a9d892fd37101b88578f17fbc04b9d60",
        "reference": "boost/1.70.0",
        "is_ref": true,
        "display_name": "boost/1.70.0",
        "id": "d45e2a7cc75163518ffbbda3fa886038ecaa8f54",
        "build_id": null,
        "remote": {
            "name": "conan-center",
            "url": "https://conan.bintray.com"
        },
        "url": "https://github.com/conan-io/conan-center-index",
        "homepage": "https://www.boost.org",
        "license": [
            "BSL-1.0"
        ],
        "description": "Boost provides free peer-reviewed portable C++ source libraries",
        "topics": [
            "conan",
            "boost",
            "libraries",
            "cpp"
        ],
        "recipe": "Cache",
        "binary": "Cache",
        "binary_remote": "conan-center",
        "creation_date": "2020-08-31 13:15:59",
        "requires": [
            "zlib/1.2.11",
            "bzip2/1.0.8"
        ]
    },
```

It should be easy to add and more convenient than running `conan search <ref>#<rrev>:<pid> --revisions` afterward.


Discussion:
I would say `-rev` and `--revisions`, because `conan search` offers the same.
I think it doesn't require an extra argument: if revisions are enabled that information would be in the JSON output, otherwise it won't
Ah yes, totally agree.
Hello @jgsogo I'll work on this issue :)
> Hello @jgsogo I'll work on this issue :)

Cool! Let us know if you have any doubts about how to implement it.

Thanks!
https://docs.conan.io/en/latest/versioning/revisions.html#how-it-works
As per this doc a package can have multiple package revisions (PREV) so I was thinking of adding an additional property `package_revisions`

```
{
....
"package_revisions": [
"4d220773f957e44a93a9a780f652e7c3",
"4e220773f957e44a93a9a780f652e7c3",
],
....
}
```

is that correct ?
`conan info` returns information for a single package, by default the one in the Conan cache. In the Conan cache there is only one package revision at a time (and only one recipe revision), so there is only one value for the `package_revision`.

The information is stored in the `metadata.json` file in the cache, you can use `self._cache.package_layout(ref).load_metadata()` to access that information. Then you have to store it in the `data` dictionary returned by method `_grab_info_data` in `CommandOutputer` (have a look at this method and how the `remote` and `revision` values are returned) and print it in the method `print_info` of `Printer` class.

These are not the best pieces of code, they contain a lot of legacy, so feel free to open a PR with a draft and we can help you with it.

Thanks!

Resolving patch:
diff --git a/conans/client/conan_command_output.py b/conans/client/conan_command_output.py
--- a/conans/client/conan_command_output.py
+++ b/conans/client/conan_command_output.py
@@ -120,6 +120,7 @@ def _grab_info_data(self, deps_graph, grab_paths):
         build_time_nodes = deps_graph.build_time_nodes()
         remotes = self._cache.registry.load_remotes()
         ret = []
+
         for (ref, package_id), list_nodes in compact_nodes.items():
             node = list_nodes[0]
             if node.recipe == RECIPE_VIRTUAL:
@@ -151,7 +152,8 @@ def _grab_info_data(self, deps_graph, grab_paths):
                 item_data["package_folder"] = package_layout.package(pref)
 
             try:
-                reg_remote = self._cache.package_layout(ref).load_metadata().recipe.remote
+                package_metadata = self._cache.package_layout(ref).load_metadata()
+                reg_remote = package_metadata.recipe.remote
                 reg_remote = remotes.get(reg_remote)
                 if reg_remote:
                     item_data["remote"] = {"name": reg_remote.name, "url": reg_remote.url}
@@ -177,8 +179,8 @@ def _add_if_exists(attrib, as_list=False):
             if isinstance(ref, ConanFileReference):
                 item_data["recipe"] = node.recipe
 
-                if get_env("CONAN_CLIENT_REVISIONS_ENABLED", False) and node.ref.revision:
-                    item_data["revision"] = node.ref.revision
+                item_data["revision"] = node.ref.revision
+                item_data["package_revision"] = node.prev
 
                 item_data["binary"] = node.binary
                 if node.binary_remote:
diff --git a/conans/client/printer.py b/conans/client/printer.py
--- a/conans/client/printer.py
+++ b/conans/client/printer.py
@@ -106,6 +106,7 @@ def _print(it_field, show_field=None, name=None, color=Color.BRIGHT_GREEN):
             _print("recipe", name="Recipe", color=None)
             if show_revisions:
                 _print("revision", name="Revision", color=None)
+                _print("package_revision", name="Package revision", color=None)
             _print("binary", name="Binary", color=None)
 
             if show("binary_remote") and is_ref:


Test patch:
diff --git a/conans/test/functional/command/info/info_test.py b/conans/test/functional/command/info/info_test.py
--- a/conans/test/functional/command/info/info_test.py
+++ b/conans/test/functional/command/info/info_test.py
@@ -9,7 +9,7 @@
 from conans.paths import CONANFILE
 from conans.test.utils.cpp_test_files import cpp_hello_conan_files
 from conans.test.utils.tools import TestClient, GenConanfile
-from conans.util.files import load, save
+from conans.util.files import save
 from conans import __version__ as client_version
 
 
@@ -342,8 +342,10 @@ def test_reuse(self):
                     Hello1/0.1@lasote/stable""")
 
         expected_output = expected_output % (
-                "\n    Revision: 63865a1afa3a2666b2f75cbc7745e8a4",
-                "\n    Revision: b2600f68000fa492234c0452214e0bbc") \
+                "\n    Revision: 63865a1afa3a2666b2f75cbc7745e8a4"
+                "\n    Package revision: None",
+                "\n    Revision: b2600f68000fa492234c0452214e0bbc"
+                "\n    Package revision: None",) \
             if self.client.cache.config.revisions_enabled else expected_output % ("", "")
 
         def clean_output(output):
@@ -416,14 +418,15 @@ def test_json_info_outputs(self):
         self._create("LibD", "0.1", ["LibB/0.1@lasote/stable", "LibC/0.1@lasote/stable"],
                      export=False)
 
-        json_file = os.path.join(self.client.current_folder, "output.json")
-        self.client.run("info . -u --json=\"{}\"".format(json_file))
+        self.client.run("info . -u --json=output.json")
 
         # Check a couple of values in the generated JSON
-        content = json.loads(load(json_file))
+        content = json.loads(self.client.load("output.json"))
         self.assertEqual(content[0]["reference"], "LibA/0.1@lasote/stable")
         self.assertEqual(content[0]["license"][0], "MIT")
         self.assertEqual(content[0]["description"], "blah")
+        self.assertEqual(content[0]["revision"], "22b1dc946e5566f5b2549e1b285d3fa7")
+        self.assertEqual(content[0]["package_revision"], None)
         self.assertEqual(content[1]["url"], "myurl")
         self.assertEqual(content[1]["required_by"][0], "conanfile.py (LibD/0.1)")