Repository: conan-io/conan
Pull request: #5572 Retrieve Conan home directory
- Add `config home` command to retrieve Conan home dir
- Add functional tests to validate `config home`

Changelog: Feature: New `conan config home` command for getting Conan home directory
Docs: https://github.com/conan-io/docs/pull/1387

closes https://github.com/conan-io/conan/issues/5572

- [x] Refer to the issue that supports this Pull Request.
- [x] If the issue has missing info, explain the purpose/use case/pain/need that covers this Pull Request.
- [x] I've read the [Contributing guide](https://github.com/conan-io/conan/blob/develop/.github/CONTRIBUTING.md).
- [x] I've followed the PEP8 style guides for Python code.
- [x] I've opened another PR in the Conan docs repo to the ``develop`` branch, documenting this one. 

<sup>**Note:** By default this PR will skip the slower tests and will use a limited set of python versions. Check [here](https://github.com/conan-io/conan/blob/develop/.github/PR_INCREASE_TESTING.md) how to increase the testing level by writing some tags in the current PR body text.</sup>


Problem statement:
Add a command to retrieve the Conan home directory
A command to know where the `conan.conf` is (we call it Conan home) which is evaluated using the `CONAN_USER_HOME` env variable or defaulted to `~/.conan` would be useful.

Proposals:
 * `conan config home`
 * `conan home`
 * `...`

Hints from issue discussion:
Amazing, I would say this feature is relevant, for many times I saw users asking it on Slack, "where is conan home directory?".

When someone wants to know where is Conan home dir you need to guess: look for env vars CONAN_USER_HOME and CONAN_USER_HOME_SHORT, or try ~/.conan or %USERPROFILE%/.conan

I would suggest `conan config get home`

Conan config is about global configurations for Conan, not the user. As `config` requires an action, `get` is the plausible sub-command. `home` is what we want, but there is no `home` property in conan.conf, which makes it as an special keyword.

At same time that I see as a possible solution, I also see a confusion, mixing Conan config file with reserved keywords. Maybe a new sub-command could fix this problem, but creating a new sub-command (e.g. global) only to solve a reserved keyword is excessive I think.
The small problem I see with ``conan config get home`` is that you cannot do ``conan config set home``, so it is not symmetric.
I didn't think about it, good catch. Even if `conan config set home` was able to set CONAN_USER_HOME, it sounds wrong.

What about a new config sub-command? 
- `conan config system home`
- `conan config global home`
- `conan config info home`

Or maybe follow Javier's suggestion: `conan home`

I think `conan config` is better for updates, if we need to add more new properties in the future, but I can't think about any other right now, only home is global, any other can be retrieved by conan.conf or env var
I think we should go with ``conan config home``, until we have further evidence that a new subcommand like ``system`` or ``global`` make sense, I cannot think of anything that is not in the conan.conf, as the only thing that is necessary to define the location of conan.conf is the HOME.

Commit message: #5572 Retrieve Conan home directory

- Add `config home` command to retrieve Conan home dir
- Add functional tests to validate `config home`

Signed-off-by: Uilian Ries <uilianries@gmail.com>

Patch to review:
diff --git a/conans/client/command.py b/conans/client/command.py
index 0892380a71d..5b7b9177222 100644
--- a/conans/client/command.py
+++ b/conans/client/command.py
@@ -508,11 +508,13 @@ def config(self, *args):
         subparsers = parser.add_subparsers(dest='subcommand', help='sub-command help')
         subparsers.required = True
 
-        rm_subparser = subparsers.add_parser('rm', help='Remove an existing config element')
-        set_subparser = subparsers.add_parser('set', help='Set a value for a configuration item')
         get_subparser = subparsers.add_parser('get', help='Get the value of configuration item')
-        install_subparser = subparsers.add_parser('install', help='install a full configuration '
+        subparsers.add_parser('home', help='Retrieve the Conan home directory')
+        install_subparser = subparsers.add_parser('install', help='Install a full configuration '
                                                                   'from a local or remote zip file')
+        rm_subparser = subparsers.add_parser('rm', help='Remove an existing config element')
+        set_subparser = subparsers.add_parser('set', help='Set a value for a configuration item')
+
         rm_subparser.add_argument("item", help="Item to remove")
         get_subparser.add_argument("item", nargs="?", help="Item to print")
         set_subparser.add_argument("item", help="'item=value' to set")
@@ -547,6 +549,8 @@ def config(self, *args):
             return self._conan.config_get(args.item)
         elif args.subcommand == "rm":
             return self._conan.config_rm(args.item)
+        elif args.subcommand == "home":
+            return self._conan.config_home()
         elif args.subcommand == "install":
             verify_ssl = get_bool_from_text(args.verify_ssl)
             return self._conan.config_install(args.item, verify_ssl, args.type, args.args,
diff --git a/conans/client/conan_api.py b/conans/client/conan_api.py
index 87d123d43db..10da0a9a479 100644
--- a/conans/client/conan_api.py
+++ b/conans/client/conan_api.py
@@ -601,6 +601,11 @@ def config_install(self, path_or_url, verify_ssl, config_type=None, args=None,
                                      args=args,
                                      source_folder=source_folder, target_folder=target_folder)
 
+    @api_method
+    def config_home(self):
+        self.app.out.info(self.cache_folder)
+        return self.cache_folder
+
     def _info_args(self, reference_or_path, install_folder, profile_names, settings, options, env,
                    lockfile=None):
         cwd = get_cwd()
diff --git a/conans/test/functional/command/config_test.py b/conans/test/functional/command/config_test.py
index 34712041329..17b85cb1997 100644
--- a/conans/test/functional/command/config_test.py
+++ b/conans/test/functional/command/config_test.py
@@ -3,6 +3,8 @@
 
 from conans.test.utils.tools import TestClient
 from conans.util.files import load
+from conans.test.utils.test_files import temp_folder
+from conans.client.tools import environment_append
 
 
 class ConfigTest(unittest.TestCase):
@@ -90,3 +92,21 @@ def remove_envvar_test(self):
     def missing_subarguments_test(self):
         self.client.run("config", assert_error=True)
         self.assertIn("ERROR: Exiting with code: 2", self.client.out)
+
+    def test_config_home_default(self):
+        self.client.run("config home")
+        self.assertIn(self.client.cache.cache_folder, self.client.out)
+
+    def test_config_home_custom_home_dir(self):
+        cache_folder = os.path.join(temp_folder(), "custom")
+        with environment_append({"CONAN_USER_HOME": cache_folder}):
+            client = TestClient(cache_folder=cache_folder)
+            client.run("config home")
+            self.assertIn(cache_folder, client.out)
+
+    def test_config_home_short_home_dir(self):
+        cache_folder = os.path.join(temp_folder(), "custom")
+        with environment_append({"CONAN_USER_HOME_SHORT": cache_folder}):
+            client = TestClient(cache_folder=cache_folder)
+            client.run("config home")
+            self.assertIn(cache_folder, client.out)


Code review comments:
diff_hunk: @@ -601,6 +601,11 @@ def config_install(self, path_or_url, verify_ssl, config_type=None, args=None,
                                      args=args,
                                      source_folder=source_folder, target_folder=target_folder)
 
+    @api_method
+    def config_home(self):
+        self.app.out.info(self.cache_folder)
path: conans/client/conan_api.py
text: @user1:
Actually it is more correct that the api returns the value, and the Command class is the one printing the output.

@author:
so we need to revisit `config get`, because it follows the same steps.

@author:
indeed I agree with you, but should I change `config get` ?

@user1:
No, you can leave it, there will be also many other things that would need to be fixed exactly the same. Just wanted to say it to outline future direction of the conan_api.

@author:
okay, I gonna update config home only then

@author:
done