def rescan():
    """
    Used to pick up PRs that might not have tickets associated with them.
    """
    if request.method == "GET":
        # just render the form
        return render_template("github_rescan.html")
    repo = request.form.get("repo") or "edx/edx-platform"
    inline = request.form.get("inline", False)
    if repo == 'all' and inline:
        return "Don't be silly."

    if inline:
        return jsonify(rescan_repository(repo))

    if repo == 'all':
        repos = get_repos_file(session=github).keys()
        workflow = group(
            rescan_repository.s(repo, wsgi_environ=minimal_wsgi_environ())
            for repo in repos
        )
        group_result = workflow.delay()
        group_result.save()  # this is necessary for groups, for some reason
        status_url = url_for("tasks.group_status", group_id=group_result.id, _external=True)
    else:
        result = rescan_repository.delay(repo, wsgi_environ=minimal_wsgi_environ())
        status_url = url_for("tasks.status", task_id=result.id, _external=True)

    resp = jsonify({"message": "queued", "status_url": status_url})
    resp.status_code = 202
    resp.headers["Location"] = status_url
    return resp
Example #2
0
def pull_request():
    """
    Process a `PullRequestEvent`_ from Github.

    .. _PullRequestEvent: https://developer.github.com/v3/activity/events/types/#pullrequestevent
    """
    # TODO: We need to untangle this, there are **four** `return`s in
    #       this function!
    msg = "Incoming GitHub PR request: {}".format(request.data)
    print(msg, file=sys.stderr)

    try:
        event = request.get_json()
    except ValueError:
        msg = "Invalid JSON from Github: {data}".format(data=request.data)
        print(msg, file=sys.stderr)
        raise ValueError(msg)
    sentry.client.extra_context({"event": event})

    if "pull_request" not in event and "hook" in event and "zen" in event:
        # this is a ping
        repo = event.get("repository", {}).get("full_name")
        print("ping from {repo}".format(repo=repo), file=sys.stderr)
        return "PONG"

    pr = event["pull_request"]
    pr_number = pr['number']
    repo = pr["base"]["repo"]["full_name"].decode('utf-8')
    action = event["action"]
    # `synchronize` action is when a new commit is made for the PR
    ignored_actions = set(("labeled", "synchronize"))
    if action in ignored_actions:
        msg = "Ignoring {action} events from github".format(action=action)
        print(msg, file=sys.stderr)
        return msg, 200

    pr_activity = "{}/pull/{} {}".format(repo, pr_number, action)
    if action == "opened":
        msg = "{}, processing...".format(pr_activity)
        print(msg, file=sys.stderr)
        result = pull_request_opened.delay(pr,
                                           wsgi_environ=minimal_wsgi_environ())
    elif action == "closed":
        msg = "{}, processing...".format(pr_activity)
        print(msg, file=sys.stderr)
        result = pull_request_closed.delay(pr)
    else:
        msg = "{}, rejecting with `400 Bad request`".format(pr_activity)
        print(msg, file=sys.stderr)
        # TODO: Is this really kosher? We should do no-op, not reject
        #       the request!
        return "Don't know how to handle this.", 400

    status_url = url_for("tasks.status", task_id=result.id, _external=True)
    print("Job status URL: {}".format(status_url), file=sys.stderr)

    resp = jsonify({"message": "queued", "status_url": status_url})
    resp.status_code = 202
    resp.headers["Location"] = status_url
    return resp
def process_pr():
    if request.method == "GET":
        return render_template("github_process_pr.html")
    repo = request.form.get("repo", "")
    if not repo:
        resp = jsonify({"error": "repo required"})
        resp.status_code = 400
        return resp
    num = request.form.get("number")
    if not num:
        resp = jsonify({"error": "num required"})
        resp.status_code = 400
        return resp
    num = int(num)
    pr_resp = github.get("/repos/{repo}/pulls/{num}".format(repo=repo, num=num))
    if not pr_resp.ok:
        resp = jsonify({"error": pr_resp.text})
        resp.status_code = 400
        return resp

    pr = pr_resp.json()
    result = pull_request_opened.delay(
        pr, ignore_internal=False, check_contractor=False,
        wsgi_environ=minimal_wsgi_environ(),
    )
    status_url = url_for("tasks.status", task_id=result.id, _external=True)
    resp = jsonify({"message": "queued", "status_url": status_url})
    resp.status_code = 202
    resp.headers["Location"] = status_url
    return resp
Example #4
0
def rescan():
    """
    Re-scan GitHub repositories to find pull requests that need OSPR issues
    on JIRA, and do not have them. If this method finds pull requests that are
    missing JIRA issues, it will automatically process those pull requests
    just as though the pull request was newly created.

    Note that this rescan functionality is the reason why
    :func:`~openedx_webhooks.tasks.github.pull_request_opened`
    must be idempotent. It could run many times over the same pull request.
    """
    repo = request.form.get("repo") or "edx/edx-platform"
    inline = request.form.get("inline", False)
    if repo == 'all' and inline:
        return "Don't be silly."

    if inline:
        return jsonify(rescan_repository(repo))

    if repo.startswith('all:'):
        org = repo[4:]
        org_url = "https://api.github.com/orgs/{org}/repos".format(org=org)
        repo_names = [
            repo_name['full_name'] for repo_name in paginated_get(org_url)
        ]
        workflow = group(
            rescan_repository.s(repository,
                                wsgi_environ=minimal_wsgi_environ())
            for repository in repo_names)
        group_result = workflow.delay()
        group_result.save()  # this is necessary for groups, for some reason
        status_url = url_for("tasks.group_status",
                             group_id=group_result.id,
                             _external=True)
    else:
        result = rescan_repository.delay(repo,
                                         wsgi_environ=minimal_wsgi_environ())
        status_url = url_for("tasks.status", task_id=result.id, _external=True)

    resp = jsonify({"message": "queued", "status_url": status_url})
    resp.status_code = 202
    resp.headers["Location"] = status_url
    return resp
Example #5
0
def process_pr():
    """
    Process (or re-process) a pull request.

    Normally, when a pull request is opened, we check to see if the author is
    an edX employee, or a contractor working for edX. If so, we don't process
    the pull request normally -- either it is skipped, or we add an informative
    comment without making a JIRA ticket. Using this endpoint will skip those
    checks. We will make a JIRA ticket if one doesn't already exist, without
    checking to see if the author is special.
    """
    repo = request.form.get("repo", "")
    if not repo:
        resp = jsonify({"error": "Pull request repo required"})
        resp.status_code = 400
        return resp
    num = request.form.get("number")
    if not num:
        resp = jsonify({"error": "Pull request number required"})
        resp.status_code = 400
        return resp
    num = int(num)
    pr_resp = github.get("/repos/{repo}/pulls/{num}".format(repo=repo,
                                                            num=num))
    if not pr_resp.ok:
        resp = jsonify({"error": pr_resp.text})
        resp.status_code = 400
        return resp

    repo_resp = github.get("/repos/{repo}".format(repo=repo))
    repo_json = repo_resp.json()
    if not repo_json["permissions"]["admin"]:
        resp = jsonify({
            "error":
            "This bot does not have permissions for repo '{}'.\n\nPlease manually make an OSPR ticket on JIRA."
            .format(repo)
        })
        resp.status_code = 400
        return resp

    pr = pr_resp.json()
    result = pull_request_opened.delay(
        pr,
        ignore_internal=False,
        check_contractor=False,
        wsgi_environ=minimal_wsgi_environ(),
    )
    status_url = url_for("tasks.status", task_id=result.id, _external=True)
    resp = jsonify({"message": "queued", "status_url": status_url})
    resp.status_code = 202
    resp.headers["Location"] = status_url
    return resp
def rescan():
    """
    Re-scan GitHub repositories to find pull requests that need OSPR issues
    on JIRA, and do not have them. If this method finds pull requests that are
    missing JIRA issues, it will automatically process those pull requests
    just as though the pull request was newly created.

    Note that this rescan functionality is the reason why
    :func:`~openedx_webhooks.tasks.github.pull_request_opened`
    must be idempotent. It could run many times over the same pull request.
    """
    repo = request.form.get("repo") or "edx/edx-platform"
    inline = request.form.get("inline", False)
    if repo == 'all' and inline:
        return "Don't be silly."

    if inline:
        return jsonify(rescan_repository(repo))

    if repo == 'all':
        repos = get_repos_file().keys()
        workflow = group(
            rescan_repository.s(repo, wsgi_environ=minimal_wsgi_environ())
            for repo in repos
        )
        group_result = workflow.delay()
        group_result.save()  # this is necessary for groups, for some reason
        status_url = url_for("tasks.group_status", group_id=group_result.id, _external=True)
    else:
        result = rescan_repository.delay(repo, wsgi_environ=minimal_wsgi_environ())
        status_url = url_for("tasks.status", task_id=result.id, _external=True)

    resp = jsonify({"message": "queued", "status_url": status_url})
    resp.status_code = 202
    resp.headers["Location"] = status_url
    return resp
Example #7
0
def pull_request():
    """
    Process a `PullRequestEvent`_ from Github.

    .. _PullRequestEvent: https://developer.github.com/v3/activity/events/types/#pullrequestevent
    """
    try:
        event = request.get_json()
    except ValueError:
        raise ValueError(
            "Invalid JSON from Github: {data}".format(data=request.data))
    sentry.client.extra_context({"event": event})

    if "pull_request" not in event and "hook" in event and "zen" in event:
        # this is a ping
        repo = event.get("repository", {}).get("full_name")
        print("ping from {repo}".format(repo=repo), file=sys.stderr)
        return "PONG"

    pr = event["pull_request"]
    repo = pr["base"]["repo"]["full_name"].decode('utf-8')
    action = event["action"]
    ignored_actions = set(("labeled", "synchronize"))
    if action in ignored_actions:
        msg = "Ignoring {action} events from github".format(action=action)
        return msg, 200

    if action == "opened":
        result = pull_request_opened.delay(pr,
                                           wsgi_environ=minimal_wsgi_environ())
    elif action == "closed":
        result = pull_request_closed.delay(pr)
    else:
        print(
            "Received {action} event on PR #{num} against {repo}, don't know how to handle it"
            .format(
                action=event["action"],
                repo=pr["base"]["repo"]["full_name"].decode('utf-8'),
                num=pr["number"],
            ),
            file=sys.stderr)
        return "Don't know how to handle this.", 400

    status_url = url_for("tasks.status", task_id=result.id, _external=True)
    resp = jsonify({"message": "queued", "status_url": status_url})
    resp.status_code = 202
    resp.headers["Location"] = status_url
    return resp
def process_pr():
    """
    Process (or re-process) a pull request.

    Normally, when a pull request is opened, we check to see if the author is
    an edX employee, or a contractor working for edX. If so, we don't process
    the pull request normally -- either it is skipped, or we add an informative
    comment without making a JIRA ticket. Using this endpoint will skip those
    checks. We will make a JIRA ticket if one doesn't already exist, without
    checking to see if the author is special.
    """
    repo = request.form.get("repo", "")
    if not repo:
        resp = jsonify({"error": "Pull request repo required"})
        resp.status_code = 400
        return resp
    num = request.form.get("number")
    if not num:
        resp = jsonify({"error": "Pull request number required"})
        resp.status_code = 400
        return resp
    num = int(num)
    pr_resp = github.get("/repos/{repo}/pulls/{num}".format(repo=repo, num=num))
    if not pr_resp.ok:
        resp = jsonify({"error": pr_resp.text})
        resp.status_code = 400
        return resp

    repo_resp = github.get("/repos/{repo}".format(repo=repo))
    repo_json = repo_resp.json()
    if not repo_json["permissions"]["admin"]:
        resp = jsonify({
            "error": "This bot does not have permissions for repo '{}'.\n\nPlease manually make an OSPR ticket on JIRA.".format(repo)
        })
        resp.status_code = 400
        return resp

    pr = pr_resp.json()
    result = pull_request_opened.delay(
        pr, ignore_internal=False, check_contractor=False,
        wsgi_environ=minimal_wsgi_environ(),
    )
    status_url = url_for("tasks.status", task_id=result.id, _external=True)
    resp = jsonify({"message": "queued", "status_url": status_url})
    resp.status_code = 202
    resp.headers["Location"] = status_url
    return resp
def pull_request():
    """
    Process a `PullRequestEvent`_ from Github.

    .. _PullRequestEvent: https://developer.github.com/v3/activity/events/types/#pullrequestevent
    """
    try:
        event = request.get_json()
    except ValueError:
        raise ValueError("Invalid JSON from Github: {data}".format(data=request.data))
    sentry.client.extra_context({"event": event})

    if "pull_request" not in event and "hook" in event and "zen" in event:
        # this is a ping
        repo = event.get("repository", {}).get("full_name")
        print("ping from {repo}".format(repo=repo), file=sys.stderr)
        return "PONG"

    pr = event["pull_request"]
    repo = pr["base"]["repo"]["full_name"].decode('utf-8')
    action = event["action"]
    ignored_actions = set(("labeled", "synchronize"))
    if action in ignored_actions:
        msg = "Ignoring {action} events from github".format(action=action)
        return msg, 200

    if action == "opened":
        result = pull_request_opened.delay(pr, wsgi_environ=minimal_wsgi_environ())
    elif action == "closed":
        result = pull_request_closed.delay(pr)
    else:
        print(
            "Received {action} event on PR #{num} against {repo}, don't know how to handle it".format(
                action=event["action"],
                repo=pr["base"]["repo"]["full_name"].decode('utf-8'),
                num=pr["number"],
            ),
            file=sys.stderr
        )
        return "Don't know how to handle this.", 400

    status_url = url_for("tasks.status", task_id=result.id, _external=True)
    resp = jsonify({"message": "queued", "status_url": status_url})
    resp.status_code = 202
    resp.headers["Location"] = status_url
    return resp
Example #10
0
def process_pr():
    if request.method == "GET":
        return render_template("github_process_pr.html")
    repo = request.form.get("repo", "")
    if not repo:
        resp = jsonify({"error": "Pull request repo required"})
        resp.status_code = 400
        return resp
    num = request.form.get("number")
    if not num:
        resp = jsonify({"error": "Pull request number required"})
        resp.status_code = 400
        return resp
    num = int(num)
    pr_resp = github.get("/repos/{repo}/pulls/{num}".format(repo=repo, num=num))
    if not pr_resp.ok:
        resp = jsonify({"error": pr_resp.text})
        resp.status_code = 400
        return resp

    repo_resp = github.get("/repos/{repo}".format(repo=repo))
    repo_json = repo_resp.json()
    if not repo_json["permissions"]["admin"]:
        resp = jsonify({
            "error": "This bot does not have permissions for repo '{}'.\n\nPlease manually make an OSPR ticket on JIRA.".format(repo)
        })
        resp.status_code = 400
        return resp

    pr = pr_resp.json()
    result = pull_request_opened.delay(
        pr, ignore_internal=False, check_contractor=False,
        wsgi_environ=minimal_wsgi_environ(),
    )
    status_url = url_for("tasks.status", task_id=result.id, _external=True)
    resp = jsonify({"message": "queued", "status_url": status_url})
    resp.status_code = 202
    resp.headers["Location"] = status_url
    return resp