github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/check-pr-title.py (about) 1 #!/usr/bin/python3 2 3 import argparse 4 import re 5 import urllib.request 6 7 from html.parser import HTMLParser 8 9 10 class InvalidPRTitle(Exception): 11 def __init__(self, invalid_title): 12 self.invalid_title = invalid_title 13 14 15 class GithubTitleParser(HTMLParser): 16 def __init__(self): 17 HTMLParser.__init__(self) 18 self._cur_tag = "" 19 self.title = "" 20 21 def handle_starttag(self, tag, attributes): 22 self._cur_tag = tag 23 24 def handle_endtag(self, tag): 25 self._cur_tag = "" 26 27 def handle_data(self, data): 28 if self._cur_tag == "title": 29 self.title = data 30 31 32 def check_pr_title(pr_number: int): 33 # ideally we would use the github API - however we can't because: 34 # a) its rate limiting and travis IPs hit the API a lot so we regularly 35 # get errors 36 # b) using a API token is tricky because travis will not allow the secure 37 # vars for forks 38 # so instead we just scrape the html title which is unlikely to change 39 # radically 40 parser = GithubTitleParser() 41 with urllib.request.urlopen( 42 "https://github.com/snapcore/snapd/pull/{}".format(pr_number) 43 ) as f: 44 parser.feed(f.read().decode("utf-8")) 45 # the title has the format: 46 # "Added api endpoint for downloading snaps by glower · Pull Request #6958 · snapcore/snapd · GitHub" 47 # so we rsplit() once to get the title (rsplit to not get confused by 48 # possible "by" words in the real title) 49 title = parser.title.rsplit(" by ", maxsplit=1)[0] 50 print(title) 51 # cover most common cases: 52 # package: foo 53 # package, otherpackage/subpackage: this is a title 54 # tests/regression/lp-12341234: foo 55 # [RFC] foo: bar 56 if not re.match(r"[a-zA-Z0-9_\-/,. \[\]{}]+: .*", title): 57 raise InvalidPRTitle(title) 58 59 60 def main(): 61 parser = argparse.ArgumentParser() 62 parser.add_argument( 63 "pr_number", metavar="PR number", help="the github PR number to check" 64 ) 65 args = parser.parse_args() 66 try: 67 check_pr_title(args.pr_number) 68 except InvalidPRTitle as e: 69 print('Invalid PR title: "{}"\n'.format(e.invalid_title)) 70 print("Please provide a title in the following format:") 71 print("module: short description") 72 print("E.g.:") 73 print("daemon: fix frobinator bug") 74 raise SystemExit(1) 75 76 77 if __name__ == "__main__": 78 main()