From 3c2d88e24eb8a11d1907867d6b46e656dd1e5653 Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Thu, 10 Jan 2019 08:57:16 +0100 Subject: [PATCH 1/7] use alternate function for SVN check to avoid warning/error message in the output (suggested by @ahauan4) --- conans/client/tools/scm.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/conans/client/tools/scm.py b/conans/client/tools/scm.py index 550314f91ef..c0189256abe 100644 --- a/conans/client/tools/scm.py +++ b/conans/client/tools/scm.py @@ -346,7 +346,13 @@ def get_branch(self): raise ConanException("Unable to get svn branch from %s: %s" % (self.folder, str(e))) def _check_svn_repo(self): + """ Check if it is a valid SVN repo """ try: - self.run("info") - except Exception: - raise ConanException("Not a valid SVN repository") \ No newline at end of file + with chdir(self.folder) if self.folder else no_op(): + process = subprocess.Popen(['svn', 'info'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + process.communicate() + except FileNotFoundError: + raise ConanException("Not a valid SVN repository") + else: + if bool(process.returncode): + raise ConanException("Not a valid SVN repository") From 62270d1631059caf52acd3fd7b3af695ebaad765 Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Thu, 10 Jan 2019 09:37:00 +0100 Subject: [PATCH 2/7] pep8 --- conans/client/tools/scm.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conans/client/tools/scm.py b/conans/client/tools/scm.py index c0189256abe..3194416c5f0 100644 --- a/conans/client/tools/scm.py +++ b/conans/client/tools/scm.py @@ -349,7 +349,8 @@ def _check_svn_repo(self): """ Check if it is a valid SVN repo """ try: with chdir(self.folder) if self.folder else no_op(): - process = subprocess.Popen(['svn', 'info'], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + process = subprocess.Popen(['svn', 'info'], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) process.communicate() except FileNotFoundError: raise ConanException("Not a valid SVN repository") From 6372ddf952ac317605d814fe2bd76a5267ca3179 Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Thu, 10 Jan 2019 10:32:47 +0100 Subject: [PATCH 3/7] specific test to check SVN::_check_svn_repo --- conans/test/unittests/util/tools_test.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/conans/test/unittests/util/tools_test.py b/conans/test/unittests/util/tools_test.py index c3d4b7d0a8c..61ae547ea8d 100644 --- a/conans/test/unittests/util/tools_test.py +++ b/conans/test/unittests/util/tools_test.py @@ -1628,6 +1628,19 @@ def build(self): @attr("slow") @attr('svn') class SVNToolTestsBasic(SVNLocalRepoTestCase): + + def test_check_svn_repo(self): + project_url, _ = self.create_project(files={'myfile': "contents"}) + tmp_folder = self.gimme_tmp() + svn = SVN(folder=tmp_folder) + with self.assertRaisesRegexp(ConanException, "Not a valid SVN repository"): + svn._check_svn_repo() + svn.checkout(url=project_url) + try: + svn._check_svn_repo() + except Exception: + self.fail("After checking out, it should be a valid SVN repository") + def test_clone(self): project_url, _ = self.create_project(files={'myfile': "contents"}) tmp_folder = self.gimme_tmp() @@ -1821,6 +1834,7 @@ def test_branch(self): svn.checkout(url='/'.join([project_url, 'prj1', 'tags', 'v12.3.4'])) self.assertEqual("tags/v12.3.4", svn.get_branch()) + @attr("slow") @attr('svn') class SVNToolTestsBasicOldVersion(SVNToolTestsBasic): From 573a6ac85e01a6af73cc00f0b4f362aeb0b4b42e Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Mon, 14 Jan 2019 12:33:36 +0100 Subject: [PATCH 4/7] raise proper message if 'svn' command is not available --- conans/client/tools/scm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conans/client/tools/scm.py b/conans/client/tools/scm.py index 3194416c5f0..f0e89aed2df 100644 --- a/conans/client/tools/scm.py +++ b/conans/client/tools/scm.py @@ -353,7 +353,7 @@ def _check_svn_repo(self): stdout=subprocess.PIPE, stderr=subprocess.PIPE) process.communicate() except FileNotFoundError: - raise ConanException("Not a valid SVN repository") + raise ConanException("Command 'svn' does not exists") else: if bool(process.returncode): raise ConanException("Not a valid SVN repository") From 7d5d7e0e18cd711614ffa72554202001334930d6 Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Fri, 15 Feb 2019 13:00:19 +0100 Subject: [PATCH 5/7] reorder params layout --- conans/client/tools/scm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conans/client/tools/scm.py b/conans/client/tools/scm.py index b91ab6d329f..08291eddfdf 100644 --- a/conans/client/tools/scm.py +++ b/conans/client/tools/scm.py @@ -366,8 +366,8 @@ def _check_svn_repo(self): """ Check if it is a valid SVN repo """ try: with chdir(self.folder) if self.folder else no_op(): - process = subprocess.Popen(['svn', 'info'], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) + process = subprocess.Popen(['svn', 'info'], stdout=subprocess.PIPE, + stderr=subprocess.PIPE) process.communicate() except FileNotFoundError: raise ConanException("Command 'svn' does not exists") From d503cc71ca12b0e8a63c7daae50d4a2ae9e9cecd Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Tue, 26 Feb 2019 17:29:44 +0100 Subject: [PATCH 6/7] merge SVN/Git check_repo functionalities with the one implemented in detect_repo_type (moved to SCM.detect_repo) --- conans/client/cmd/export.py | 9 ++-- conans/client/tools/scm.py | 61 +++++++++++++----------- conans/model/scm.py | 38 +++++---------- conans/test/unittests/model/scm_test.py | 37 ++++++++++++++ conans/test/unittests/util/tools_test.py | 8 ++-- 5 files changed, 93 insertions(+), 60 deletions(-) create mode 100644 conans/test/unittests/model/scm_test.py diff --git a/conans/client/cmd/export.py b/conans/client/cmd/export.py index d2340e34ab6..b96e5f89ebc 100644 --- a/conans/client/cmd/export.py +++ b/conans/client/cmd/export.py @@ -8,11 +8,9 @@ from conans.client.file_copier import FileCopier from conans.client.output import ScopedOutput from conans.client.remover import DiskRemover -from conans.client.tools import Git, SVN from conans.errors import ConanException from conans.model.manifest import FileTreeManifest from conans.model.scm import SCM, get_scm_data -from conans.model.scm import detect_repo_type from conans.paths import CONANFILE from conans.search.search import search_recipes, search_packages from conans.util.files import is_dirty, load, mkdir, rmdir, save, set_dirty, remove @@ -228,11 +226,14 @@ def _replace_scm_data_in_conanfile(conanfile_path, scm_data): def _detect_scm_revision(path): - repo_type = detect_repo_type(path) + if not path: + return None, None + + repo_type = SCM.detect_scm(path) if not repo_type: return None, None - repo_obj = {"git": Git, "svn": SVN}.get(repo_type)(path) + repo_obj = SCM.availables.get(repo_type)(path) return repo_obj.get_revision(), repo_type diff --git a/conans/client/tools/scm.py b/conans/client/tools/scm.py index 08291eddfdf..806daec6d85 100644 --- a/conans/client/tools/scm.py +++ b/conans/client/tools/scm.py @@ -15,6 +15,24 @@ from conans.util.files import decode_text, to_file_bytes, walk +def _run_muted(cmd, folder=None): + with chdir(folder) if folder else no_op(): + process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + process.communicate() + return process.returncode + + +def _check_repo(cmd, folder, msg=None): + msg = msg or "Not a valid '{}' repository".format(cmd[0]) + try: + ret = _run_muted(cmd, folder=folder) + except Exception: + raise ConanException(msg) + else: + if bool(ret): + raise ConanException(msg) + + class SCMBase(object): cmd_command = None @@ -90,7 +108,7 @@ def clone(self, url, branch=None, args=""): return output def checkout(self, element, submodule=None): - self._check_git_repo() + self.check_repo() output = self.run('checkout "%s"' % element) if submodule: @@ -130,7 +148,7 @@ def excluded_files(self): return ret def get_remote_url(self, remote_name=None, remove_credentials=False): - self._check_git_repo() + self.check_repo() remote_name = remote_name or "origin" remotes = self.run("remote -v") for remote in remotes.splitlines(): @@ -147,7 +165,7 @@ def is_local_repository(self): return os.path.exists(url) def get_commit(self): - self._check_git_repo() + self.check_repo() try: commit = self.run("rev-parse HEAD") commit = commit.strip() @@ -158,7 +176,7 @@ def get_commit(self): get_revision = get_commit def is_pristine(self): - self._check_git_repo() + self.check_repo() status = self.run("status --porcelain").strip() if not status: return True @@ -166,11 +184,11 @@ def is_pristine(self): return False def get_repo_root(self): - self._check_git_repo() + self.check_repo() return self.run("rev-parse --show-toplevel") def get_branch(self): - self._check_git_repo() + self.check_repo() try: status = self.run("status -bs --porcelain") # ## feature/scm_branch...myorigin/feature/scm_branch @@ -180,7 +198,7 @@ def get_branch(self): raise ConanException("Unable to get git branch from %s: %s" % (self.folder, str(e))) def get_tag(self): - self._check_git_repo() + self.check_repo() try: status = self.run("describe --exact-match --tags") tag = status.strip() @@ -188,11 +206,9 @@ def get_tag(self): except Exception: return None - def _check_git_repo(self): - try: - self.run("status") - except Exception: - raise ConanException("Not a valid git repository") + def check_repo(self): + """ Check if it is a valid GIT repo """ + _check_repo(["git", "status"], folder=self.folder) class SVN(SCMBase): @@ -234,7 +250,7 @@ def run(self, command): return super(SVN, self).run(command="{} {}".format(command, extra_options)) def _show_item(self, item, target='.'): - self._check_svn_repo() + self.check_repo() if self.version >= SVN.API_CHANGE_VERSION: value = self.run("info --show-item {item} \"{target}\"".format(item=item, target=target)) return value.strip() @@ -260,7 +276,7 @@ def _show_item(self, item, target='.'): def checkout(self, url, revision="HEAD"): output = "" try: - self._check_svn_repo() + self.check_repo() except ConanException: output += self.run('co "{url}" .'.format(url=url)) else: @@ -272,11 +288,11 @@ def checkout(self, url, revision="HEAD"): return output def update(self, revision='HEAD'): - self._check_svn_repo() + self.check_repo() return self.run("update -r {rev}".format(rev=revision)) def excluded_files(self): - self._check_svn_repo() + self.check_repo() excluded_list = [] output = self.run("status --no-ignore") for it in output.splitlines(): @@ -362,15 +378,6 @@ def _get_item(self, pattern, item_name): item = re.search(pattern, url) return item.group(0) if item else None - def _check_svn_repo(self): + def check_repo(self): """ Check if it is a valid SVN repo """ - try: - with chdir(self.folder) if self.folder else no_op(): - process = subprocess.Popen(['svn', 'info'], stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - process.communicate() - except FileNotFoundError: - raise ConanException("Command 'svn' does not exists") - else: - if bool(process.returncode): - raise ConanException("Not a valid SVN repository") + _check_repo(["svn", "info"], folder=self.folder) diff --git a/conans/model/scm.py b/conans/model/scm.py index 637ed555688..4d69357ff4e 100644 --- a/conans/model/scm.py +++ b/conans/model/scm.py @@ -1,10 +1,8 @@ import json import os -import subprocess from conans.client.tools.scm import Git, SVN from conans.errors import ConanException -from conans.tools import chdir def get_scm_data(conanfile): @@ -52,29 +50,9 @@ def __repr__(self): return json.dumps(d, sort_keys=True) -def detect_repo_type(folder): - if not folder: - return None - - def _run_command_ignore_output(cmd): - with chdir(folder): - try: - ret = subprocess.call(cmd, shell=True, - stdout=open(os.devnull, 'w'), stderr=subprocess.STDOUT) - if ret != 0: - return False - except Exception: - return False - return True - - if _run_command_ignore_output("git status"): - return "git" - elif _run_command_ignore_output("svn info"): - return "svn" - return None - - class SCM(object): + availables = {'git': Git, 'svn': SVN} + def __init__(self, data, repo_folder, output): self._data = data self._output = output @@ -82,8 +60,18 @@ def __init__(self, data, repo_folder, output): # Finally instance a repo self.repo = self._get_repo() + @classmethod + def detect_scm(cls, folder): + for name, candidate in cls.availables.items(): + try: + candidate(folder).check_repo() + return name + except ConanException: + pass + return None + def _get_repo(self): - repo_class = {"git": Git, "svn": SVN}.get(self._data.type) + repo_class = self.availables.get(self._data.type) if not repo_class: raise ConanException("SCM not supported: %s" % self._data.type) diff --git a/conans/test/unittests/model/scm_test.py b/conans/test/unittests/model/scm_test.py new file mode 100644 index 00000000000..af086470b44 --- /dev/null +++ b/conans/test/unittests/model/scm_test.py @@ -0,0 +1,37 @@ +# coding=utf-8 + +import tempfile +import unittest + +from mock import mock + +from conans.errors import ConanException +from conans.model.scm import SCM + + +class SCMDetectRepoTest(unittest.TestCase): + + def setUp(self): + self.folder = tempfile.gettempdir() + # Be sure there is no repo in the folder to test + for name, candidate in SCM.availables.items(): + try: + candidate(folder=self.folder).check_repo() + except ConanException: + pass + else: + self.fail("There is a repo of type '{}' in the folder to test".format(name)) + + def test_svn(self): + with mock.patch("conans.client.tools.scm.SVN.check_repo", return_value=None): + r = SCM.detect_scm(folder=tempfile.gettempdir()) + self.assertEqual(r, "svn") + + def test_git(self): + with mock.patch("conans.client.tools.scm.Git.check_repo", return_value=None): + r = SCM.detect_scm(folder=tempfile.gettempdir()) + self.assertEqual(r, "git") + + def test_none(self): + r = SCM.detect_scm(folder=tempfile.gettempdir()) + self.assertEqual(r, None) diff --git a/conans/test/unittests/util/tools_test.py b/conans/test/unittests/util/tools_test.py index 81e401d9192..c43aca34fb3 100644 --- a/conans/test/unittests/util/tools_test.py +++ b/conans/test/unittests/util/tools_test.py @@ -1696,7 +1696,7 @@ def test_get_tag_no_git_repo(self): Try to get tag out of a git repo """ git = Git(folder=temp_folder()) - with self.assertRaisesRegexp(ConanException, "Not a valid git repository"): + with self.assertRaisesRegexp(ConanException, "Not a valid 'git' repository"): git.get_tag() def test_excluded_files(self): @@ -1715,11 +1715,11 @@ def test_check_svn_repo(self): project_url, _ = self.create_project(files={'myfile': "contents"}) tmp_folder = self.gimme_tmp() svn = SVN(folder=tmp_folder) - with self.assertRaisesRegexp(ConanException, "Not a valid SVN repository"): - svn._check_svn_repo() + with self.assertRaisesRegexp(ConanException, "Not a valid 'svn' repository"): + svn.check_repo() svn.checkout(url=project_url) try: - svn._check_svn_repo() + svn.check_repo() except Exception: self.fail("After checking out, it should be a valid SVN repository") From 5a71beba57bb3dace081380211f80548b75403d3 Mon Sep 17 00:00:00 2001 From: "Javier G. Sogo" Date: Tue, 26 Feb 2019 17:47:32 +0100 Subject: [PATCH 7/7] fix tests (typo) --- conans/test/functional/scm/test_command_export.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/conans/test/functional/scm/test_command_export.py b/conans/test/functional/scm/test_command_export.py index dca978c1ed6..811d336ddc2 100644 --- a/conans/test/functional/scm/test_command_export.py +++ b/conans/test/functional/scm/test_command_export.py @@ -33,7 +33,8 @@ def test_no_repo(self, repo_type, autos): rev_value=rev_value) }) self.client.run("export . lib/version@user/channel", assert_error=True) - self.assertIn("ERROR: Not a valid {} repository".format(repo_type), self.client.out) + self.assertIn("ERROR: Not a valid '{}' repository".format(repo_type.lower()), + self.client.out) class ExportCommandTestCase(unittest.TestCase):