diff --git a/doctr/__main__.py b/doctr/__main__.py index 94a26810..d558f6af 100644 --- a/doctr/__main__.py +++ b/doctr/__main__.py @@ -35,7 +35,7 @@ from textwrap import dedent from .local import (generate_GitHub_token, encrypt_variable, encrypt_file, - upload_GitHub_deploy_key, generate_ssh_key, check_repo_exists, GitHub_login) + upload_GitHub_deploy_key, generate_ssh_key, check_repo_exists, GitHub_login, set_encrypted_variable) from .travis import (setup_GitHub_push, commit_docs, push_docs, get_current_repo, sync_from_log, find_sphinx_build_dir, run) from . import __version__ @@ -122,6 +122,20 @@ def get_parser(config=None): deploy_parser = subcommand.add_parser('deploy', help="""Deploy the docs to GitHub from Travis.""") deploy_parser.set_defaults(func=deploy) deploy_parser_add_argument = make_parser_with_config_adder(deploy_parser, config) + + _user_fork_parser = subcommand.add_parser('fork-deploy', help="""Deploy the docs to GitHub from Travis to the current user fork""") + _user_fork_parser.set_defaults(func=enable_fork_build) + + _user_fork_parser.add_argument("--no-upload-key", action="store_false", default=True, + dest="upload_key", help="""Don't automatically upload the deploy key to GitHub. If you select this + option, you will not be prompted for your GitHub credentials, so this option is not compatible with + private repositories.""") + _user_fork_parser.add_argument('--token', action='store_true', default=False, + help="""Push to GitHub using a personal access token. Use this if you + used 'doctr configure --token'.""") + _user_fork_parser.add_argument('--force', action='store_true', help="""Run the deploy command even + if we do not appear to be on Travis.""") + deploy_parser_add_argument('--force', action='store_true', help="""Run the deploy command even if we do not appear to be on Travis.""") deploy_parser_add_argument('deploy_directory', type=str, nargs='?', @@ -388,6 +402,74 @@ def configure(args, parser): in your .travis.yml. """.format(encrypted_variable=encrypted_variable.decode('utf-8'), N=N))) +def enable_fork_build(args, parser): + if not args.force and on_travis(): + parser.error("doctr appears to be running on Travis. Use " + "doctr configure --force to run anyway.") + + if args.upload_key: + login_kwargs = GitHub_login() + else: + login_kwargs = {'auth': None, 'headers': None} + + build_repo = input("What repo do you want to build the docs for (/reponame, like 'drdoctr/doctr')? ") + is_private = check_repo_exists(build_repo, service='github', **login_kwargs) + check_repo_exists(build_repo, service='travis') + + deploy_repo = input("What repo do you want to deploy the docs to? [{build_repo}/] ".format(build_repo=build_repo)) + if not deploy_repo: + deploy_repo = build_repo + + if deploy_repo != build_repo: + check_repo_exists(deploy_repo, service='github', **login_kwargs) + + N = IncrementingInt(1) + + header = "\n================== You should now do the following ==================\n" + + if args.token: + pass + #token = generate_GitHub_token(**login_kwargs)['token'] + #encrypted_variable = encrypt_variable("GH_TOKEN={token}".format(token=token).encode('utf-8'), + # build_repo=build_repo, is_private=is_private, **login_kwargs) + #print(dedent(""" + #A personal access token for doctr has been created. + + #You can go to https://github.com/settings/tokens to revoke it.""")) + + #print(header) + else: + ssh_key = generate_ssh_key("doctr deploy key for {deploy_repo}".format(deploy_repo=deploy_repo), keypath='tmp_key') + import base64 + with open('tmp_key', 'rb') as f: + data = ''.join(base64.encodebytes(f.read()).decode().splitlines()) + # TODO remove + # key = encrypt_file('tmp_key', delete=True) + set_encrypted_variable("DOCTR_USER_DEPLOY", data, build_repo=build_repo, is_private=True, **login_kwargs) + + deploy_keys_url = 'https://github.com/{deploy_repo}/settings/keys'.format(deploy_repo=deploy_repo) + + if args.upload_key: + + upload_GitHub_deploy_key(deploy_repo, ssh_key, **login_kwargs, title='Doctr key blah blah self deploy of {}'.format(build_repo)) + + print(dedent(""" + The deploy key has been added for {deploy_repo}. + + You can go to {deploy_keys_url} to revoke the deploy key.\ + """.format(deploy_repo=deploy_repo, deploy_keys_url=deploy_keys_url, keypath='tmp_key'))) + print(header) + else: + print(header) + print(dedent("""\ + {N}. Go to {deploy_keys_url} + and add the following as a new key: + + {ssh_key} + Be sure to allow write access for the key. + """.format(ssh_key=ssh_key, deploy_keys_url=deploy_keys_url, N=N))) + + def main(): config = get_config() return process_args(get_parser(config=config)) diff --git a/doctr/local.py b/doctr/local.py index 2f232b29..88750a4e 100644 --- a/doctr/local.py +++ b/doctr/local.py @@ -19,6 +19,64 @@ from cryptography.hazmat.primitives import serialization +def set_encrypted_variable(variable, value, build_repo, *, public_key=None, is_private=False, **login_kwargs): + """ + set the env variable `variable` encrypted using travis API. + """ + + # if not isinstance(variable, str): + # raise TypeError("variable should be str") + # if not isinstance(variable, str): + # raise TypeError("values should be str") + + if not public_key: + headers = {'Accept': 'application/vnd.travis-ci.2+json', + 'Content-Type': 'application/json', + 'User-Agent': 'MyClient/1.0.0'} + if is_private: + tok_dict = generate_GitHub_token(scopes=["read:org", "user:email", "repo"], + note="temporary token to auth against travis", + **login_kwargs) + data = {'github_token': tok_dict['token']} + token_id = tok_dict['id'] + res = requests.post('https://api.travis-ci.org/auth/github', data=json.dumps(data), headers=headers) + res.raise_for_status() + access_token = res.json()['access_token'] + headers['Authorization'] = 'token {}'.format(access_token) + print(res.json()['access_token']) + tld = 'org' + else: + tld = 'org' + res = requests.get('https://api.travis-ci.{tld}/repos/{build_repo}'.format(build_repo=build_repo, tld=tld), + headers=headers) + if res.status_code == requests.codes.not_found: + raise RuntimeError('Could not find requested repo on Travis. Is Travis enabled?') + if not res.status_code == requests.codes.ok and is_private: + print('del stuff') + # delete_GitHub_token(token_id, **login_kwargs) + res.raise_for_status() + repo_id = res.json()['repo']['id'] + res = requests.post('https://api.travis-ci.{tld}/settings/env_vars?repository_id={repo_id}'.format(repo_id=repo_id, tld=tld), + headers=headers, + data=json.dumps({ + 'env_var':{ + "name": variable, + "value": value, + "public": False, + "repository_id": repo_id + } + })) + if is_private: + pass #delete_GitHub_token(token_id, **login_kwargs) + + res.raise_for_status() + + # Remove temporary GH token + + + + + def encrypt_variable(variable, build_repo, *, public_key=None, is_private=False, **login_kwargs): """ Encrypt an environment variable for ``build_repo`` for Travis