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
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
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
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
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
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