Esempio n. 1
0
def github_community_pr_comment(pull_request, jira_issue, people=None, session=None):
    """
    For a newly-created pull request from an open source contributor,
    write a welcoming comment on the pull request. The comment should:

    * contain a link to the JIRA issue
    * check for contributor agreement
    * check for AUTHORS entry
    * contain a link to our process documentation
    """
    people = people or get_people_file(session=session)
    people = {user.lower(): values for user, values in people.items()}
    pr_author = pull_request["user"]["login"].decode("utf-8").lower()
    created_at = parse_date(pull_request["created_at"]).replace(tzinfo=None)
    # does the user have a valid, signed contributor agreement?
    has_signed_agreement = pr_author in people and people[pr_author].get("expires_on", date.max) > created_at.date()
    # is the user in the AUTHORS file?
    in_authors_file = False
    name = people.get(pr_author, {}).get("name", "")
    if name:
        authors_url = "https://raw.githubusercontent.com/{repo}/{branch}/AUTHORS".format(
            repo=pull_request["head"]["repo"]["full_name"].decode("utf-8"),
            branch=pull_request["head"]["ref"].decode("utf-8"),
        )
        authors_resp = github.get(authors_url)
        if authors_resp.ok:
            authors_content = authors_resp.text
            if name in authors_content:
                in_authors_file = True

    return render_template(
        "github_community_pr_comment.md.j2",
        user=pull_request["user"]["login"].decode("utf-8"),
        repo=pull_request["base"]["repo"]["full_name"].decode("utf-8"),
        number=pull_request["number"],
        issue_key=jira_issue["key"].decode("utf-8"),
        has_signed_agreement=has_signed_agreement,
        in_authors_file=in_authors_file,
    )
Esempio n. 2
0
def pull_request_opened(pull_request, ignore_internal=True, check_contractor=True):
    """
    Process a pull request. This is called when a pull request is opened, or
    when the pull requests of a repo are re-scanned. By default, this function
    will ignore internal pull requests, and will add a comment to pull requests
    made by contractors (if if has not yet added a comment). However,
    this function can be called in such a way that it processes those pull
    requests anyway.

    Returns a 2-tuple. The first element in the tuple is the key of the JIRA
    issue associated with the pull request, if any, as a string. The second
    element in the tuple is a boolean indicating if this function did any
    work, such as making a JIRA issue or commenting on the pull request.
    """
    pr = pull_request
    user = pr["user"]["login"].decode("utf-8")
    repo = pr["base"]["repo"]["full_name"]
    num = pr["number"]
    if ignore_internal and is_internal_pull_request(pr, session=github):
        # not an open source pull request, don't create an issue for it
        logger.info("@{user} opened PR #{num} against {repo} (internal PR)".format(user=user, repo=repo, num=num))
        return None, False

    if check_contractor and is_contractor_pull_request(pr, session=github):
        # have we already left a contractor comment?
        if has_contractor_comment(pr, session=github):
            return None, False

        # don't create a JIRA issue, but leave a comment
        comment = {"body": github_contractor_pr_comment(pr)}
        url = "/repos/{repo}/issues/{num}/comments".format(repo=repo, num=num)
        comment_resp = github.post(url, json=comment)
        comment_resp.raise_for_status()
        return None, True

    issue_key = get_jira_issue_key(pr)
    if issue_key:
        msg = "Already created {key} for PR #{num} against {repo}".format(
            key=issue_key, num=pr["number"], repo=pr["base"]["repo"]["full_name"]
        )
        logger.info(msg)
        return issue_key, False

    repo = pr["base"]["repo"]["full_name"].decode("utf-8")
    people = get_people_file(session=github)
    custom_fields = get_jira_custom_fields(jira)

    user_name = None
    if user in people:
        user_name = people[user].get("name", "")
    if not user_name:
        user_resp = github.get(pr["user"]["url"])
        if user_resp.ok:
            user_name = user_resp.json().get("name", user)
        else:
            user_name = user

    # create an issue on JIRA!
    new_issue = {
        "fields": {
            "project": {"key": "OSPR"},
            "issuetype": {"name": "Pull Request Review"},
            "summary": pr["title"],
            "description": pr["body"],
            custom_fields["URL"]: pr["html_url"],
            custom_fields["PR Number"]: pr["number"],
            custom_fields["Repo"]: pr["base"]["repo"]["full_name"],
            custom_fields["Contributor Name"]: user_name,
        }
    }
    institution = people.get(user, {}).get("institution", None)
    if institution:
        new_issue["fields"][custom_fields["Customer"]] = [institution]
    sentry.client.extra_context({"new_issue": new_issue})

    resp = jira.post("/rest/api/2/issue", json=new_issue)
    resp.raise_for_status()
    new_issue_body = resp.json()
    issue_key = new_issue_body["key"].decode("utf-8")
    new_issue["key"] = issue_key
    sentry.client.extra_context({"new_issue": new_issue})
    # add a comment to the Github pull request with a link to the JIRA issue
    comment = {"body": github_community_pr_comment(pr, new_issue_body, people)}
    url = "/repos/{repo}/issues/{num}/comments".format(repo=repo, num=pr["number"])
    comment_resp = github.post(url, json=comment)
    comment_resp.raise_for_status()

    # Add the "Needs Triage" label to the PR
    issue_url = "/repos/{repo}/issues/{num}".format(repo=repo, num=pr["number"])
    label_resp = github.patch(issue_url, data=json.dumps({"labels": ["needs triage", "open-source-contribution"]}))
    label_resp.raise_for_status()

    logger.info(
        "@{user} opened PR #{num} against {repo}, created {issue} to track it".format(
            user=user, repo=repo, num=pr["number"], issue=issue_key
        )
    )
    return issue_key, True