Esempio n. 1
0
    def configure_remotes(self, remote_name: str, remote_url: str,
                          upstream_remote_name: str, upstream_remote_url: str) -> None:
        """Configure remotes names for project and upstream

        :param str remote_name: Project remote name
        :param str remote_url: Project remote url
        :param str upstream_remote_name: Upstream remote name
        :param str upstream_remote_url: Upstream remote url
        """

        if not existing_git_repo(self.repo_path):
            return

        try:
            remotes = self.repo.remotes
        except GitError as err:
            LOG.debug('No remotes', err)
            return
        else:
            for remote in remotes:
                if remote_url == self._remote_get_url(remote.name) and remote.name != remote_name:
                    self._rename_remote(remote.name, remote_name)
                    continue
                if upstream_remote_url == self._remote_get_url(remote.name) and remote.name != upstream_remote_name:
                    self._rename_remote(remote.name, upstream_remote_name)
            self._compare_remotes(remote_name, remote_url, upstream_remote_name, upstream_remote_url)
Esempio n. 2
0
    def new_commits_count(self, upstream: bool = False) -> int:
        """Returns the number of new commits

        :param bool upstream: Whether to find number of new upstream or local commits
        :return: Int number of new commits
        """

        try:
            local_branch = self.repo.active_branch
        except (GitError, TypeError) as err:
            LOG.debug(error=err)
            return 0
        else:
            tracking_branch = local_branch.tracking_branch()
            if local_branch is None or tracking_branch is None:
                return 0

            try:
                commits = f'{local_branch.commit.hexsha}...{tracking_branch.commit.hexsha}'
                rev_list_count = self.repo.git.rev_list(
                    '--count', '--left-right', commits)
            except (GitError, ValueError) as err:
                LOG.debug(error=err)
                return 0
            else:
                index = 1 if upstream else 0
                return int(str(rev_list_count).split()[index])
Esempio n. 3
0
async def run_sync(func: Callable, limit: trio.CapacityLimiter,
                   project: ResolvedProject, progress: tqdm) -> None:
    LOG.debug(f'START PARALLEL {project.name}')
    await trio.to_thread.run_sync(func, limiter=limit)
    limit.release_on_behalf_of(project)
    progress.update()
    LOG.debug(f'END PARALLEL {project.name}')
Esempio n. 4
0
    def herd_tag(self, url: str, tag: str, depth: int = 0,
                 rebase: bool = False, config: Optional[GitConfig] = None) -> None:
        """Herd tag

        :param str url: URL of repo
        :param str tag: Tag name
        :param int depth: Git clone depth. 0 indicates full clone, otherwise must be a positive integer
        :param bool rebase: Whether to use rebase instead of pulling latest changes
        :param Optional[GitConfig] config: Custom git config
        """

        fetch = depth != 0

        if not existing_git_repo(self.repo_path):
            self._init_repo()
            self._create_remote(self.remote, url, remove_dir=True)
            try:
                self._checkout_new_repo_tag(tag, self.remote, depth)
            except Exception as err:
                LOG.debug('Failed checkout new repo tag', err)
                self.herd(url, depth=depth, fetch=fetch, rebase=rebase)
                return

        self.install_project_git_herd_alias()
        if config is not None:
            self._update_git_config(config)
        try:
            self.fetch(self.remote, ref=GitRef(tag=tag), depth=depth)
            self._checkout_tag(tag)
        except Exception as err:
            LOG.debug('Failed fetch and checkout tag', err)
            self.herd(url, depth=depth, fetch=fetch, rebase=rebase)
Esempio n. 5
0
    def start(self, remote: str, branch: str, depth: int, tracking: bool) -> None:
        """Start new branch in repository and checkout

        :param str remote: Remote name
        :param str branch: Local branch name to create
        :param int depth: Git clone depth. 0 indicates full clone, otherwise must be a positive integer
        :param bool tracking: Whether to create a remote branch with tracking relationship
        """

        if branch not in self.repo.heads:
            if not is_offline():
                self.fetch(remote, ref=GitRef(branch=branch), depth=depth)
            try:
                self._create_branch_local(branch)
                self._checkout_branch_local(branch)
            except BaseException as err:
                LOG.debug('Failed to create and checkout branch', err)
                raise
        else:
            CONSOLE.stdout(f' - {fmt.ref(branch)} already exists')
            if self._is_branch_checked_out(branch):
                CONSOLE.stdout(' - On correct branch')
            else:
                self._checkout_branch_local(branch)

        if tracking and not is_offline():
            self._create_branch_remote_tracking(branch, remote, depth)
Esempio n. 6
0
    def _get_remote_tag(self,
                        tag: str,
                        remote: str,
                        depth: int = 0,
                        remove_dir: bool = False) -> Optional[Tag]:
        """Returns Tag object

        :param str tag: Tag name
        :param str remote: Remote name
        :param int depth: Git clone depth. 0 indicates full clone, otherwise must be a positive integer
        :param bool remove_dir: Whether to remove the directory if commands fail
        :return: GitPython Tag object if it exists, otherwise None
        """

        self._remote(remote, remove_dir=remove_dir)
        self.fetch(remote,
                   depth=depth,
                   ref=GitRef(tag=tag),
                   remove_dir=remove_dir)

        try:
            return self.repo.tags[tag]
        except (GitError, IndexError) as err:
            LOG.error(f'No existing tag {fmt.ref(tag)}')
            if remove_dir:
                remove_directory(self.repo_path, check=False)
                raise
            LOG.debug(error=err)
            return None
        except BaseException:
            LOG.error('Failed to get tag')
            if remove_dir:
                remove_directory(self.repo_path, check=False)
            raise
Esempio n. 7
0
    def is_lfs_installed(self) -> bool:
        """Check whether git lfs hooks are installed"""

        try:
            self.repo.git.config('--get', 'filter.lfs.smudge')
            self.repo.git.config('--get', 'filter.lfs.clean')
            self.repo.git.config('--get', 'filter.lfs.process')
            self.repo.git.config('--get', 'filter.lfs.required')
        except GitError as err:
            LOG.debug(error=err)
            return False
        else:
            return True
Esempio n. 8
0
def get_default_branch_from_remote(url: str) -> Optional[str]:
    """Get default branch from remote repo"""

    try:
        command = ['git', 'ls-remote', '--symref', url, 'HEAD']
        result = execute_command(command, Path.cwd(), print_output=False)
        output: str = result.stdout
        output_list = output.split()
        branch = [remove_prefix(chunk, 'refs/heads/') for chunk in output_list if chunk.startswith('refs/heads/')]
        return branch[0]
    except CalledProcessError as err:
        LOG.debug('Failed to get default branch from remote git repo', err)
        return None
Esempio n. 9
0
    def has_remote_branch(self, branch: str, remote: str) -> bool:
        """Check if remote branch exists

        :param str branch: Branch name
        :param str remote: Remote name
        :return: True, if remote branch exists
        """

        try:
            origin = self.repo.remotes[remote]
            return branch in origin.refs
        except (GitError, IndexError) as err:
            LOG.debug(error=err)
            return False
Esempio n. 10
0
def get_default_branch_from_local(repo_path: Path, remote: str) -> Optional[str]:
    """Get default branch from local repo"""

    try:
        command = ['git', 'symbolic-ref', f'refs/remotes/{remote}/HEAD']
        result = execute_command(command, repo_path, print_output=False)
        output: str = result.stdout
        output_list = output.split()
        branch = [remove_prefix(chunk, f'refs/remotes/{remote}/') for chunk in output_list
                  if chunk.startswith(f'refs/remotes/{remote}/')]
        return branch[0]
    except CalledProcessError as err:
        LOG.debug('Failed to get default branch from local git repo', err)
        return None
Esempio n. 11
0
    def _is_branch_checked_out(self, branch: str) -> bool:
        """Check if branch is checked out

        :param str branch: Branch name
        :return: True, if branch is checked out
        """

        try:
            default_branch = self.repo.heads[branch]
            is_detached = self.repo.head.is_detached
            same_branch = self.repo.head.ref == default_branch
            return not is_detached and same_branch
        except (GitError, TypeError) as err:
            LOG.debug(error=err)
            return False
Esempio n. 12
0
    def _run_forall_command(self, command: str, env: dict,
                            ignore_errors: bool) -> None:
        """Run command or script in project directory

        :param str command: Command to run
        :param dict env: Environment variables
        :param bool ignore_errors: Whether to exit if command returns a non-zero exit code
        """

        CONSOLE.stdout(fmt.command(command))
        try:
            execute_forall_command(command, self.full_path, env)
        except CalledProcessError as err:
            if ignore_errors:
                LOG.debug(f'Command failed: {command}', err)
            else:
                LOG.error(f'Command failed: {command}')
                raise
Esempio n. 13
0
    def herd_remote(self, url: str, remote: str, branch: Optional[str] = None) -> None:
        """Herd remote repo

        :param str url: URL of repo
        :param str remote: Remote name
        :param Optional[str] branch: Branch name
        """

        self._create_remote(remote, url)

        if branch is None:
            self.fetch(remote, ref=self.default_ref)
            return

        try:
            self.fetch(remote, ref=GitRef(branch=branch))
        except Exception as err:
            LOG.debug('Failed fetch', err)
            self.fetch(remote, ref=self.default_ref)
Esempio n. 14
0
    def fetch(self,
              remote: str,
              ref: Optional[GitRef] = None,
              depth: int = 0,
              remove_dir: bool = False,
              allow_failure: bool = False) -> None:
        """Fetch from a specific remote ref

        :param str remote: Remote name
        :param Optional[GitRef] ref: Ref to fetch
        :param int depth: Git clone depth. 0 indicates full clone, otherwise must be a positive integer
        :param bool remove_dir: Whether to remove the directory if commands fail
        :param bool allow_failure: Whether to allow failure
        """

        if depth == 0 or ref is None:
            CONSOLE.stdout(f' - Fetch from {fmt.remote(remote)}')
            error_message = f'Failed to fetch from remote {fmt.remote(remote)}'
        else:
            CONSOLE.stdout(
                f' - Fetch from {fmt.remote(remote)} {fmt.ref(ref.short_ref)}')
            error_message = f'Failed to fetch from {fmt.remote(remote)} {fmt.ref(ref.short_ref)}'

        try:
            if depth == 0:
                execute_command(f'git fetch {remote} --prune --tags',
                                self.repo_path)
            elif ref is None:
                command = f'git fetch {remote} --depth {depth} --prune --tags'
                execute_command(command, self.repo_path)
            else:
                command = f'git fetch {remote} {ref.short_ref} --depth {depth} --prune --tags'
                execute_command(command, self.repo_path)
        except BaseException as err:
            LOG.error(error_message)
            if remove_dir:
                remove_directory(self.repo_path, check=False)
            if allow_failure:
                LOG.debug(error=err)
                return
            raise
Esempio n. 15
0
    def __init__(self):
        """ClowderController __init__

        :raise MissingYamlError:
        """

        self.error: Optional[Exception] = None

        self._initialize_properties()

        try:
            if ENVIRONMENT.clowder_yaml is None:
                raise MissingYamlError(
                    f"{Path('clowder.yml')} appears to be missing")
            yaml = load_yaml_file(ENVIRONMENT.clowder_yaml,
                                  ENVIRONMENT.clowder_dir)
            validate_yaml_file(yaml, ENVIRONMENT.clowder_yaml)

            self._clowder = ClowderBase(yaml)

            # Register all sources as we come across them
            defaults = self._clowder.defaults
            if defaults is not None and defaults.source is not None:
                SOURCE_CONTROLLER.add_source(defaults.source)

            sources = self._clowder.sources
            if sources is not None:
                for s in sources:
                    SOURCE_CONTROLLER.add_source(s)

            projects = self._clowder.clowder.projects
            sections = self._clowder.clowder.sections
            if projects is None:
                projects = [p for s in sections for p in s.projects]

            for project in projects:
                SOURCE_CONTROLLER.add_source(project.source)
                if project.upstream is not None:
                    SOURCE_CONTROLLER.add_source(project.upstream.source)
            # Validate all source names have a defined source with url
            SOURCE_CONTROLLER.validate_sources()

            if sections is None:
                resolved_projects = [
                    ResolvedProject(p,
                                    defaults=defaults,
                                    protocol=self._clowder.protocol)
                    for p in projects
                ]
            else:
                resolved_projects = [
                    ResolvedProject(p,
                                    defaults=defaults,
                                    section=s,
                                    protocol=self._clowder.protocol)
                    for s in sections for p in s.projects
                ]

            self.projects = tuple(
                sorted(resolved_projects, key=lambda p: p.name))
            self._update_properties()
            self._populate_default_branches()
        except Exception as err:
            LOG.debug('Failed to init clowder controller')
            self.error = err
            self._initialize_properties()