Ejemplo n.º 1
0
class RepoOperator(object):
    def __init__(self, git_credentials=None):
        """
        Create a repository interface object

        :return:
        """
        self._git_credentials = git_credentials

        self.git = GitBit(verbose=True)
        if self._git_credentials:
            self.setup_gitbit()

    def setup_gitbit(self, credentials=None):
        """
        Set gitbit credentials.
        :return:
        """
        if credentials is None:
            if self._git_credentials is None:
                return
            else:
                credentials = self._git_credentials
        else:
            self._git_credentials = credentials
        for url_cred_pair in credentials:
            url, cred = url_cred_pair.split(',')
            self.git.add_credential_from_variable(url, cred)

    def set_git_dryrun(self, dryrun):
        self.git.set_dryrun(dryrun)

    def set_git_verbose(self, verbose):
        self.git.set_verbose(verbose)

    def set_git_executable(self, executable):
        self.git.set_excutable(excutable)

    @staticmethod
    def print_command_summary(name, results):
        """
        Print the results of running commands.
          first the command line itself
            and the error code if it's non-zero
          then the stdout & stderr values from running that command

        :param name:
        :param results:
        :return: True if any command exited with an error condition
        """

        error_found = False

        print "============================"
        print "Command output for {0}".format(name)

        if 'commands' in results[name]:
            commands = results[name]['commands']
            for command in commands:
                for key in ['command', 'stdout', 'stderr']:
                    if key in command:
                        if command[key] != '':
                            print command[key]
                        if key == 'command':
                            if command['return_code'] != 0:
                                error_found = True
                                print "EXITED: {0}".format(
                                    command['return_code'])
                            else:
                                print "SUCCEED"

        return error_found

    def clone_repo_list(self, repo_list, dest_dir, jobs=1):
        """
        check out repository to dest dir based on repo list
        :param repo_list: a list of repository entry which should contain:
                          'repository': the url of repository, it is required
                          'branch': the branch to be check out, it is optional
                          'commit-id': the commit id to be reset, it is optional
        :param dest_dir: the directory where repository will be check out
        :param jobs: Number of parallel jobs to run
        :return:
        """
        cloner = RepoCloner(jobs)
        if cloner is not None:
            for repo in repo_list:
                data = {
                    'repo': repo,
                    'builddir': dest_dir,
                    'credentials': self._git_credentials
                }
                cloner.add_task(data)
            cloner.finish()
            results = cloner.get_results()

            error = False
            for name in results.keys():
                error |= self.print_command_summary(name, results)

            if error:
                raise RuntimeError("Failed to clone repositories")

    def clone_repo(self, repo_url, dest_dir, repo_commit="HEAD"):
        """
        check out a repository to dest directory from the repository url
        :param repo_url: the url of repository, it is required
        :param dest_dir: the directory where repository will be check out
        :return: the directory of the repository
        """
        repo = {}
        repo["repository"] = repo_url
        repo["commit-id"] = repo_commit
        repo_list = [repo]
        self.clone_repo_list(repo_list, dest_dir)

        repo_directory_name = strip_suffix(os.path.basename(repo_url), ".git")
        return os.path.join(dest_dir, repo_directory_name)

    def get_latest_commit_date(self, repo_dir):
        """
        :param repo_dir: path of the repository
        :return: commit-date
        """
        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError(
                "The repository directory {0} is not specified. or its format is wrong"
                .format(repo_dir))
        return_code, output, error = self.git.run(
            ['show', '-s', '--pretty=format:%ct'], directory=repo_dir)
        if return_code == 0:
            return output.strip()
        else:
            raise RuntimeError(
                "Unable to get commit date in directory {0}".format(repo_dir))

    def get_latest_commit_id(self, repo_dir):
        """
        :param repo_dir: path of the repository
        :return: commit-id
        """

        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError(
                "The repository directory {0} is not specified. or its format is wrong"
                .format(repo_dir))

        return_code, output, error = self.git.run(
            ['log', '--format=format:%H', '-n', '1'], directory=repo_dir)

        if return_code == 0:
            return output.strip()
        else:
            raise RuntimeError(
                "Unable to get commit id in directory {0}".format(repo_dir))

    def get_latest_merge_commit_before_date(self, repo_dir, date):
        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError(
                "The repository directory {0} is not specified. or its format is wrong"
                .format(repo_dir))

        return_code, output, error = self.git.run([
            'log', '--merges', '--format=format:%H', '--before=' + date, '-n',
            '1'
        ],
                                                  directory=repo_dir)

        if return_code == 0:
            return output.strip()
        else:
            raise RuntimeError("Unable to get commit id before {date} in directory {repo_dir}"\
                  .format(date=date, repo_dir=repo_dir))

    def get_latest_author_commit_before_date(self, repo_dir, date, author):
        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError(
                "The repository directory {0} is not specified. or its format is wrong"
                .format(repo_dir))

        return_code, output, error = self.git.run([
            'log', '--author=' + author, '--format=format:%H',
            '--before=' + date, '-n', '1'
        ],
                                                  directory=repo_dir)

        if return_code == 0:
            return output.strip()
        else:
            raise RuntimeError("Unable to get commit id of {author} before {date} in directory {repo_dir}"\
                  .format(author=author, date=date, repo_dir=repo_dir))

    def get_newer_commit(self, repo_dir, commit1, commit2):
        '''
        if commit1 is newer than commit2, return True
        else, return False
        '''
        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError(
                "The repository directory {0} is not specified. or its format is wrong"
                .format(repo_dir))
        return_code, output, error = self.git.run(
            ['rev-list', commit1 + '..' + commit2, '--count'],
            directory=repo_dir)
        if return_code == 0:
            if output.strip() == "0":
                return commit1
            else:
                return commit2
        else:
            raise RuntimeError("Unable to get any commit between {commit1} and {commit2} in directory {repo_dir}"\
                  .format(commit1=commit1, commit2=commit2, repo_dir=repo_dir))

    def get_commit_message(self, repo_dir, commit):
        """
        :param repo_dir: path of the repository
        :param commit: the commit id of the repository
        :return: commit-message
        """

        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError(
                "The repository directory {0} is not specified. or its format is wrong"
                .format(repo_dir))

        return_code, output, error = self.git.run(
            ['log', '--format=format:%B', '-n', '1', commit],
            directory=repo_dir)

        if return_code == 0:
            return output.strip()
        else:
            raise RuntimeError("Unable to get commit message of {commit_id} in directory {repo_dir}"\
                  .format(commit_id=commit, repo_dir=repo_dir))

    def get_repo_url(self, repo_dir):
        """
        get the remote url of the repository
        :param repo_dir: the directory of the repository
        :return: repository url
        """

        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError(
                "The repository directory {0} is not specified. or its format is wrong"
                .format(repo_dir))

        return_code, output, error = self.git.run(['ls-remote', '--get-url'],
                                                  directory=repo_dir)

        if return_code == 0:
            return output.strip()
        else:
            raise RuntimeError(
                "Unable to find the repository url in directory {0}".format(
                    repo_dir))

    def get_current_branch(self, repo_dir):
        """
        get the current branch name of the repository
        :param repo_dir: the directory of the repository
        :return: branch name
        """

        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError(
                "The repository directory {0} is not specified. or its format is wrong"
                .format(repo_dir))

        return_code, output, error = self.git.run(
            ['symbolic-ref', '--short', 'HEAD'], directory=repo_dir)

        if return_code == 0:
            return output.strip()
        else:
            raise RuntimeError(
                "Unable to find the current branch in directory {0}".format(
                    repo_dir))

    def check_branch(self, repo_url, branch):
        """
        Checks if the specified branch name exists for the provided repository. Leave only the characters
        following the final "/". This is to handle remote repositories.
        Raise RuntimeError if it is not found.
        :return: None
        """
        if "/" in branch:
            sliced_branch = branch.split("/")[-1]
        else:
            sliced_branch = branch

        return_code, output, error = self.git.run(
            ['ls-remote', repo_url, 'heads/*{0}'.format(sliced_branch)])

        if return_code is not 0 or output is '':
            raise RuntimeError(
                "The branch, '{0}', provided for '{1}', does not exist.".
                format(branch, repo_url))

    def set_repo_tagname(self, repo_url, repo_dir, tag_name):
        """
        Sets tagname on the repo
        :param repo_url: the url of the repository
        :param repo_dir: the directory of the repository
        :param tag_name: the tag name to be set
        :return: None
        """
        # See if that tag exists for the repo
        return_code, output, error = self.git.run(["tag", "-l", tag_name],
                                                  repo_dir)

        # Return if tag already exists, otherwise create it
        if return_code == 0 and output != '':
            print "Tag {0} already exists in {1}".format(output, repo_url)
            return
        else:
            print "Creating tag {0} for repo {1}".format(tag_name, repo_url)
            return_code, output, error = self.git.run(
                ["tag", "-a", tag_name, "-m", "\"Creating new tag\""],
                repo_dir)
            if return_code != 0:
                raise RuntimeError(
                    "Failed to create tag {0} for {1}.\nError: {2}".format(
                        tag_name, repo_url, error))
            return_code, output, error = self.git.run(
                ["push", "origin", "--tags"], repo_dir)
            if return_code != 0:
                raise RuntimeError(
                    "Failed to push tag {0} for {1}.\nError: {2}".format(
                        tag_name, repo_url, error))

    def create_repo_branch(self, repo_url, repo_dir, branch_name):
        """
        Creates branch on the repo
        :param repo_url: the url of the repository
        :param repo_dir: the directory of the repository
        :param branch_name: the branch name to be set
        :return: None
        """
        # See if that branch exists for the repo
        return_code, output, error = self.git.run(
            ["ls-remote", "--exit-code", "--heads", repo_url, branch_name],
            repo_dir)
        # Raise RuntimeError if branch already exists, otherwise create it
        if return_code == 0 and output != '':
            raise RuntimeError(
                "Error: Branch {0} already exists - exiting now...".format(
                    output))
        else:
            print "Creating branch {0} for repo {1}".format(
                branch_name, repo_url)
            return_code, output, error = self.git.run(["branch", branch_name],
                                                      repo_dir)
            if return_code != 0:
                print output
                raise RuntimeError(
                    "Error: Failed to create local branch {0} with error: {1}."
                    .format(branch_name, error))
            return_code, output, error = self.git.run(
                ["push", "-u", "origin", branch_name], repo_dir)
            if return_code != 0:
                print output
                raise RuntimeError(
                    "Error: Failed to publish local branch {0} with error: {1}"
                    .format(branch_name, error))

    def checkout_repo_branch(self, repo_dir, branch_name):
        """
        Check out to specify branch on the repo
        :param repo_dir: the directory of the repository
        :param branch_name: the branch name to be checked
        :return: None
        """
        return_code, output, error = self.git.run(["checkout", branch_name],
                                                  repo_dir)

        if return_code != 0:
            raise RuntimeError(
                "Error: Failed to checkout branch {0}".format(output))

    def push_repo_changes(self, repo_dir, commit_message, push_all=False):
        """
        publish changes of reposioty
        :param repo_dir: the directory of the repository
        :param commit_message: the message to be added to commit
        :return: None
        """
        run_add = False
        run_commit = False
        status_code, status_out, status_error = self.git.run(['status'],
                                                             repo_dir)
        if status_code == 0:
            if "nothing to commit, working directory clean" in status_out and \
               "Your branch is up-to-date with" in status_out :
                print status_out
                return

            if "Changes not staged for commit" in status_out:
                run_add = True
                run_commit = True

            if "Changes to be committed" in status_out:
                run_commit = True

        if run_add:
            if push_all:
                add_code, add_out, add_error = self.git.run(['add', '-A'],
                                                            repo_dir)
            else:
                add_code, add_out, add_error = self.git.run(['add', '-u'],
                                                            repo_dir)

            if add_code != 0:
                raise RuntimeError('Unable to add files for commiting.\n{0}\n{1}\n{2}'.format\
                                  (add_code, add_out, add_error))

        if run_commit:
            commit_code, commit_out, commit_error = self.git.run(
                ['commit', '-m', commit_message], repo_dir)
            if commit_code != 0:
                raise RuntimeError('Unable to commit changes for pushing.\n{0}\n{1}\n{2}'.format\
                                  (commit_code, commit_out, commit_error))

        push_code, push_out, push_error = self.git.run(['push'], repo_dir)
        if push_code != 0:
            raise RuntimeError('Unable to push changes.\n{0}\n{1}\n{2}'.format(
                push_code, push_out, push_error))
        return
Ejemplo n.º 2
0
class RepoOperator(object):
    def __init__(self, git_credentials=None):
        """
        Create a repository interface object

        :return:
        """
        self._git_credentials = git_credentials

        self.git = GitBit(verbose=True)
        if self._git_credentials:
            self.setup_gitbit()

    def setup_gitbit(self, credentials=None):
        """
        Set gitbit credentials.
        :return:
        """
        self.git.set_identity(config.gitbit_identity['username'],
                              config.gitbit_identity['email'])
        if credentials is None:
            if self._git_credentials is None:
                return
            else:
                credentials = self._git_credentials
        else:
            self._git_credentials = credentials
        for url_cred_pair in credentials:
            url, cred = url_cred_pair.split(',')
            self.git.add_credential_from_variable(url, cred)

    def set_git_dryrun(self, dryrun):
        self.git.set_dryrun(dryrun)

    def set_git_verbose(self, verbose):
        self.git.set_verbose(verbose)

    def set_git_executable(self, executable):
        self.git.set_excutable(excutable)

    @staticmethod
    def print_command_summary(name, results):
        """
        Print the results of running commands.
          first the command line itself
            and the error code if it's non-zero
          then the stdout & stderr values from running that command

        :param name:
        :param results:
        :return: True if any command exited with an error condition
        """

        error_found = False

        print "============================"
        print "Command output for {0}".format(name)

        if 'commands' in results[name]:
            commands = results[name]['commands']
            for command in commands:
                for key in ['command', 'stdout', 'stderr']:
                    if key in command:
                        if command[key] != '':
                            print command[key]
                        if key == 'command':
                            if command['return_code'] != 0:
                                error_found = True
                                print "EXITED: {0}".format(
                                    command['return_code'])

        return error_found

    def clone_repo_list(self, repo_list, dest_dir, jobs=1):
        """
        check out repository to dest dir based on repo list
        :param repo_list: a list of repository entry which should contain:
                          'repository': the url of repository, it is required
                          'branch': the branch to be check out, it is optional
                          'commit-id': the commit id to be reset, it is optional
        :param dest_dir: the directory where repository will be check out
        :param jobs: Number of parallel jobs to run
        :return:
        """
        cloner = RepoCloner(jobs)
        if cloner is not None:
            for repo in repo_list:
                data = {
                    'repo': repo,
                    'builddir': dest_dir,
                    'credentials': self._git_credentials
                }

                cloner.add_task(data)
            cloner.finish()
            results = cloner.get_results()

            error = False
            for name in results.keys():
                error |= self.print_command_summary(name, results)

            if error:
                raise RuntimeError("Failed to clone repositories")

    def clone_repo(self, repo_url, dest_dir, repo_commit="HEAD"):
        """
        check out a repository to dest directory from the repository url
        :param repo_url: the url of repository, it is required
        :param dest_dir: the directory where repository will be check out
        :return: the directory of the repository
        """
        repo = {}
        repo["repository"] = repo_url
        repo["commit-id"] = repo_commit
        repo_list = [repo]
        self.clone_repo_list(repo_list, dest_dir)

        repo_directory_name = strip_suffix(os.path.basename(repo_url), ".git")
        return os.path.join(dest_dir, repo_directory_name)

    def get_lastest_commit_date(self, repo_dir):
        """
        :param repo_dir: path of the repository
        :return: commit-date
        """
        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError("The repository directory is not a directory")
        code, output, error = self.git.run(
            ['show', '-s', '--pretty=format:%ct'], directory=repo_dir)
        if code == 0:
            return output.strip()
        else:
            raise RuntimeError(
                "Unable to get commit date in directory {0}".format(repo_dir))

    def get_lastest_commit_id(self, repo_dir):
        """
        :param repo_dir: path of the repository
        :return: commit-id
        """

        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError("The repository directory is not a directory")

        code, output, error = self.git.run(
            ['log', '--format=format:%H', '-n', '1'], directory=repo_dir)

        if code == 0:
            return output.strip()
        else:
            raise RuntimeError(
                "Unable to get commit id in directory {0}".format(repo_dir))

    def get_commit_message(self, repo_dir, commit):
        """
        :param repo_dir: path of the repository
        :param commit: the commit id of the repository
        :return: commit-message
        """

        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError("The repository directory is not a directory")

        code, output, error = self.git.run(
            ['log', '--format=format:%B', '-n', '1', commit],
            directory=repo_dir)

        if code == 0:
            return output.strip()
        else:
            raise RuntimeError("Unable to get commit message of {commit_id} in directory {repo_dir}"\
                  .format(commit_id=commit, repo_dir=repo_dir))

    def get_repo_url(self, repo_dir):
        """
        get the remote url of the repository
        :param repo_dir: the directory of the repository
        :return: repository url
        """

        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError("The repository directory is not a directory")

        code, output, error = self.git.run(['ls-remote', '--get-url'],
                                           directory=repo_dir)

        if code == 0:
            return output.strip()
        else:
            raise RuntimeError(
                "Unable to find the repository url in directory {0}".format(
                    repo_dir))

    def get_current_branch(self, repo_dir):
        """
        get the current branch name of the repository
        :param repo_dir: the directory of the repository
        :return: branch name
        """

        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError("The repository directory is not a directory")

        code, output, error = self.git.run(['symbolic-ref', '--short', 'HEAD'],
                                           directory=repo_dir)

        if code == 0:
            return output.strip()
        else:
            raise RuntimeError(
                "Unable to find the current branch in directory {0}".format(
                    repo_dir))

    def check_branch(self, repo_url, branch):
        """
        Checks if the specified branch name exists for the provided repository. Leave only the characters
        following the final "/". This is to handle remote repositories.
        Raise RuntimeError if it is not found.
        :return: None
        """
        if "/" in branch:
            sliced_branch = branch.split("/")[-1]
        else:
            sliced_branch = branch

        cmd_returncode, cmd_value, cmd_error = self.git.run(
            ['ls-remote', repo_url, 'heads/*{0}'.format(sliced_branch)])

        if cmd_returncode is not 0 or cmd_value is '':
            raise RuntimeError(
                "The branch, '{0}', provided for '{1}', does not exist.".
                format(branch, repo_url))

    def set_repo_tagname(self, repo_url, repo_dir, tag_name):
        """
        Sets tagname on the repo
        :param repo_url: the url of the repository
        :param repo_dir: the directory of the repository
        :param tag_name: the tag name to be set
        :return: None
        """
        # See if that tag exists for the repo
        cmd_returncode, cmd_value, cmd_error = self.git.run(
            ["tag", "-l", tag_name], repo_dir)

        # Raise RuntimeError if tag already exists, otherwise create it
        if cmd_returncode == 0 and cmd_value != '':
            raise RuntimeError(
                "Error: Tag {0} already exists - exiting now...".format(
                    cmd_value))
        else:
            print "Creating tag {0} for repo {1}".format(tag_name, repo_url)
            self.git.run(["tag", "-a", tag_name, "-m", "\"Creating new tag\""],
                         repo_dir)
            self.git.run(["push", "origin", "--tags"], repo_dir)
Ejemplo n.º 3
0
class RepoOperator(object):

    def __init__(self, git_credentials=None):
        """
        Create a repository interface object

        :return:
        """
        self._git_credentials = git_credentials
        
        self.git = GitBit(verbose=True)
        if self._git_credentials:
            self.setup_gitbit()


    def setup_gitbit(self, credentials=None):
        """
        Set gitbit credentials.
        :return:
        """
        if credentials is None:
            if self._git_credentials is None:
                return
            else:
                credentials = self._git_credentials
        else:
            self._git_credentials = credentials
        for url_cred_pair in credentials:
            url, cred = url_cred_pair.split(',')
            self.git.add_credential_from_variable(url, cred)

    def set_git_dryrun(self, dryrun):
        self.git.set_dryrun(dryrun)
    
    def set_git_verbose(self, verbose):
        self.git.set_verbose(verbose)

    def set_git_executable(self, executable):
        self.git.set_excutable(excutable)

    @staticmethod
    def print_command_summary(name, results):
        """
        Print the results of running commands.
          first the command line itself
            and the error code if it's non-zero
          then the stdout & stderr values from running that command

        :param name:
        :param results:
        :return: True if any command exited with an error condition
        """

        error_found = False

        print "============================"
        print "Command output for {0}".format(name)

        if 'commands' in results[name]:
            commands = results[name]['commands']
            for command in commands:
                for key in ['command', 'stdout', 'stderr']:
                    if key in command:
                        if command[key] != '':
                            print command[key]
                        if key == 'command':
                            if command['return_code'] != 0:
                                error_found = True
                                print "EXITED: {0}".format(command['return_code'])
                            else:
                                print "SUCCEED"

        return error_found

    def clone_repo_list(self, repo_list, dest_dir, jobs=1):
        """
        check out repository to dest dir based on repo list
        :param repo_list: a list of repository entry which should contain:
                          'repository': the url of repository, it is required
                          'branch': the branch to be check out, it is optional
                          'commit-id': the commit id to be reset, it is optional
        :param dest_dir: the directory where repository will be check out
        :param jobs: Number of parallel jobs to run
        :return:
        """
        cloner = RepoCloner(jobs)
        if cloner is not None:
            for repo in repo_list:
                data = {'repo': repo,
                        'builddir': dest_dir,
                        'credentials': self._git_credentials
                       }
                cloner.add_task(data)
            cloner.finish()
            results = cloner.get_results()

            error = False
            for name in results.keys():
                error |= self.print_command_summary(name, results)

            if error:
                raise RuntimeError("Failed to clone repositories")

    def clone_repo(self, repo_url, dest_dir, repo_commit="HEAD"):
        """
        check out a repository to dest directory from the repository url
        :param repo_url: the url of repository, it is required
        :param dest_dir: the directory where repository will be check out
        :return: the directory of the repository
        """
        repo = {}
        repo["repository"] = repo_url
        repo["commit-id"] = repo_commit
        repo_list = [repo]
        self.clone_repo_list(repo_list, dest_dir)
        
        repo_directory_name = strip_suffix(os.path.basename(repo_url), ".git")
        return os.path.join(dest_dir, repo_directory_name)

    def get_latest_commit_date(self, repo_dir):
        """
        :param repo_dir: path of the repository
        :return: commit-date
        """
        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError("The repository directory {0} is not specified. or its format is wrong".format(repo_dir))
        return_code, output, error = self.git.run(['show', '-s', '--pretty=format:%ct'], directory=repo_dir)
        if return_code == 0:
            return output.strip()
        else:
            raise RuntimeError("Unable to get commit date in directory {0}".format(repo_dir))

    def get_latest_commit_id(self, repo_dir):
        """
        :param repo_dir: path of the repository
        :return: commit-id
        """
         
        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError("The repository directory {0} is not specified. or its format is wrong".format(repo_dir))

        return_code, output, error = self.git.run(['log', '--format=format:%H', '-n', '1'], directory=repo_dir)

        if return_code == 0:
            return output.strip()
        else:
            raise RuntimeError("Unable to get commit id in directory {0}".format(repo_dir))

    def get_latest_merge_commit_before_date(self, repo_dir, date):
        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError("The repository directory {0} is not specified. or its format is wrong".format(repo_dir))

        return_code, output, error = self.git.run(['log', '--merges', '--format=format:%H', '--before='+date, '-n', '1'], directory=repo_dir)

        if return_code == 0:
            return output.strip()
        else:
            raise RuntimeError("Unable to get commit id before {date} in directory {repo_dir}"\
                  .format(date=date, repo_dir=repo_dir))

    def get_latest_author_commit_before_date(self, repo_dir, date, author):
        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError("The repository directory {0} is not specified. or its format is wrong".format(repo_dir))

        return_code, output, error = self.git.run(['log', '--author='+author, '--format=format:%H', '--before='+date, '-n', '1'], directory=repo_dir)

        if return_code == 0:
            return output.strip()
        else:
            raise RuntimeError("Unable to get commit id of {author} before {date} in directory {repo_dir}"\
                  .format(author=author, date=date, repo_dir=repo_dir))


    def get_newer_commit(self, repo_dir, commit1, commit2):
        '''
        if commit1 is newer than commit2, return True
        else, return False
        '''
        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError("The repository directory {0} is not specified. or its format is wrong".format(repo_dir))
        return_code, output, error = self.git.run(['rev-list', commit1+'..'+commit2, '--count'], directory=repo_dir)
        if return_code == 0:
            if output.strip() == "0":
                return commit1
            else:
                return commit2
        else:
            raise RuntimeError("Unable to get any commit between {commit1} and {commit2} in directory {repo_dir}"\
                  .format(commit1=commit1, commit2=commit2, repo_dir=repo_dir))


    def get_commit_message(self, repo_dir, commit):
        """
        :param repo_dir: path of the repository
        :param commit: the commit id of the repository
        :return: commit-message
        """

        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError("The repository directory {0} is not specified. or its format is wrong".format(repo_dir))

        return_code, output, error = self.git.run(['log', '--format=format:%B', '-n', '1', commit], directory=repo_dir)

        if return_code == 0:
            return output.strip()
        else:
            raise RuntimeError("Unable to get commit message of {commit_id} in directory {repo_dir}"\
                  .format(commit_id=commit, repo_dir=repo_dir))


    def get_repo_url(self, repo_dir):
        """
        get the remote url of the repository
        :param repo_dir: the directory of the repository
        :return: repository url
        """

        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError("The repository directory {0} is not specified. or its format is wrong".format(repo_dir))

        return_code, output, error = self.git.run(['ls-remote', '--get-url'], directory=repo_dir)

        if return_code == 0:
            return output.strip()
        else:
            raise RuntimeError("Unable to find the repository url in directory {0}".format(repo_dir))

    def get_current_branch(self, repo_dir):
        """
        get the current branch name of the repository
        :param repo_dir: the directory of the repository
        :return: branch name
        """

        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError("The repository directory {0} is not specified. or its format is wrong".format(repo_dir))

        return_code, output, error = self.git.run(['symbolic-ref', '--short', 'HEAD'], directory=repo_dir)

        if return_code == 0:
            return output.strip()
        else:
            raise RuntimeError("Unable to find the current branch in directory {0}".format(repo_dir))

    def check_branch(self, repo_url, branch):
        """
        Checks if the specified branch name exists for the provided repository. Leave only the characters
        following the final "/". This is to handle remote repositories.
        Raise RuntimeError if it is not found.
        :return: None
        """
        if "/" in branch:
            sliced_branch = branch.split("/")[-1]
        else:
            sliced_branch = branch

        return_code, output, error = self.git.run(['ls-remote', repo_url, 'heads/*{0}'.format(sliced_branch)])

        if return_code is not 0 or output is '':
            raise RuntimeError("The branch, '{0}', provided for '{1}', does not exist."
                               .format(branch, repo_url))

    def set_repo_tagname(self, repo_url, repo_dir, tag_name):
        """
        Sets tagname on the repo
        :param repo_url: the url of the repository
        :param repo_dir: the directory of the repository
        :param tag_name: the tag name to be set
        :return: None
        """
        # See if that tag exists for the repo
        return_code, output, error  = self.git.run(["tag", "-l", tag_name], repo_dir)

        # Return if tag already exists, otherwise create it
        if return_code == 0 and output != '':
            print "Tag {0} already exists in {1}".format(output, repo_url)
            return
        else:
            print "Creating tag {0} for repo {1}".format(tag_name, repo_url)
            return_code, output, error = self.git.run(["tag", "-a", tag_name, "-m", "\"Creating new tag\""], repo_dir)
            if return_code != 0:
                raise RuntimeError("Failed to create tag {0} for {1}.\nError: {2}".format(tag_name, repo_url, error))
            return_code, output, error = self.git.run(["push", "origin", "--tags"], repo_dir)
            if return_code != 0:
                raise RuntimeError("Failed to push tag {0} for {1}.\nError: {2}".format(tag_name, repo_url, error))

    def create_repo_branch(self, repo_url, repo_dir, branch_name):
        """
        Creates branch on the repo
        :param repo_url: the url of the repository
        :param repo_dir: the directory of the repository
        :param branch_name: the branch name to be set
        :return: None
        """
        # See if that branch exists for the repo
        return_code, output, error  = self.git.run(["ls-remote", "--exit-code", "--heads", repo_url, branch_name], repo_dir)
        # Raise RuntimeError if branch already exists, otherwise create it
        if return_code == 0 and output != '':
            raise RuntimeError("Error: Branch {0} already exists - exiting now...".format(output))
        else:
            print "Creating branch {0} for repo {1}".format(branch_name, repo_url)
            return_code, output, error = self.git.run(["branch", branch_name], repo_dir)
            if return_code != 0:
                print output
                raise RuntimeError("Error: Failed to create local branch {0} with error: {1}.".format(branch_name, error))
            return_code, output, error = self.git.run(["push", "-u", "origin", branch_name], repo_dir)
            if return_code != 0:
                print output
                raise RuntimeError("Error: Failed to publish local branch {0} with error: {1}".format(branch_name, error))

    def checkout_repo_branch(self, repo_dir, branch_name):
        """
        Check out to specify branch on the repo
        :param repo_dir: the directory of the repository
        :param branch_name: the branch name to be checked
        :return: None
        """
        return_code, output, error  = self.git.run(["checkout", branch_name], repo_dir)

        if return_code != 0:
            raise RuntimeError("Error: Failed to checkout branch {0}".format(output))

    def push_repo_changes(self, repo_dir, commit_message, push_all=False):
        """
        publish changes of reposioty
        :param repo_dir: the directory of the repository
        :param commit_message: the message to be added to commit
        :return: None
        """
        run_add = False
        run_commit = False
        status_code, status_out, status_error = self.git.run(['status'], repo_dir)
        if status_code == 0:
            if "nothing to commit, working directory clean" in status_out and \
               "Your branch is up-to-date with" in status_out :
                print status_out
                return

            if "Changes not staged for commit" in status_out:
                run_add = True
                run_commit = True

            if "Changes to be committed" in status_out:
                run_commit = True

        if run_add:
            if push_all:
                add_code, add_out, add_error = self.git.run(['add', '-A'], repo_dir)
            else:
                add_code, add_out, add_error = self.git.run(['add', '-u'], repo_dir)

            if add_code != 0:
                raise RuntimeError('Unable to add files for commiting.\n{0}\n{1}\n{2}'.format\
                                  (add_code, add_out, add_error))

        if run_commit:
            commit_code, commit_out, commit_error = self.git.run(['commit', '-m', commit_message], repo_dir)
            if commit_code != 0:
                raise RuntimeError('Unable to commit changes for pushing.\n{0}\n{1}\n{2}'.format\
                                  (commit_code, commit_out, commit_error))

        push_code, push_out, push_error = self.git.run(['push'], repo_dir)
        if push_code !=0:
            raise RuntimeError('Unable to push changes.\n{0}\n{1}\n{2}'.format(push_code, push_out, push_error))
        return
Ejemplo n.º 4
0
class RepoOperator(object):

    def __init__(self, git_credentials=None):
        """
        Create a repository interface object

        :return:
        """
        self._git_credentials = git_credentials
        
        self.git = GitBit(verbose=True)
        if self._git_credentials:
            self.setup_gitbit()


    def setup_gitbit(self, credentials=None):
        """
        Set gitbit credentials.
        :return:
        """
        self.git.set_identity(config.gitbit_identity['username'], config.gitbit_identity['email'])
        if credentials is None:
            if self._git_credentials is None:
                return
            else:
                credentials = self._git_credentials
        else:
            self._git_credentials = credentials
        for url_cred_pair in credentials:
            url, cred = url_cred_pair.split(',')
            self.git.add_credential_from_variable(url, cred)

    def set_git_dryrun(self, dryrun):
        self.git.set_dryrun(dryrun)
    
    def set_git_verbose(self, verbose):
        self.git.set_verbose(verbose)

    def set_git_executable(self, executable):
        self.git.set_excutable(excutable)


    @staticmethod
    def print_command_summary(name, results):
        """
        Print the results of running commands.
          first the command line itself
            and the error code if it's non-zero
          then the stdout & stderr values from running that command

        :param name:
        :param results:
        :return: True if any command exited with an error condition
        """

        error_found = False

        print "============================"
        print "Command output for {0}".format(name)

        if 'commands' in results[name]:
            commands = results[name]['commands']
            for command in commands:
                for key in ['command', 'stdout', 'stderr']:
                    if key in command:
                        if command[key] != '':
                            print command[key]
                        if key == 'command':
                            if command['return_code'] != 0:
                                error_found = True
                                print "EXITED: {0}".format(command['return_code'])

        return error_found

    def clone_repo_list(self, repo_list, dest_dir, jobs=1):
        """
        check out repository to dest dir based on repo list
        :param repo_list: a list of repository entry which should contain:
                          'repository': the url of repository, it is required
                          'branch': the branch to be check out, it is optional
                          'commit-id': the commit id to be reset, it is optional
        :param dest_dir: the directory where repository will be check out
        :param jobs: Number of parallel jobs to run
        :return:
        """
        cloner = RepoCloner(jobs)
        if cloner is not None:
            for repo in repo_list:
                data = {'repo': repo,
                        'builddir': dest_dir,
                        'credentials': self._git_credentials
                       }

                cloner.add_task(data)
            cloner.finish()
            results = cloner.get_results()

            error = False
            for name in results.keys():
                error |= self.print_command_summary(name, results)

            if error:
                raise RuntimeError("Failed to clone repositories")

    
    def clone_repo(self, repo_url, dest_dir, repo_commit="HEAD"):
        """
        check out a repository to dest directory from the repository url
        :param repo_url: the url of repository, it is required
        :param dest_dir: the directory where repository will be check out
        :return: the directory of the repository
        """
        repo = {}
        repo["repository"] = repo_url
        repo["commit-id"] = repo_commit
        repo_list = [repo]
        self.clone_repo_list(repo_list, dest_dir)
        
        repo_directory_name = strip_suffix(os.path.basename(repo_url), ".git")
        return os.path.join(dest_dir, repo_directory_name)

    def get_lastest_commit_date(self, repo_dir):
        """
        :param repo_dir: path of the repository
        :return: commit-date
        """
        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError("The repository directory is not a directory")
        code, output, error = self.git.run(['show', '-s', '--pretty=format:%ct'], directory=repo_dir)
        if code == 0:
            return output.strip()
        else:
            raise RuntimeError("Unable to get commit date in directory {0}".format(repo_dir))

    def get_lastest_commit_id(self, repo_dir):
        """
        :param repo_dir: path of the repository
        :return: commit-id
        """
         
        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError("The repository directory is not a directory")

        code, output, error = self.git.run(['log', '--format=format:%H', '-n', '1'], directory=repo_dir)

        if code == 0:
            return output.strip()
        else:
            raise RuntimeError("Unable to get commit id in directory {0}".format(repo_dir))


    def get_commit_message(self, repo_dir, commit):
        """
        :param repo_dir: path of the repository
        :param commit: the commit id of the repository
        :return: commit-message
        """

        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError("The repository directory is not a directory")

        code, output, error = self.git.run(['log', '--format=format:%B', '-n', '1', commit], directory=repo_dir)

        if code == 0:
            return output.strip()
        else:
            raise RuntimeError("Unable to get commit message of {commit_id} in directory {repo_dir}"\
                  .format(commit_id=commit, repo_dir=repo_dir))


    def get_repo_url(self, repo_dir):
        """
        get the remote url of the repository
        :param repo_dir: the directory of the repository
        :return: repository url
        """

        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError("The repository directory is not a directory")

        code, output, error = self.git.run(['ls-remote', '--get-url'], directory=repo_dir)

        if code == 0:
            return output.strip()
        else:
            raise RuntimeError("Unable to find the repository url in directory {0}".format(repo_dir))

    def get_current_branch(self, repo_dir):
        """
        get the current branch name of the repository
        :param repo_dir: the directory of the repository
        :return: branch name
        """

        if repo_dir is None or not os.path.isdir(repo_dir):
            raise RuntimeError("The repository directory is not a directory")

        code, output, error = self.git.run(['symbolic-ref', '--short', 'HEAD'], directory=repo_dir)

        if code == 0:
            return output.strip()
        else:
            raise RuntimeError("Unable to find the current branch in directory {0}".format(repo_dir))

    def check_branch(self, repo_url, branch):
        """
        Checks if the specified branch name exists for the provided repository. Leave only the characters
        following the final "/". This is to handle remote repositories.
        Raise RuntimeError if it is not found.
        :return: None
        """
        if "/" in branch:
            sliced_branch = branch.split("/")[-1]
        else:
            sliced_branch = branch

        cmd_returncode, cmd_value, cmd_error = self.git.run(['ls-remote', repo_url, 'heads/*{0}'
                                                                    .format(sliced_branch)])

        if cmd_returncode is not 0 or cmd_value is '':
            raise RuntimeError("The branch, '{0}', provided for '{1}', does not exist."
                               .format(branch, repo_url))

    def set_repo_tagname(self, repo_url, repo_dir, tag_name):
        """
        Sets tagname on the repo
        :param repo_url: the url of the repository
        :param repo_dir: the directory of the repository
        :param tag_name: the tag name to be set
        :return: None
        """
        # See if that tag exists for the repo
        cmd_returncode, cmd_value, cmd_error  = self.git.run(["tag", "-l", tag_name], repo_dir)

        # Raise RuntimeError if tag already exists, otherwise create it
        if cmd_returncode == 0 and cmd_value != '':
            raise RuntimeError("Error: Tag {0} already exists - exiting now...".format(cmd_value))
        else:
            print "Creating tag {0} for repo {1}".format(tag_name, repo_url)
            self.git.run(["tag", "-a", tag_name, "-m", "\"Creating new tag\""], repo_dir)
            self.git.run(["push", "origin", "--tags"], repo_dir)