Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

npm-run-pr (uplift to 0.60.x) #1653

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 198 additions & 0 deletions script/lib/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import re
import requests
import sys
import base64
from util import execute, scoped_cwd

REQUESTS_DIR = os.path.abspath(os.path.join(__file__, '..', '..', '..',
'vendor', 'requests'))
Expand Down Expand Up @@ -80,3 +82,199 @@ def __getattr__(self, attr):

name = '%s/%s' % (self._name, attr)
return _Callable(self._gh, name)


def get_authenticated_user_login(token):
"""given a valid GitHub access token, return the associated GitHub user login"""
# for more info see: https://developer.github.com/v3/users/#get-the-authenticated-user
user = GitHub(token).user()
try:
response = user.get()
return response['login']
except Exception as e:
print('[ERROR] ' + str(e))


def parse_user_logins(token, login_csv, verbose=False):
"""given a list of logins in csv format, parse into a list and validate logins"""
if login_csv is None:
return []
login_csv = login_csv.replace(" ", "")
parsed_logins = login_csv.split(',')

users = GitHub(token).users()

invalid_logins = []

# check login/username against GitHub
# for more info see: https://developer.github.com/v3/users/#get-a-single-user
for login in parsed_logins:
try:
response = users(login).get()
if verbose:
print('[INFO] Login "' + login + '" found: ' + str(response))
except Exception as e:
if verbose:
print('[INFO] Login "' + login + '" does not appear to be valid. ' + str(e))
invalid_logins.append(login)

if len(invalid_logins) > 0:
raise Exception('Invalid logins found. Are they misspelled? ' + ','.join(invalid_logins))

return parsed_logins


def parse_labels(token, repo_name, label_csv, verbose=False):
global config
if label_csv is None:
return []
label_csv = label_csv.replace(" ", "")
parsed_labels = label_csv.split(',')

invalid_labels = []

# validate labels passed in are correct
# for more info see: https://developer.github.com/v3/issues/labels/#get-a-single-label
repo = GitHub(token).repos(repo_name)
for label in parsed_labels:
try:
response = repo.labels(label).get()
if verbose:
print('[INFO] Label "' + label + '" found: ' + str(response))
except Exception as e:
if verbose:
print('[INFO] Label "' + label + '" does not appear to be valid. ' + str(e))
invalid_labels.append(label)

if len(invalid_labels) > 0:
raise Exception('Invalid labels found. Are they misspelled? ' + ','.join(invalid_labels))

return parsed_labels


def get_file_contents(token, repo_name, filename, branch=None):
# NOTE: API only supports files up to 1MB in size
# for more info see: https://developer.github.com/v3/repos/contents/
repo = GitHub(token).repos(repo_name)
get_data = {}
if branch:
get_data['ref'] = branch
file = repo.contents(filename).get(params=get_data)
if file['encoding'] == 'base64':
return base64.b64decode(file['content'])
return file['content']


def add_reviewers_to_pull_request(token, repo_name, pr_number, reviewers=[], verbose=False, dryrun=False):
# add reviewers to pull request
# for more info see: https://developer.github.com/v3/pulls/review_requests/
repo = GitHub(token).repos(repo_name)
patch_data = {}
if len(reviewers) > 0:
patch_data['reviewers'] = reviewers
if dryrun:
print('[INFO] would call `repo.pulls(' + str(pr_number) +
').requested_reviewers.post(' + str(patch_data) + ')`')
return
response = repo.pulls(pr_number).requested_reviewers.post(data=patch_data)
if verbose:
print('repo.pulls(' + str(pr_number) + ').requested_reviewers.post(data) response:\n' + str(response))
return response


def get_milestones(token, repo_name, verbose=False):
# get all milestones for a repo
# for more info see: https://developer.github.com/v3/issues/milestones/
repo = GitHub(token).repos(repo_name)
response = repo.milestones.get()
if verbose:
print('repo.milestones.get() response:\n' + str(response))
return response


def create_pull_request(token, repo_name, title, body, branch_src, branch_dst,
open_in_browser=False, verbose=False, dryrun=False):
post_data = {
'title': title,
'head': branch_src,
'base': branch_dst,
'body': body,
'maintainer_can_modify': True
}
# create the pull request
# for more info see: http://developer.github.com/v3/pulls
if dryrun:
print('[INFO] would call `repo.pulls.post(' + str(post_data) + ')`')
if open_in_browser:
print('[INFO] would open PR in web browser')
return 1234
repo = GitHub(token).repos(repo_name)
response = repo.pulls.post(data=post_data)
if verbose:
print('repo.pulls.post(data) response:\n' + str(response))
if open_in_browser:
import webbrowser
webbrowser.open(response['html_url'])
return int(response['number'])


def set_issue_details(token, repo_name, issue_number, milestone_number=None,
assignees=[], labels=[], verbose=False, dryrun=False):
patch_data = {}
if milestone_number:
patch_data['milestone'] = milestone_number
if len(assignees) > 0:
patch_data['assignees'] = assignees
if len(labels) > 0:
patch_data['labels'] = labels
# TODO: error if no keys in patch_data

# add milestone and assignee to issue / pull request
# for more info see: https://developer.github.com/v3/issues/#edit-an-issue
if dryrun:
print('[INFO] would call `repo.issues(' + str(issue_number) + ').patch(' + str(patch_data) + ')`')
return
repo = GitHub(token).repos(repo_name)
response = repo.issues(issue_number).patch(data=patch_data)
if verbose:
print('repo.issues(' + str(issue_number) + ').patch(data) response:\n' + str(response))


def fetch_origin_check_staged(path):
"""given a path on disk (to a git repo), fetch origin and ensure there aren't unstaged files"""
with scoped_cwd(path):
execute(['git', 'fetch', 'origin'])
status = execute(['git', 'status', '-s']).strip()
if len(status) > 0:
print('[ERROR] There appear to be unstaged changes.\n' +
'Please resolve these before running (ex: `git status`).')
return 1
return 0


def get_local_branch_name(path):
with scoped_cwd(path):
return execute(['git', 'rev-parse', '--abbrev-ref', 'HEAD']).strip()


def get_title_from_first_commit(path, branch_to_compare):
"""get the first commit subject (useful for the title of a pull request)"""
with scoped_cwd(path):
local_branch = execute(['git', 'rev-parse', '--abbrev-ref', 'HEAD']).strip()
title_list = execute(['git', 'log', 'origin/' + branch_to_compare +
'..HEAD', '--pretty=format:%s', '--reverse'])
title_list = title_list.split('\n')
if len(title_list) == 0:
raise Exception('No commits found! Local branch matches "' + branch_to_compare + '"')
return title_list[0]


def push_branches_to_remote(path, branches_to_push, dryrun=False):
if dryrun:
print('[INFO] would push the following local branches to remote: ' + str(branches_to_push))
else:
with scoped_cwd(path):
for branch_to_push in branches_to_push:
print('- pushing ' + branch_to_push + '...')
# TODO: if they already exist, force push?? or error??
execute(['git', 'push', '-u', 'origin', branch_to_push])
14 changes: 12 additions & 2 deletions script/lib/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,21 @@
from .config import get_raw_version

BRAVE_REPO = "brave/brave-browser"
BRAVE_CORE_REPO = "brave/brave-core"


def channels():
return ['nightly', 'dev', 'beta', 'release']


def get_channel_display_name():
d = {'beta': 'Beta', 'canary': 'Canary', 'dev': 'Developer',
'release': 'Release'}
raw = channels()
d = {
'canary': 'Canary',
raw[1]: 'Developer',
raw[2]: 'Beta',
raw[3]: 'Release'
}
return d[release_channel()]


Expand Down
Loading