def pull(user, repo, number): token = session['token'] commits = github.get_pull_request_commits(token, user, repo, number) pr = github.get_pull_request(token, user, repo, number) comments = github.get_pull_request_comments(token, user, repo, number) commit_to_comments = defaultdict(int) for comment in comments['diff_level']: commit_to_comments[comment['original_commit_id']] += 1 commits.reverse() # Add an entry for the base commit. commits.append({ 'sha': pr['base']['sha'], 'commit': { 'message': '(%s)' % pr['base']['ref'], 'author': {'date': ''} }, 'author': {'login': ''} }) for commit in commits: commit['comment_count'] = commit_to_comments[commit['sha']] return render_template('pull_request.html', commits=commits, user=user, repo=repo, pull_request=pr, comments=comments)
def get_release_pr(github_access_token, org, repo): """ Look up the pull request information for a release, or return None if it doesn't exist Args: github_access_token (str): The github access token org (str): The github organization (eg mitodl) repo (str): The github repository (eg micromasters) Returns: ReleasePR: The information about the release pull request, or None if there is no release PR in progress """ pr = get_pull_request(github_access_token, org, repo, 'release-candidate') if pr is None: return None title = pr['title'] match = re.match(r'^Release (?P<version>\d+\.\d+\.\d+)$', title) if not match: raise ReleaseException("Release PR title has an unexpected format") version = match.group('version') return ReleasePR( version=version, body=pr['body'], url=pr['html_url'], )
def check_for_updates(): owner = request.form['owner'] repo = request.form['repo'] pull_number = request.form['pull_number'] updated_at = request.form['updated_at'] token = session['token'] pr = github.get_pull_request(token, owner, repo, pull_number, bust_cache=True) if not pr: return "Error" if pr['updated_at'] <= updated_at: return "OK" # Invalidate associated RPCs: commit list, comments github.expire_cache_for_pull_request_children(owner, repo, pull_number) return "Update"
def post_comment(): owner = request.form['owner'] repo = request.form['repo'] pull_number = request.form['pull_number'] path = request.form['path'] commit_id = request.form['commit_id'] line_number = int(request.form['line_number']) body = request.form['body'] if not owner: return "Incomplete post_comment request, missing owner" if not repo: return "Incomplete post_comment request, missing repo" if not pull_number: return "Incomplete post_comment request, missing pull_number" if not path: return "Incomplete post_comment request, missing path" if not commit_id: return "Incomplete post_comment request, missing commit_id" if not line_number: return "Incomplete post_comment request, missing line_number" if not body: return "Incomplete post_comment request, missing body" token = session['token'] if not token: return "You must be oauthed to post a comment." pr = github.get_pull_request(token, owner, repo, pull_number) base_sha = pr['base']['sha'] diff_position = github_comments.lineNumberToDiffPosition(token, owner, repo, base_sha, path, commit_id, line_number, False) # False = on_left (for now!) if not diff_position: return "Unable to get diff position for %s:%s @%s" % (path, line_number, commit_id) sys.stderr.write('diff_position=%s\n' % diff_position) response = github.post_comment(token, owner, repo, pull_number, commit_id, path, diff_position, body) if response: github_comments.add_line_number_to_comment(token, owner, repo, base_sha, response) return jsonify(response)
def gh_merge_do(owner, repo, pr_id): import github user = github.user_from_oauth(bottle.request.oauth_token) pr = github.get_pull_request(owner, repo, pr_id) if 'login' not in user: raise bottle.HTTPError(403, 'Could not identify user') if 'user' not in pr: raise bottle.HTTPError(403, 'Could not identify PR') if user['login'] != pr['user']['login']: raise bottle.HTTPError(403, 'Merge requester is not the PR author') if pr['merged']: raise bottle.HTTPError(403, 'PR is already merged') if not pr['mergeable']: raise bottle.HTTPError(403, 'PR cannot be merged. Please rebase') if not github.is_pull_request_buildable(pr): raise bottle.HTTPError(403, 'PR status not green. Wait or fix errors') if not github.is_pull_request_self_mergeable(pr): raise bottle.HTTPError(403, 'Nobody allowed you to merge this PR') github.merge_pr(pr) bottle.redirect(pr['html_url'])
def gh_merge_do(owner, repo, pr_id): import github user = github.user_from_oauth(bottle.request.oauth_token) pr = github.get_pull_request(owner, repo, pr_id) if "login" not in user: raise bottle.HTTPError(403, "Could not identify user") if "user" not in pr: raise bottle.HTTPError(403, "Could not identify PR") if user["login"] != pr["user"]["login"]: raise bottle.HTTPError(403, "Merge requester is not the PR author") if pr["merged"]: raise bottle.HTTPError(403, "PR is already merged") if not pr["mergeable"]: raise bottle.HTTPError(403, "PR cannot be merged. Please rebase") if not github.is_pull_request_buildable(pr): raise bottle.HTTPError(403, "PR status not green. Wait or fix errors") if not github.is_pull_request_self_mergeable(pr): raise bottle.HTTPError(403, "Nobody allowed you to merge this PR") github.merge_pr(pr) bottle.redirect(pr["html_url"])
def save_draft_comment(): owner = request.form['owner'] repo = request.form['repo'] path = request.form['path'] pull_number = request.form['pull_number'] commit_id = request.form['commit_id'] line_number = int(request.form['line_number']) in_reply_to = request.args.get('in_reply_to') comment = { 'owner': owner, 'repo': repo, 'pull_number': pull_number, 'path': path, 'original_commit_id': commit_id, 'body': request.form['body'] } if in_reply_to: comment['in_reply_to'] = in_reply_to comment_id = request.form.get('id') if comment_id: comment['id'] = comment_id token = session['token'] pr = github.get_pull_request(token, owner, repo, pull_number) base_sha = pr['base']['sha'] position, hunk = github_comments.lineNumberToDiffPositionAndHunk(token, owner, repo, base_sha, path, commit_id, line_number, False) if not position: return "Unable to get diff position for %s:%s @%s" % (path, line_number, commit_id) comment['original_position'] = position comment['diff_hunk'] = hunk result = db.add_draft_comment(session['login'], comment) result = db.githubify_comment(result) # This is a bit roundabout, but more reliable! github_comments.add_line_number_to_comment(token, owner, repo, base_sha, result) return jsonify(result)
def file_diff(user, repo, number): path = request.args.get('path', '') sha1 = request.args.get('sha1', '') sha2 = request.args.get('sha2', '') if not (path and sha1 and sha2): return "Incomplete request (need path, sha1, sha2)" # TODO(danvk): consolidate this code with the pull route token = session['token'] commits = github.get_pull_request_commits(token, user, repo, number) pr = github.get_pull_request(token, user, repo, number) comments = github.get_pull_request_comments(token, user, repo, number) open('/tmp/commits.txt', 'wb').write(json.dumps(commits, indent=2)) commit_to_comments = defaultdict(int) for comment in comments['diff_level']: commit_to_comments[comment['original_commit_id']] += 1 commits.reverse() # Add an entry for the base commit. commits.append({ 'sha': pr['base']['sha'], 'commit': { 'message': '(%s)' % pr['base']['ref'], 'author': {'date': ''} }, 'author': {'login': ''} }) # github excludes the first four header lines of "git diff" diff_info = github.get_diff_info(token, user, repo, sha1, sha2) unified_diff = github.get_file_diff(token, user, repo, path, sha1, sha2) if not unified_diff or not diff_info: return "Unable to get diff for %s..%s" % (sha1, sha2) github_diff = '\n'.join(unified_diff.split('\n')[4:]) # TODO(danvk): only annotate comments on this file. github_comments.add_line_numbers_to_comments(token, user, repo, pr['base']['sha'], comments['diff_level']) differing_files = [f['filename'] for f in diff_info['files']] before = github.get_file_at_ref(token, user, repo, path, sha1) after = github.get_file_at_ref(token, user, repo, path, sha2) def diff_url(path): return (url_for('file_diff', user=user, repo=repo, number=number) + '?path=' + urllib.quote(path) + '&sha1=' + urllib.quote(sha1) + '&sha2=' + urllib.quote(sha2)) linked_files = [{'path':p, 'link': diff_url(p)} for p in differing_files] if path in differing_files: file_idx = differing_files.index(path) prev_file = linked_files[file_idx - 1] if file_idx > 0 else None next_file = linked_files[file_idx + 1] if file_idx < len(linked_files) - 1 else None else: # The current file is not part of this diff. # Just do something sensible. prev_file = None next_file = linked_files[0] if len(linked_files) > 0 else None pull_request_url = url_for('pull', user=user, repo=repo, number=number) return render_template('file_diff.html', commits=commits, user=user, repo=repo, pull_request=pr, comments=comments, path=path, sha1=sha1, sha2=sha2, before_contents=before, after_contents=after, differing_files=linked_files, prev_file=prev_file, next_file=next_file, github_diff=github_diff, pull_request_url=pull_request_url)