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
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)
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
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)