def _create_git_remote_repo( remote_repos_path: pathlib.Path, remote_repo_name: str, remote_repo_post_init: Optional[CreateProjectCallbackProtocol] = None, ) -> pathlib.Path: remote_repo_path = remote_repos_path / remote_repo_name run(["git", "init", remote_repo_name], cwd=remote_repos_path) if remote_repo_post_init is not None and callable(remote_repo_post_init): remote_repo_post_init(remote_repo_path=remote_repo_path) return remote_repo_path
def _create_hg_remote_repo( remote_repos_path: pathlib.Path, remote_repo_name: str, remote_repo_post_init: Optional[CreateProjectCallbackProtocol] = None, ) -> pathlib.Path: """Create a test hg repo to for checkout / commit purposes""" remote_repo_path = remote_repos_path / remote_repo_name run(["hg", "init", remote_repo_name], cwd=remote_repos_path) if remote_repo_post_init is not None and callable(remote_repo_post_init): remote_repo_post_init(remote_repo_path=remote_repo_path) return remote_repo_path
def _create_svn_remote_repo( remote_repos_path: pathlib.Path, remote_repo_name: str, remote_repo_post_init: Optional[CreateProjectCallbackProtocol] = None, ) -> pathlib.Path: """Create a test SVN repo to for checkout / commit purposes""" remote_repo_path = remote_repos_path / remote_repo_name run(["svnadmin", "create", remote_repo_path]) if remote_repo_post_init is not None and callable(remote_repo_post_init): remote_repo_post_init(remote_repo_path=remote_repo_path) return remote_repo_path
def test_repo_git_obtain_initial_commit_repo( tmp_path: pathlib.Path, constructor: ProjectTestFactory, lazy_constructor_options: ProjectTestFactoryLazyKwargs, ) -> None: """initial commit repos return 'initial'. note: this behaviors differently from git(1)'s use of the word "bare". running `git rev-parse --is-bare-repository` would return false. """ repo_name = "my_git_project" run(["git", "init", repo_name], cwd=tmp_path) bare_dir = tmp_path / repo_name git_repo: GitProject = constructor(**lazy_constructor_options(**locals())) git_repo.obtain() assert git_repo.get_revision() == "initial"
def git_remote_repo_single_commit_post_init( remote_repo_path: pathlib.Path) -> None: testfile_filename = "testfile.test" run(["touch", testfile_filename], cwd=remote_repo_path) run(["git", "add", testfile_filename], cwd=remote_repo_path) run(["git", "commit", "-m", "test file for dummyrepo"], cwd=remote_repo_path)
def test_repo_git_obtain_full( tmp_path: pathlib.Path, git_remote_repo: pathlib.Path, constructor: ProjectTestFactory, lazy_constructor_options: ProjectTestFactoryLazyKwargs, ) -> None: git_repo: GitProject = constructor(**lazy_constructor_options(**locals())) git_repo.obtain() test_repo_revision = run(["git", "rev-parse", "HEAD"], cwd=git_remote_repo) assert git_repo.get_revision() == test_repo_revision assert os.path.exists(tmp_path / "myrepo")
def test_progress_callback( tmp_path: pathlib.Path, git_remote_repo: pathlib.Path, mocker: MockerFixture, constructor: ProjectTestFactory, lazy_constructor_options: ProjectTestFactoryLazyKwargs, ) -> None: def progress_callback_spy(output: str, timestamp: datetime.datetime) -> None: assert isinstance(output, str) assert isinstance(timestamp, datetime.datetime) progress_callback = mocker.Mock(name="progress_callback_stub", side_effect=progress_callback_spy) run(["git", "rev-parse", "HEAD"], cwd=git_remote_repo) # create a new repo with the repo as a remote git_repo: GitProject = constructor(**lazy_constructor_options(**locals())) git_repo.obtain() assert progress_callback.called
def test_repo_mercurial( tmp_path: pathlib.Path, projects_path: pathlib.Path, hg_remote_repo: pathlib.Path, ) -> None: repo_name = "my_mercurial_project" mercurial_repo = create_project( url=f"file://{hg_remote_repo}", dir=projects_path / repo_name, vcs="hg", ) run(["hg", "init", mercurial_repo.repo_name], cwd=tmp_path) mercurial_repo.update_repo() test_repo_revision = run( ["hg", "parents", "--template={rev}"], cwd=projects_path / repo_name ) assert mercurial_repo.get_revision() == test_repo_revision
def _get_svn_url_rev(cls, location: str) -> Tuple[Optional[str], int]: _svn_xml_url_re = re.compile('url="([^"]+)"') _svn_rev_re = re.compile(r'committed-rev="(\d+)"') _svn_info_xml_rev_re = re.compile(r'\s*revision="(\d+)"') _svn_info_xml_url_re = re.compile(r"<url>(.*)</url>") entries_path = os.path.join(location, ".svn", "entries") if os.path.exists(entries_path): with open(entries_path) as f: data = f.read() else: # subversion >= 1.7 does not have the 'entries' file data = "" url = None if data.startswith("8") or data.startswith("9") or data.startswith("10"): entries = list(map(str.splitlines, data.split("\n\x0c\n"))) del entries[0][0] # get rid of the '8' url = entries[0][3] revs = [int(d[9]) for d in entries if len(d) > 9 and d[9]] + [0] elif data.startswith("<?xml"): match = _svn_xml_url_re.search(data) if not match: raise ValueError(f"Badly formatted data: {data!r}") url = match.group(1) # get repository URL revs = [int(m.group(1)) for m in _svn_rev_re.finditer(data)] + [0] else: try: # subversion >= 1.7 # Note that using get_remote_call_options is not necessary here # because `svn info` is being run against a local directory. # We don't need to worry about making sure interactive mode # is being used to prompt for passwords, because passwords # are only potentially needed for remote server requests. xml = run( ["svn", "info", "--xml", location], ) match = _svn_info_xml_url_re.search(xml) assert match is not None url = match.group(1) revs = [int(m.group(1)) for m in _svn_info_xml_rev_re.finditer(xml)] except Exception: url, revs = None, [] if revs: rev = max(revs) else: rev = 0 return url, rev
def run( self, cmd: _CMD, cwd: None = None, check_returncode: bool = True, log_in_real_time: Optional[bool] = None, *args: Any, **kwargs: Any, ) -> str: """Return combined stderr/stdout from a command. This method will also prefix the VCS command bin_name. By default runs using the cwd `libvcs.projects.base.BaseProject.dir` of the repo. Parameters ---------- cwd : str dir command is run from, defaults to `libvcs.projects.base.BaseProject.dir`. check_returncode : bool Indicate whether a :exc:`~exc.CommandError` should be raised if return code is different from 0. Returns ------- str combined stdout/stderr in a big string, newlines retained """ if cwd is None: cwd = getattr(self, "dir", None) if isinstance(cmd, Sequence): cmd = [self.bin_name, *cmd] else: cmd = [self.bin_name, cmd] return run( cmd, callback=(self.progress_callback if callable(self.progress_callback) else None), check_returncode=check_returncode, log_in_real_time=log_in_real_time or self.log_in_real_time or False, cwd=cwd, )
def gitconfig(user_path: pathlib.Path) -> pathlib.Path: gitconfig = user_path / ".gitconfig" user_email = "*****@*****.**" gitconfig.write_text( textwrap.dedent(f""" [user] email = {user_email} name = {getpass.getuser()} [color] diff = auto """), encoding="utf-8", ) output = run(["git", "config", "--get", "user.email"]) assert user_email in output, "Should use our fixture config and home directory" return gitconfig
def run( self, args: _CMD, *, quiet: Optional[bool] = None, username: Optional[str] = None, password: Optional[str] = None, no_auth_cache: Optional[bool] = None, non_interactive: Optional[bool] = True, trust_server_cert: Optional[bool] = None, config_dir: Optional[pathlib.Path] = None, config_option: Optional[pathlib.Path] = None, **kwargs: Any, ) -> str: """ Passing None to a subcommand option, the flag won't be passed unless otherwise stated. `svn help` and `svn help [cmd]` Wraps svn's `Options <https://svnbook.red-bean.com/en/1.7/svn.ref.svn.html#svn.ref.svn.sw>`_. Parameters ---------- quiet : -q / --quiet username : --username password : --password no_auth_cache : --no-auth-cache non_interactive : --non-interactive, defaults to True trust_server_cert : --trust-server-cert config_dir : --config-dir config_option : --config-option, ``FILE:SECTION:OPTION=[VALUE]`` cwd : :attr:`libvcs._internal.types.StrOrBytesPath`, optional Defaults to :attr:`~.cwd` Examples -------- >>> svn = Svn(dir=tmp_path) >>> svn.run(['help']) "usage: svn <subcommand> [options] [args]..." """ if isinstance(args, Sequence): cli_args = ["svn", *args] else: cli_args = ["svn", args] if "cwd" not in kwargs: kwargs["cwd"] = self.dir if no_auth_cache is True: cli_args.append("--no-auth-cache") if non_interactive is True: cli_args.append("--non-interactive") if username is not None: cli_args.extend(["--username", username]) if password is not None: cli_args.extend(["--password", password]) if trust_server_cert is True: cli_args.append("--trust-server_cert") if config_dir is not None: cli_args.extend(["--config-dir", str(config_dir)]) if config_option is not None: cli_args.extend(["--config-option", str(config_option)]) return run(args=cli_args, **kwargs)
def run( self, args: _CMD, *, config: Optional[str] = None, repository: Optional[str] = None, quiet: Optional[bool] = None, help: Optional[bool] = None, encoding: Optional[str] = None, encoding_mode: Optional[str] = None, verbose: Optional[bool] = None, traceback: Optional[bool] = None, debug: Optional[bool] = None, debugger: Optional[bool] = None, profile: Optional[bool] = None, version: Optional[bool] = None, hidden: Optional[bool] = None, time: Optional[bool] = None, pager: Optional[HgPagerType] = None, color: Optional[HgColorType] = None, **kwargs: Any, ) -> str: """ Passing None to a subcommand option, the flag won't be passed unless otherwise stated. `hg help` and `hg help [cmd]` Wraps hg's `Options <https://www.mercurial-scm.org/doc/hg.1.html>`_. Parameters ---------- quiet : bool -q / --quiet repository : str ``--repository REPO`` cwd : :attr:`libvcs._internal.types.StrOrBytesPath`, optional ``--cwd DIR``, Defaults to :attr:`~.cwd` verbose : bool ``-v / --verbose`` non_interactive : bool ``-y / --noninteractive``, defaults to True color : HgColorTypeLiteral ``--color`` debug : bool ``--debug`` debugger : bool ``--debugger`` encoding : str ``--encoding ENCODE`` encoding_mode : str ``--encodingmode MODE`` traceback : bool ``--traceback`` time : bool ``--time`` profile : bool ``--profile`` version : bool ``--version`` help : bool ``-h / --help`` hidden : bool ``--hidden`` pager : HgPagerType ``--pager TYPE`` config : ``--config CONFIG [+]``, ``section.name=value`` Examples -------- >>> hg = Hg(dir=tmp_path) >>> hg.run(['help']) "Mercurial Distributed SCM..." """ if isinstance(args, Sequence): cli_args = ["hg", *args] else: cli_args = ["hg", args] if "cwd" not in kwargs: kwargs["cwd"] = self.dir if repository is not None: cli_args.extend(["--repository", repository]) if config is not None: cli_args.extend(["--config", config]) if pager is not None: cli_args.append(["--pager", pager]) if color is not None: cli_args.append(["--color", color]) if verbose is True: cli_args.append("verbose") if quiet is True: cli_args.append("--quiet") if debug is True: cli_args.append("--debug") if debugger is True: cli_args.append("--debugger") if traceback is True: cli_args.append("--traceback") if time is True: cli_args.append("--time") if profile is True: cli_args.append("--profile") if version is True: cli_args.append("--version") if help is True: cli_args.append("--help") return run(args=cli_args, **kwargs)