Repository: conan-io/conan

Issue:
[SCM] SCM feature + recipe in a repo subfolder leads to "same computer clone" bug
Getting back to our Conan recipe, conan `1.7.1` solves the problem described #3322 thus (re)allowing a recipe living in subfolder of the repository to use the `scm` feature.
Sadly, another bug, initially described in #3069 (under the name _same computer optimization problem_) is still present: when `conan create`ing the recipe, in this situation, the creation fails, complaining there are no files in the `build` folder (and indeed, there are none).

It seems the analysis made in #3069 is still relevant:
Basically, when calling `conan create`, an optimization described in the [tip box of this documentation section](https://docs.conan.io/en/latest/creating_packages/package_repo.html#capturing-the-remote-and-commit-from-git-scm-experimental) creates an `scm_folder.txt` file in the conan package. When this file is present, conan tries to clone the repo from the path in this file instead of the git url stored in the exported recipe. 

Sadly, this file contains the path to the folder containing the recipe, instead of the path to the root of the repository. So, when the recipe is at the root of the repository, the behaviour appears correct. But if the recipe is stored in a subfolder, then it seems nothing is cloned over to the build folder, failing the process.

Discussion:

Patch:
diff --git a/conans/client/cmd/export.py b/conans/client/cmd/export.py
index 020980d7c..f242fb0ad 100644
--- a/conans/client/cmd/export.py
+++ b/conans/client/cmd/export.py
@@ -60,7 +60,7 @@ def cmd_export(conanfile_path, conanfile, reference, keep_source, output, client
                           keep_source)
 
 
-def _capture_export_scm_data(conanfile, src_path, destination_folder, output, paths, conan_ref):
+def _capture_export_scm_data(conanfile, conanfile_dir, destination_folder, output, paths, conan_ref):
 
     scm_src_file = paths.scm_folder(conan_ref)
     if os.path.exists(scm_src_file):
@@ -71,7 +71,7 @@ def _capture_export_scm_data(conanfile, src_path, destination_folder, output, pa
     if not scm_data or not (scm_data.capture_origin or scm_data.capture_revision):
         return
 
-    scm = SCM(scm_data, src_path)
+    scm = SCM(scm_data, conanfile_dir)
 
     if scm_data.url == "auto":
         origin = scm.get_remote_url()
@@ -87,6 +87,7 @@ def _capture_export_scm_data(conanfile, src_path, destination_folder, output, pa
         output.success("Revision deduced by 'auto': %s" % scm_data.revision)
 
     # Generate the scm_folder.txt file pointing to the src_path
+    src_path = scm.get_repo_root()
     save(scm_src_file, src_path.replace("\\", "/"))
     scm_data.replace_in_file(os.path.join(destination_folder, "conanfile.py"))
 
diff --git a/conans/client/tools/scm.py b/conans/client/tools/scm.py
index 0c1c84c2d..50b06b46d 100644
--- a/conans/client/tools/scm.py
+++ b/conans/client/tools/scm.py
@@ -13,8 +13,8 @@ from conans.util.files import decode_text, to_file_bytes
 
 class Git(object):
 
-    def __init__(self, folder=None, verify_ssl=True, username=None, password=None, force_english=True,
-                 runner=None):
+    def __init__(self, folder=None, verify_ssl=True, username=None, password=None,
+                 force_english=True, runner=None):
         self.folder = folder or os.getcwd()
         if not os.path.exists(self.folder):
             os.makedirs(self.folder)
@@ -33,6 +33,9 @@ class Git(object):
                 else:
                     return self._runner(command)
 
+    def get_repo_root(self):
+        return self.run("rev-parse --show-toplevel")
+
     def get_url_with_credentials(self, url):
         if not self._username or not self._password:
             return url
diff --git a/conans/model/scm.py b/conans/model/scm.py
index e7ff64aca..84210fc25 100644
--- a/conans/model/scm.py
+++ b/conans/model/scm.py
@@ -75,3 +75,6 @@ class SCM(object):
 
     def get_revision(self):
         return self.repo.get_revision()
+
+    def get_repo_root(self):
+        return self.repo.get_repo_root()


Test patch:
diff --git a/conans/test/functional/scm_test.py b/conans/test/functional/scm_test.py
index 24a7ea320..1e19a06f4 100644
--- a/conans/test/functional/scm_test.py
+++ b/conans/test/functional/scm_test.py
@@ -149,6 +149,21 @@ class ConanLib(ConanFile):
         self.assertTrue(os.path.exists(os.path.join(folder, "mysub", "myfile.txt")))
         self.assertFalse(os.path.exists(os.path.join(folder, "mysub", "conanfile.py")))
 
+    def test_auto_conanfile_no_root(self):
+        """
+        Conanfile is not in the root of the repo: https://github.com/conan-io/conan/issues/3465
+        """
+        curdir = self.client.current_folder
+        conanfile = base.format(url="auto", revision="auto")
+        self.client.save({"conan/conanfile.py": conanfile, "myfile.txt": "content of my file"})
+        self._commit_contents()
+        self.client.runner('git remote add origin https://myrepo.com.git', cwd=curdir)
+
+        # Create the package
+        self.client.run("create conan/ user/channel")
+        sources_dir = self.client.client_cache.scm_folder(self.reference)
+        self.assertEquals(load(sources_dir), curdir.replace('\\', '/'))  # Root of git is 'curdir'
+
     def test_deleted_source_folder(self):
         path, commit = create_local_git_repo({"myfile": "contents"}, branch="my_release")
         curdir = self.client.current_folder.replace("\\", "/")
diff --git a/conans/test/util/tools_test.py b/conans/test/util/tools_test.py
index d60c3365c..d3db53ab2 100644
--- a/conans/test/util/tools_test.py
+++ b/conans/test/util/tools_test.py
@@ -1100,6 +1100,19 @@ ProgramFiles(x86)=C:\Program Files (x86)
 
 class GitToolTest(unittest.TestCase):
 
+    def test_repo_root(self):
+        root_path, _ = create_local_git_repo({"myfile": "anything"})
+
+        # Initialized in the root folder
+        git = Git(root_path)
+        self.assertEqual(root_path, git.get_repo_root())
+
+        # Initialized elsewhere
+        subfolder = os.path.join(root_path, 'subfolder')
+        os.makedirs(subfolder)
+        git = Git(subfolder)
+        self.assertEqual(root_path, git.get_repo_root())
+
     def test_clone_git(self):
         path, _ = create_local_git_repo({"myfile": "contents"})
         tmp = temp_folder()