def test_git_clone_with_source_layout(): client = TestClient() repo = temp_folder() conanfile = textwrap.dedent(""" import os from conans import ConanFile class Pkg(ConanFile): exports_sources = "*.txt" def layout(self): self.folders.source = "src" def source(self): self.run('git clone "{}" .') """).format(repo.replace("\\", "/")) client.save({"conanfile.py": conanfile, "myfile.txt": "My file is copied"}) with client.chdir(repo): client.save({"cloned.txt": "foo"}, repo) client.init_git_repo() client.run("create . hello/1.0@") sf = client.cache.package_layout( ConanFileReference.loads("hello/1.0@")).source() assert os.path.exists(os.path.join(sf, "myfile.txt")) # The conanfile is cleared from the root before cloning assert not os.path.exists(os.path.join(sf, "conanfile.py")) assert not os.path.exists(os.path.join(sf, "cloned.txt")) assert os.path.exists(os.path.join(sf, "src", "cloned.txt")) assert not os.path.exists(os.path.join(sf, "src", "myfile.txt"))
def test_reuse_customize_scm(self): client = TestClient() conanfile = textwrap.dedent(""" from conans import ConanFile class SomeBase(object): base_repo = "somerepo" scm = {"type" : "git", "url" : base_repo, "revision" : "auto"} class MyConanfileBase(SomeBase, ConanFile): pass """) client.init_git_repo({"conanfile.py": conanfile}, branch="my_release") client.run("export . base/1.1@user/testing") client.run("get base/1.1@user/testing") self.assertIn('"url": "somerepo"', client.out) reuse = textwrap.dedent(""" from conans import ConanFile class PkgTest(ConanFile): base_repo = "other_repo" python_requires = "base/1.1@user/testing" python_requires_extend = "base.SomeBase" def init(self): self.scm["url"] = self.base_repo """) client.save({"conanfile.py": reuse}) client.run("export . Pkg/0.1@user/testing") client.run("get Pkg/0.1@user/testing") self.assertNotIn("scm = base.scm", client.out) self.assertIn('scm = {"revision":', client.out) self.assertIn('"type": "git",', client.out) self.assertIn('"url": "other_repo"', client.out)
def test_reuse_scm_multiple_conandata(self): # https://github.com/conan-io/conan/issues/7236 # This only works when using conandata.yml, conanfile.py replace is broken client = TestClient() conanfile = textwrap.dedent(""" from conans import ConanFile class SomeBase(object): scm = {"type" : "git", "url" : "remote1", "revision" : "auto"} class MyConanfileBase(SomeBase, ConanFile): pass """) base_rev = client.init_git_repo({"conanfile.py": conanfile}, branch="my_release", folder="base") client.run("config set general.scm_to_conandata=1") client.run("export base base/1.1@user/testing") reuse = textwrap.dedent(""" from conans import ConanFile class PkgTest(ConanFile): name = "%s" python_requires = "base/1.1@user/testing" python_requires_extend = "base.SomeBase" """) reuse1_rev = client.init_git_repo({"conanfile.py": reuse % "reuse1"}, branch="release", folder="reuse1") reuse2_rev = client.init_git_repo({"conanfile.py": reuse % "reuse2"}, branch="release", folder="reuse2") client.run("export reuse1 reuse1/1.1@user/testing") client.run("export reuse2 reuse2/1.1@user/testing") client.run("inspect base/1.1@user/testing -a=scm --json=base.json") base_json = json.loads(client.load("base.json")) client.run("inspect reuse1/1.1@user/testing -a=scm --json=reuse1.json") reuse1_json = json.loads(client.load("reuse1.json")) client.run("inspect reuse2/1.1@user/testing -a=scm --json=reuse2.json") reuse2_json = json.loads(client.load("reuse2.json")) self.assertEqual(base_json["scm"]["revision"], base_rev) self.assertEqual(reuse1_json["scm"]["revision"], reuse1_rev) self.assertEqual(reuse2_json["scm"]["revision"], reuse2_rev) self.assertNotEqual(base_rev, reuse1_rev) self.assertNotEqual(base_rev, reuse2_rev) self.assertNotEqual(reuse2_rev, reuse1_rev)
def test_empty_conandata(self): # https://github.com/conan-io/conan/issues/8209 conanfile = textwrap.dedent(""" from conans import ConanFile class Recipe(ConanFile): scm = {"type": "git", "url": "auto", "revision": "auto"} """) t = TestClient() commit = t.init_git_repo({ 'conanfile.py': conanfile, 'conandata.yml': "" }) t.run_command('git remote add origin https://myrepo.com.git') t.run("config set general.scm_to_conandata=1") t.run("export . name/version@") # Check exported files package_layout = t.cache.package_layout(self.ref) exported_conanfile = load(package_layout.conanfile()) self.assertEqual(exported_conanfile, conanfile) exported_conandata = load( os.path.join(package_layout.export(), DATA_YML)) conan_data = yaml.safe_load(exported_conandata) self.assertDictEqual(conan_data['.conan']['scm'], { "type": "git", "url": 'https://myrepo.com.git', "revision": commit })
def test_auto_is_replaced(self): conanfile = textwrap.dedent(""" import os from conans import ConanFile class Recipe(ConanFile): scm = {"type": "git", "url": "auto", "revision": "auto"} """) t = TestClient() commit = t.init_git_repo({'conanfile.py': conanfile}) t.run_command('git remote add origin https://myrepo.com.git') t.run("config set general.scm_to_conandata=1") t.run("export . name/version@") # Check exported files package_layout = t.cache.package_layout(self.ref) exported_conanfile = load(package_layout.conanfile()) self.assertEqual(exported_conanfile, conanfile) exported_conandata = load( os.path.join(package_layout.export(), DATA_YML)) conan_data = yaml.safe_load(exported_conandata) self.assertDictEqual(conan_data['.conan']['scm'], { "type": "git", "url": 'https://myrepo.com.git', "revision": commit }) # Check the recipe gets the proper values t.run("inspect name/version@ -a scm") self.assertIn("revision: {}".format(commit), t.out) self.assertIn("type: git", t.out) self.assertIn("url: https://myrepo.com.git", t.out)
def test_delegated_python_code(self): client = TestClient() code_file = textwrap.dedent(""" from conans.tools import Git from conans import ConanFile def get_commit(repo_path): git = Git(repo_path) return git.get_commit() class MyLib(ConanFile): pass """) client.save({"conanfile.py": code_file}) client.run("export . tool/0.1@user/testing") conanfile = textwrap.dedent(""" import os from conans import ConanFile, python_requires from conans.tools import load tool = python_requires("tool/0.1@user/testing") class MyLib(ConanFile): scm = {'type': 'git', 'url': '%s', 'revision': tool.get_commit(os.path.dirname(__file__))} def build(self): self.output.info("File: {}".format(load("file.txt"))) """ % client.current_folder.replace("\\", "/")) commit = client.init_git_repo({"conanfile.py": conanfile, "file.txt": "hello!"}) client.run("export . pkg/0.1@user/channel") ref = ConanFileReference.loads("pkg/0.1@user/channel") exported_conanfile = client.cache.package_layout(ref).conanfile() content = load(exported_conanfile) self.assertIn(commit, content)
def test_revision_mode_scm(self): t = TestClient() commit = t.init_git_repo({'conanfile.py': self.conanfile.format(revision_mode="scm")}) ref = ConanFileReference.loads("name/version@user/channel") t.run("export . {}".format(ref)) meta = t.cache.package_layout(ref, short_paths=False).load_metadata() self.assertEqual(meta.recipe.revision, commit)
def test_auto_can_be_automated(): # https://github.com/conan-io/conan/issues/8881 conanfile = textwrap.dedent(""" import os from conans import ConanFile class Recipe(ConanFile): scm = {"type": "git", "url": os.getenv("USER_EXTERNAL_URL", "auto"), "revision": os.getenv("USER_EXTERNAL_COMMIT", "auto")} """) t = TestClient() commit = t.init_git_repo({'conanfile.py': conanfile, 'conandata.yml': ""}) t.run_command('git remote add origin https://myrepo.com.git') t.run("config set general.scm_to_conandata=1") t.run("export . pkg/1.0@") def _check(client): # Check exported files ref = ConanFileReference.loads("pkg/1.0") package_layout = client.cache.package_layout(ref) exported_conanfile = load(package_layout.conanfile()) assert exported_conanfile == conanfile exported_conandata = load( os.path.join(package_layout.export(), DATA_YML)) conan_data = yaml.safe_load(exported_conandata) assert conan_data['.conan']['scm'] == { "type": "git", "url": 'https://myrepo.com.git', "revision": commit } _check(t) # Now try from another folder, doing a copy and using the env-vars t = TestClient(default_server_user=True) t.run("config set general.scm_to_conandata=1") t.save({"conanfile.py": conanfile}) with environment_append({ "USER_EXTERNAL_URL": "https://myrepo.com.git", "USER_EXTERNAL_COMMIT": commit }): t.run("export . pkg/1.0@") _check(t) t.run("upload * -c") t.run("remove * -f") t.run("install pkg/1.0@ --build", assert_error=True) assert "pkg/1.0: SCM: Getting sources from url: 'https://myrepo.com.git'" in t.out with environment_append({ "USER_EXTERNAL_URL": "https://other.different.url", "USER_EXTERNAL_COMMIT": "invalid commit" }): t.run("install pkg/1.0@ --build", assert_error=True) print(t.out) assert "pkg/1.0: SCM: Getting sources from url: 'https://myrepo.com.git'" in t.out
def test_capture_commit_local(self): """ A local repo, without remote, will have commit, but no URL """ c = TestClient() c.save({"conanfile.py": self.conanfile}) commit = c.init_git_repo() c.run("export .") assert "pkg/0.1: COMMIT: {}".format(commit) in c.out assert "pkg/0.1: URL: None" in c.out assert "pkg/0.1: COMMIT IN REMOTE: False" in c.out assert "pkg/0.1: DIRTY: False" in c.out
def test_scm_with_source_layout(): """If we have the sources in git repository""" conan_file = GenConanfile() \ .with_name("app").with_version("1.0") \ .with_settings("os", "arch", "build_type", "compiler") \ .with_scm({"type": "git", "revision": "auto", "url": "auto"})\ .with_cmake_build() conan_file = str(conan_file) conan_file += """ def layout(self): self.folders.source = "my_src" self.folders.build = "build_{}".format(self.settings.build_type) """ cmake = gen_cmakelists(appname="my_app", appsources=["main.cpp"]) app = gen_function_cpp(name="main") remote_path, _ = create_local_git_repo({"foo": "var"}, branch="my_release") client = TestClient() client.save({ "conanfile.py": conan_file, "my_src/main.cpp": app, "my_src/CMakeLists.txt": cmake, ".gitignore": "build_*\n" }) client.init_git_repo() client.run_command('git remote add origin "%s"' % remote_path.replace("\\", "/")) client.run_command('git push origin master') client.run("install . -if=install") client.run("build . -if=install") assert os.path.exists( os.path.join(client.current_folder, "build_Release", app_name)) client.run("create . ") assert "Created package revision" in client.out
def test_capture_commit_local(self): """ A local repo, without remote, will provide its own URL to the export(), and if it has local changes, it will be marked as dirty, and raise an error """ c = TestClient() c.save({"conanfile.py": self.conanfile}) commit = c.init_git_repo() c.run("export .") assert "This revision will not be buildable in other computer" in c.out assert "pkg/0.1: SCM COMMIT: {}".format(commit) in c.out assert "pkg/0.1: SCM URL: {}".format(c.current_folder.replace("\\", "/")) in c.out c.save({"conanfile.py": self.conanfile + "\n# something...."}) c.run("export .", assert_error=True) assert "Repo is dirty, cannot capture url and commit" in c.out
class SVNSCMTest(SVNLocalRepoTestCase): def setUp(self): self.ref = ConanFileReference.loads("lib/0.1@user/channel") self.client = TestClient() def test_scm_other_type_ignored(self): conanfile = ''' from conans import ConanFile, tools class ConanLib(ConanFile): name = "lib" version = "0.1" scm = ["Other stuff"] def build(self): self.output.writeln("scm: {}".format(self.scm)) ''' self.client.save({"conanfile.py": conanfile}) # nothing breaks self.client.run("create . user/channel") self.assertIn("['Other stuff']", self.client.out) def test_repeat_clone_changing_subfolder(self): tmp = ''' from conans import ConanFile, tools class ConanLib(ConanFile): name = "lib" version = "0.1" scm = {{ "type": "svn", "url": "{url}", "revision": "{revision}", "subfolder": "onesubfolder" }} ''' project_url, rev = self.create_project(files={"myfile": "contents"}) conanfile = tmp.format(url=project_url, revision=rev) self.client.save({ "conanfile.py": conanfile, "myfile.txt": "My file is copied" }) self.client.run("create . user/channel") conanfile = conanfile.replace('"onesubfolder"', '"othersubfolder"') self.client.save({"conanfile.py": conanfile}) self.client.run("create . user/channel") ref = ConanFileReference.loads("lib/0.1@user/channel") folder = self.client.cache.package_layout(ref).source() self.assertIn("othersubfolder", os.listdir(folder)) self.assertTrue( os.path.exists(os.path.join(folder, "othersubfolder", "myfile"))) def test_auto_filesystem_remote_svn(self): # SVN origin will never be a local path (local repo has at least protocol file:///) pass def test_auto_svn(self): conanfile = base_svn.format(directory="None", url=_quoted("auto"), revision="auto") project_url, _ = self.create_project(files={ "conanfile.py": conanfile, "myfile.txt": "My file is copied" }) project_url = project_url.replace(" ", "%20") self.client.run_command('svn co "{url}" "{path}"'.format( url=project_url, path=self.client.current_folder)) curdir = self.client.current_folder.replace("\\", "/") # Create the package, will copy the sources from the local folder self.client.run("create . user/channel") self.assertIn( "Repo origin deduced by 'auto': {}".format(project_url).lower(), str(self.client.out).lower()) self.assertIn("Revision deduced by 'auto'", self.client.out) self.assertIn("SCM: Getting sources from folder: %s" % curdir, self.client.out) self.assertIn("My file is copied", self.client.out) # Export again but now with absolute reference, so no pointer file is created nor kept svn = SVN(curdir) self.client.save({ "conanfile.py": base_svn.format(url=_quoted(svn.get_remote_url()), revision=svn.get_revision()) }) self.client.run("create . user/channel") self.assertNotIn("Repo origin deduced by 'auto'", self.client.out) self.assertNotIn("Revision deduced by 'auto'", self.client.out) self.assertIn( "SCM: Getting sources from url: '{}'".format(project_url).lower(), str(self.client.out).lower()) self.assertIn("My file is copied", self.client.out) def test_auto_subfolder(self): conanfile = base_svn.replace( '"revision": "{revision}"', '"revision": "{revision}",\n ' '"subfolder": "mysub"') conanfile = conanfile.replace("short_paths = True", "short_paths = False") conanfile = conanfile.format(directory="None", url=_quoted("auto"), revision="auto") project_url, _ = self.create_project(files={ "conanfile.py": conanfile, "myfile.txt": "My file is copied" }) project_url = project_url.replace(" ", "%20") self.client.run_command('svn co "{url}" "{path}"'.format( url=project_url, path=self.client.current_folder)) self.client.run("create . user/channel") ref = ConanFileReference.loads("lib/0.1@user/channel") folder = self.client.cache.package_layout(ref).source() 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 conanfile = base_svn.format(url="get_svn_remote('..')", revision="auto") project_url, _ = self.create_project(files={ "conan/conanfile.py": conanfile, "myfile.txt": "My file is copied" }) self.client.run_command('svn co "{url}" "{path}"'.format( url=project_url, path=self.client.current_folder)) self.client.run("create conan/ user/channel") # Check that the conanfile is on the source/conan ref = ConanFileReference.loads("lib/0.1@user/channel") source_folder = self.client.cache.package_layout(ref).source() self.assertTrue( os.path.exists(os.path.join(source_folder, "conan", "conanfile.py"))) def test_deleted_source_folder(self): # SVN will always retrieve from 'remote' pass def test_excluded_repo_fies(self): conanfile = base_svn.format(url=_quoted("auto"), revision="auto") conanfile = conanfile.replace("short_paths = True", "short_paths = False") # SVN ignores pyc files by default: # http://blogs.collab.net/subversion/repository-dictated-configuration-day-3-global-ignores project_url, _ = self.create_project( files={ "myfile": "contents", "ignored.pyc": "bin", # ".gitignore": "*.pyc\n", "myfile.txt": "My file!", "conanfile.py": conanfile }) project_url = project_url.replace(" ", "%20") self.client.run_command('svn co "{url}" "{path}"'.format( url=project_url, path=self.client.current_folder)) self.client.run("create . user/channel") self.assertIn("Copying sources to build folder", self.client.out) pref = PackageReference( ConanFileReference.loads("lib/0.1@user/channel"), NO_SETTINGS_PACKAGE_ID) bf = self.client.cache.package_layout(pref.ref).build(pref) self.assertTrue(os.path.exists(os.path.join(bf, "myfile.txt"))) self.assertTrue(os.path.exists(os.path.join(bf, "myfile"))) self.assertTrue(os.path.exists(os.path.join(bf, ".svn"))) self.assertFalse(os.path.exists(os.path.join(bf, "ignored.pyc"))) def test_local_source(self): curdir = self.client.current_folder.replace("\\", "/") conanfile = base_svn.format(url=_quoted("auto"), revision="auto") conanfile += """ def source(self): self.output.warn("SOURCE METHOD CALLED") """ project_url, _ = self.create_project(files={ "conanfile.py": conanfile, "myfile.txt": "My file is copied" }) project_url = project_url.replace(" ", "%20") self.client.run_command('svn co "{url}" "{path}"'.format( url=project_url, path=self.client.current_folder)) self.client.save({"aditional_file.txt": "contents"}) self.client.run("source . --source-folder=./source") self.assertTrue( os.path.exists(os.path.join(curdir, "source", "myfile.txt"))) self.assertIn("SOURCE METHOD CALLED", self.client.out) # Even the not commited files are copied self.assertTrue( os.path.exists(os.path.join(curdir, "source", "aditional_file.txt"))) self.assertIn("SCM: Getting sources from folder: %s" % curdir, self.client.out) # Export again but now with absolute reference, so no pointer file is created nor kept svn = SVN(curdir) conanfile = base_svn.format(url=_quoted(svn.get_remote_url()), revision=svn.get_revision()) conanfile += """ def source(self): self.output.warn("SOURCE METHOD CALLED") """ self.client.save({ "conanfile.py": conanfile, "myfile2.txt": "My file is copied" }) self.client.run_command("svn add myfile2.txt") self.client.run_command('svn commit -m "commiting"') self.client.run("source . --source-folder=./source2") # myfile2 is no in the specified commit self.assertFalse( os.path.exists(os.path.join(curdir, "source2", "myfile2.txt"))) self.assertTrue( os.path.exists(os.path.join(curdir, "source2", "myfile.txt"))) self.assertIn( "SCM: Getting sources from url: '{}'".format(project_url).lower(), str(self.client.out).lower()) self.assertIn("SOURCE METHOD CALLED", self.client.out) def test_local_source_subfolder(self): curdir = self.client.current_folder conanfile = base_svn.replace( '"revision": "{revision}"', '"revision": "{revision}",\n ' '"subfolder": "mysub"') conanfile = conanfile.format(url=_quoted("auto"), revision="auto") conanfile += """ def source(self): self.output.warn("SOURCE METHOD CALLED") """ project_url, _ = self.create_project(files={ "conanfile.py": conanfile, "myfile.txt": "My file is copied" }) project_url = project_url.replace(" ", "%20") self.client.run_command('svn co "{url}" "{path}"'.format( url=project_url, path=self.client.current_folder)) self.client.run("source . --source-folder=./source") self.assertFalse( os.path.exists(os.path.join(curdir, "source", "myfile.txt"))) self.assertTrue( os.path.exists( os.path.join(curdir, "source", "mysub", "myfile.txt"))) self.assertIn("SOURCE METHOD CALLED", self.client.out) def test_install_checked_out(self): test_server = TestServer() self.servers = {"myremote": test_server} self.client = TestClient(servers=self.servers, users={"myremote": [("lasote", "mypass")]}) conanfile = base_svn.format(url=_quoted("auto"), revision="auto") project_url, _ = self.create_project(files={ "conanfile.py": conanfile, "myfile.txt": "My file is copied" }) project_url = project_url.replace(" ", "%20") self.client.run_command('svn co "{url}" "{path}"'.format( url=project_url, path=self.client.current_folder)) self.client.run("export . lasote/channel") self.client.run("upload lib* -c") # Take other client, the old client folder will be used as a remote client2 = TestClient(servers=self.servers, users={"myremote": [("lasote", "mypass")]}) client2.run("install lib/0.1@lasote/channel --build") self.assertIn("My file is copied", client2.out) def test_source_removed_in_local_cache(self): conanfile = ''' from conans import ConanFile, tools class ConanLib(ConanFile): scm = { "type": "svn", "url": "auto", "revision": "auto", } def build(self): contents = tools.load("myfile") self.output.warn("Contents: %s" % contents) ''' project_url, _ = self.create_project(files={ "myfile": "contents", "conanfile.py": conanfile }) project_url = project_url.replace(" ", "%20") self.client.run_command('svn co "{url}" "{path}"'.format( url=project_url, path=self.client.current_folder)) self.client.run("create . lib/1.0@user/channel") self.assertIn("Contents: contents", self.client.out) self.client.save({"myfile": "Contents 2"}) self.client.run("create . lib/1.0@user/channel") self.assertIn("Contents: Contents 2", self.client.out) def test_submodule(self): # SVN has no submodules, may add something related to svn:external? pass def test_source_method_export_sources_and_scm_mixed(self): project_url, rev = self.create_project(files={"myfile": "contents"}) project_url = project_url.replace(" ", "%20") self.client.run_command('svn co "{url}" "{path}"'.format( url=project_url, path=self.client.current_folder)) conanfile = ''' import os from conans import ConanFile, tools class ConanLib(ConanFile): name = "lib" version = "0.1" exports_sources = "file.txt" scm = {{ "type": "svn", "url": "{url}", "revision": "{rev}", "subfolder": "src" }} def source(self): self.output.warn("SOURCE METHOD CALLED") assert(os.path.exists("file.txt")) assert(os.path.exists(os.path.join("src", "myfile"))) tools.save("cosa.txt", "contents") def build(self): assert(os.path.exists("file.txt")) assert(os.path.exists("cosa.txt")) self.output.warn("BUILD METHOD CALLED") '''.format(url=project_url, rev=rev) self.client.save({ "conanfile.py": conanfile, "file.txt": "My file is copied" }) self.client.run("create . user/channel") self.assertIn("SOURCE METHOD CALLED", self.client.out) self.assertIn("BUILD METHOD CALLED", self.client.out) def test_non_commited_changes_export(self): conanfile = base_git.format(revision="auto", url='"auto"') self.client.save({ "conanfile.py": conanfile, "myfile.txt": "My file is copied" }) self.client.init_git_repo() self.client.run_command('git remote add origin https://myrepo.com.git') # Dirty file self.client.save({"dirty": "you dirty contents"}) for command in ("export .", "create ."): self.client.run(command) self.assertIn( "WARN: There are uncommitted changes, skipping the replacement " "of 'scm.url' and 'scm.revision' auto fields. " "Use --ignore-dirty to force it.", self.client.out) # We confirm that the replacement hasn't been done ref = ConanFileReference.loads("lib/0.1@") folder = self.client.cache.package_layout(ref).export() conanfile_contents = load(os.path.join(folder, "conanfile.py")) self.assertIn('"revision": "auto"', conanfile_contents) self.assertIn('"url": "auto"', conanfile_contents) # We repeat the export/create but now using the --ignore-dirty for command in ("export .", "create ."): self.client.run("{} --ignore-dirty".format(command)) self.assertNotIn( "WARN: There are uncommitted changes, skipping the replacement " "of 'scm.url' and 'scm.revision' auto fields. " "Use --ignore-dirty to force it.", self.client.out) # We confirm that the replacement has been done ref = ConanFileReference.loads("lib/0.1@") folder = self.client.cache.package_layout(ref).export() conanfile_contents = load(os.path.join(folder, "conanfile.py")) self.assertNotIn('"revision": "auto"', conanfile_contents) self.assertNotIn('"url": "auto"', conanfile_contents) def test_double_create(self): # https://github.com/conan-io/conan/issues/5195#issuecomment-551848955 self.client = TestClient(default_server_user=True) conanfile = str(GenConanfile().with_scm({ "type": "git", "revision": "auto", "url": "auto" }).with_import("import os").with_import( "from conans import tools").with_name("lib").with_version("1.0")) conanfile += """ def build(self): contents = tools.load("bla.sh") self.output.warn("Bla? {}".format(contents)) """ self.client.save({ "conanfile.py": conanfile, "myfile.txt": "My file is copied" }) self.client.init_git_repo() self.client.run_command('git remote add origin https://myrepo.com.git') # modified blah.sh self.client.save({"bla.sh": "bla bla"}) self.client.run("create . user/channel") self.assertIn("Bla? bla bla", self.client.out) # modified blah.sh again self.client.save({"bla.sh": "bla2 bla2"}) # Run conan create again self.client.run("create . user/channel") self.assertIn("Bla? bla2 bla2", self.client.out)
class GitSCMTest(unittest.TestCase): def setUp(self): self.ref = ConanFileReference.loads("lib/0.1@user/channel") self.client = TestClient() def _commit_contents(self): self.client.run_command("git init .") self.client.run_command('git config user.email "*****@*****.**"') self.client.run_command('git config user.name "Your Name"') self.client.run_command("git add .") self.client.run_command('git commit -m "commiting"') def test_scm_other_type_ignored(self): conanfile = ''' from conans import ConanFile, tools class ConanLib(ConanFile): name = "lib" version = "0.1" scm = ["Other stuff"] def build(self): self.output.writeln("scm: {}".format(self.scm)) ''' self.client.save({"conanfile.py": conanfile}) # nothing breaks self.client.run("create . user/channel") self.assertIn("['Other stuff']", self.client.out) def test_repeat_clone_changing_subfolder(self): tmp = ''' from conans import ConanFile, tools class ConanLib(ConanFile): name = "lib" version = "0.1" scm = {{ "type": "git", "url": "{url}", "revision": "{revision}", "subfolder": "onesubfolder" }} ''' path, commit = create_local_git_repo({"myfile": "contents"}, branch="my_release") conanfile = tmp.format(url=path, revision=commit) self.client.save({ "conanfile.py": conanfile, "myfile.txt": "My file is copied" }) self.client.run("create . user/channel") conanfile = conanfile.replace('"onesubfolder"', '"othersubfolder"') self.client.save({"conanfile.py": conanfile}) self.client.run("create . user/channel") folder = self.client.cache.package_layout(self.ref).source() self.assertIn("othersubfolder", os.listdir(folder)) self.assertTrue( os.path.exists(os.path.join(folder, "othersubfolder", "myfile"))) def test_auto_filesystem_remote_git(self): # https://github.com/conan-io/conan/issues/3109 conanfile = base_git.format(directory="None", url=_quoted("auto"), revision="auto") repo = temp_folder() self.client.save( { "conanfile.py": conanfile, "myfile.txt": "My file is copied" }, repo) with self.client.chdir(repo): self.client.run_command("git init .") self.client.run_command('git config user.email "*****@*****.**"') self.client.run_command('git config user.name "Your Name"') self.client.run_command("git add .") self.client.run_command('git commit -m "commiting"') self.client.run_command('git clone "%s" .' % repo) self.client.run("export . user/channel") self.assertIn("WARN: Repo origin looks like a local path", self.client.out) self.client.run("remove lib/0.1* -s -f" ) # Remove the source folder, it will get from url self.client.run("install lib/0.1@user/channel --build") self.assertIn("lib/0.1@user/channel: SCM: Getting sources from url:", self.client.out) def test_auto_git(self): curdir = get_cased_path(self.client.current_folder).replace("\\", "/") conanfile = base_git.format(directory="None", url=_quoted("auto"), revision="auto") self.client.save({ "conanfile.py": conanfile, "myfile.txt": "My file is copied" }) self.client.init_git_repo() self.client.run("export . user/channel") self.assertIn( "WARN: Repo origin cannot be deduced, 'auto' fields won't be replaced", self.client.out) self.client.run_command('git remote add origin https://myrepo.com.git') # Create the package, will copy the sources from the local folder self.client.run("create . user/channel") self.assertIn("Repo origin deduced by 'auto': https://myrepo.com.git", self.client.out) self.assertIn("Revision deduced by 'auto'", self.client.out) self.assertIn("SCM: Getting sources from folder: %s" % curdir, self.client.out) self.assertIn("My file is copied", self.client.out) # check blank lines are respected in replacement self.client.run("get lib/0.1@user/channel") self.assertIn("""} def build(self):""", self.client.out) # Export again but now with absolute reference, so no sources are copied from the local dir git = Git(curdir) self.client.save({ "conanfile.py": base_git.format(url=_quoted(curdir), revision=git.get_revision()) }) self.client.run("create . user/channel") self.assertNotIn("Repo origin deduced by 'auto'", self.client.out) self.assertNotIn("Revision deduced by 'auto'", self.client.out) self.assertNotIn("Getting sources from folder: %s" % curdir, self.client.out) self.assertIn("SCM: Getting sources from url: '%s'" % curdir, self.client.out) self.assertIn("My file is copied", self.client.out) def test_auto_subfolder(self): conanfile = base_git.replace( '"revision": "{revision}"', '"revision": "{revision}",\n ' '"subfolder": "mysub"') conanfile = conanfile.replace("short_paths = True", "short_paths = False") conanfile = conanfile.format(directory="None", url=_quoted("auto"), revision="auto") self.client.save({ "conanfile.py": conanfile, "myfile.txt": "My file is copied" }) self.client.init_git_repo() self.client.run_command('git remote add origin https://myrepo.com.git') self.client.run("create . user/channel") ref = ConanFileReference.loads("lib/0.1@user/channel") folder = self.client.cache.package_layout(ref).source() 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_ignore_dirty_subfolder(self): # https://github.com/conan-io/conan/issues/6070 conanfile = textwrap.dedent(""" import os from conans import ConanFile, tools class ConanLib(ConanFile): name = "lib" version = "0.1" short_paths = True scm = { "type": "git", "url": "auto", "revision": "auto", } def build(self): path = os.path.join("base_file.txt") assert os.path.exists(path) """) self.client.save({ "test/main/conanfile.py": conanfile, "base_file.txt": "foo" }) self.client.init_git_repo() self.client.run_command('git remote add origin https://myrepo.com.git') # Introduce changes self.client.save({"dirty_file.txt": "foo"}) # The build() method will verify that the files from the repository are copied ok self.client.run("create test/main/conanfile.py user/channel") self.assertIn("Package '{}' created".format(NO_SETTINGS_PACKAGE_ID), self.client.out) def test_auto_conanfile_no_root(self): """ Conanfile is not in the root of the repo: https://github.com/conan-io/conan/issues/3465 """ conanfile = base_git.format(url=_quoted("auto"), revision="auto") self.client.save({ "conan/conanfile.py": conanfile, "myfile.txt": "content of my file" }) self._commit_contents() self.client.run_command('git remote add origin https://myrepo.com.git') # Create the package self.client.run("create conan/ user/channel") # Check that the conanfile is on the source/conan ref = ConanFileReference.loads("lib/0.1@user/channel") source_folder = self.client.cache.package_layout(ref).source() self.assertTrue( os.path.exists(os.path.join(source_folder, "conan", "conanfile.py"))) def test_deleted_source_folder(self): path, _ = create_local_git_repo({"myfile": "contents"}, branch="my_release") conanfile = base_git.format(url=_quoted("auto"), revision="auto") self.client.save({ "conanfile.py": conanfile, "myfile.txt": "My file is copied" }) self.client.init_git_repo() self.client.run_command('git remote add origin "%s"' % path.replace("\\", "/")) self.client.run_command('git push origin master') self.client.run("export . user/channel") # delete old source, but it doesn't matter because the sources are in the cache rmdir(self.client.current_folder) new_curdir = temp_folder() self.client.current_folder = new_curdir self.client.run("install lib/0.1@user/channel --build") self.assertNotIn( "Getting sources from url: '%s'" % path.replace("\\", "/"), self.client.out) # If the remove the source folder, then it is fetched from the "remote" doing an install self.client.run("remove lib/0.1@user/channel -f -s") self.client.run("install lib/0.1@user/channel --build") self.assertIn( "SCM: Getting sources from url: '%s'" % path.replace("\\", "/"), self.client.out) def test_excluded_repo_files(self): conanfile = base_git.format(url=_quoted("auto"), revision="auto") conanfile = conanfile.replace("short_paths = True", "short_paths = False") gitignore = textwrap.dedent(""" *.pyc my_excluded_folder other_folder/excluded_subfolder """) self.client.init_git_repo( { "myfile": "contents", "ignored.pyc": "bin", ".gitignore": gitignore, "myfile.txt": "My file!", "my_excluded_folder/some_file": "hey Apple!", "other_folder/excluded_subfolder/some_file": "hey Apple!", "other_folder/valid_file": "!", "conanfile.py": conanfile }, branch="my_release") self.client.run("create . user/channel") self.assertIn("Copying sources to build folder", self.client.out) pref = PackageReference( ConanFileReference.loads("lib/0.1@user/channel"), NO_SETTINGS_PACKAGE_ID) bf = self.client.cache.package_layout(pref.ref).build(pref) self.assertTrue(os.path.exists(os.path.join(bf, "myfile.txt"))) self.assertTrue(os.path.exists(os.path.join(bf, "myfile"))) self.assertTrue(os.path.exists(os.path.join(bf, ".git"))) self.assertFalse(os.path.exists(os.path.join(bf, "ignored.pyc"))) self.assertFalse(os.path.exists(os.path.join(bf, "my_excluded_folder"))) self.assertTrue( os.path.exists(os.path.join(bf, "other_folder", "valid_file"))) self.assertFalse( os.path.exists( os.path.join(bf, "other_folder", "excluded_subfolder"))) def test_local_source(self): curdir = self.client.current_folder.replace("\\", "/") conanfile = base_git.format(url=_quoted("auto"), revision="auto") conanfile += """ def source(self): self.output.warn("SOURCE METHOD CALLED") """ self.client.save({ "conanfile.py": conanfile, "myfile.txt": "My file is copied" }) self.client.init_git_repo() self.client.save({"aditional_file.txt": "contents"}) self.client.run("source . --source-folder=./source") self.assertTrue( os.path.exists(os.path.join(curdir, "source", "myfile.txt"))) self.assertIn("SOURCE METHOD CALLED", self.client.out) # Even the not commited files are copied self.assertTrue( os.path.exists(os.path.join(curdir, "source", "aditional_file.txt"))) self.assertIn("SCM: Getting sources from folder: %s" % curdir, str(self.client.out).replace("\\", "/")) # Export again but now with absolute reference, so no pointer file is created nor kept git = Git(curdir.replace("\\", "/")) conanfile = base_git.format(url=_quoted(curdir.replace("\\", "/")), revision=git.get_revision()) conanfile += """ def source(self): self.output.warn("SOURCE METHOD CALLED") """ self.client.save({ "conanfile.py": conanfile, "myfile2.txt": "My file is copied" }) self.client.init_git_repo() self.client.run("source . --source-folder=./source2") # myfile2 is no in the specified commit self.assertFalse( os.path.exists(os.path.join(curdir, "source2", "myfile2.txt"))) self.assertTrue( os.path.exists(os.path.join(curdir, "source2", "myfile.txt"))) self.assertIn( "SCM: Getting sources from url: '%s'" % curdir.replace("\\", "/"), self.client.out) self.assertIn("SOURCE METHOD CALLED", self.client.out) def test_local_source_subfolder(self): curdir = self.client.current_folder conanfile = base_git.replace( '"revision": "{revision}"', '"revision": "{revision}",\n ' '"subfolder": "mysub"') conanfile = conanfile.format(url=_quoted("auto"), revision="auto") conanfile += """ def source(self): self.output.warn("SOURCE METHOD CALLED") """ self.client.save({ "conanfile.py": conanfile, "myfile.txt": "My file is copied" }) self.client.init_git_repo() self.client.run("source . --source-folder=./source") self.assertFalse( os.path.exists(os.path.join(curdir, "source", "myfile.txt"))) self.assertTrue( os.path.exists( os.path.join(curdir, "source", "mysub", "myfile.txt"))) self.assertIn("SOURCE METHOD CALLED", self.client.out) @parameterized.expand([ ("local", "v1.0", True), ("local", None, False), ("https://github.com/conan-io/conan.git", "0.22.1", True), ("https://github.com/conan-io/conan.git", "c6cc15fa2f4b576bd70c9df11942e61e5cc7d746", False) ]) def test_shallow_clone_remote(self, remote, revision, is_tag): # https://github.com/conan-io/conan/issues/5570 self.client = TestClient() # "remote" git if remote == "local": remote, rev = create_local_git_repo( tags=[revision] if is_tag else None, files={"conans/model/username.py": "foo"}) if revision is None: # Get the generated commit revision = rev # Use explicit URL to avoid local optimization (scm_folder.txt) conanfile = ''' import os from conans import ConanFile, tools class ConanLib(ConanFile): name = "lib" version = "0.1" scm = {"type": "git", "url": "%s", "revision": "%s"} def build(self): assert os.path.exists(os.path.join(self.build_folder, "conans", "model", "username.py")) ''' % (remote, revision) self.client.save({"conanfile.py": conanfile}) self.client.run("create . user/channel") def test_install_checked_out(self): test_server = TestServer() self.servers = {"myremote": test_server} self.client = TestClient(servers=self.servers, users={"myremote": [("lasote", "mypass")]}) curdir = self.client.current_folder.replace("\\", "/") conanfile = base_git.format(url=_quoted("auto"), revision="auto") self.client.save({ "conanfile.py": conanfile, "myfile.txt": "My file is copied" }) self.client.init_git_repo() cmd = 'git remote add origin "%s"' % curdir self.client.run_command(cmd) self.client.run("export . lasote/channel") self.client.run("upload lib* -c") # Take other client, the old client folder will be used as a remote client2 = TestClient(servers=self.servers, users={"myremote": [("lasote", "mypass")]}) client2.run("install lib/0.1@lasote/channel --build") self.assertIn("My file is copied", client2.out) def test_source_removed_in_local_cache(self): conanfile = textwrap.dedent(''' from conans import ConanFile, tools class ConanLib(ConanFile): scm = { "type": "git", "url": "auto", "revision": "auto", } def build(self): contents = tools.load("myfile") self.output.warn("Contents: %s" % contents) ''') self.client.init_git_repo( { "myfile": "contents", "conanfile.py": conanfile }, branch="my_release", origin_url="https://myrepo.com.git") self.client.run("create . lib/1.0@user/channel") self.assertIn("Contents: contents", self.client.out) self.client.save({"myfile": "Contents 2"}) self.client.run("create . lib/1.0@user/channel") self.assertIn("Contents: Contents 2", self.client.out) def test_submodule(self): subsubmodule, _ = create_local_git_repo({"subsubmodule": "contents"}) submodule, _ = create_local_git_repo({"submodule": "contents"}, submodules=[subsubmodule]) path, commit = create_local_git_repo({"myfile": "contents"}, branch="my_release", submodules=[submodule]) def _relative_paths(folder): submodule_path = os.path.join( folder, os.path.basename(os.path.normpath(submodule))) subsubmodule_path = os.path.join( submodule_path, os.path.basename(os.path.normpath(subsubmodule))) return submodule_path, subsubmodule_path # Check old (default) behaviour tmp = ''' from conans import ConanFile, tools class ConanLib(ConanFile): name = "lib" version = "0.1" scm = {{ "type": "git", "url": "{url}", "revision": "{revision}" }} ''' conanfile = tmp.format(url=path, revision=commit) self.client.save({"conanfile.py": conanfile}) self.client.run("create . user/channel") ref = ConanFileReference.loads("lib/0.1@user/channel") folder = self.client.cache.package_layout(ref).source() submodule_path, _ = _relative_paths(folder) self.assertTrue(os.path.exists(os.path.join(folder, "myfile"))) self.assertFalse( os.path.exists(os.path.join(submodule_path, "submodule"))) # Check invalid value tmp = ''' from conans import ConanFile, tools class ConanLib(ConanFile): name = "lib" version = "0.1" scm = {{ "type": "git", "url": "{url}", "revision": "{revision}", "submodule": "{submodule}" }} ''' conanfile = tmp.format(url=path, revision=commit, submodule="invalid") self.client.save({"conanfile.py": conanfile}) self.client.run("create . user/channel", assert_error=True) self.assertIn("Invalid 'submodule' attribute value in the 'scm'.", self.client.out) # Check shallow conanfile = tmp.format(url=path, revision=commit, submodule="shallow") self.client.save({"conanfile.py": conanfile}) self.client.run("create . user/channel") ref = ConanFileReference.loads("lib/0.1@user/channel") folder = self.client.cache.package_layout(ref).source() submodule_path, subsubmodule_path = _relative_paths(folder) self.assertTrue(os.path.exists(os.path.join(folder, "myfile"))) self.assertTrue( os.path.exists(os.path.join(submodule_path, "submodule"))) self.assertFalse( os.path.exists(os.path.join(subsubmodule_path, "subsubmodule"))) # Check recursive conanfile = tmp.format(url=path, revision=commit, submodule="recursive") self.client.save({"conanfile.py": conanfile}) self.client.run("create . user/channel") ref = ConanFileReference.loads("lib/0.1@user/channel") folder = self.client.cache.package_layout(ref).source() submodule_path, subsubmodule_path = _relative_paths(folder) self.assertTrue(os.path.exists(os.path.join(folder, "myfile"))) self.assertTrue( os.path.exists(os.path.join(submodule_path, "submodule"))) self.assertTrue( os.path.exists(os.path.join(subsubmodule_path, "subsubmodule"))) def test_scm_bad_filename(self): # Fixes: #3500 badfilename = "\xE3\x81\x82badfile.txt" path, _ = create_local_git_repo({"goodfile.txt": "good contents"}, branch="my_release") save( to_file_bytes(os.path.join(self.client.current_folder, badfilename)), "contents") self.client.run_command('git remote add origin "%s"' % path.replace("\\", "/"), cwd=path) conanfile = ''' import os from conans import ConanFile, tools class ConanLib(ConanFile): name = "lib" version = "0.1" scm = { "type": "git", "url": "auto", "revision": "auto" } def build(self): pass ''' self.client.current_folder = path self.client.save({"conanfile.py": conanfile}) self.client.run("create . user/channel") def test_source_method_export_sources_and_scm_mixed(self): path, _ = create_local_git_repo({"myfile": "contents"}, branch="my_release") conanfile = ''' import os from conans import ConanFile, tools class ConanLib(ConanFile): name = "lib" version = "0.1" exports_sources = "file.txt" scm = { "type": "git", "url": "%s", "revision": "my_release", "subfolder": "src/nested" } def source(self): self.output.warn("SOURCE METHOD CALLED") assert(os.path.exists("file.txt")) assert(os.path.exists(os.path.join("src", "nested", "myfile"))) tools.save("cosa.txt", "contents") def build(self): assert(os.path.exists("file.txt")) assert(os.path.exists("cosa.txt")) self.output.warn("BUILD METHOD CALLED") ''' % path self.client.save({ "conanfile.py": conanfile, "file.txt": "My file is copied" }) self.client.run("create . user/channel") self.assertIn("SOURCE METHOD CALLED", self.client.out) self.assertIn("BUILD METHOD CALLED", self.client.out) def test_scm_serialization(self): data = { "url": "myurl", "revision": "myrevision", "username": "******", "password": "******", "type": "git", "verify_ssl": True, "subfolder": "mysubfolder" } conanfile = namedtuple("ConanfileMock", "scm")(data) scm_data = SCMData(conanfile) expected_output = '{"password": "******", "revision": "myrevision",' \ ' "subfolder": "mysubfolder", "type": "git", "url": "myurl",' \ ' "username": "******"}' self.assertEqual(str(scm_data), expected_output) def test_git_delegated_function(self): conanfile = textwrap.dedent(""" import os from conans import ConanFile from conans.client.tools.scm import Git def get_revision(): here = os.path.dirname(__file__) git = Git(here) return git.get_commit() def get_url(): def nested_url(): here = os.path.dirname(__file__) git = Git(here) return git.get_remote_url() return nested_url() class MyLib(ConanFile): name = "issue" version = "3831" scm = {'type': 'git', 'url': get_url(), 'revision': get_revision()} """) commit = self.client.init_git_repo({"conanfile.py": conanfile}) self.client.run("export . user/channel") ref = ConanFileReference.loads("issue/3831@user/channel") exported_conanfile = self.client.cache.package_layout(ref).conanfile() content = load(exported_conanfile) self.assertIn(commit, content) def test_delegated_python_code(self): client = TestClient() code_file = textwrap.dedent(""" from conans.tools import Git from conans import ConanFile def get_commit(repo_path): git = Git(repo_path) return git.get_commit() class MyLib(ConanFile): pass """) client.save({"conanfile.py": code_file}) client.run("export . tool/0.1@user/testing") conanfile = textwrap.dedent(""" import os from conans import ConanFile, python_requires from conans.tools import load tool = python_requires("tool/0.1@user/testing") class MyLib(ConanFile): scm = {'type': 'git', 'url': '%s', 'revision': tool.get_commit(os.path.dirname(__file__))} def build(self): self.output.info("File: {}".format(load("file.txt"))) """ % client.current_folder.replace("\\", "/")) commit = client.init_git_repo({ "conanfile.py": conanfile, "file.txt": "hello!" }) client.run("export . pkg/0.1@user/channel") ref = ConanFileReference.loads("pkg/0.1@user/channel") exported_conanfile = client.cache.package_layout(ref).conanfile() content = load(exported_conanfile) self.assertIn(commit, content)
def test_third_party_patch_flow(): """ this test emulates the work of a developer contributing recipes to ConanCenter, and having to do multiple patches to the original library source code: - Everything is local, not messing with the cache - Using layout() to define location of things """ conanfile = textwrap.dedent(r""" import os from conan import ConanFile from conan.tools.files import save, load, apply_conandata_patches class Pkg(ConanFile): name = "mypkg" version = "1.0" exports_sources = "*" def layout(self): self.folders.source = "src" self.folders.build = "build" def source(self): # emulate a download from web site save(self, "myfile.cpp", "mistake1\nsomething\nmistake2\nmistake3\nsome\n") apply_conandata_patches(self) def build(self): content = load(self, os.path.join(self.source_folder, "myfile.cpp")) for i in (1, 2, 3): if "mistake{}".format(i) in content: raise Exception("MISTAKE{} BUILD!".format(i)) """) client = TestClient() client.save({"conanfile.py": conanfile, "conandata.yml": ""}) client.run("install .") client.run("source .") client.run("build .", assert_error=True) assert "MISTAKE1 BUILD!" in client.out # user decides to create patches, first init the repo client.init_git_repo(folder="src") # Using helper for user/email repo init client.save( {"src/myfile.cpp": "correct1\nsomething\nmistake2\nmistake3\nsome\n"}) # compute the patch mkdir(os.path.join(client.current_folder, "patches")) client.run_command("cd src && git diff > ../patches/patch1") client.run_command("cd src && git add . && git commit -m patch1") conandata = textwrap.dedent(""" patches: "1.0": - patch_file: "patches/patch1" """) client.save({"conandata.yml": conandata}) client.run("source .") client.run("build .", assert_error=True) assert "MISTAKE2 BUILD!" in client.out client.save( {"src/myfile.cpp": "correct1\nsomething\ncorrect2\nmistake3\nsome\n"}) # compute the patch mkdir(os.path.join(client.current_folder, "patches")) client.run_command("cd src && git diff > ../patches/patch2") client.run_command("cd src && git add . && git commit -m patch1") conandata = textwrap.dedent(""" patches: "1.0": - patch_file: "patches/patch1" - patch_file: "patches/patch2" """) client.save({"conandata.yml": conandata}) client.run("source .") client.run("build .", assert_error=True) assert "MISTAKE3 BUILD!" in client.out client.save( {"src/myfile.cpp": "correct1\nsomething\ncorrect2\ncorrect3\nsome\n"}) # compute the patch mkdir(os.path.join(client.current_folder, "patches")) client.run_command("cd src && git diff > ../patches/patch3") client.run_command("cd src && git add . && git commit -m patch1") conandata = textwrap.dedent(""" patches: "1.0": - patch_file: "patches/patch1" - patch_file: "patches/patch2" - patch_file: "patches/patch3" """) client.save({"conandata.yml": conandata}) client.run("source .") client.run("build .") assert "conanfile.py (mypkg/1.0): Calling build()" in client.out # of course create should work too client.run("create .") assert "mypkg/1.0: Created package" in client.out