def build_now(): job_name = request.form.get("f_job_name") repo = request.form.get("f_repo") assignment = get_assignment_by_name(job_name) if not assignment: abort(400) assignment = assignment.student_view(repo) if assignment.manual_grading: abort(400) with DbCursor() as c: student = _get_student(c) user_id, _, _, login, _, _ = student super_value = get_super(c, user_id) dropped = super_value is not None and super_value < 0 if assignment.is_group: repos = get_groups(c, user_id) else: repos = [login] if repo not in repos: abort(403) if now_compare(assignment.not_visible_before, add_grace_period( assignment.cannot_build_after)) != 0 or dropped: abort(400) branch_hash = get_branch_hash(repo, "master") message = None if branch_hash: message = get_commit_message(repo, branch_hash) # This section doesn't absolutely NEED to be consistent with the previous transaction, # since we have not specified any actual data constraints. The only possible logical error # would be to fulfill a request when the current user's permissions have been revoked # between these two transactions. with DbCursor() as c: build_name = create_build(c, job_name, repo, branch_hash, message) if should_limit_source(repo, job_name): rate_limit_fail_build(build_name) else: job = Job(build_name, repo, "Web interface") dockergrader_queue.enqueue(job) return redirect(url_for("dashboard.builds_one", name=build_name))
def pushhook(): payload_bytes = request.get_data() if request.form.get("_csrf_token"): # You should not be able to use a CSRF token for this abort(400) try: payload = json.loads(payload_bytes) assert isinstance(payload, dict) if payload.get("action", "push") != "push": logging.warning( "Dropped GitHub pushhook payload because action was %s" % str(payload.get("action"))) return ('', 204) ref = payload["ref"] before = payload["before"] after = payload["after"] assert isinstance(ref, str) assert isinstance(before, str) assert isinstance(after, str) repo_name = payload["repository"]["name"] assert isinstance(repo_name, str) file_list = get_diff_file_list(repo_name, before, after) if not file_list: file_list = [] # This is a useful hook to use, if you want to add custom logic to determine which jobs get # run on a Git push. # # Arguments: # jobs -- The original list of jobs (default: empty list) # repo_name -- The name of the repo that caused the pushhook # ref -- The name of the ref that was pushed (e.g. "refs/heads/master") # modified_files -- A list of files that were changed in the push, relative to repo root # # Returns: # A list of job names. (e.g. ["hw0", "hw0-style-check"]) jobs_to_run = apply_filters("pushhooks-jobs-to-run", [], repo_name, ref, file_list) if not jobs_to_run: return ('', 204) # We could probably grab this from the payload, but let's call this method for the sake # of consistency. message = get_commit_message(repo_name, after) for job_to_run in jobs_to_run: while True: try: with DbCursor() as c: build_name = create_build(c, job_to_run, repo_name, after, message) break except apsw.Error: logging.exception("Failed to create build, retrying...") if should_limit_source(repo_name, job_to_run): rate_limit_fail_build(build_name) else: job = Job(build_name, repo_name, "GitHub push") dockergrader_queue.enqueue(job) return ('', 204) except Exception: logging.exception( "Error occurred while processing GitHub pushhook payload") abort(500)