| 
									
										
										
										
											2017-06-05 16:02:39 +03:00
										 |  |  | # Copyright (c) 2014-present PlatformIO <contact@platformio.org> | 
					
						
							| 
									
										
										
										
											2016-05-31 00:22:25 +03:00
										 |  |  | # | 
					
						
							|  |  |  | # Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  | # you may not use this file except in compliance with the License. | 
					
						
							|  |  |  | # You may obtain a copy of the License at | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | #    http://www.apache.org/licenses/LICENSE-2.0 | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  | # distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  | # See the License for the specific language governing permissions and | 
					
						
							|  |  |  | # limitations under the License. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  | import re | 
					
						
							|  |  |  | from os.path import join | 
					
						
							| 
									
										
										
										
											2017-11-29 22:46:57 +02:00
										 |  |  | from subprocess import CalledProcessError, check_call | 
					
						
							| 
									
										
										
										
											2016-05-31 00:22:25 +03:00
										 |  |  | from sys import modules | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-07-03 13:35:39 +03:00
										 |  |  | from platformio.exception import PlatformioException, UserSideException | 
					
						
							| 
									
										
										
										
											2019-05-16 21:03:15 +03:00
										 |  |  | from platformio.proc import exec_command | 
					
						
							| 
									
										
										
										
											2016-05-31 00:22:25 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-26 20:54:29 +02:00
										 |  |  | try: | 
					
						
							|  |  |  |     from urllib.parse import urlparse | 
					
						
							|  |  |  | except ImportError: | 
					
						
							|  |  |  |     from urlparse import urlparse | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-31 00:22:25 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | class VCSClientFactory(object): | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							| 
									
										
										
										
											2017-01-30 01:04:06 +02:00
										 |  |  |     def newClient(src_dir, remote_url, silent=False): | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  |         result = urlparse(remote_url) | 
					
						
							|  |  |  |         type_ = result.scheme | 
					
						
							| 
									
										
										
										
											2017-01-24 17:36:34 +02:00
										 |  |  |         tag = None | 
					
						
							| 
									
										
										
										
											2017-11-27 21:04:51 +02:00
										 |  |  |         if not type_ and remote_url.startswith("git+"): | 
					
						
							| 
									
										
										
										
											2016-09-02 22:41:27 +03:00
										 |  |  |             type_ = "git" | 
					
						
							| 
									
										
										
										
											2017-11-27 21:04:51 +02:00
										 |  |  |             remote_url = remote_url[4:] | 
					
						
							| 
									
										
										
										
											2016-09-02 22:41:27 +03:00
										 |  |  |         elif "+" in result.scheme: | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  |             type_, _ = result.scheme.split("+", 1) | 
					
						
							| 
									
										
										
										
											2019-09-23 23:13:48 +03:00
										 |  |  |             remote_url = remote_url[len(type_) + 1 :] | 
					
						
							| 
									
										
										
										
											2017-01-24 17:36:34 +02:00
										 |  |  |         if "#" in remote_url: | 
					
						
							|  |  |  |             remote_url, tag = remote_url.rsplit("#", 1) | 
					
						
							| 
									
										
										
										
											2016-09-02 22:41:27 +03:00
										 |  |  |         if not type_: | 
					
						
							| 
									
										
										
										
											2019-09-23 23:13:48 +03:00
										 |  |  |             raise PlatformioException("VCS: Unknown repository type %s" % remote_url) | 
					
						
							|  |  |  |         obj = getattr(modules[__name__], "%sClient" % type_.title())( | 
					
						
							|  |  |  |             src_dir, remote_url, tag, silent | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2016-05-31 00:22:25 +03:00
										 |  |  |         assert isinstance(obj, VCSClientBase) | 
					
						
							|  |  |  |         return obj | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class VCSClientBase(object): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     command = None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-30 01:04:06 +02:00
										 |  |  |     def __init__(self, src_dir, remote_url=None, tag=None, silent=False): | 
					
						
							| 
									
										
										
										
											2016-07-18 01:38:35 +03:00
										 |  |  |         self.src_dir = src_dir | 
					
						
							|  |  |  |         self.remote_url = remote_url | 
					
						
							| 
									
										
										
										
											2016-08-02 19:10:29 +03:00
										 |  |  |         self.tag = tag | 
					
						
							| 
									
										
										
										
											2017-01-30 01:04:06 +02:00
										 |  |  |         self.silent = silent | 
					
						
							| 
									
										
										
										
											2016-05-31 00:22:25 +03:00
										 |  |  |         self.check_client() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def check_client(self): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             assert self.command | 
					
						
							| 
									
										
										
										
											2017-01-30 01:04:06 +02:00
										 |  |  |             if self.silent: | 
					
						
							|  |  |  |                 self.get_cmd_output(["--version"]) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 assert self.run_cmd(["--version"]) | 
					
						
							|  |  |  |         except (AssertionError, OSError, PlatformioException): | 
					
						
							| 
									
										
										
										
											2017-07-03 13:35:39 +03:00
										 |  |  |             raise UserSideException( | 
					
						
							| 
									
										
										
										
											2019-09-23 23:13:48 +03:00
										 |  |  |                 "VCS: `%s` client is not installed in your system" % self.command | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2016-05-31 00:22:25 +03:00
										 |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-18 01:38:35 +03:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def storage_dir(self): | 
					
						
							|  |  |  |         return join(self.src_dir, "." + self.command) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def export(self): | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  |     def update(self): | 
					
						
							|  |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def can_be_updated(self): | 
					
						
							| 
									
										
										
										
											2016-08-02 19:10:29 +03:00
										 |  |  |         return not self.tag | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def get_current_revision(self): | 
					
						
							| 
									
										
										
										
											2016-05-31 00:22:25 +03:00
										 |  |  |         raise NotImplementedError | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-30 01:04:06 +02:00
										 |  |  |     def get_latest_revision(self): | 
					
						
							|  |  |  |         return None if self.can_be_updated else self.get_current_revision() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-18 01:38:35 +03:00
										 |  |  |     def run_cmd(self, args, **kwargs): | 
					
						
							|  |  |  |         args = [self.command] + args | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  |         if "cwd" not in kwargs: | 
					
						
							| 
									
										
										
										
											2019-09-23 23:13:48 +03:00
										 |  |  |             kwargs["cwd"] = self.src_dir | 
					
						
							| 
									
										
										
										
											2017-11-29 22:46:57 +02:00
										 |  |  |         try: | 
					
						
							|  |  |  |             check_call(args, **kwargs) | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |         except CalledProcessError as e: | 
					
						
							| 
									
										
										
										
											2019-09-23 23:13:48 +03:00
										 |  |  |             raise PlatformioException("VCS: Could not process command %s" % e.cmd) | 
					
						
							| 
									
										
										
										
											2016-07-18 01:38:35 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def get_cmd_output(self, args, **kwargs): | 
					
						
							|  |  |  |         args = [self.command] + args | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  |         if "cwd" not in kwargs: | 
					
						
							| 
									
										
										
										
											2019-09-23 23:13:48 +03:00
										 |  |  |             kwargs["cwd"] = self.src_dir | 
					
						
							| 
									
										
										
										
											2019-05-16 21:03:15 +03:00
										 |  |  |         result = exec_command(args, **kwargs) | 
					
						
							| 
									
										
										
										
											2019-09-23 23:13:48 +03:00
										 |  |  |         if result["returncode"] == 0: | 
					
						
							|  |  |  |             return result["out"].strip() | 
					
						
							| 
									
										
										
										
											2016-07-18 01:38:35 +03:00
										 |  |  |         raise PlatformioException( | 
					
						
							| 
									
										
										
										
											2019-09-23 23:13:48 +03:00
										 |  |  |             "VCS: Could not receive an output from `%s` command (%s)" % (args, result) | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2016-05-31 00:22:25 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class GitClient(VCSClientBase): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     command = "git" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-11-29 22:46:57 +02:00
										 |  |  |     def check_client(self): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             return VCSClientBase.check_client(self) | 
					
						
							|  |  |  |         except UserSideException: | 
					
						
							|  |  |  |             raise UserSideException( | 
					
						
							| 
									
										
										
										
											2019-09-23 23:13:48 +03:00
										 |  |  |                 "Please install Git client from https://git-scm.com/downloads" | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2017-11-29 22:46:57 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  |     def get_branches(self): | 
					
						
							|  |  |  |         output = self.get_cmd_output(["branch"]) | 
					
						
							|  |  |  |         output = output.replace("*", "")  # fix active branch | 
					
						
							|  |  |  |         return [b.strip() for b in output.split("\n")] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-30 20:21:16 +02:00
										 |  |  |     def get_current_branch(self): | 
					
						
							|  |  |  |         output = self.get_cmd_output(["branch"]) | 
					
						
							|  |  |  |         for line in output.split("\n"): | 
					
						
							|  |  |  |             line = line.strip() | 
					
						
							|  |  |  |             if line.startswith("*"): | 
					
						
							| 
									
										
										
										
											2017-01-30 23:54:55 +02:00
										 |  |  |                 branch = line[1:].strip() | 
					
						
							|  |  |  |                 if branch != "(no branch)": | 
					
						
							|  |  |  |                     return branch | 
					
						
							| 
									
										
										
										
											2017-01-30 20:21:16 +02:00
										 |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  |     def get_tags(self): | 
					
						
							|  |  |  |         output = self.get_cmd_output(["tag", "-l"]) | 
					
						
							|  |  |  |         return [t.strip() for t in output.split("\n")] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @staticmethod | 
					
						
							|  |  |  |     def is_commit_id(text): | 
					
						
							|  |  |  |         return text and re.match(r"[0-9a-f]{7,}$", text) is not None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def can_be_updated(self): | 
					
						
							| 
									
										
										
										
											2016-08-02 19:10:29 +03:00
										 |  |  |         return not self.tag or not self.is_commit_id(self.tag) | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-18 01:38:35 +03:00
										 |  |  |     def export(self): | 
					
						
							| 
									
										
										
										
											2016-08-02 19:10:29 +03:00
										 |  |  |         is_commit = self.is_commit_id(self.tag) | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  |         args = ["clone", "--recursive"] | 
					
						
							| 
									
										
										
										
											2016-08-02 19:10:29 +03:00
										 |  |  |         if not self.tag or not is_commit: | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  |             args += ["--depth", "1"] | 
					
						
							| 
									
										
										
										
											2016-08-02 19:10:29 +03:00
										 |  |  |             if self.tag: | 
					
						
							|  |  |  |                 args += ["--branch", self.tag] | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  |         args += [self.remote_url, self.src_dir] | 
					
						
							|  |  |  |         assert self.run_cmd(args) | 
					
						
							|  |  |  |         if is_commit: | 
					
						
							| 
									
										
										
										
											2019-10-02 11:52:14 +03:00
										 |  |  |             assert self.run_cmd(["reset", "--hard", self.tag]) | 
					
						
							| 
									
										
										
										
											2019-10-02 12:34:20 +03:00
										 |  |  |             return self.run_cmd( | 
					
						
							|  |  |  |                 ["submodule", "update", "--init", "--recursive", "--force"] | 
					
						
							|  |  |  |             ) | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  |         return True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def update(self): | 
					
						
							| 
									
										
										
										
											2017-04-15 16:19:41 +03:00
										 |  |  |         args = ["pull", "--recurse-submodules"] | 
					
						
							| 
									
										
										
										
											2016-07-18 01:38:35 +03:00
										 |  |  |         return self.run_cmd(args) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  |     def get_current_revision(self): | 
					
						
							|  |  |  |         return self.get_cmd_output(["rev-parse", "--short", "HEAD"]) | 
					
						
							| 
									
										
										
										
											2016-05-31 00:22:25 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-30 01:04:06 +02:00
										 |  |  |     def get_latest_revision(self): | 
					
						
							|  |  |  |         if not self.can_be_updated: | 
					
						
							| 
									
										
										
										
											2017-01-30 23:54:55 +02:00
										 |  |  |             return self.get_current_revision() | 
					
						
							| 
									
										
										
										
											2017-01-30 20:21:16 +02:00
										 |  |  |         branch = self.get_current_branch() | 
					
						
							| 
									
										
										
										
											2017-01-30 23:54:55 +02:00
										 |  |  |         if not branch: | 
					
						
							|  |  |  |             return self.get_current_revision() | 
					
						
							| 
									
										
										
										
											2017-01-30 01:04:06 +02:00
										 |  |  |         result = self.get_cmd_output(["ls-remote"]) | 
					
						
							|  |  |  |         for line in result.split("\n"): | 
					
						
							| 
									
										
										
										
											2017-01-30 20:21:16 +02:00
										 |  |  |             ref_pos = line.strip().find("refs/heads/" + branch) | 
					
						
							|  |  |  |             if ref_pos > 0: | 
					
						
							|  |  |  |                 return line[:ref_pos].strip()[:7] | 
					
						
							| 
									
										
										
										
											2017-01-30 01:04:06 +02:00
										 |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-31 00:22:25 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | class HgClient(VCSClientBase): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     command = "hg" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-18 01:38:35 +03:00
										 |  |  |     def export(self): | 
					
						
							| 
									
										
										
										
											2016-05-31 00:22:25 +03:00
										 |  |  |         args = ["clone"] | 
					
						
							| 
									
										
										
										
											2016-08-02 19:10:29 +03:00
										 |  |  |         if self.tag: | 
					
						
							|  |  |  |             args.extend(["--updaterev", self.tag]) | 
					
						
							| 
									
										
										
										
											2016-07-18 01:38:35 +03:00
										 |  |  |         args.extend([self.remote_url, self.src_dir]) | 
					
						
							|  |  |  |         return self.run_cmd(args) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  |     def update(self): | 
					
						
							|  |  |  |         args = ["pull", "--update"] | 
					
						
							|  |  |  |         return self.run_cmd(args) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_current_revision(self): | 
					
						
							|  |  |  |         return self.get_cmd_output(["identify", "--id"]) | 
					
						
							| 
									
										
										
										
											2016-05-31 00:22:25 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-30 01:04:06 +02:00
										 |  |  |     def get_latest_revision(self): | 
					
						
							|  |  |  |         if not self.can_be_updated: | 
					
						
							|  |  |  |             return self.get_latest_revision() | 
					
						
							|  |  |  |         return self.get_cmd_output(["identify", "--id", self.remote_url]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-31 00:22:25 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | class SvnClient(VCSClientBase): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     command = "svn" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-18 01:38:35 +03:00
										 |  |  |     def export(self): | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  |         args = ["checkout"] | 
					
						
							| 
									
										
										
										
											2016-08-02 19:10:29 +03:00
										 |  |  |         if self.tag: | 
					
						
							|  |  |  |             args.extend(["--revision", self.tag]) | 
					
						
							| 
									
										
										
										
											2016-07-18 01:38:35 +03:00
										 |  |  |         args.extend([self.remote_url, self.src_dir]) | 
					
						
							|  |  |  |         return self.run_cmd(args) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  |     def update(self): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         args = ["update"] | 
					
						
							|  |  |  |         return self.run_cmd(args) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def get_current_revision(self): | 
					
						
							| 
									
										
										
										
											2016-12-23 21:57:11 +02:00
										 |  |  |         output = self.get_cmd_output( | 
					
						
							| 
									
										
										
										
											2019-09-23 23:13:48 +03:00
										 |  |  |             ["info", "--non-interactive", "--trust-server-cert", "-r", "HEAD"] | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2016-08-01 17:05:48 +03:00
										 |  |  |         for line in output.split("\n"): | 
					
						
							|  |  |  |             line = line.strip() | 
					
						
							|  |  |  |             if line.startswith("Revision:"): | 
					
						
							|  |  |  |                 return line.split(":", 1)[1].strip() | 
					
						
							|  |  |  |         raise PlatformioException("Could not detect current SVN revision") |