def update(self, dest, url, rev_options): # type: (str, HiddenText, RevOptions) -> None cmd_args = make_command( 'update', self.get_remote_call_options(), rev_options.to_args(), dest, ) self.run_command(cmd_args)
def fetch_new(self, dest, url, rev_options): # type: (str, HiddenText, RevOptions) -> None rev_display = rev_options.to_display() logger.info( 'Cloning hg %s%s to %s', url, rev_display, display_path(dest), ) self.run_command(make_command('clone', '--noupdate', '-q', url, dest)) self.run_command( make_command('update', '-q', rev_options.to_args()), cwd=dest, )
def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: cmd_args = make_command( "switch", self.get_remote_call_options(), rev_options.to_args(), url, dest, ) self.run_command(cmd_args)
def switch(self, dest, url, rev_options): # type: (str, HiddenText, RevOptions) -> None cmd_args = make_command( "switch", self.get_remote_call_options(), rev_options.to_args(), url, dest, ) self.run_command(cmd_args)
def fetch_new(self, dest, url, rev_options): # type: (str, HiddenText, RevOptions) -> None rev_display = rev_options.to_display() logger.info( "Checking out %s%s to %s", url, rev_display, display_path(dest), ) cmd_args = make_command("branch", "-q", rev_options.to_args(), url, dest) self.run_command(cmd_args)
def export(self, location, url): # type: (str, HiddenText) -> None """ Export the Bazaar repository at the url to the destination location """ # Remove the location to make sure Bazaar can export it correctly if os.path.exists(location): rmtree(location) url, rev_options = self.get_url_rev_options(url) self.run_command( make_command("export", location, url, rev_options.to_args()))
def run_command( cls, cmd, # type: Union[List[str], CommandArgs] show_stdout=True, # type: bool cwd=None, # type: Optional[str] on_returncode="raise", # type: Literal["raise", "warn", "ignore"] extra_ok_returncodes=None, # type: Optional[Iterable[int]] command_desc=None, # type: Optional[str] extra_environ=None, # type: Optional[Mapping[str, Any]] spinner=None, # type: Optional[SpinnerInterface] log_failed_cmd=True, # type: bool stdout_only=False, # type: bool ): # type: (...) -> str """ Run a VCS subcommand This is simply a wrapper around call_subprocess that adds the VCS command name, and checks that the VCS is available """ cmd = make_command(cls.name, *cmd) try: return call_subprocess( cmd, show_stdout, cwd, on_returncode=on_returncode, extra_ok_returncodes=extra_ok_returncodes, command_desc=command_desc, extra_environ=extra_environ, unset_environ=cls.unset_environ, spinner=spinner, log_failed_cmd=log_failed_cmd, stdout_only=stdout_only, ) except FileNotFoundError: # errno.ENOENT = no such file or directory # In other words, the VCS executable isn't available raise BadCommand( f"Cannot find command {cls.name!r} - do you have " f"{cls.name!r} installed and in your PATH?" ) except PermissionError: # errno.EACCES = Permission denied # This error occurs, for instance, when the command is installed # only for another user. So, the current user don't have # permission to call the other user command. raise BadCommand( f"No permission to execute {cls.name!r} - install it " f"locally, globally (ask admin), or check your PATH. " f"See possible solutions at " f"https://pip.pypa.io/en/latest/reference/pip_freeze/" f"#fixing-permission-denied." )
def fetch_new(self, dest, url, rev_options): # type: (str, HiddenText, RevOptions) -> None rev_display = rev_options.to_display() logger.info("Cloning %s%s to %s", url, rev_display, display_path(dest)) self.run_command(make_command("clone", "-q", url, dest)) if rev_options.rev: # Then a specific revision was requested. rev_options = self.resolve_revision(dest, url, rev_options) branch_name = getattr(rev_options, "branch_name", None) if branch_name is None: # Only do a checkout if the current commit id doesn't match # the requested revision. if not self.is_commit_id_equal(dest, rev_options.rev): cmd_args = make_command( "checkout", "-q", rev_options.to_args(), ) self.run_command(cmd_args, cwd=dest) elif self.get_current_branch(dest) != branch_name: # Then a specific branch was requested, and that branch # is not yet checked out. track_branch = f"origin/{branch_name}" cmd_args = [ "checkout", "-b", branch_name, "--track", track_branch, ] self.run_command(cmd_args, cwd=dest) else: sha = self.get_revision(dest) rev_options = rev_options.make_new(sha) logger.info("Resolved %s to commit %s", url, rev_options.rev) #: repo may contain submodules self.update_submodules(dest)
def switch(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: repo_config = os.path.join(dest, self.dirname, "hgrc") config = configparser.RawConfigParser() try: config.read(repo_config) config.set("paths", "default", url.secret) with open(repo_config, "w") as config_file: config.write(config_file) except (OSError, configparser.NoSectionError) as exc: logger.warning("Could not switch Mercurial repository to %s: %s", url, exc) else: cmd_args = make_command("update", "-q", rev_options.to_args()) self.run_command(cmd_args, cwd=dest)
def update(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: # First fetch changes from the default remote if self.get_git_version() >= (1, 9): # fetch tags in addition to everything else self.run_command(["fetch", "-q", "--tags"], cwd=dest) else: self.run_command(["fetch", "-q"], cwd=dest) # Then reset to wanted revision (maybe even origin/master) rev_options = self.resolve_revision(dest, url, rev_options) cmd_args = make_command("reset", "--hard", "-q", rev_options.to_args()) self.run_command(cmd_args, cwd=dest) #: update submodules self.update_submodules(dest)
def fetch_new(self, dest, url, rev_options): # type: (str, HiddenText, RevOptions) -> None rev_display = rev_options.to_display() logger.info( 'Checking out %s%s to %s', url, rev_display, display_path(dest), ) cmd_args = ( make_command('branch', '-q', rev_options.to_args(), url, dest) ) run_command(cmd_args)
def fetch_new( self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int ) -> None: rev_display = rev_options.to_display() logger.info( "Cloning hg %s%s to %s", url, rev_display, display_path(dest), ) if verbosity <= 0: flags: Tuple[str, ...] = ("--quiet",) elif verbosity == 1: flags = () elif verbosity == 2: flags = ("--verbose",) else: flags = ("--verbose", "--debug") self.run_command(make_command("clone", "--noupdate", *flags, url, dest)) self.run_command( make_command("update", *flags, rev_options.to_args()), cwd=dest, )
def update(self, dest, url, rev_options): # type: (str, HiddenText, RevOptions) -> None # First fetch changes from the default remote if self.get_git_version() >= parse_version('1.9.0'): # fetch tags in addition to everything else self.run_command(['fetch', '-q', '--tags'], cwd=dest) else: self.run_command(['fetch', '-q'], cwd=dest) # Then reset to wanted revision (maybe even origin/master) rev_options = self.resolve_revision(dest, url, rev_options) cmd_args = make_command('reset', '--hard', '-q', rev_options.to_args()) self.run_command(cmd_args, cwd=dest) #: update submodules self.update_submodules(dest)
def fetch_new(self, dest, url, rev_options): # type: (str, HiddenText, RevOptions) -> None rev_display = rev_options.to_display() logger.info( 'Checking out %s%s to %s', url, rev_display, display_path(dest), ) cmd_args = make_command( 'checkout', '-q', self.get_remote_call_options(), rev_options.to_args(), url, dest, ) self.run_command(cmd_args)
def run_command( cls, cmd, # type: Union[List[str], CommandArgs] show_stdout=True, # type: bool cwd=None, # type: Optional[str] on_returncode='raise', # type: str extra_ok_returncodes=None, # type: Optional[Iterable[int]] command_desc=None, # type: Optional[str] extra_environ=None, # type: Optional[Mapping[str, Any]] spinner=None, # type: Optional[SpinnerInterface] log_failed_cmd=True, # type: bool stdout_only=False, # type: bool ): # type: (...) -> str """ Run a VCS subcommand This is simply a wrapper around call_subprocess that adds the VCS command name, and checks that the VCS is available """ cmd = make_command(cls.name, *cmd) try: return call_subprocess(cmd, show_stdout, cwd, on_returncode=on_returncode, extra_ok_returncodes=extra_ok_returncodes, command_desc=command_desc, extra_environ=extra_environ, unset_environ=cls.unset_environ, spinner=spinner, log_failed_cmd=log_failed_cmd, stdout_only=stdout_only) except FileNotFoundError: # errno.ENOENT = no such file or directory # In other words, the VCS executable isn't available raise BadCommand('Cannot find command {cls.name!r} - do you have ' '{cls.name!r} installed and in your ' 'PATH?'.format(**locals()))
def export(self, location, url): # type: (str, HiddenText) -> None """Export the svn repository at the url to the destination location""" url, rev_options = self.get_url_rev_options(url) logger.info('Exporting svn repository %s to %s', url, location) with indent_log(): if os.path.exists(location): # Subversion doesn't like to check out over an existing # directory --force fixes this, but was only added in svn 1.5 rmtree(location) cmd_args = make_command( 'export', self.get_remote_call_options(), rev_options.to_args(), url, location, )
def switch(self, dest, url, rev_options): # type: (str, HiddenText, RevOptions) -> None repo_config = os.path.join(dest, self.dirname, 'hgrc') config = configparser.RawConfigParser() try: config.read(repo_config) config.set('paths', 'default', url.secret) with open(repo_config, 'w') as config_file: config.write(config_file) except (OSError, configparser.NoSectionError) as exc: logger.warning( 'Could not switch Mercurial repository to %s: %s', url, exc, ) else: cmd_args = make_command('update', '-q', rev_options.to_args()) self.run_command(cmd_args, cwd=dest)
def fetch_new(self, dest: str, url: HiddenText, rev_options: RevOptions) -> None: rev_display = rev_options.to_display() logger.info( "Checking out %s%s to %s", url, rev_display, display_path(dest), ) cmd_args = make_command( "checkout", "-q", self.get_remote_call_options(), rev_options.to_args(), url, dest, ) self.run_command(cmd_args)
def fetch_new(self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int) -> None: rev_display = rev_options.to_display() logger.info( "Checking out %s%s to %s", url, rev_display, display_path(dest), ) if verbosity <= 0: flag = "--quiet" elif verbosity == 1: flag = "" else: flag = f"-{'v'*verbosity}" cmd_args = make_command("branch", flag, rev_options.to_args(), url, dest) self.run_command(cmd_args)
def resolve_revision( cls, dest: str, url: HiddenText, rev_options: RevOptions ) -> RevOptions: """ Resolve a revision to a new RevOptions object with the SHA1 of the branch, tag, or ref if found. Args: rev_options: a RevOptions object. """ rev = rev_options.arg_rev # The arg_rev property's implementation for Git ensures that the # rev return value is always non-None. assert rev is not None sha, is_branch = cls.get_revision_sha(dest, rev) if sha is not None: rev_options = rev_options.make_new(sha) rev_options.branch_name = rev if is_branch else None return rev_options # Do not show a warning for the common case of something that has # the form of a Git commit hash. if not looks_like_hash(rev): logger.warning( "Did not find branch or tag '%s', assuming revision or ref.", rev, ) if not cls._should_fetch(dest, rev): return rev_options # fetch the requested revision cls.run_command( make_command("fetch", "-q", url, rev_options.to_args()), cwd=dest, ) # Change the revision to the SHA of the ref we fetched sha = cls.get_revision(dest, rev="FETCH_HEAD") rev_options = rev_options.make_new(sha) return rev_options
def resolve_revision(cls, dest, url, rev_options): # type: (str, HiddenText, RevOptions) -> RevOptions """ Resolve a revision to a new RevOptions object with the SHA1 of the branch, tag, or ref if found. Args: rev_options: a RevOptions object. """ rev = rev_options.arg_rev # The arg_rev property's implementation for Git ensures that the # rev return value is always non-None. assert rev is not None sha, is_branch = cls.get_revision_sha(dest, rev) if sha is not None: rev_options = rev_options.make_new(sha) rev_options.branch_name = rev if is_branch else None return rev_options # Do not show a warning for the common case of something that has # the form of a Git commit hash. if not looks_like_hash(rev): logger.warning( "Did not find branch or tag '%s', assuming revision or ref.", rev, ) if not rev.startswith('refs/'): return rev_options # If it looks like a ref, we have to fetch it explicitly. cls.run_command( make_command('fetch', '-q', url, rev_options.to_args()), cwd=dest, ) # Change the revision to the SHA of the ref we fetched sha = cls.get_revision(dest, rev='FETCH_HEAD') rev_options = rev_options.make_new(sha) return rev_options
def run_command( cls, cmd, # type: Union[List[str], CommandArgs] show_stdout=True, # type: bool cwd=None, # type: Optional[str] on_returncode="raise", # type: str extra_ok_returncodes=None, # type: Optional[Iterable[int]] command_desc=None, # type: Optional[str] extra_environ=None, # type: Optional[Mapping[str, Any]] spinner=None, # type: Optional[SpinnerInterface] log_failed_cmd=True, # type: bool ): # type: (...) -> Text """ Run a VCS subcommand This is simply a wrapper around call_subprocess that adds the VCS command name, and checks that the VCS is available """ cmd = make_command(cls.name, *cmd) try: return call_subprocess( cmd, show_stdout, cwd, on_returncode=on_returncode, extra_ok_returncodes=extra_ok_returncodes, command_desc=command_desc, extra_environ=extra_environ, unset_environ=cls.unset_environ, spinner=spinner, log_failed_cmd=log_failed_cmd, ) except OSError as e: # errno.ENOENT = no such file or directory # In other words, the VCS executable isn't available if e.errno == errno.ENOENT: raise BadCommand( "Cannot find command %r - do you have " "%r installed and in your " "PATH?" % (cls.name, cls.name) ) else: raise # re-raise exception if a different error occurred
def fetch_new(self, dest: str, url: HiddenText, rev_options: RevOptions, verbosity: int) -> None: rev_display = rev_options.to_display() logger.info( "Checking out %s%s to %s", url, rev_display, display_path(dest), ) if verbosity <= 0: flag = "--quiet" else: flag = "" cmd_args = make_command( "checkout", flag, self.get_remote_call_options(), rev_options.to_args(), url, dest, ) self.run_command(cmd_args)
def run_command( cls, cmd, # type: Union[List[str], CommandArgs] cwd=None, # type: Optional[str] extra_environ=None, # type: Optional[Mapping[str, Any]] extra_ok_returncodes=None, # type: Optional[Iterable[int]] log_failed_cmd=True # type: bool ): cmd = make_command(cls.name, *cmd) try: return call_subprocess(cmd, cwd, extra_environ=extra_environ, extra_ok_returncodes=extra_ok_returncodes, log_failed_cmd=log_failed_cmd) except OSError as e: if e.errno == errno.ENOENT: raise BadCommand( 'Cannot find command {cls.name!r} - do you have ' '{cls.name!r} installed and in your ' 'PATH?'.format(**locals())) else: raise # re-raise exception if a different error occurred
def update(self, dest, url, rev_options): # type: (str, HiddenText, RevOptions) -> None cmd_args = make_command("pull", "-q", rev_options.to_args()) self.run_command(cmd_args, cwd=dest)
@pytest.mark.parametrize( "args, expected", [ (["pip", "list"], "pip list"), ( [ "foo", "space space", "new\nline", 'double"quote', "single'quote" ], """foo 'space space' 'new\nline' 'double"quote' 'single'"'"'quote'""", ), # Test HiddenText arguments. ( make_command(hide_value("secret1"), "foo", hide_value("secret2")), "'****' foo '****'", ), ], ) def test_format_command_args(args: CommandArgs, expected: str) -> None: actual = format_command_args(args) assert actual == expected def test_make_subprocess_output_error() -> None: cmd_args = ["test", "has space"] cwd = "/path/to/cwd" lines = ["line1\n", "line2\n", "line3\n"] actual = make_subprocess_output_error( cmd_args=cmd_args,
@classmethod def run_command( cls, cmd, # type: Union[List[str], CommandArgs] cwd=None, # type: Optional[str] extra_environ=None, # type: Optional[Mapping[str, Any]] extra_ok_returncodes=None, # type: Optional[Iterable[int]] log_failed_cmd=True # type: bool ): # type: (...) -> Text """ Run a VCS subcommand This is simply a wrapper around call_subprocess that adds the VCS command name, and checks that the VCS is available """ cmd = make_command(cls.name, *cmd) try: return call_subprocess(cmd, cwd, extra_environ=extra_environ, extra_ok_returncodes=extra_ok_returncodes, log_failed_cmd=log_failed_cmd) except OSError as e: # errno.ENOENT = no such file or directory # In other words, the VCS executable isn't available if e.errno == errno.ENOENT: raise BadCommand( 'Cannot find command {cls.name!r} - do you have ' '{cls.name!r} installed and in your ' 'PATH?'.format(**locals())) else: raise # re-raise exception if a different error occurred
format_command_args, make_command, make_subprocess_output_error, subprocess_logger, ) @pytest.mark.parametrize( 'args, expected', [ (['pip', 'list'], 'pip list'), (['foo', 'space space', 'new\nline', 'double"quote', "single'quote"], """foo 'space space' 'new\nline' 'double"quote' 'single'"'"'quote'""" ), # Test HiddenText arguments. (make_command(hide_value('secret1'), 'foo', hide_value('secret2')), "'****' foo '****'"), ]) def test_format_command_args(args, expected): actual = format_command_args(args) assert actual == expected def test_make_subprocess_output_error(): cmd_args = ['test', 'has space'] cwd = '/path/to/cwd' lines = ['line1\n', 'line2\n', 'line3\n'] actual = make_subprocess_output_error( cmd_args=cmd_args, cwd=cwd, lines=lines, exit_status=3,
def update(self, dest, url, rev_options): # type: (str, HiddenText, RevOptions) -> None self.run_command(['pull', '-q'], cwd=dest) cmd_args = make_command('update', '-q', rev_options.to_args()) self.run_command(cmd_args, cwd=dest)
def switch(self, dest, url, rev_options): # type: (str, HiddenText, RevOptions) -> None self.run_command(make_command('switch', url), cwd=dest)