def _setup_travis_ci(gh_repo_name, auth_token, repo_access_token): # travis-ci.org is the open source endpoint only! We will need to hit # travis-ci.com for private projects! # Headers for API v2. This is only necessary because generating a Travis # API token from a GitHub Token isn't possible in the API v3 yet. This way # is recommended as per: # https://github.com/travis-ci/travis-ci/issues/9273 headers_v2_only = { 'User-Agent': 'Memote', 'Accept': 'application/vnd.travis-ci.2+json', } # Generate Travis API token: try: LOGGER.info("Generating Travis API token.") response = requests.post("https://api.travis-ci.org/auth/github", headers=headers_v2_only, data={'github_token': auth_token}) response.raise_for_status() except HTTPError as error: LOGGER.critical( "Something is wrong with the generated APIv3 authentication token " "or you did not link your GitHub account on " "'https://travis-ci.org/'? Please refer to the following error " "message for further information: {}".format(str(error))) sys.exit(1) else: LOGGER.info("Success!") travis_api_token = response.json()["access_token"] # Headers for API v3! headers = { 'Travis-API-Version': '3', 'Authorization': 'token {}'.format(travis_api_token), 'User-Agent': 'Memote Query' } # Authenticate the User on Travis try: LOGGER.info("Authorizing with TravisCI.") response = requests.get("https://api.travis-ci.org/user", headers=headers) response.raise_for_status() except HTTPError as error: LOGGER.critical( "Something is wrong with the generated token or you did not " "link your GitHub account on 'https://travis-ci.org/'? Please " "refer to the following error code for " "further information: {}".format(str(error))) sys.exit(1) else: LOGGER.info("Success!") t_user = response.json() # Synchronize a User's projects between GitHub and Travis LOGGER.info("Synchronizing user projects between GitHub and Travis.") synced = False for _ in range(60): response = requests.post( "https://api.travis-ci.org/user/{}/sync".format(t_user["id"]), headers=headers) if response.status_code == 200: synced = True LOGGER.info("Success!") break else: LOGGER.info("Still synchronizing...") sleep(0.5) if not synced: LOGGER.critical( "Could not synchronize your projects between GitHub and Travis!" "The latest response code is {}".format(response.status_code)) sys.exit(1) # Make sure GitHub repo can be found on Travis CI.# url_safe_repo_name = quote_plus(gh_repo_name) try: LOGGER.info("Find repository {} on Travis CI".format(gh_repo_name)) response = requests.get( "https://api.travis-ci.org/repo/{}".format(url_safe_repo_name), headers=headers) response.raise_for_status() except HTTPError as error: if error.response.status_code == 404: LOGGER.critical( "Repository could not be found. Is it on GitHub already and " "spelled correctly?") sys.exit(1) else: LOGGER.critical( "An error occurred. Please refer to the following error " "message for further information: {}".format(str(error))) sys.exit(1) else: LOGGER.info("Success!") t_repo = response.json() # Use repo ID to activate Travis CI for this repo try: LOGGER.info("Activating automatic testing for you " "on Travis CI.") endpoint = "https://api.travis-ci.org/repo/{}/activate".format( url_safe_repo_name) response = requests.post(endpoint, headers=headers) response.raise_for_status() except HTTPError as error: LOGGER.critical("Unable to enable automatic testing on Travis CI! " "Please refer to the following error message for " "further information: {}".format(str(error))) sys.exit(1) # Check if activation was successful activated = False for _ in range(60): try: LOGGER.info("Check if activation {} on Travis CI " "was successful".format(gh_repo_name)) response = requests.get( "https://api.travis-ci.org/repo/{}".format(url_safe_repo_name), headers=headers) except HTTPError as error: LOGGER.critical( "An error occurred. Please refer to the following error " "message for further information: {}".format(str(error))) sys.exit(1) else: t_repo = response.json() if t_repo["active"]: activated = True LOGGER.info( "Your repository is now on GitHub and automatic testing has " "been enabled on Travis CI. Congrats!") break else: LOGGER.info("Still activating...") sleep(0.5) if not activated: LOGGER.critical("Unable to enable automatic testing on Travis CI! " "Delete all tokens belonging to this repo at " "https://github.com/settings/tokens then try running " "`memote online` again. If this fails yet again, " "please open an issue at " "https://github.com/opencobra/memote/issues.") sys.exit(1) LOGGER.info("Encrypting GitHub token for repo '{}'.".format(gh_repo_name)) key = te.retrieve_public_key(gh_repo_name) secret = te.encrypt_key( key, "GITHUB_TOKEN={}".format(repo_access_token).encode()) return secret
def online(note, github_repository, github_username): """Upload the repository to GitHub and enable testing on Travis CI.""" try: repo = git.Repo() except git.InvalidGitRepositoryError: LOGGER.critical( "The history requires a git repository in order to follow " "the current branch's commit history.") sys.exit(1) password = getpass("GitHub Password: "******" ") LOGGER.info( "Logged in to user '{}' created on '{}'.".format(user.login, when)) except BadCredentialsException: LOGGER.critical("Incorrect username or password!") sys.exit(1) try: gh_repo = user.get_repo(github_repository) LOGGER.warning( "Using existing repository '{}'. This may override previous " "settings.".format(github_repository)) except UnknownObjectException: gh_repo = user.create_repo(github_repository) try: LOGGER.info("Creating token.") auth = user.create_authorization(scopes=["repo"], note=note) except GithubException: LOGGER.critical( "A personal access token with the note '{}' already exists. " "Either delete it or choose another note.".format(note)) sys.exit(1) try: LOGGER.info("Authorizing with TravisCI.") travis = TravisPy.github_auth(auth.token) t_user = travis.user() except TravisError: LOGGER.critical( "Something is wrong with the generated token or you did not " "link your GitHub account on 'https://travis-ci.org/'!") sys.exit(1) LOGGER.info("Synchronizing repositories.") while not t_user.sync(): sleep(0.1) try: t_repo = travis.repo(gh_repo.full_name) except TravisError: LOGGER.critical( "Repository could not be found. Is it on GitHub already and " "spelled correctly?") sys.exit(1) if t_repo.enable(): LOGGER.info( "Your repository is now on GitHub and automatic testing has " "been enabled on Travis CI. Congrats!") else: LOGGER.critical("Unable to enable automatic testing on Travis CI!") sys.exit(1) LOGGER.info( "Encrypting GitHub token for repo '{}'.".format(gh_repo.full_name)) key = retrieve_public_key(gh_repo.full_name) secret = encrypt_key(key, "GITHUB_TOKEN={}".format(auth.token).encode()) LOGGER.info("Storing GitHub token in '.travis.yml'.") with io.open(".travis.yml", "r") as file_h: config = yaml.load(file_h, yaml.RoundTripLoader) config["env"]["global"].append({"secure": secret}) with io.open(".travis.yml", "w") as file_h: yaml.dump(config, file_h, Dumper=yaml.RoundTripDumper) repo.index.add([".travis.yml"]) repo.index.commit("chore: add encrypted GitHub access token") repo.remotes.origin.push(all=True)
def test_encrypt_key(repository): """Test the encrypt module's encrypt_key function.""" public_key = retrieve_public_key(repository) password = '******' encrypted_password = encrypt_key(public_key, password.encode()) assert isinstance(encrypted_password, six.text_type)
def test_invalid_credentials(): """Test that an InvalidCredentialsError is raised.""" with pytest.raises(InvalidCredentialsError): retrieve_public_key("INVALID_USER_NAME/INVALID_REPO")
def test_public_key_retrieval(repository): """Test the encrypt module's retrieve_public_key function.""" public_key = retrieve_public_key(repository) assert isinstance(public_key, six.text_type) assert 'BEGIN PUBLIC KEY' in public_key assert 'END PUBLIC KEY' in public_key
def cli(username, repository, path, password, deploy, env, clipboard, env_file): """Encrypt passwords and environment variables for use with Travis CI. Travis Encrypt requires as arguments the user's GitHub username and repository name. Once the arguments are passed, a password prompt will ask for the password that needs to be encrypted. The given password will then be encrypted via the PKCS1v15 padding scheme and printed to standard output. If the path to a .travis.yml file is given as an argument, the encrypted password is added to the .travis.yml file. """ key = retrieve_public_key('{}/{}'.format(username, repository)) if env_file: if path: config = load_travis_configuration(path) for env_var, value in dotenv_values(env_file).items(): encrypted_env = encrypt_key(key, value.encode()) config.setdefault('env', {}).setdefault('global', {})[env_var] = { 'secure': encrypted_env } dump_travis_configuration(config, path) print('Encrypted variables from {} added to {}'.format( env_file, path)) else: print('\nPlease add the following to your .travis.yml:') for env_var, value in dotenv_values(env_file).items(): encrypted_env = encrypt_key(key, value.encode()) print("{}:\n secure: {}".format(env_var, encrypted_env)) return encrypted_password = encrypt_key(key, password.encode()) if path: config = load_travis_configuration(path) if deploy: config.setdefault('deploy', {}).setdefault('password', {})['secure'] = encrypted_password elif env: try: config.setdefault('env', {}).setdefault( 'global', {})['secure'] = encrypted_password except TypeError: for item in config['env']['global']: if isinstance(item, dict) and 'secure' in item: item['secure'] = encrypted_password else: config.setdefault('password', {})['secure'] = encrypted_password dump_travis_configuration(config, path) print('Encrypted password added to {}'.format(path)) elif clipboard: pyperclip.copy(encrypted_password) print('\nThe encrypted password has been copied to your clipboard.') else: print('\nPlease add the following to your .travis.yml:\nsecure: {}'. format(encrypted_password))
def _setup_travis_ci(gh_repo_name, auth_token, repo_access_token): # travis-ci.org is the open source endpoint only! We will need to hit # travis-ci.com for private projects! # Headers for API v2. This is only necessary because generating a Travis # API token from a GitHub Token isn't possible in the API v3 yet. This way # is recommended as per: # https://github.com/travis-ci/travis-ci/issues/9273 headers_v2_only = { 'User-Agent': 'Memote', 'Accept': 'application/vnd.travis-ci.2+json', } # Generate Travis API token: try: LOGGER.info("Generating Travis API token.") response = requests.post( "https://api.travis-ci.org/auth/github", headers=headers_v2_only, data={'github_token': auth_token} ) response.raise_for_status() except HTTPError as error: LOGGER.critical( "Something is wrong with the generated APIv3 authentication token " "or you did not link your GitHub account on " "'https://travis-ci.org/'? Please refer to the following error " "message for further information: {}".format(str(error))) sys.exit(1) else: LOGGER.info("Success!") travis_api_token = response.json()["access_token"] # Headers for API v3! headers = { 'Travis-API-Version': '3', 'Authorization': 'token {}'.format(travis_api_token), 'User-Agent': 'Memote Query' } # Authenticate the User on Travis try: LOGGER.info("Authorizing with TravisCI.") response = requests.get( "https://api.travis-ci.org/user", headers=headers ) response.raise_for_status() except HTTPError as error: LOGGER.critical( "Something is wrong with the generated token or you did not " "link your GitHub account on 'https://travis-ci.org/'? Please " "refer to the following error code for " "further information:".format(str(error))) sys.exit(1) else: LOGGER.info("Success!") t_user = response.json() # Synchronize a User's projects between GitHub and Travis LOGGER.info("Synchronizing user projects between GitHub and Travis.") synced = False for _ in range(60): response = requests.post( "https://api.travis-ci.org/user/{}/sync".format(t_user["id"]), headers=headers ) if response.status_code == 200: synced = True LOGGER.info("Success!") break else: LOGGER.info("Still synchronizing...") sleep(0.5) if not synced: LOGGER.critical( "Could not synchronize your projects between GitHub and Travis!" "The latest response code is {}".format(response.status_code)) sys.exit(1) # Make sure GitHub repo can be found on Travis CI.# url_safe_repo_name = quote_plus(gh_repo_name) try: LOGGER.info("Find repository {} on Travis CI".format(gh_repo_name)) response = requests.get( "https://api.travis-ci.org/repo/{}".format(url_safe_repo_name), headers=headers ) response.raise_for_status() except HTTPError as error: if error.response.status_code == 404: LOGGER.critical( "Repository could not be found. Is it on GitHub already and " "spelled correctly?") sys.exit(1) else: LOGGER.critical( "An error occurred. Please refer to the following error " "message for further information: {}".format(str(error))) sys.exit(1) else: LOGGER.info("Success!") t_repo = response.json() # Use repo ID to activate Travis CI for this repo try: LOGGER.info( "Activating automatic testing for you " "on Travis CI.") endpoint = "https://api.travis-ci.org/repo/{}/activate".format( url_safe_repo_name ) response = requests.post( endpoint, headers=headers ) response.raise_for_status() except HTTPError as error: LOGGER.critical("Unable to enable automatic testing on Travis CI! " "Please refer to the following error message for " "further information: {}".format(str(error))) sys.exit(1) # Check if activation was successful activated = False for _ in range(60): try: LOGGER.info("Check if activation {} on Travis CI " "was successful".format(gh_repo_name)) response = requests.get( "https://api.travis-ci.org/repo/{}".format(url_safe_repo_name), headers=headers ) except HTTPError as error: LOGGER.critical( "An error occurred. Please refer to the following error " "message for further information: {}".format(str(error))) sys.exit(1) else: t_repo = response.json() if t_repo["active"]: activated = True LOGGER.info( "Your repository is now on GitHub and automatic testing has " "been enabled on Travis CI. Congrats!") break else: LOGGER.info("Still activating...") sleep(0.5) if not activated: LOGGER.critical("Unable to enable automatic testing on Travis CI! " "Delete all tokens belonging to this repo at " "https://github.com/settings/tokens then try running " "`memote online` again. If this fails yet again, " "please open an issue at " "https://github.com/opencobra/memote/issues.") sys.exit(1) LOGGER.info( "Encrypting GitHub token for repo '{}'.".format(gh_repo_name)) key = te.retrieve_public_key(gh_repo_name) secret = te.encrypt_key( key, "GITHUB_TOKEN={}".format(repo_access_token).encode() ) return secret