1a2adebf4STom Stellard#!/usr/bin/env python3 2a2adebf4STom Stellard# 3a2adebf4STom Stellard# ======- github-automation - LLVM GitHub Automation Routines--*- python -*--==# 4a2adebf4STom Stellard# 5a2adebf4STom Stellard# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 6a2adebf4STom Stellard# See https://llvm.org/LICENSE.txt for license information. 7a2adebf4STom Stellard# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 8a2adebf4STom Stellard# 9a2adebf4STom Stellard# ==-------------------------------------------------------------------------==# 10a2adebf4STom Stellard 11a2adebf4STom Stellardimport argparse 12daf82a51STom Stellardfrom git import Repo # type: ignore 13a2adebf4STom Stellardimport github 14a2adebf4STom Stellardimport os 15daf82a51STom Stellardimport re 16*f673dcc6STom Stellardimport requests 17daf82a51STom Stellardimport sys 1817d4796cSTom Stellardimport time 19daf82a51STom Stellardfrom typing import * 20a2adebf4STom Stellard 21a2adebf4STom Stellardclass IssueSubscriber: 22a2adebf4STom Stellard 23a2adebf4STom Stellard @property 24a2adebf4STom Stellard def team_name(self) -> str: 25a2adebf4STom Stellard return self._team_name 26a2adebf4STom Stellard 27a2adebf4STom Stellard def __init__(self, token:str, repo:str, issue_number:int, label_name:str): 28a2adebf4STom Stellard self.repo = github.Github(token).get_repo(repo) 29a2adebf4STom Stellard self.org = github.Github(token).get_organization(self.repo.organization.login) 30a2adebf4STom Stellard self.issue = self.repo.get_issue(issue_number) 31a2adebf4STom Stellard self._team_name = 'issue-subscribers-{}'.format(label_name).lower() 32a2adebf4STom Stellard 33a2adebf4STom Stellard def run(self) -> bool: 34a2adebf4STom Stellard for team in self.org.get_teams(): 35a2adebf4STom Stellard if self.team_name != team.name.lower(): 36a2adebf4STom Stellard continue 37a2adebf4STom Stellard comment = '@llvm/{}'.format(team.slug) 38a2adebf4STom Stellard self.issue.create_comment(comment) 39a2adebf4STom Stellard return True 40a2adebf4STom Stellard return False 41a2adebf4STom Stellard 42daf82a51STom Stellarddef setup_llvmbot_git(git_dir = '.'): 43daf82a51STom Stellard """ 44daf82a51STom Stellard Configure the git repo in `git_dir` with the llvmbot account so 45daf82a51STom Stellard commits are attributed to llvmbot. 46daf82a51STom Stellard """ 47daf82a51STom Stellard repo = Repo(git_dir) 48daf82a51STom Stellard with repo.config_writer() as config: 49daf82a51STom Stellard config.set_value('user', 'name', 'llvmbot') 50daf82a51STom Stellard config.set_value('user', 'email', '[email protected]') 51daf82a51STom Stellard 52*f673dcc6STom Stellarddef phab_api_call(phab_token:str, url:str, args:dict) -> dict: 53*f673dcc6STom Stellard """ 54*f673dcc6STom Stellard Make an API call to the Phabricator web service and return a dictionary 55*f673dcc6STom Stellard containing the json response. 56*f673dcc6STom Stellard """ 57*f673dcc6STom Stellard data = { "api.token" : phab_token } 58*f673dcc6STom Stellard data.update(args) 59*f673dcc6STom Stellard response = requests.post(url, data = data) 60*f673dcc6STom Stellard return response.json() 61*f673dcc6STom Stellard 62*f673dcc6STom Stellard 63*f673dcc6STom Stellarddef phab_login_to_github_login(phab_token:str, repo:github.Repository.Repository, phab_login:str) -> str: 64*f673dcc6STom Stellard """ 65*f673dcc6STom Stellard Tries to translate a Phabricator login to a github login by 66*f673dcc6STom Stellard finding a commit made in Phabricator's Differential. 67*f673dcc6STom Stellard The commit's SHA1 is then looked up in the github repo and 68*f673dcc6STom Stellard the committer's login associated with that commit is returned. 69*f673dcc6STom Stellard 70*f673dcc6STom Stellard :param str phab_token: The Conduit API token to use for communication with Pabricator 71*f673dcc6STom Stellard :param github.Repository.Repository repo: The github repo to use when looking for the SHA1 found in Differential 72*f673dcc6STom Stellard :param str phab_login: The Phabricator login to be translated. 73*f673dcc6STom Stellard """ 74*f673dcc6STom Stellard 75*f673dcc6STom Stellard args = { 76*f673dcc6STom Stellard "constraints[authors][0]" : phab_login, 77*f673dcc6STom Stellard # PHID for "LLVM Github Monorepo" repository 78*f673dcc6STom Stellard "constraints[repositories][0]" : "PHID-REPO-f4scjekhnkmh7qilxlcy", 79*f673dcc6STom Stellard "limit" : 1 80*f673dcc6STom Stellard } 81*f673dcc6STom Stellard # API documentation: https://reviews.llvm.org/conduit/method/diffusion.commit.search/ 82*f673dcc6STom Stellard r = phab_api_call(phab_token, "https://reviews.llvm.org/api/diffusion.commit.search", args) 83*f673dcc6STom Stellard data = r['result']['data'] 84*f673dcc6STom Stellard if len(data) == 0: 85*f673dcc6STom Stellard # Can't find any commits associated with this user 86*f673dcc6STom Stellard return None 87*f673dcc6STom Stellard 88*f673dcc6STom Stellard commit_sha = data[0]['fields']['identifier'] 89*f673dcc6STom Stellard return repo.get_commit(commit_sha).committer.login 90*f673dcc6STom Stellard 91*f673dcc6STom Stellarddef phab_get_commit_approvers(phab_token:str, repo:github.Repository.Repository, commit:github.Commit.Commit) -> list: 92*f673dcc6STom Stellard args = { "corpus" : commit.commit.message } 93*f673dcc6STom Stellard # API documentation: https://reviews.llvm.org/conduit/method/differential.parsecommitmessage/ 94*f673dcc6STom Stellard r = phab_api_call(phab_token, "https://reviews.llvm.org/api/differential.parsecommitmessage", args) 95*f673dcc6STom Stellard review_id = r['result']['revisionIDFieldInfo']['value'] 96*f673dcc6STom Stellard 97*f673dcc6STom Stellard args = { 98*f673dcc6STom Stellard 'constraints[ids][0]' : review_id, 99*f673dcc6STom Stellard 'attachments[reviewers]' : True 100*f673dcc6STom Stellard } 101*f673dcc6STom Stellard # API documentation: https://reviews.llvm.org/conduit/method/differential.revision.search/ 102*f673dcc6STom Stellard r = phab_api_call(phab_token, "https://reviews.llvm.org/api/differential.revision.search", args) 103*f673dcc6STom Stellard reviewers = r['result']['data'][0]['attachments']['reviewers']['reviewers'] 104*f673dcc6STom Stellard accepted = [] 105*f673dcc6STom Stellard for reviewer in reviewers: 106*f673dcc6STom Stellard if reviewer['status'] != 'accepted': 107*f673dcc6STom Stellard continue 108*f673dcc6STom Stellard phid = reviewer['reviewerPHID'] 109*f673dcc6STom Stellard args = { 'constraints[phids][0]' : phid } 110*f673dcc6STom Stellard # API documentation: https://reviews.llvm.org/conduit/method/user.search/ 111*f673dcc6STom Stellard r = phab_api_call(phab_token, "https://reviews.llvm.org/api/user.search", args) 112*f673dcc6STom Stellard accepted.append(r['result']['data'][0]['fields']['username']) 113*f673dcc6STom Stellard return accepted 114*f673dcc6STom Stellard 115daf82a51STom Stellardclass ReleaseWorkflow: 116daf82a51STom Stellard 1173929f913STom Stellard CHERRY_PICK_FAILED_LABEL = 'release:cherry-pick-failed' 1183929f913STom Stellard 119daf82a51STom Stellard """ 120daf82a51STom Stellard This class implements the sub-commands for the release-workflow command. 121daf82a51STom Stellard The current sub-commands are: 122daf82a51STom Stellard * create-branch 123daf82a51STom Stellard * create-pull-request 124daf82a51STom Stellard 125daf82a51STom Stellard The execute_command method will automatically choose the correct sub-command 126daf82a51STom Stellard based on the text in stdin. 127daf82a51STom Stellard """ 128daf82a51STom Stellard 129daf82a51STom Stellard def __init__(self, token:str, repo:str, issue_number:int, 130daf82a51STom Stellard branch_repo_name:str, branch_repo_token:str, 131*f673dcc6STom Stellard llvm_project_dir:str, phab_token:str) -> None: 132daf82a51STom Stellard self._token = token 133daf82a51STom Stellard self._repo_name = repo 134daf82a51STom Stellard self._issue_number = issue_number 135daf82a51STom Stellard self._branch_repo_name = branch_repo_name 136daf82a51STom Stellard if branch_repo_token: 137daf82a51STom Stellard self._branch_repo_token = branch_repo_token 138daf82a51STom Stellard else: 139daf82a51STom Stellard self._branch_repo_token = self.token 140daf82a51STom Stellard self._llvm_project_dir = llvm_project_dir 141*f673dcc6STom Stellard self._phab_token = phab_token 142daf82a51STom Stellard 143daf82a51STom Stellard @property 144daf82a51STom Stellard def token(self) -> str: 145daf82a51STom Stellard return self._token 146daf82a51STom Stellard 147daf82a51STom Stellard @property 148daf82a51STom Stellard def repo_name(self) -> str: 149daf82a51STom Stellard return self._repo_name 150daf82a51STom Stellard 151daf82a51STom Stellard @property 152daf82a51STom Stellard def issue_number(self) -> int: 153daf82a51STom Stellard return self._issue_number 154daf82a51STom Stellard 155daf82a51STom Stellard @property 156daf82a51STom Stellard def branch_repo_name(self) -> str: 157daf82a51STom Stellard return self._branch_repo_name 158daf82a51STom Stellard 159daf82a51STom Stellard @property 160daf82a51STom Stellard def branch_repo_token(self) -> str: 161daf82a51STom Stellard return self._branch_repo_token 162daf82a51STom Stellard 163daf82a51STom Stellard @property 164daf82a51STom Stellard def llvm_project_dir(self) -> str: 165daf82a51STom Stellard return self._llvm_project_dir 166daf82a51STom Stellard 167daf82a51STom Stellard @property 168*f673dcc6STom Stellard def phab_token(self) -> str: 169*f673dcc6STom Stellard return self._phab_token 170*f673dcc6STom Stellard 171*f673dcc6STom Stellard @property 172*f673dcc6STom Stellard def repo(self) -> github.Repository.Repository: 173daf82a51STom Stellard return github.Github(self.token).get_repo(self.repo_name) 174daf82a51STom Stellard 175daf82a51STom Stellard @property 176daf82a51STom Stellard def issue(self) -> github.Issue.Issue: 177*f673dcc6STom Stellard return self.repo.get_issue(self.issue_number) 178daf82a51STom Stellard 179daf82a51STom Stellard @property 180daf82a51STom Stellard def push_url(self) -> str: 181daf82a51STom Stellard return 'https://{}@github.com/{}'.format(self.branch_repo_token, self.branch_repo_name) 182daf82a51STom Stellard 183daf82a51STom Stellard @property 184daf82a51STom Stellard def branch_name(self) -> str: 185daf82a51STom Stellard return 'issue{}'.format(self.issue_number) 186daf82a51STom Stellard 187daf82a51STom Stellard @property 188daf82a51STom Stellard def release_branch_for_issue(self) -> Optional[str]: 189daf82a51STom Stellard issue = self.issue 190daf82a51STom Stellard milestone = issue.milestone 191daf82a51STom Stellard if milestone is None: 192daf82a51STom Stellard return None 193daf82a51STom Stellard m = re.search('branch: (.+)',milestone.description) 194daf82a51STom Stellard if m: 195daf82a51STom Stellard return m.group(1) 196daf82a51STom Stellard return None 197daf82a51STom Stellard 198daf82a51STom Stellard def print_release_branch(self) -> None: 199daf82a51STom Stellard print(self.release_branch_for_issue) 200daf82a51STom Stellard 201daf82a51STom Stellard def issue_notify_branch(self) -> None: 202daf82a51STom Stellard self.issue.create_comment('/branch {}/{}'.format(self.branch_repo_name, self.branch_name)) 203daf82a51STom Stellard 204daf82a51STom Stellard def issue_notify_pull_request(self, pull:github.PullRequest.PullRequest) -> None: 205daf82a51STom Stellard self.issue.create_comment('/pull-request {}#{}'.format(self.branch_repo_name, pull.number)) 206daf82a51STom Stellard 20751eaaa30STom Stellard def make_ignore_comment(self, comment: str) -> str: 20851eaaa30STom Stellard """ 20951eaaa30STom Stellard Returns the comment string with a prefix that will cause 21051eaaa30STom Stellard a Github workflow to skip parsing this comment. 21151eaaa30STom Stellard 21251eaaa30STom Stellard :param str comment: The comment to ignore 21351eaaa30STom Stellard """ 21451eaaa30STom Stellard return "<!--IGNORE-->\n"+comment 21551eaaa30STom Stellard 21651eaaa30STom Stellard def issue_notify_no_milestone(self, comment:List[str]) -> None: 21751eaaa30STom Stellard message = "{}\n\nError: Command failed due to missing milestone.".format(''.join(['>' + line for line in comment])) 21851eaaa30STom Stellard self.issue.create_comment(self.make_ignore_comment(message)) 21951eaaa30STom Stellard 220daf82a51STom Stellard @property 221daf82a51STom Stellard def action_url(self) -> str: 222daf82a51STom Stellard if os.getenv('CI'): 223daf82a51STom Stellard return 'https://github.com/{}/actions/runs/{}'.format(os.getenv('GITHUB_REPOSITORY'), os.getenv('GITHUB_RUN_ID')) 224daf82a51STom Stellard return "" 225daf82a51STom Stellard 226daf82a51STom Stellard def issue_notify_cherry_pick_failure(self, commit:str) -> github.IssueComment.IssueComment: 22751eaaa30STom Stellard message = self.make_ignore_comment("Failed to cherry-pick: {}\n\n".format(commit)) 228daf82a51STom Stellard action_url = self.action_url 229daf82a51STom Stellard if action_url: 230daf82a51STom Stellard message += action_url + "\n\n" 231daf82a51STom Stellard message += "Please manually backport the fix and push it to your github fork. Once this is done, please add a comment like this:\n\n`/branch <user>/<repo>/<branch>`" 232daf82a51STom Stellard issue = self.issue 233daf82a51STom Stellard comment = issue.create_comment(message) 2343929f913STom Stellard issue.add_to_labels(self.CHERRY_PICK_FAILED_LABEL) 235daf82a51STom Stellard return comment 236daf82a51STom Stellard 237daf82a51STom Stellard def issue_notify_pull_request_failure(self, branch:str) -> github.IssueComment.IssueComment: 238daf82a51STom Stellard message = "Failed to create pull request for {} ".format(branch) 239daf82a51STom Stellard message += self.action_url 240daf82a51STom Stellard return self.issue.create_comment(message) 241daf82a51STom Stellard 2423929f913STom Stellard def issue_remove_cherry_pick_failed_label(self): 2433929f913STom Stellard if self.CHERRY_PICK_FAILED_LABEL in [l.name for l in self.issue.labels]: 2443929f913STom Stellard self.issue.remove_from_labels(self.CHERRY_PICK_FAILED_LABEL) 245daf82a51STom Stellard 246*f673dcc6STom Stellard def pr_request_review(self, pr:github.PullRequest.PullRequest): 247*f673dcc6STom Stellard """ 248*f673dcc6STom Stellard This function will try to find the best reviewers for `commits` and 249*f673dcc6STom Stellard then add a comment requesting review of the backport and assign the 250*f673dcc6STom Stellard pull request to the selected reviewers. 251*f673dcc6STom Stellard 252*f673dcc6STom Stellard The reviewers selected are those users who approved the patch in 253*f673dcc6STom Stellard Phabricator. 254*f673dcc6STom Stellard """ 255*f673dcc6STom Stellard reviewers = [] 256*f673dcc6STom Stellard for commit in pr.get_commits(): 257*f673dcc6STom Stellard approvers = phab_get_commit_approvers(self.phab_token, self.repo, commit) 258*f673dcc6STom Stellard for a in approvers: 259*f673dcc6STom Stellard login = phab_login_to_github_login(self.phab_token, self.repo, a) 260*f673dcc6STom Stellard if not login: 261*f673dcc6STom Stellard continue 262*f673dcc6STom Stellard reviewers.append(login) 263*f673dcc6STom Stellard if len(reviewers): 264*f673dcc6STom Stellard message = "{} What do you think about merging this PR to the release branch?".format( 265*f673dcc6STom Stellard " ".join(["@" + r for r in reviewers])) 266*f673dcc6STom Stellard pr.create_issue_comment(message) 267*f673dcc6STom Stellard pr.add_to_assignees(*reviewers) 268*f673dcc6STom Stellard 269daf82a51STom Stellard def create_branch(self, commits:List[str]) -> bool: 270daf82a51STom Stellard """ 271daf82a51STom Stellard This function attempts to backport `commits` into the branch associated 272daf82a51STom Stellard with `self.issue_number`. 273daf82a51STom Stellard 274daf82a51STom Stellard If this is successful, then the branch is pushed to `self.branch_repo_name`, if not, 275daf82a51STom Stellard a comment is added to the issue saying that the cherry-pick failed. 276daf82a51STom Stellard 277daf82a51STom Stellard :param list commits: List of commits to cherry-pick. 278daf82a51STom Stellard 279daf82a51STom Stellard """ 280daf82a51STom Stellard print('cherry-picking', commits) 281daf82a51STom Stellard branch_name = self.branch_name 282daf82a51STom Stellard local_repo = Repo(self.llvm_project_dir) 283daf82a51STom Stellard local_repo.git.checkout(self.release_branch_for_issue) 284daf82a51STom Stellard 285daf82a51STom Stellard for c in commits: 286daf82a51STom Stellard try: 287daf82a51STom Stellard local_repo.git.cherry_pick('-x', c) 288daf82a51STom Stellard except Exception as e: 289daf82a51STom Stellard self.issue_notify_cherry_pick_failure(c) 290daf82a51STom Stellard raise e 291daf82a51STom Stellard 292daf82a51STom Stellard push_url = self.push_url 293daf82a51STom Stellard print('Pushing to {} {}'.format(push_url, branch_name)) 2941889170aSTobias Hieta local_repo.git.push(push_url, 'HEAD:{}'.format(branch_name), force=True) 295daf82a51STom Stellard 296daf82a51STom Stellard self.issue_notify_branch() 2973929f913STom Stellard self.issue_remove_cherry_pick_failed_label() 298daf82a51STom Stellard return True 299daf82a51STom Stellard 30049ad577cSTobias Hieta def check_if_pull_request_exists(self, repo:github.Repository.Repository, head:str) -> bool: 30149ad577cSTobias Hieta pulls = repo.get_pulls(head=head) 302fde9ef52STobias Hieta return pulls.totalCount != 0 303daf82a51STom Stellard 304daf82a51STom Stellard def create_pull_request(self, owner:str, branch:str) -> bool: 305daf82a51STom Stellard """ 306daf82a51STom Stellard reate a pull request in `self.branch_repo_name`. The base branch of the 307daf82a51STom Stellard pull request will be choosen based on the the milestone attached to 308daf82a51STom Stellard the issue represented by `self.issue_number` For example if the milestone 309daf82a51STom Stellard is Release 13.0.1, then the base branch will be release/13.x. `branch` 310daf82a51STom Stellard will be used as the compare branch. 311daf82a51STom Stellard https://docs.github.com/en/get-started/quickstart/github-glossary#base-branch 312daf82a51STom Stellard https://docs.github.com/en/get-started/quickstart/github-glossary#compare-branch 313daf82a51STom Stellard """ 314daf82a51STom Stellard repo = github.Github(self.token).get_repo(self.branch_repo_name) 315daf82a51STom Stellard issue_ref = '{}#{}'.format(self.repo_name, self.issue_number) 316daf82a51STom Stellard pull = None 317daf82a51STom Stellard release_branch_for_issue = self.release_branch_for_issue 318daf82a51STom Stellard if release_branch_for_issue is None: 319daf82a51STom Stellard return False 32017d4796cSTom Stellard head_branch = branch 32117d4796cSTom Stellard if not repo.fork: 32217d4796cSTom Stellard # If the target repo is not a fork of llvm-project, we need to copy 32317d4796cSTom Stellard # the branch into the target repo. GitHub only supports cross-repo pull 32417d4796cSTom Stellard # requests on forked repos. 32517d4796cSTom Stellard head_branch = f'{owner}-{branch}' 32617d4796cSTom Stellard local_repo = Repo(self.llvm_project_dir) 32717d4796cSTom Stellard push_done = False 32817d4796cSTom Stellard for i in range(0,5): 32917d4796cSTom Stellard try: 33017d4796cSTom Stellard local_repo.git.fetch(f'https://github.com/{owner}/llvm-project', f'{branch}:{branch}') 33117d4796cSTom Stellard local_repo.git.push(self.push_url, f'{branch}:{head_branch}', force=True) 33217d4796cSTom Stellard push_done = True 33317d4796cSTom Stellard break 33417d4796cSTom Stellard except Exception as e: 33517d4796cSTom Stellard print(e) 33617d4796cSTom Stellard time.sleep(30) 33717d4796cSTom Stellard continue 33817d4796cSTom Stellard if not push_done: 33917d4796cSTom Stellard raise Exception("Failed to mirror branch into {}".format(self.push_url)) 34017d4796cSTom Stellard owner = repo.owner.login 34117d4796cSTom Stellard 34217d4796cSTom Stellard head = f"{owner}:{head_branch}" 34349ad577cSTobias Hieta if self.check_if_pull_request_exists(repo, head): 34449ad577cSTobias Hieta print("PR already exists...") 34549ad577cSTobias Hieta return True 346daf82a51STom Stellard try: 34749ad577cSTobias Hieta pull = repo.create_pull(title=f"PR for {issue_ref}", 348daf82a51STom Stellard body='resolves {}'.format(issue_ref), 349daf82a51STom Stellard base=release_branch_for_issue, 35049ad577cSTobias Hieta head=head, 351daf82a51STom Stellard maintainer_can_modify=False) 352*f673dcc6STom Stellard 353*f673dcc6STom Stellard try: 354*f673dcc6STom Stellard if self.phab_token: 355*f673dcc6STom Stellard self.pr_request_review(pull) 356*f673dcc6STom Stellard except Exception as e: 357*f673dcc6STom Stellard print("error: Failed while searching for reviewers", e) 358*f673dcc6STom Stellard 359daf82a51STom Stellard except Exception as e: 360daf82a51STom Stellard self.issue_notify_pull_request_failure(branch) 361daf82a51STom Stellard raise e 362daf82a51STom Stellard 363daf82a51STom Stellard if pull is None: 364daf82a51STom Stellard return False 365daf82a51STom Stellard 366daf82a51STom Stellard self.issue_notify_pull_request(pull) 3673929f913STom Stellard self.issue_remove_cherry_pick_failed_label() 368daf82a51STom Stellard 369daf82a51STom Stellard # TODO(tstellar): Do you really want to always return True? 370daf82a51STom Stellard return True 371daf82a51STom Stellard 372daf82a51STom Stellard 373daf82a51STom Stellard def execute_command(self) -> bool: 374daf82a51STom Stellard """ 375daf82a51STom Stellard This function reads lines from STDIN and executes the first command 376daf82a51STom Stellard that it finds. The 2 supported commands are: 377daf82a51STom Stellard /cherry-pick commit0 <commit1> <commit2> <...> 378daf82a51STom Stellard /branch <owner>/<repo>/<branch> 379daf82a51STom Stellard """ 380daf82a51STom Stellard for line in sys.stdin: 381daf82a51STom Stellard line.rstrip() 382daf82a51STom Stellard m = re.search("/([a-z-]+)\s(.+)", line) 383daf82a51STom Stellard if not m: 384daf82a51STom Stellard continue 385daf82a51STom Stellard command = m.group(1) 386daf82a51STom Stellard args = m.group(2) 387daf82a51STom Stellard 388daf82a51STom Stellard if command == 'cherry-pick': 389daf82a51STom Stellard return self.create_branch(args.split()) 390daf82a51STom Stellard 391daf82a51STom Stellard if command == 'branch': 392daf82a51STom Stellard m = re.match('([^/]+)/([^/]+)/(.+)', args) 393daf82a51STom Stellard if m: 394daf82a51STom Stellard owner = m.group(1) 395daf82a51STom Stellard branch = m.group(3) 396daf82a51STom Stellard return self.create_pull_request(owner, branch) 397daf82a51STom Stellard 398daf82a51STom Stellard print("Do not understand input:") 399daf82a51STom Stellard print(sys.stdin.readlines()) 400daf82a51STom Stellard return False 401a2adebf4STom Stellard 402a2adebf4STom Stellardparser = argparse.ArgumentParser() 403daf82a51STom Stellardparser.add_argument('--token', type=str, required=True, help='GitHub authentiation token') 404daf82a51STom Stellardparser.add_argument('--repo', type=str, default=os.getenv('GITHUB_REPOSITORY', 'llvm/llvm-project'), 405daf82a51STom Stellard help='The GitHub repository that we are working with in the form of <owner>/<repo> (e.g. llvm/llvm-project)') 406a2adebf4STom Stellardsubparsers = parser.add_subparsers(dest='command') 407a2adebf4STom Stellard 408a2adebf4STom Stellardissue_subscriber_parser = subparsers.add_parser('issue-subscriber') 409a2adebf4STom Stellardissue_subscriber_parser.add_argument('--label-name', type=str, required=True) 410a2adebf4STom Stellardissue_subscriber_parser.add_argument('--issue-number', type=int, required=True) 411a2adebf4STom Stellard 412daf82a51STom Stellardrelease_workflow_parser = subparsers.add_parser('release-workflow') 413daf82a51STom Stellardrelease_workflow_parser.add_argument('--llvm-project-dir', type=str, default='.', help='directory containing the llvm-project checout') 414daf82a51STom Stellardrelease_workflow_parser.add_argument('--issue-number', type=int, required=True, help='The issue number to update') 415*f673dcc6STom Stellardrelease_workflow_parser.add_argument('--phab-token', type=str, help='Phabricator conduit API token. See https://reviews.llvm.org/settings/user/<USER>/page/apitokens/') 416daf82a51STom Stellardrelease_workflow_parser.add_argument('--branch-repo-token', type=str, 417daf82a51STom Stellard help='GitHub authentication token to use for the repository where new branches will be pushed. Defaults to TOKEN.') 41821c060c4STom Stellardrelease_workflow_parser.add_argument('--branch-repo', type=str, default='llvm/llvm-project-release-prs', 419daf82a51STom Stellard help='The name of the repo where new branches will be pushed (e.g. llvm/llvm-project)') 420daf82a51STom Stellardrelease_workflow_parser.add_argument('sub_command', type=str, choices=['print-release-branch', 'auto'], 421daf82a51STom Stellard help='Print to stdout the name of the release branch ISSUE_NUMBER should be backported to') 422daf82a51STom Stellard 423daf82a51STom Stellardllvmbot_git_config_parser = subparsers.add_parser('setup-llvmbot-git', help='Set the default user and email for the git repo in LLVM_PROJECT_DIR to llvmbot') 424daf82a51STom Stellard 425a2adebf4STom Stellardargs = parser.parse_args() 426a2adebf4STom Stellard 427a2adebf4STom Stellardif args.command == 'issue-subscriber': 428a2adebf4STom Stellard issue_subscriber = IssueSubscriber(args.token, args.repo, args.issue_number, args.label_name) 429a2adebf4STom Stellard issue_subscriber.run() 430daf82a51STom Stellardelif args.command == 'release-workflow': 431daf82a51STom Stellard release_workflow = ReleaseWorkflow(args.token, args.repo, args.issue_number, 432daf82a51STom Stellard args.branch_repo, args.branch_repo_token, 433*f673dcc6STom Stellard args.llvm_project_dir, args.phab_token) 43451eaaa30STom Stellard if not release_workflow.release_branch_for_issue: 43551eaaa30STom Stellard release_workflow.issue_notify_no_milestone(sys.stdin.readlines()) 43651eaaa30STom Stellard sys.exit(1) 437daf82a51STom Stellard if args.sub_command == 'print-release-branch': 438daf82a51STom Stellard release_workflow.print_release_branch() 439daf82a51STom Stellard else: 440daf82a51STom Stellard if not release_workflow.execute_command(): 441daf82a51STom Stellard sys.exit(1) 442daf82a51STom Stellardelif args.command == 'setup-llvmbot-git': 443daf82a51STom Stellard setup_llvmbot_git() 444