def add_labels_based_on_size(pr: MergeRequest, size_scheme: str = 'size/{size}'): """ Labels the pull request with size labels according to the amount of code touched, commits and files involved. Helps plan the review in advance. """ sizes = {'XXL', 'XL', 'L', 'M', 'S', 'XS'} with lock_igitt_object('label mr', pr): labels = pr.labels.difference( {size_scheme.format(size=size) for size in sizes}) lines_added, lines_deleted = pr.diffstat commit_score = 4 * len(pr.commits) file_score = 4 * len(pr.affected_files) if commit_score + file_score + lines_added + lines_deleted <= 100: pr.labels = {size_scheme.format(size='XS')}.union(labels) elif commit_score + file_score + lines_added + lines_deleted <= 250: pr.labels = {size_scheme.format(size='S')}.union(labels) elif commit_score + file_score + lines_added + lines_deleted <= 500: pr.labels = {size_scheme.format(size='M')}.union(labels) elif commit_score + file_score + lines_added + lines_deleted <= 1000: pr.labels = {size_scheme.format(size='L')}.union(labels) elif commit_score + file_score + lines_added + lines_deleted <= 1500: pr.labels = {size_scheme.format(size='XL')}.union(labels) else: pr.labels = {size_scheme.format(size='XXL')}.union(labels)
def add_stale_label_to_issues( repo: Repository, issue_expire_limit: int = 'Expiry limit in no. of days for issues', stale_label: str = 'Label to be used for marking stale', unassign: bool = 'Unassign assignees if an issue goes stale', ): """ Assigns the chosen label to issues which haven't been updated in a certain period of time. """ minimum_issue_update_time = (datetime.now() - timedelta(days=issue_expire_limit)).date() for issue in repo.search_issues( updated_before=minimum_issue_update_time, state=IssueStates.OPEN, ): with lock_igitt_object('label issue', issue): if stale_label not in issue.labels: issue.labels = issue.labels | {stale_label} if unassign and issue.assignees: users = ', '.join(f'@{a.username}' for a in issue.assignees) issue.assignees = set() issue.add_comment( 'This issue seems stale!\n\n' + users + ' please reassign yourself if you\'re still ' 'working on this.\n\n' '(Powered by [GitMate.io](https://gitmate.io))')
def label_hotspots( pr: MergeRequest, pattern: str = 'Pattern for matching against', hotspot_label: str = 'Label to be added if hotspot found', ): if len(get_hotspot_files(pattern, pr).intersection(pr.affected_files)): with lock_igitt_object('label mr', pr): pr.labels |= {hotspot_label}
def sync_pr_with_updated_issue(issue: Issue, sync_assignees: bool='Synchronize Assignees'): if not sync_assignees: # pragma: no cover return for pr_object in MergeRequestModel.find_mrs_with_issue(issue): pr = pr_object.igitt_pr with lock_igitt_object('assign mr', pr): pr.assignees |= issue.assignees pr_object.closes_issues.update({str(issue.number): True}) pr_object.save()
def remove_stale_label_from_issues( entity: (Issue, MergeRequest), *args, stale_label: str = 'Label to be used for marking stale issues' ): """ Unassigns the chosen label from issues when they are updated again or if they are mentioned from other pull requests. """ if len(args) > 0 and args[0] == stale_label: # LABELED and UNLABELED events return the label used, skip action if # the label was ``stale_label`` return if isinstance(entity, MergeRequest): issues = entity.mentioned_issues for issue in issues: with lock_igitt_object('label issue', issue): issue.labels = issue.labels - {stale_label} else: with lock_igitt_object('label issue', entity): entity.labels = entity.labels - {stale_label}
def sync_updated_pr_with_issue(pr: MergeRequest, sync_assignees: bool='Synchronize Assignees'): issues = pr.closes_issues repo = Repository.from_igitt_repo(pr.repository) pr_obj = MergeRequestModel.objects.get_or_create( repo=repo, number=pr.number)[0] data = defaultdict(dict) with lock_igitt_object('label mr', pr): labels = pr.labels for issue in issues: labels = issue.labels | labels pr.labels = labels if sync_assignees: with lock_igitt_object('assign mr', pr): assignees = pr.assignees for issue in issues: assignees |= issue.assignees data[str(issue.number)]['assignees'] = True pr.assignees = assignees pr_obj.closes_issues = data pr_obj.save()
def add_assignees_to_issue( issue: Issue, keywords: dict() = 'Keywords that trigger assignments', ): issue_summary = issue.title.lower() + ' ' + issue.description.lower() new_assignees = { assignee for assignee, l_keywords in keywords.items() for keyword in l_keywords.split(',') if keyword.strip() and keyword in issue_summary } with lock_igitt_object('assign issue', issue, refresh_needed=False): for assignee in new_assignees: issue.assign(assignee)
def remove_stale_label_from_merge_requests( pr: MergeRequest, *args, stale_label: str = 'Label to be used for marking stale' ): """ Unassigns the chosen label from pull requests when they are updated again. """ if len(args) > 0 and args[0] == stale_label: # LABELED and UNLABELED events return the label used, skip action if # the label was ``stale_label`` return with lock_igitt_object('label mr', pr): pr.labels = pr.labels - {stale_label}
def add_stale_label_to_merge_requests( repo: Repository, pr_expire_limit: int = 'Expiry limit in no. of day for pull requests', stale_label: str = 'Label to be used for marking stale' ): """ Assigns the chosen label to pull requests which haven't been updated in a certain period of time. """ minimum_pr_update_time = (datetime.now() - timedelta(days=pr_expire_limit)).date() for pr in repo.search_mrs( updated_before=minimum_pr_update_time, state=MergeRequestStates.OPEN, ): with lock_igitt_object('label mr', pr): if stale_label not in pr.labels: pr.labels = pr.labels | {stale_label}
def add_approved_label( commit: Commit, approved_label: str = 'status/ci-approved', status_labels: str = 'status/pending_review, status/WIP'): """ Labels the PR as approved when the head commit passes all tests. """ status_labels = [ label.strip() for label in status_labels.split(',') if label.strip() ] for db_pr in MergeRequestModel.objects.filter(head_sha=commit.sha): pr = db_pr.igitt_pr with lock_igitt_object('label mr', pr): labels = pr.labels if commit.combined_status is Status.SUCCESS: pr.labels = {approved_label} | labels - set(status_labels) else: pr.labels = labels - {approved_label}
def mark_pending_review_or_wip_accordingly( pr: MergeRequest, wip_label: str = 'Work in progress', pending_review_label: str = 'Review pending'): """ Labels the pull request as pending review and removes work in progress on every changed PR accordingly. But retains work in progress label, if title of the pull request begins with "wip". """ with lock_igitt_object('label mr', pr): labels = pr.labels # Allows [wip] and WIP: if not 'wip' in pr.title.lower()[:4]: labels.add(pending_review_label) labels.discard(wip_label) else: labels.add(wip_label) labels.discard(pending_review_label) pr.labels = labels
def add_labels_to_issue( issue: Issue, keywords: dict() = 'Keywords that trigger respective labels', label_texts_as_keywords: bool = 'Apply mentioned labels automatically', ): issue_summary = issue.title.lower() + ' ' + issue.description.lower() new_labels = { label for label, l_keywords in keywords.items() for keyword in l_keywords.split(',') if keyword.strip() and keyword in issue_summary } if label_texts_as_keywords: new_labels |= { label for label in issue.available_labels if label.lower() in issue_summary } with lock_igitt_object('label issue', issue): issue.labels = new_labels.union(issue.labels)
def sync_label_remove_from_issue_with_pr(issue: Issue, label: str): for pr_object in MergeRequestModel.find_mrs_with_issue(issue): pr = pr_object.igitt_pr with lock_igitt_object('label mr', pr): pr.labels -= {label}