Ejemplo n.º 1
0
def fork():
    if not github.authorized:
        flash("Not authorized. Please sign in first.", "error")
        return redirect(url_for("index"))

    cfg = app.config.get_namespace("AUTOFORK_")
    user = cfg["user"]
    repo = cfg["repo"]

    resp = github.post(f"/repos/{user}/{repo}/forks")
    body = resp.json()

    link = '<a class="link" target="_blank" rel="noopener" href="{link}">{name}</a>'.format
    if not resp:
        message = f"Couldn't fork {user}/{repo}:"
        details = json.dumps(body, indent=2)
        flash(message, "error info")
        flash(details, 'details')
    else:
        is_fork = body.get("fork")
        html_url = body.get("html_url")

        if is_fork:
            url = link(link=html_url, name=html_url)
            flash(Markup(f"Successfully forked {repo} to {url}"), "info")
        else:
            url = link(link=html_url, name=f'{user}/{repo}')
            flash(Markup(f"You already have a copy of {url}"), "info")

    return redirect(url_for("index"))
Ejemplo n.º 2
0
def make_request(query, variables=None):
    resp = github.post('/graphql', json={'query': query, 'variables': dumps(variables)})

    try:
        return resp.json()
    except JSONDecodeError:
        return Response(resp.text, status=503)
Ejemplo n.º 3
0
def jira_issue_rejected(issue):
    issue_key = to_unicode(issue["key"])

    pr_num = github_pr_num(issue)
    pr_url = github_pr_url(issue)
    issue_url = pr_url.replace("pulls", "issues")

    gh_issue_resp = github.get(issue_url)
    gh_issue_resp.raise_for_status()
    gh_issue = gh_issue_resp.json()
    sentry.client.extra_context({"github_issue": gh_issue})
    if gh_issue["state"] == "closed":
        # nothing to do
        msg = "{key} was rejected, but PR #{num} was already closed".format(
            key=issue_key, num=pr_num
        )
        print(msg, file=sys.stderr)
        return msg

    # Comment on the PR to explain to look at JIRA
    username = to_unicode(gh_issue["user"]["login"])
    comment = {"body": (
        "Hello @{username}: We are unable to continue with "
        "review of your submission at this time. Please see the "
        "associated JIRA ticket for more explanation.".format(username=username)
    )}
    comment_resp = github.post(issue_url + "/comments", json=comment)
    comment_resp.raise_for_status()

    # close the pull request on Github
    close_resp = github.patch(pr_url, json={"state": "closed"})
    close_resp.raise_for_status()

    return "Closed PR #{num}".format(num=pr_num)
Ejemplo n.º 4
0
def jira_issue_rejected(issue):
    issue_key = to_unicode(issue["key"])

    pr_num = github_pr_num(issue)
    pr_url = github_pr_url(issue)
    issue_url = pr_url.replace("pulls", "issues")

    gh_issue_resp = github.get(issue_url)
    gh_issue_resp.raise_for_status()
    gh_issue = gh_issue_resp.json()
    sentry.client.extra_context({"github_issue": gh_issue})
    if gh_issue["state"] == "closed":
        # nothing to do
        msg = "{key} was rejected, but PR #{num} was already closed".format(
            key=issue_key, num=pr_num)
        print(msg, file=sys.stderr)
        return msg

    # Comment on the PR to explain to look at JIRA
    username = to_unicode(gh_issue["user"]["login"])
    comment = {
        "body": ("Hello @{username}: We are unable to continue with "
                 "review of your submission at this time. Please see the "
                 "associated JIRA ticket for more explanation.".format(
                     username=username))
    }
    comment_resp = github.post(issue_url + "/comments", json=comment)
    comment_resp.raise_for_status()

    # close the pull request on Github
    close_resp = github.patch(pr_url, json={"state": "closed"})
    close_resp.raise_for_status()

    return "Closed PR #{num}".format(num=pr_num)
Ejemplo n.º 5
0
def fork():
    """Endpoint for fork service. 
    Service is idempotent; can be called multiple times in the event of a time-out or transmission error."""
    try:
        uname = request.form.get('username')
        repos = request.form.get('repository')
        response = github.post('/repos/%s/%s/forks' % (uname, repos))
        if not response.ok:
            raise Exception(response.raise_for_status())
        data = {'success': True}
    except Exception as e:
        data = {'success': False, 'error': str(e)}

    return Response(json.dumps(data), mimetype='application/json')
Ejemplo n.º 6
0
def github_login():
    if not github.authorized:
        return redirect(url_for('github.login'))

    account_info = github.get('/user')
    if not account_info.ok:
        return "Something has gone wrong. Try refreshing the page."

    else:
        fork_repo = github.post('/repos/bmcniff/self-rep-repo/forks')

        account_info_json = account_info.json()
        return '<h1>The repo has been replicated, and can be found   <a href="http://www.github.com/{}/self-rep-repo">here</a>.'.format(
            account_info_json['login'])
Ejemplo n.º 7
0
def github_login():
    if not github.authorized:
        return redirect(url_for('github.login'))

    account_info = github.get('/user')
    if not account_info.ok:
        return "Something has gone wrong! Please try refreshing the page."

    fork_repo = github.post(f'/repos/{get_username}/{get_repo_name}/forks')

    if not fork_repo.ok:
        return "Something has gone wrong! Please try refreshing the page..."

        return '<h1>Your Github name is {}'.format(account_info_json['login'])

    return f'Success!'
Ejemplo n.º 8
0
def jira_issue_rejected(issue, bugsnag_context=None):
    bugsnag_context = bugsnag_context or {}
    issue_key = to_unicode(issue["key"])

    pr_num = github_pr_num(issue)
    pr_url = github_pr_url(issue)
    issue_url = pr_url.replace("pulls", "issues")

    gh_issue_resp = github.get(issue_url)
    if not gh_issue_resp.ok:
        raise requests.exceptions.RequestException(gh_issue_resp.text)
    gh_issue = gh_issue_resp.json()
    bugsnag_context["github_issue"] = gh_issue
    bugsnag.configure_request(meta_data=bugsnag_context)
    if gh_issue["state"] == "closed":
        # nothing to do
        msg = "{key} was rejected, but PR #{num} was already closed".format(
            key=issue_key, num=pr_num
        )
        print(msg, file=sys.stderr)
        return msg

    # Comment on the PR to explain to look at JIRA
    username = to_unicode(gh_issue["user"]["login"])
    comment = {"body": (
        "Hello @{username}: We are unable to continue with "
        "review of your submission at this time. Please see the "
        "associated JIRA ticket for more explanation.".format(username=username)
    )}
    comment_resp = github.post(issue_url + "/comments", json=comment)

    # close the pull request on Github
    close_resp = github.patch(pr_url, json={"state": "closed"})
    if not close_resp.ok or not comment_resp.ok:
        bugsnag_context['request_headers'] = close_resp.request.headers
        bugsnag_context['request_url'] = close_resp.request.url
        bugsnag_context['request_method'] = close_resp.request.method
        bugsnag.configure_request(meta_data=bugsnag_context)
        bug_text = ''
        if not close_resp.ok:
            bug_text += "Failed to close; " + close_resp.text
        if not comment_resp.ok:
            bug_text += "Failed to comment on the PR; " + comment_resp.text
        raise requests.exceptions.RequestException(bug_text)
    return "Closed PR #{num}".format(num=pr_num)
Ejemplo n.º 9
0
def index():
    if not github.authorized:
        return redirect(url_for("github.login"))

    user_resp = github.get("/user")
    if not user_resp.ok:
        return "Sorry, cannot grab user's data at this time."
    link_to_repo = get_link_to_repo(user_resp.json()["login"], repo_name)

    fork_resp = github.post(f'/repos/{host_username}/{repo_name}/forks')

    if not fork_resp.ok:
        if fork_resp.status_code == status.HTTP_404_NOT_FOUND:
            return f"Sorry, it looks like you're attempting to fork your own repo; or the repo doesn't exist."

        return "Sorry, something went wrong when trying to fork the repo. Try refreshing a page. "

    return f"You have successfully forked a repository. URL: {link_to_repo}"
Ejemplo n.º 10
0
def github_install():
    if request.method == "GET":
        return render_template("install.html")
    repo = request.form.get("repo", "")
    if repo:
        repos = (repo, )
    else:
        repos = get_repos_file().keys()

    secure = request.is_secure or request.headers.get("X-Forwarded-Proto",
                                                      "http") == "https"
    api_url = url_for(
        "github_pull_request",
        _external=True,
        _scheme="https" if secure else "http",
    )
    success = []
    failed = []
    for repo in repos:
        url = "/repos/{repo}/hooks".format(repo=repo)
        body = {
            "name": "web",
            "events": ["pull_request"],
            "config": {
                "url": api_url,
                "content_type": "json",
            }
        }
        bugsnag_context = {"repo": repo, "body": body}
        bugsnag.configure_request(meta_data=bugsnag_context)

        hook_resp = github.post(url, json=body)
        if hook_resp.ok:
            success.append(repo)
        else:
            failed.append((repo, hook_resp.text))

    if failed:
        resp = make_response(json.dumps(failed), 502)
    else:
        resp = make_response(json.dumps(success), 200)
    resp.headers["Content-Type"] = "application/json"
    return resp
Ejemplo n.º 11
0
def index():
    if not github.authorized:
        return redirect(url_for("github.login"))

    user_resp = github.get("/user")
    if not user_resp.ok:
        return "Oops! Something went wrong :( Try to refresh a page!"

    link_to_repo = get_link_to_repo(user_resp.json()["login"], repo_name)

    forking = github.post(f'/repos/{username}/{repo_name}/forks')

    if not forking.ok:
        if forking.status_code == status.HTTP_404_NOT_FOUND:
            return f"You are trying to fork the repo which does not exist."

        return "Oops! Something went wrong :( Try to refresh a page!"

    return render_template("success.html", link_to_repo=link_to_repo)
Ejemplo n.º 12
0
def fork():
    if not github.authorized:
        return redirect(url_for('github.login'))
    else:
        repos_info = github.get('/user/repos')
        repos_info_list = repos_info.json()
        repo_ids = []
        for repo in repos_info_list:
            repo_ids.append(repo.get('id'))

        forkResponse = github.post('repos/{}/{}/forks'.format(
            environ.get('GITHUB_ACCOUNT'), environ.get('GITHUB_REPO')))
        if forkResponse.json().get('id') in repo_ids:
            return 'Fork already exists'
        else:
            return 'Fork created successfully. Check here : {}'.format(
                forkResponse.json().get('html_url'))

    return '<h1>Request failed!</h1>'
Ejemplo n.º 13
0
def install():
    if request.method == "GET":
        return render_template("install.html")
    owner_login = request.values.get("owner", "")
    if not owner_login:
        return jsonify({"error": "missing owner param"}), 400
    repo_name = request.values.get("repo", "")
    if not repo_name:
        return jsonify({"error": "missing repo param"}), 400

    hook_url = "/repos/{owner}/{repo}/hooks".format(
        owner=owner_login,
        repo=repo_name,
    )
    body = {
        "name": "web",
        "events": ["pull_request", "issue"],
        "config": {
            "url": url_for("replication.main", _external=True),
            "content_type": "json",
        }
    }
    bugsnag_context = {"owner": owner_login, "repo": repo_name, "body": body}
    bugsnag.configure_request(meta_data=bugsnag_context)

    logging.info("POST {}".format(hook_url))
    hook_resp = github.post(hook_url, json=body)
    if not hook_resp.ok:
        error_obj = hook_resp.json()
        resp = jsonify({"error": error_obj["message"]})
        resp.status_code = 503
        return resp
    else:
        hook_data = hook_resp.json()
        process_repository_hook(
            hook_data,
            via="api",
            fetched_at=datetime.now(),
            commit=True,
            requestor_id=current_user.get_id(),
        )

    return jsonify({"message": "success"})
Ejemplo n.º 14
0
def replication():
    """The function that forks the GitHub repository."""
    if not github.authorized:
        return redirect(url_for("github.login"))

    user_resp = github.get("/user")

    if not user_resp.ok:
        return "WOW! Something went wrong :( , try to refresh page!!!"

    link_to_repo = get_link(user_resp.json()["login"], repo_name)
    forking = github.post(f'/repos/{username}/{repo_name}/forks')

    if not forking.ok:
        if forking.status_code == status.HTTP_404_NOT_FOUND:
            return f"You are trying to fork the repo which does not exist."

        return "WOW! Something went wrong :( , try to refresh page!!!"

    return render_template("base.html", link_to_repo=link_to_repo)
Ejemplo n.º 15
0
def github_install():
    if request.method == "GET":
        return render_template("install.html")
    repo = request.form.get("repo", "")
    if repo:
        repos = (repo,)
    else:
        repos = get_repos_file().keys()

    secure = request.is_secure or request.headers.get("X-Forwarded-Proto", "http") == "https"
    api_url = url_for(
        "github_pull_request", _external=True,
        _scheme="https" if secure else "http",
    )
    success = []
    failed = []
    for repo in repos:
        url = "/repos/{repo}/hooks".format(repo=repo)
        body = {
            "name": "web",
            "events": ["pull_request"],
            "config": {
                "url": api_url,
                "content_type": "json",
            }
        }
        bugsnag_context = {"repo": repo, "body": body}
        bugsnag.configure_request(meta_data=bugsnag_context)

        hook_resp = github.post(url, json=body)
        if hook_resp.ok:
            success.append(repo)
        else:
            failed.append((repo, hook_resp.text))

    if failed:
        resp = make_response(json.dumps(failed), 502)
    else:
        resp = make_response(json.dumps(success), 200)
    resp.headers["Content-Type"] = "application/json"
    return resp
Ejemplo n.º 16
0
def index():
    if not github.authorized:
        return redirect(url_for("github.login"))

    user_resp = github.get("/user")
    if not user_resp.ok:
        return "Something went wrong when trying to get logged user's data. " \
               "Try refreshing a page. If it doesn't help - write to " \
               "*****@*****.**"

    link_to_repo = get_link_to_repo(user_resp.json()["login"], repo_name)

    fork_resp = github.post(f'/repos/{host_username}/{repo_name}/forks')

    if not fork_resp.ok:
        if fork_resp.status_code == status.HTTP_404_NOT_FOUND:
            return f"You are trying to fork a repo that does not exist."

        return "Something went wrong when trying to fork the repo. Try refreshing a page. " \
               "If it doesn't help - write to [email protected]. "

    return f"You have successfully forked a repository. URL: {link_to_repo}"
Ejemplo n.º 17
0
def index():
    if not github.authorized:
        return redirect(url_for("github.login"))

    origin_repo = github.get('/repos/' + app.config['ORIGIN_REPO_USER'] + '/' +
                             app.config['ORIGIN_REPO_NAME'])
    if not origin_repo.ok:
        return (
            'Repo <b>{repo}</b> for user <b>@{login}</b> not found on GitHub. '
            'Please restart application with a valid user/repo combination'
            ' to replicate.'.format(repo=app.config['ORIGIN_REPO_NAME'],
                                    login=app.config['ORIGIN_REPO_USER']))

    origin_repo_name = origin_repo.json()['name']
    user_login = github.get('/user').json()['login']

    new_repo_name = origin_repo_name
    duplicate_incrementor = 1
    while github.get("/repos/" + user_login + '/' + new_repo_name).ok:
        new_repo_name = origin_repo_name + str(duplicate_incrementor)
        duplicate_incrementor += 1

    payload = {
        'name': new_repo_name,
        'description': origin_repo.json()['description']
    }
    response = github.post("/user/repos", json.dumps(payload))

    repo = Repo.clone_from(origin_repo.json()['clone_url'],
                           tmp + '/' + new_repo_name)
    remote = repo.create_remote(
        'target',
        response.json()['clone_url'][:8] + github.token['access_token'] + ':' +
        'x-oauth-basic@' + response.json()['clone_url'][8:])
    remote.push(refspec='{}:{}'.format('master', 'master'))
    shutil.rmtree(tmp + '/' + new_repo_name)

    return redirect(response.json()['svn_url'])
Ejemplo n.º 18
0
def install():
    """
    Install GitHub webhooks for a repo.
    """
    repo = request.form.get("repo", "")
    if repo:
        repos = [repo]
    else:
        repos = get_repos_file().keys()

    api_url = url_for("github_views.pull_request", _external=True)
    success = []
    failed = []
    for repo in repos:
        url = "/repos/{repo}/hooks".format(repo=repo)
        body = {
            "name": "web",
            "events": ["pull_request"],
            "config": {
                "url": api_url,
                "content_type": "json",
            }
        }
        sentry.client.extra_context({"repo": repo, "body": body})

        hook_resp = github.post(url, json=body)
        if hook_resp.ok:
            success.append(repo)
        else:
            failed.append((repo, hook_resp.text))

    if failed:
        resp = make_response(json.dumps(failed), 502)
    else:
        resp = make_response(json.dumps(success), 200)
    resp.headers["Content-Type"] = "application/json"
    return resp
Ejemplo n.º 19
0
def install():
    """
    Install GitHub webhooks for a repo.
    """
    repo = request.form.get("repo", "")
    if repo:
        repos = [repo]
    else:
        repos = get_repos_file().keys()

    api_url = url_for("github_views.pull_request", _external=True)
    success = []
    failed = []
    for repo in repos:
        url = "/repos/{repo}/hooks".format(repo=repo)
        body = {
            "name": "web",
            "events": ["pull_request"],
            "config": {
                "url": api_url,
                "content_type": "json",
            }
        }
        sentry.client.extra_context({"repo": repo, "body": body})

        hook_resp = github.post(url, json=body)
        if hook_resp.ok:
            success.append(repo)
        else:
            failed.append((repo, hook_resp.text))

    if failed:
        resp = make_response(json.dumps(failed), 502)
    else:
        resp = make_response(json.dumps(success), 200)
    resp.headers["Content-Type"] = "application/json"
    return resp
Ejemplo n.º 20
0
def pr_opened(pr, bugsnag_context=None):
    bugsnag_context = bugsnag_context or {}
    user = pr["user"]["login"]
    repo = pr["base"]["repo"]["full_name"]
    people = get_people_file()

    if user in people and people[user].get("institution", "") == "edX":
        # not an open source pull request, don't create an issue for it
        print(
            "@{user} opened PR #{num} against {repo} (internal PR)".format(
                user=user, repo=pr["base"]["repo"]["full_name"],
                num=pr["number"]
            ),
            file=sys.stderr
        )
        return "internal pull request"

    field_resp = jira.get("/rest/api/2/field")
    if not field_resp.ok:
        raise requests.exceptions.RequestException(field_resp.text)
    field_map = dict(pop_dict_id(f) for f in field_resp.json())
    custom_fields = {
        value["name"]: id
        for id, value in field_map.items()
        if value["custom"]
    }

    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]
    bugsnag_context["new_issue"] = new_issue
    bugsnag.configure_request(meta_data=bugsnag_context)

    resp = jira.post("/rest/api/2/issue", data=json.dumps(new_issue))
    if not resp.ok:
        raise requests.exceptions.RequestException(resp.text)
    new_issue_body = resp.json()
    bugsnag_context["new_issue"]["key"] = new_issue_body["key"]
    bugsnag.configure_request(meta_data=bugsnag_context)
    # add a comment to the Github pull request with a link to the JIRA issue
    comment = {
        "body": github_pr_comment(pr, new_issue_body, people),
    }
    url = "/repos/{repo}/issues/{num}/comments".format(
        repo=repo, num=pr["number"],
    )
    comment_resp = github.post(url, data=json.dumps(comment))
    if not comment_resp.ok:
        raise requests.exceptions.RequestException(comment_resp.text)
    print(
        "@{user} opened PR #{num} against {repo}, created {issue} to track it".format(
            user=user, repo=repo,
            num=pr["number"], issue=new_issue_body["key"]
        ),
        file=sys.stderr
    )
    return "created!"
Ejemplo n.º 21
0
def pr_opened(pr, ignore_internal=True, check_contractor=True, bugsnag_context=None):
    bugsnag_context = bugsnag_context or {}
    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):
        # not an open source pull request, don't create an issue for it
        print(
            "@{user} opened PR #{num} against {repo} (internal PR)".format(
                user=user, repo=repo, num=num,
            ),
            file=sys.stderr
        )
        return "internal pull request"

    if check_contractor and is_contractor_pull_request(pr):
        # 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 "contractor pull request"

    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"],
        )
        print(msg, file=sys.stderr)
        return msg

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

    if user in people:
        user_name = people[user].get("name", "")
    else:
        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]
    bugsnag_context["new_issue"] = new_issue
    bugsnag.configure_request(meta_data=bugsnag_context)

    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')
    bugsnag_context["new_issue"]["key"] = issue_key
    bugsnag.configure_request(meta_data=bugsnag_context)
    # 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()

    print(
        "@{user} opened PR #{num} against {repo}, created {issue} to track it".format(
            user=user, repo=repo,
            num=pr["number"], issue=issue_key,
        ),
        file=sys.stderr
    )
    return "created {key}".format(key=issue_key)
Ejemplo n.º 22
0
def make_a_fork(owner, repo_name):
    resp = github.post(f'/repos/{owner}/{repo_name}/forks')
    assert resp.ok

    return resp.json()
Ejemplo n.º 23
0
def jira_issue_updated():
    """
    Received an "issue updated" event from JIRA.
    https://developer.atlassian.com/display/JIRADEV/JIRA+Webhooks+Overview
    """
    try:
        event = request.get_json()
    except ValueError:
        raise ValueError("Invalid JSON from JIRA: {data}".format(
            data=request.data.decode('utf-8')
        ))
    bugsnag_context = {"event": event}
    bugsnag.configure_request(meta_data=bugsnag_context)

    if app.debug:
        print(json.dumps(event), file=sys.stderr)

    issue_key = event["issue"]["key"].decode('utf-8')

    # is the issue an open source pull request?
    if event["issue"]["fields"]["project"]["key"] != "OSPR":
        # TODO: if the issue has just been moved from the OSPR project to a new project,
        # change the label to "engineering review". Need to figure out if we can tell that
        # the ticket has just moved projects.
        return "I don't care"

    # is there a changelog?
    changelog = event.get("changelog")
    if not changelog:
        # it was just someone adding a comment
        return "I don't care"

    # did the issue change status?
    status_changelog_items = [item for item in changelog["items"] if item["field"] == "status"]
    if len(status_changelog_items) == 0:
        return "I don't care"

    # construct Github API URL
    custom_fields = get_jira_custom_fields()
    pr_repo = event["issue"]["fields"].get(custom_fields["Repo"], "")
    pr_num = event["issue"]["fields"].get(custom_fields["PR Number"])
    if not pr_repo or not pr_num:
        fail_msg = '{key} is missing "Repo" or "PR Number" fields'.format(key=issue_key)
        raise Exception(fail_msg)
    pr_num = int(pr_num)

    pr_url = "/repos/{repo}/pulls/{num}".format(repo=pr_repo, num=pr_num)
    # Need to use the Issues API for label manipulation
    issue_url = "/repos/{repo}/issues/{num}".format(repo=pr_repo, num=pr_num)

    old_status = status_changelog_items[0]["fromString"]
    new_status = status_changelog_items[0]["toString"]

    if new_status == "Rejected":
        issue_resp = github.get(issue_url)
        if not issue_resp.ok:
            raise requests.exceptions.RequestException(issue_resp.text)
        issue = issue_resp.json()
        if issue["state"] == "closed":
            # nothing to do
            msg = "{key} was rejected, but PR #{num} was already closed".format(
                key=issue_key, num=pr_num
            )
            print(msg, file=sys.stderr)
            return msg

        # Comment on the PR to explain to look at JIRA
        username = issue["user"]["login"].decode('utf-8')
        comment = {"body": (
            "Hello @{username}: We are unable to continue with "
            "review of your submission at this time. Please see the "
            "associated JIRA ticket for more explanation.".format(username=username)
        )}
        comment_resp = github.post(issue_url + "/comments", json=comment)

        # close the pull request on Github
        close_resp = github.patch(pr_url, json={"state": "closed"})
        if not close_resp.ok or not comment_resp.ok:
            bugsnag_context['request_headers'] = close_resp.request.headers
            bugsnag_context['request_url'] = close_resp.request.url
            bugsnag_context['request_method'] = close_resp.request.method
            bugsnag.configure_request(meta_data=bugsnag_context)
            bug_text = ''
            if not close_resp.ok:
                bug_text += "Failed to close; " + close_resp.text
            if not comment_resp.ok:
                bug_text += "Failed to comment on the PR; " + comment_resp.text
            raise requests.exceptions.RequestException(bug_text)
        return "Closed PR #{num}".format(num=pr_num)

    elif new_status in STATUS_LABEL_DICT:
        # Get all the existing labels on this PR
        label_list = github.get(issue_url).json()["labels"]

        # Add in the label representing the new status - just add in the plain string label
        label_list.append(STATUS_LABEL_DICT[new_status][0])

        # remove the label representing the old status, if it exists
        if old_status in STATUS_LABEL_DICT:
            # Sometimes labels are strings ("needs triage") whereas other times they're dictionaries
            # with the label name, color, and url defined. Have not pinned down when or why this happens.
            for old_label in STATUS_LABEL_DICT[old_status]:
                try:
                    if isinstance(old_label, dict):
                        old_label["url"] = old_label["url"].format(pr_repo=pr_repo)
                    label_list.remove(old_label)
                except ValueError:
                    print("PR {num} does not have label {old_label} to remove".format(num=pr_num, old_label=old_label))
                    print("PR {num} only has labels {labels}".format(num=pr_num, labels=label_list))
                else:
                    print("PR {num}: Successfully removed label {old_label}".format(num=pr_num, old_label=old_label))
                    break

        # Post the new set of labels to github
        label_resp = github.patch(issue_url, json={"labels": label_list})
        if not label_resp.ok:
            raise requests.exceptions.RequestException(label_resp.text)
        return "Changed label of PR #{num} to {labels}".format(num=pr_num, labels=label_list)

    return "no change necessary"
Ejemplo n.º 24
0
def pr_opened(pr, ignore_internal=True, check_contractor=True, bugsnag_context=None):
    bugsnag_context = bugsnag_context or {}
    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):
        # not an open source pull request, don't create an issue for it
        print(
            "@{user} opened PR #{num} against {repo} (internal PR)".format(
                user=user, repo=repo, num=num,
            ),
            file=sys.stderr
        )
        return "internal pull request"

    if check_contractor and is_contractor_pull_request(pr):
        # 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)
        if not comment_resp.ok:
            raise requests.exceptions.RequestException(comment_resp.text)
        return "contractor pull request"

    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"],
        )
        print(msg, file=sys.stderr)
        return msg

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

    if user in people:
        user_name = people[user].get("name", "")
    else:
        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]
    bugsnag_context["new_issue"] = new_issue
    bugsnag.configure_request(meta_data=bugsnag_context)

    resp = jira.post("/rest/api/2/issue", json=new_issue)
    if not resp.ok:
        raise requests.exceptions.RequestException(resp.text)
    new_issue_body = resp.json()
    issue_key = new_issue_body["key"].decode('utf-8')
    bugsnag_context["new_issue"]["key"] = issue_key
    bugsnag.configure_request(meta_data=bugsnag_context)
    # 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)
    if not comment_resp.ok:
        raise requests.exceptions.RequestException(comment_resp.text)

    # 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"]}))
    if not label_resp.ok:
        raise requests.exceptions.RequestException(label_resp.text)

    print(
        "@{user} opened PR #{num} against {repo}, created {issue} to track it".format(
            user=user, repo=repo,
            num=pr["number"], issue=issue_key,
        ),
        file=sys.stderr
    )
    return "created {key}".format(key=issue_key)