Example #1
0
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)
Example #2
0
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)
Example #3
0
    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:
Example #4
0
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)