Skip to content

Commit

Permalink
Fastai make pr branch to python (#1891)
Browse files Browse the repository at this point in the history
* Basic argparser

* Shell scripts wrappers

* Bash script ported

* Docstring

* Fixing commands, better output

* Git token or failure

* Abort script on missing token
  • Loading branch information
devforfu authored and stas00 committed Mar 27, 2019
1 parent 00b8276 commit 8c8fed8
Showing 1 changed file with 75 additions and 16 deletions.
91 changes: 75 additions & 16 deletions tools/fastai-make-pr-branch-py 100755 → 100644
Expand Up @@ -28,24 +28,28 @@ notes:
import argparse
import os
from os.path import basename, exists
from textwrap import dedent
from pathlib import Path
import re
import sys
from subprocess import call, PIPE, Popen
from pathlib import Path
from textwrap import dedent


PY3 = sys.version_info >= (3, 0)
ME = basename(sys.argv[0])
ORIG_USER_NAME = 'fastai'
ENV_VAR = 'GITHUB_TOKEN'


def main():
check_token()
args = parse_args()
was_already_forked = fork_if_needed(args.url, args.user_name, args.repo_name)
was_already_forked = fork_if_needed(args)
need_clone = clone_is_needed(args.url)
if need_clone:
clone(args.url, args.path)
run_after_git_clone()
track_upstream(args.orig_repo_url)
track_upstream(strip_token(args.orig_repo_url))
if was_already_forked:
sync_with_master(args)
create_branch(args.new_branch_name)
Expand All @@ -57,7 +61,7 @@ def parse_args():
parser = SysExitArgumentParser(usage=__doc__.format(me=ME))
parser.add_argument(
'auth',
metavar='AUTH', choices={'ssh', 'https'},
metavar='AUTH', choices={'https'}, # no ssh support for now
help='ssh or https (use ssh if your github account is setup to use ssh)'
)
parser.add_argument(
Expand All @@ -75,12 +79,25 @@ def parse_args():
metavar='BR',
help='name of the branch to create'
)
parser.add_argument(
'-orig', '--orig-user-name',
metavar='ORIG', required=False, default=ORIG_USER_NAME,
help='name of original repository user (default: %(default)s)'
)

args = parser.parse_args()
args.prefix = 'git@github.com:' if args.auth == 'ssh' else 'https://github.com/'
args.token = os.environ[ENV_VAR]
args.prefix = f'https://{args.token}@github.com'

# support token access only
# if args.auth == 'ssh':
# args.prefix = f'git@github.com:'
# else:
# args.prefix = f'https://{args.token}@github.com/'

args.path = f'{args.repo_name}-{args.new_branch_name}'
args.url = f'{args.prefix}{args.user_name}/{args.repo_name}.git'
args.orig_repo_url = f'{args.prefix}{ORIG_USER_NAME}/{args.repo_name}.git'
args.orig_repo_url = f'{args.prefix}{args.orig_user_name}/{args.repo_name}.git'

return args

Expand All @@ -94,16 +111,49 @@ def check_curl_is_available():
sys.exit(1)


def fork_if_needed(url, user, repo):
def check_token():
if ENV_VAR not in os.environ:
print(dedent('''
*** WARNING! ***
The HTTPS authentication method requires to explicitly enter your Github user's credentials.
Unfortunately, up to the moment the tool doesn't support secured credentials entering. Therefore,
you need to have a GITHUB_TOKEN environment variable set to use HTTPS authentication method.
Please use the following instruction to setup token, or switch to SSH.
1. Create an access token:
https://help.github.com/en/articles/creating-a-personal-access-token-for-the-command-line
2. Copy the token and save it as an environment variable:
$ echo "export GITHUB_TOKEN=<your-token>" >> ~/.bashrc
$ source ~/.bashrc
3. Now the token is used for an access.
Also, if you would like to help to improve this tool and enable the possibility to safely
enter the credentials, please submit a PR! Check the following discussion to know more:
https://forums.fast.ai/t/port-fastai-make-pr-branch-from-bash-to-python/29853/28
*** WARNING! ***
'''))
sys.exit(1)


def fork_if_needed(args):
"Fork the repo if it hasn't been forked yet."
url, user, orig, repo = args.url, args.user_name, args.orig_user_name, args.repo_name
print('\n\n*** Checking if we need to fork')
if run_and_check_success(f'git ls-remote {url} >/dev/null 2>&1'):
print(f'{url} is already forked')
return True
else:
print(f"{url} doesn't exist - need to fork")
check_curl_is_available()
run(f"curl -u {user} https://api.github.com/repos/fastai/{repo}/forks -d ''")
creds = user if args.auth == 'ssh' else f'{user}:{args.token}'
run(f"curl -u {creds} https://api.github.com/repos/{orig}/{repo}/forks -d ''")
return False


Expand Down Expand Up @@ -149,15 +199,15 @@ def track_upstream(orig_repo_url):
def sync_with_master(args):
"Sync the forked repo master with the upstream master if it hasn't been just forked."
user, repo = args.user_name, args.repo_name
print(f'\n\n*** Syncing {user}/master with {ORIG_USER_NAME}/master')
print(f'\n\n*** Syncing {user}/master with {args.orig_user_name}/master')
run('git fetch upstream')
run('git checkout master')
# XXX: this will fail if the user was committing directly to their forked `master` branch is
ok = run_and_check_success('git merge --no-edit upstream/master')
if not ok:
run('git merge --abort')
print(dedent(f'''
Error: {user}/{repo}/master can't be merged with {ORIG_USER_NAME}/{repo}/master.
Error: {user}/{repo}/master can't be merged with {args.orig_user_name}/{repo}/master.
You must merge manually, or reset {user}/{repo}/master.
See: https://docs-dev.fast.ai/git.html#how-to-reset-your-forked-master-branch
Aborting."
Expand Down Expand Up @@ -219,6 +269,11 @@ def print_instructions(args, need_clone):
'''))


def strip_token(url):
"Removes HTTPS token part from the URL string."
return re.sub("[\d\w]+@github.com", "github.com", url)


# --------------
# Shell wrappers
# --------------
Expand All @@ -245,15 +300,19 @@ def run(command):
print(out)


# (devforfu): Would it be better to replace with a different implementation?
#
# https://forums.fast.ai/t/port-fastai-make-pr-branch-from-bash-to-python/29853/20
#
def run_and_return_output(command):
"Returns (return-code, stdout, stderr)."
p = Popen(command, stdout=PIPE, stderr=PIPE, shell=True)
output, err = p.communicate()
out, err = p.communicate()
rc = p.returncode
if PY3:
output = output.decode("ascii")
err = err.decode("ascii")
return rc, output.strip(), err.strip()
if PY3 and out: out = out.decode("utf-8")
if PY3 and err: err = err.decode("utf-8")
return rc, out.strip(), err.strip()



def run_and_read_all(command):
Expand Down

0 comments on commit 8c8fed8

Please sign in to comment.