def handle_issue_comment(data): """ Handle a issue_comment event. This event is triggered any time a pull request or issue is commented on. """ # We're only interested in pull request comments for now if "pull_request" not in data["issue"]: return # TODO: Might need to do something different for different "action" values # if different action values are ever implemented # TODO message-id # Figure out what email this will be a reply to pull_request = get_github(data["issue"]["pull_request"]["url"]).json() cover_msg_id = pull_request_msg_id(pull_request) subject = "Re: " + pull_request_subject(pull_request) from_addr = "%s <%s>" % (data["comment"]["user"]["login"], os.environ["GHEH_EMAIL_FROM"]) body = data["comment"]["body"] + email_footer(data["issue"]["html_url"]) msg = MIMEText(body) msg['From'] = from_addr msg['Subject'] = subject msg['In-Reply-To'] = cover_msg_id msg['Date'] = json_to_email_date(data["issue"]["updated_at"]) send_email(msg)
def read_message(url): """ Read a URL and parse the result as a email.message object This will return a message with a content-transfer-encoding of 8bit, which doesn't work with smtplib, because computers are the worst. Once the payload is finished, it is up to the caller to encode the payload properly, for example by deleteing Content-Transfer-Encoding and setting the charset to utf-8. """ response = get_github(url) if response.status_code != 200: raise ValueError("Unexpected status code %s" % response.status_code) return _parser.parsestr(response.text)
for pull_request in pull_request_coll.find(): # Second, all the commits in the pull request for commit in pull_request['commit_list']: # Construct the API url for the comment list on the commit in the source repo comment_list_url = "%s/commits/%s/comments" % \ (pull_request['pull_request']['head']['repo']['url'], commit['sha']) # Check if we have a etag stored from a previous run record = commit_comment_coll.find_one({'comment_list_url': comment_list_url}) if record: etag = record['etag'] else: etag = None # Fetch the comment list comment_list_response = get_github(comment_list_url, etag) # If we got a 304, there are no new comments, so we're done with this commit if comment_list_response.status_code == 304: continue # Hopefully the status code is 200 otherwise, but if not bail on this commit if comment_list_response.status_code != 200: print("ERR: Received status code %s from github for %s" % (comment_list_response.status_code, comment_list_url), file=sys.stderr) continue comment_list = comment_list_response.json() # Iterate over the comments and look for the ones not yet emailed for comment in comment_list:
def handle_pull_request(data): """ Handle a pull_request event. This event is triggered when a pull request is assigned, unassigned, labeled, unlabeled, opened, closed, reopened, or synchronized. https://developer.github.com/v3/activity/events/types/#pullrequestevent """ # Pull requests form the base of the email threads used in this webook. # Any time a request is opened or synchronized (push --force on the branch # to be pulled), start a new email thread using the pull request body as # the cover letter, and reply to it with each of the patches. pull_request = data["pull_request"] # Construct the message ID for the cover letter that we will either be # sending or replying to cover_msg_id = pull_request_msg_id(data["pull_request"]) subject_base = pull_request_subject(pull_request) if data["action"] in ("opened", "synchronize"): if data["action"] == "opened": subject = "New: " else: subject = "Updated: " subject += subject_base from_addr = "%s <%s>" % (data["sender"]["login"], os.environ["GHEH_EMAIL_FROM"]) body = pull_request["body"] + email_footer(pull_request["html_url"]) cover_letter = MIMEText(body) cover_letter['From'] = from_addr cover_letter['Subject'] = subject cover_letter['Message-Id'] = cover_msg_id cover_letter['Date'] = json_to_email_date(pull_request['updated_at']) send_email(cover_letter) # Get a list of commits in this pull request commit_list = get_github(pull_request["url"] + "/commits").json() patch_num = 1 tot_num = len(commit_list) # Start with the PR date for the patches and add one second to each # as we go so that the patches appear in the right order. # This is the same thing that git-send-email does, both in terms of # throwing away the commit Date and using incrementing seconds to # order the patch emails. patch_date_ts = iso8601.parse_date(pull_request['updated_at']).timestamp() for commit in commit_list: # Start with the .patch file provided by github since it's # formatted all nice and email-like msg = read_message(commit['html_url'] + '.patch') # Set the message as a reply to the cover letter msg['In-Reply-To'] = cover_msg_id msg['Message-Id'] = patch_msg_id(pull_request, commit["sha"]) del msg['Date'] msg['Date'] = email.utils.formatdate(timeval=patch_date_ts) # Prepend a From: to the body of the message so git-am works right. # Add the footer. msg.set_payload('From: %s\n\n%s\n%s' % \ (msg['From'], msg.get_payload(), email_footer(commit['html_url'], msg_type='commit'))) # Replace the From header with ourselves del msg['From'] msg['From'] = from_addr # Reset the Content-Transfer-Encoding so that non-ascii characters # get encoded right. This will encode the payload as base64. del msg['Content-Transfer-Encoding'] msg.set_charset('utf-8') # Monkey with the subject to get the patch numbers and branch name in there subject = msg['Subject'].replace('[PATCH]', '[%s %d/%d]' % (pull_request["base"]["ref"], patch_num, tot_num), 1) del msg['Subject'] msg['Subject'] = subject send_email(msg) patch_date_ts += 1 patch_num += 1 # Create (or update) a database record with the pull request and # the list of commits client = pymongo.MongoClient(os.environ[os.environ["GHEH_DB_ENVVAR"]]) db = client[os.environ['GHEH_DB_NAME']] pull_request_coll = db[PULL_REQUEST_COLLECTION] record = pull_request_coll.find_one({'pull_request.id': pull_request['id']}) if record: record['pull_request'] = pull_request record['commit_list'] = commit_list pull_request_coll.save(record) else: pull_request_coll.insert({'pull_request': pull_request, 'commit_list': commit_list}) elif data["action"] in ("closed", "reopened", "assigned", "unassigned", "labeled", "unlabeled"): subject = "Re: %s" % subject_base # If the action was assigned, say whom it was assigned to in the body if data["action"] == "assigned": body = "Assigned to %s" % data["assignee"]["login"] # For labels, put the label in the body elif data["action"] == "labeled": body = "Added label: %s." % data["label"]["name"] elif data["action"] == "unlabeled": body = "Removed label: %s." % data["label"]["name"] # For everything else just say what happened # uppercase the first letter else: body = "%s." % data["action"].title() from_addr = "%s <%s>" % (data["sender"]["login"], os.environ["GHEH_EMAIL_FROM"]) msg = MIMEText(body + email_footer(pull_request["html_url"])) msg["From"] = from_addr msg["Subject"] = subject msg["In-Reply-To"] = cover_msg_id msg["Date"] = json_to_email_date(pull_request["updated_at"]) send_email(msg)