Repository: conan-io/conan
Pull request: fixing missing download of conan_sources.tgz for export_sources() method
Changelog: BugFix: Fix missing download of ``conan_sources.tgz`` created using ``export_sources()`` method.
Docs: Omit


Fix https://github.com/conan-io/conan/issues/7377

Problem statement:
[bug] conan install only considers exports_sources
### Environment Details (include every applicable attribute)
  * Conan version: 1.26.0+

### Steps to reproduce (Include if Applicable)

Put a `dummy.txt` next to the following `conanfile.py` in some directory.

    # conanfile.py
    from conans import ConanFile
    from os.path import isfile


    class ExportSourcesTestConan(ConanFile):
        name = "export-sources-test"
        version = "1.0"
        settings = None
        #exports_sources = "dummy.txt",

        def export_sources(self):
            self.copy("dummy.txt")

        def build(self):
            if not isfile("dummy.txt"):
                raise RuntimeError("Oh no!")

Create and upload the package

    $ conan create . user/testing
    $ conan upload -r <remote> export-sources-test/1.0@user/testing

Remove the package from the local cache and install it from your remote:

    $ conan remove export-sources-test*
    $ conan install export-sources-test/1.0@user/testing --build missing

If the `conanfile.py` uses `export_sources()` the build step will raise the exception. If `exports_sources` is used, the build step succeeds.

### Cause

The function `complete_recipe_sources()` in `client/source.py` only tests whether `conanfile.exports_sources` is defined. If only `export_sources()` is defined the sources won't be downloaded and the source directory stays empty.

### Workaround

Defining both `exports_sources` and `export_sources()` triggers the download of the sources. But it would be nice to use `export_sources()` completely independent from `exports_sources`.

Hints from issue discussion:
Thanks very much for your complete report and detailed investigation. Indeed a bug that needs to be fixed.
You were just one ``if`` from a Pull Request, next time don't hesitate to submit it, we can help with the testing if necessary :)

I have contributed the fix in https://github.com/conan-io/conan/pull/7380, planned for next release (Conan 1.28)
You're welcome. I'm amazed how quick you're with feedback to new issues. Great work!
No problem, thanks to you for reporting! Let's keep the issue open it will be automatically closed when the PR is merged to develop.

Commit message: fire CI

Patch to review:
diff --git a/conans/client/source.py b/conans/client/source.py
index ab4e3b5fd53..603d10e8c3d 100644
--- a/conans/client/source.py
+++ b/conans/client/source.py
@@ -25,7 +25,7 @@ def complete_recipe_sources(remote_manager, cache, conanfile, ref, remotes):
     if os.path.exists(sources_folder):
         return None
 
-    if conanfile.exports_sources is None:
+    if conanfile.exports_sources is None and not hasattr(conanfile, "export_sources"):
         mkdir(sources_folder)
         return None
 
diff --git a/conans/test/functional/command/export/exports_method_test.py b/conans/test/functional/command/export/exports_method_test.py
index fd216e629a0..6357b6de8cd 100644
--- a/conans/test/functional/command/export/exports_method_test.py
+++ b/conans/test/functional/command/export/exports_method_test.py
@@ -209,3 +209,26 @@ def exports_sources(self):
         client.run("export . pkg/0.1@", assert_error=True)
         self.assertIn("ERROR: conanfile 'exports_sources' shouldn't be a method, "
                       "use 'export_sources()' instead", client.out)
+
+    def test_exports_sources_upload_error(self):
+        # https://github.com/conan-io/conan/issues/7377
+        client = TestClient(default_server_user=True)
+        conanfile = textwrap.dedent("""
+            from conans import ConanFile, load
+
+            class MethodConan(ConanFile):
+                def export_sources(self):
+                    self.copy("*")
+                def build(self):
+                    self.output.info("CONTENT: %s" % load("myfile.txt"))
+            """)
+        client.save({"conanfile.py": conanfile,
+                     "myfile.txt": "mycontent"})
+        client.run("export . pkg/0.1@")
+        self.assertIn("pkg/0.1 export_sources() method: Copied 1 '.txt' file: myfile.txt",
+                      client.out)
+        client.run("upload pkg/0.1@")
+        client.run("remove * -f")
+        client.run("install pkg/0.1@ --build")
+        self.assertIn("Downloading conan_sources.tgz", client.out)
+        self.assertIn("pkg/0.1: CONTENT: mycontent", client.out)


Code review comments:
diff_hunk: @@ -209,3 +209,26 @@ def exports_sources(self):
         client.run("export . pkg/0.1@", assert_error=True)
         self.assertIn("ERROR: conanfile 'exports_sources' shouldn't be a method, "
                       "use 'export_sources()' instead", client.out)
+
+    def test_exports_sources_upload_error(self):
+        # https://github.com/conan-io/conan/issues/7377
+        client = TestClient(default_server_user=True)
+        conanfile = textwrap.dedent("""
+            from conans import ConanFile, load
+
+            class MethodConan(ConanFile):
+                def export_sources(self):
+                    self.copy("*")
+                def build(self):
+                    self.output.info("CONTENT: %s" % load("myfile.txt"))
path: conans/test/functional/command/export/exports_method_test.py
text: @user1:
```suggestion
                    self.output.info("CONTENT: %s" % tools.load("myfile.txt"))
```

diff_hunk: @@ -209,3 +209,26 @@ def exports_sources(self):
         client.run("export . pkg/0.1@", assert_error=True)
         self.assertIn("ERROR: conanfile 'exports_sources' shouldn't be a method, "
                       "use 'export_sources()' instead", client.out)
+
+    def test_exports_sources_upload_error(self):
+        # https://github.com/conan-io/conan/issues/7377
+        client = TestClient(default_server_user=True)
+        conanfile = textwrap.dedent("""
+            from conans import ConanFile, load
path: conans/test/functional/command/export/exports_method_test.py
text: @user1:
```suggestion
            from conans import ConanFile, tools
```