def write_patch_for_issue(self, issue_id=None): """ Start a patch This is intended to be the first step in a process of providing a patch. Every patch should be written in its own branch. Therefore we provide a simple cmd to create a meaningfull named branch related to its issue. Thats why the issue id must be provided. There some consistency checks around starting a new branch. """ __ret = [] # first we have to do a few consistency checks if not check_string_to_int(issue_id): __ret.append(('WARNING', "ID must be an integer", 1)) return __ret issue_id = int(issue_id) if not git.is_git_repo(): __ret.append(('WARNING', "Not a git repository", 1)) return __ret if not git.branch_is_clean(): __ret.append(( 'WARNING', "Your branch is not clean. Please commit your changes first.")) return __ret # Transform iid to id try: issue_uid = self.api.issue_iid_to_uid(issue_id) issue = self.api.getprojectissue(self.p_id, issue_uid) if not issue: __ret.append(('WARNING', "Issue ID not found", 1)) return __ret if issue['project_id'] != self.p_id: __ret.append(( 'WARNING', "The issue ID does not correspond to the current " + "git repository/project", 1)) return __ret except TypeError as e: __ret.append(('FAIL', 'Something went wrong: {0}'. format(e.message))) return __ret # the workflow itself: # 1. create a branch # 2. switch to that branch. git.change_or_create_branch("issue_" + str(issue_id)) return __ret
def edit_issue(self, issue_id=None, do=None): """ Close or reopen an issue 'do' must be either 'close' or 'reopen' """ __ret = [] if not check_string_to_int(issue_id): __ret.append(('WARNING', "ID must be an integer", 1)) return __ret issue_id = int(issue_id) issue_uid = self.api.issue_iid_to_uid(issue_id) if not issue_uid: __ret.append(('FAIL', "Issue {0} not found".format(issue_id), 1)) return __ret if do == 'close': state_event = 'close' elif do == 'reopen': state_event = 'reopen' else: raise ValueError('do must be close or reopen') try: ret_val = self.api.edit_issue(issue_uid, state_event=state_event) if ret_val['state'] == 'closed': __ret.append(('GREEN', 'Issue {0} closed'.format(issue_id))) elif ret_val['state'] == 'reopened': __ret.append(('GREEN', 'Issue {0} reopened'.format(issue_id))) else: __ret.append(( 'FAIL', 'Issue {0} has state: {1}' .format(issue_id, ret_val['state']))) except: __ret.append(('FAIL', 'Something went wrong')) return __ret
def push_patch(self, assignee_id=None, mr_text=None): """ Push the patch and provide a mergerequest The main problem is to avoid CONFLICTs while accepting a MR in accept_mergerequest(). To support that we check if origin/master can be merged to our issue branch. If not the committer has to solve the conflict first before the patch might be submitted. Btw we force the committer not to use the master branch of the repository because we want to keep it clean as far as possible. After the patch is pushed to the remote as a new branch the MR will be created. """ __ret = [] if git.get_current_branch() == "master": __ret.append(( 'WARNING', "You can not open a mergerequest from your " "local master branch.\n" "Please switch to your issue branch!", 1)) return __ret if not git.branch_is_clean(): output = git.git(['status', '-s']) __ret.append(('INFO', output)) __ret.append(('WARNING', "You have uncommitted changes. Please commit them first!", 1)) return __ret # We have to do some validating: # 1. Check whether the current commit is already in # the remote master branch # 2. If not if we need to push our local changes to the # remote. There might be 3 reasons: # - The source_branch of the mergerequest doesn't exists # on the remote. # - If there is no source_branch, we have to merge # the origin/master into our issue branch to avoid # conflicts on the origin side. # - The source_branch exists but we have new commits for that MR # # We now have our local changes at the remote in a seperate branch. # Move on: # # 3. If there is no MR present, create one. # # Pray. # Step 1: Check whether the commit exist in the remote master branch last_local_sha = git.get_last_commit_sha() sha_is_on_remote = git.is_commit_on_remote(last_local_sha) if sha_is_on_remote: __ret.append(( 'WARNING', "Your local commit is already in the remote master " "branch.\nAborting!", 1)) return __ret # Step 2: Check whether we have to push our local changes to the remote need_push = False sourcebranch = git.get_current_branch() __ret.append(('GREEN', 'Branch: {0}'.format(sourcebranch))) # First check whether the MR branch exists on the remote sourcebranch_on_remote = self.api.remote_branch_exists(sourcebranch) if not sourcebranch_on_remote: need_push = True # We need to merge the origin/master into our issue branch because # of avoiding conflicts in the merge workflow on the origin side. try: __ret.append(( 'INFO', 'Try to rebase origin master into {0}' .format(sourcebranch))) git.git(['fetch']) git.git(['rebase', 'origin/master']) except ValueError as e: __ret.append(( 'FAIL', "Merge into {0} failed: {1}". format(sourcebranch, e.message))) git.git(['rebase', '--abort']) __ret.append(('INFO', 'Please run \n\ngit pull --rebase\n\nand manually resolve your CONFLICTs.')) __ret.append(('INFO', 'Then run\n\ngit add <FILE>\n git rebase --continue')) __ret.append(('INFO', 'At least run\n\nnacl-flow cp again', 1)) return __ret else: # Second check whether we have un-pushed local commits. # We check the local source branch compared to the remote # source branch. unpushed_commits = git.need_pull_push( return__returncode=True, local_branch=sourcebranch, remote_branch=sourcebranch) if unpushed_commits == 2: need_push = True if need_push: __ret.append(('INFO', "Pushing to origin " + sourcebranch)) git.git(['push', 'origin', sourcebranch]) else: __ret.append(('INFO', "Local and remote are up-to-date.")) # We are done with pushing commits. # Step 3. Creating a MR if assignee_id: if not check_string_to_int(assignee_id): __ret.append(('WARNING', "ID must be an integer", 1)) return __ret assignee_id = int(assignee_id) targetbranch = 'master' if mr_text: title = str(mr_text) else: title = git.git(['log', '--format=%s', '-n', '1']) is_new_mergerequest = self.api.is_mergerequest_new( sourcebranch, targetbranch) if is_new_mergerequest: __ret.append(('GREEN', "Create a new mergerequest")) if not self.api.createmergerequest( self.p_id, sourcebranch, targetbranch, title, assignee_id=assignee_id): __ret.append(('FAIL', "Creating Mergerequest failed!")) else: __ret.append(('INFO', "Mergerequests exists. Skipping")) return __ret