def test_upload_non_interactive(self): pkgname = 'new_pkg' env = Env.read_envfile() self.assertTrue( os.environ.get('KECHAIN_URL'), "KECHAIN_URL is not set in environment, cannot perform this test") with temp_chdir() as d: runner = CliRunner() result = runner.invoke(kecpkg, ['new', pkgname, '--no-venv']) package_dir = get_package_dir(pkgname) self.assertTrue(os.path.exists(package_dir)) os.chdir(package_dir) result = runner.invoke(kecpkg, ['build', pkgname]) self.assertEqual(result.exit_code, 0) self.assertExists(os.path.join(package_dir, 'dist')) pkgpath = os.path.join(package_dir, 'dist', '{}-0.0.1-py3.5.kecpkg'.format(pkgname)) self.assertExists(pkgpath) result = runner.invoke( kecpkg, [ 'upload', pkgname, '--url', os.environ.get('KECHAIN_URL'), '--token', os.environ.get('KECHAIN_TOKEN'), '--scope-id', os.environ.get('KECHAIN_SCOPE_ID'), '--kecpkg', pkgpath, '--store' # store the service_id in the settings (for teardown) ]) self.assertEqual(result.exit_code, 0) # teardown the just uploaded service from kecpkg.settings import load_settings settings = load_settings(package_dir=get_package_dir(pkgname)) from pykechain import get_project scope = get_project(url=os.environ.get('KECHAIN_URL'), token=os.environ.get('KECHAIN_TOKEN'), scope_id=os.environ.get('KECHAIN_SCOPE_ID')) service = scope.service(pk=settings.get('service_id')) service.delete()
def test_build_with_alternate_config(self): pkgname = 'new_pkg' alt_settings = 'alt-settings.json' with temp_chdir() as d: runner = CliRunner() result = runner.invoke(kecpkg, ['new', pkgname, '--no-venv']) package_dir = get_package_dir(pkgname) self.assertTrue(os.path.exists(package_dir)) # set alternative settings path settings = copy_default_settings() settings["package_name"] = pkgname save_settings(settings, package_dir=package_dir, settings_filename=alt_settings) os.chdir(package_dir) result = runner.invoke( kecpkg, ['build', pkgname, '--config', alt_settings]) self.assertEqual( result.exit_code, 0, "Results of the run were: \n---\n{}\n---".format( result.output)) self.assertExists(os.path.join(package_dir, 'dist')) dist_dir_contents = os.listdir(os.path.join(package_dir, 'dist')) self.assertTrue(len(dist_dir_contents), 1) self.assertTrue( pkgname in dist_dir_contents[0], "the name of the pkg `{}` should be in the name of " "the built kecpkg `{}`".format(pkgname, dist_dir_contents[0]))
def test_build_with_prune(self): pkgname = 'new_pkg' with temp_chdir() as d: runner = CliRunner() result = runner.invoke(kecpkg, ['new', pkgname, '--no-venv']) package_dir = get_package_dir(pkgname) self.assertTrue(os.path.exists(package_dir)) os.chdir(package_dir) result = runner.invoke(kecpkg, ['build', pkgname]) self.assertEqual( result.exit_code, 0, "Results of the run were: \n---\n{}\n---".format( result.output)) self.assertExists(os.path.join(package_dir, 'dist')) # check if dist is filled package_dir_contents = os.listdir(os.path.join( package_dir, 'dist')) self.assertTrue(len(package_dir_contents), 1) # restart the build, with prune and check if dist still has 1 result = runner.invoke(kecpkg, ['build', pkgname, '--prune']) self.assertEqual( result.exit_code, 0, "Results of the run were: \n---\n{}\n---".format( result.output)) self.assertExists(os.path.join(package_dir, 'dist')) # check if dist is filled package_dir_contents = os.listdir(os.path.join( package_dir, 'dist')) self.assertTrue(len(package_dir_contents), 1)
def build(package=None, **options): """Build the package and create a kecpkg file.""" echo_info('Locating package ``'.format(package)) package_dir = get_package_dir(package_name=package) package_name = os.path.basename(package_dir) echo_info('Package `{}` has been selected'.format(package_name)) settings = load_settings( package_dir=package_dir, settings_filename=options.get('settings_filename')) # ensure build directory is there build_dir = settings.get('build_dir', 'dist') build_path = os.path.join(package_dir, build_dir) if options.get('update_package_info'): render_package_info(settings, package_dir=package_dir, backup=True) if options.get('clean_first'): remove_path(build_path) ensure_dir_exists(build_path) # do package building build_package(package_dir, build_path, settings, options=options, verbose=options.get('verbose')) echo_success('Complete')
def test_purge_non_interactive(self): pkgname = 'new_pkg' with temp_chdir() as d: runner = CliRunner() result = runner.invoke(kecpkg, ['new', pkgname, '--no-venv']) package_dir = get_package_dir(pkgname) self.assertTrue(os.path.exists(package_dir)) result = runner.invoke(kecpkg, ['purge', pkgname, '--force']) self.assertFalse(os.path.exists(package_dir))
def save_settings(settings, package_dir=None, settings_filename=None): """ Save settings in path (in the package). :param settings: settings to save :param settings_filename: (optional) pathname of the file where the settings are stored :param package_dir: (optional) package_dir to save to :return: None """ if settings.get('package_name') and not package_dir: package_dir = get_package_dir(settings.get('package_name')) settings_filepath = get_settings_filepath(package_dir, settings_filename) ensure_dir_exists(os.path.dirname(settings_filepath)) with atomic_write(settings_filepath, overwrite=True) as f: f.write(json.dumps(settings, indent=4))
def test_build_with_extra_ignores(self): pkgname = 'new_pkg' with temp_chdir() as d: runner = CliRunner() result = runner.invoke(kecpkg, ['new', pkgname, '--no-venv']) package_dir = get_package_dir(pkgname) self.assertTrue(os.path.exists(package_dir)) ensure_dir_exists(os.path.join(package_dir, 'data')) # add additional files (to exclude for building later) touch_file(os.path.join(package_dir, 'data', 'somefile.txt')) touch_file( os.path.join(package_dir, 'local_extra_file.someext.txt')) os.chdir(package_dir) # check if those files exists package_dir_contents = os.listdir(os.path.join(package_dir)) self.assertTrue( 'local_extra_file.someext.txt' in package_dir_contents) self.assertTrue('data' in package_dir_contents) # set exclude_paths in settings settings = copy_default_settings() settings["exclude_paths"] = ["data", "local_extra_file.*"] save_settings(settings, package_dir=package_dir) # run the builder result = runner.invoke(kecpkg, ['build', pkgname, '--verbose']) self.assertEqual( result.exit_code, 0, "Results of the run were: \n---\n{}\n---".format( result.output)) self.assertExists(os.path.join(package_dir, 'dist')) # check the zip such that the extra files are not packaged dist_list = os.listdir(os.path.join(package_dir, 'dist')) zipfile = ZipFile(os.path.join(package_dir, 'dist', dist_list[0]), 'r') contents = zipfile.namelist() self.assertFalse('local_extra_file.someext.txt' in contents) self.assertFalse('data' in contents)
def prune(package, **options): """Remove a project's build artifacts.""" package_name = package or get_package_name() or click.prompt('Provide package name') package_dir = get_package_dir(package_name) settings = load_settings(package_name) # ensure build directory is there build_dir = settings.get('build_dir', 'dist') build_path = os.path.join(package_dir, build_dir) if os.path.exists(build_path): if options.get('force') or click.confirm( "Do you want to prune build artifacts for package '{}'?".format(package_name)): remove_path(build_path) if os.path.exists(build_path): echo_failure('Something went wrong pruning pacakage `{}`'.format(package_name)) else: echo_warning('Package `{}` will not be pruned'.format(package_name)) else: echo_failure('Package `{}` does not exist'.format(package_name))
def purge(package, **options): """ Purge and clean a package directory structure. :param package: Name of the kecpkg package :param options: :return: """ package_name = package or click.prompt('Provide package name') package_dir = get_package_dir(package_name) if os.path.exists(package_dir): if options.get('force') or click.confirm( "Do you want to purge and completely remove '{}'?".format(package_name)): remove_path(package_dir) if not os.path.exists(package_dir): echo_success('Package `{}` is purged and removed from disk'.format(package_name)) else: echo_failure('Something went wrong pruning pacakage `{}`'.format(package_name)) else: echo_warning('Package `{}` will not be purged'.format(package_name)) else: echo_failure('Package `{}` does not exist'.format(package_name))
def _do_create_key(gpg, options): echo_info( "Will create a secret key and store it into the KECPKG keyring.") package_dir = get_package_dir(package_name=package, fail=False) settings = DEFAULT_SETTINGS if package_dir is not None: package_name = os.path.basename(package_dir) echo_info('Package `{}` has been selected'.format(package_name)) settings = load_settings( package_dir=package_dir, settings_filename=options.get('settings_filename')) key_info = { 'name_real': click.prompt("Name", default=settings.get('name')), 'name_comment': click.prompt("Comment", default="KECPKG SIGNING KEY"), 'name_email': click.prompt("Email", default=settings.get('email')), 'expire_date': click.prompt("Expiration in months", default=12, value_proc=lambda i: "{}m".format(i)), 'key_type': 'RSA', 'key_length': 4096, 'key_usage': '', 'subkey_type': 'RSA', 'subkey_length': 4096, 'subkey_usage': 'encrypt,sign,auth', 'passphrase': '' } passphrase = click.prompt("Passphrase", hide_input=True) passphrase_confirmed = click.prompt("Confirm passphrase", hide_input=True) if passphrase == passphrase_confirmed: key_info['passphrase'] = passphrase else: raise ValueError("The passphrases did not match.") echo_info( "Creating the secret key '{name_real} ({name_comment}) <{name_email}>'" .format(**key_info)) echo_info( "Please move around mouse or generate other activity to introduce sufficient entropy. " "This might take a minute...") result = gpg.gen_key(gpg.gen_key_input(**key_info)) pprint(result.__dict__) if result and result.stderr.find('KEY_CREATED'): echo_success("The key is succesfully created") _do_list(gpg=gpg) sys.exit(0) echo_failure("Could not generate the key due to an error: '{}'".format( result.stderr)) sys.exit(1)
def config(package, **options): """Manage the configuration (or settings) of the package. The various settings in the .kecpkg-settings.json file are: package_name: name of the package version: version number of the package description: longer description of the package name: name of the author of the package email: email of the author of the package python_version: python version on which the kecpkg will run, corresponds with an executable KE-crunch environment entrypoint_script: the name of the script that will be executed first entrypoint_func: function name inside the script that will be executed. Ensure that it takes *args, **kwargs. venv_dir: python virtual environment directory in the development environment requirements_filename: name of the requirements file with list of package that will be installed before running build_dir: directory where the built kecpkg will be stored exclude_paths: list of paths that will be excluded from the package, next to the build in excludes url: url where the package will be uploaded token: token of the user under which the package is uploaded scope_id: identification of the scope under which the package is uploaded service_id: identification under which the package is re-uploaded (or recently uploaded) last_upload: date and time of the last upload """ echo_info('Locating package ``'.format(package)) package_dir = get_package_dir(package_name=package) package_name = os.path.basename(package_dir) echo_info('Package `{}` has been selected'.format(package_name)) if options.get('init'): if os.path.exists(os.path.join(package_dir, options.get('settings_filename'))) and \ click.confirm('Are you sure you want to overwrite the current settingsfile ' '(old settings will be a backup)?'): copy_path( os.path.join(package_dir, options.get('settings_filename')), os.path.join( package_dir, "{}-backup".format(options.get('settings_filename')))) echo_info('Creating new settingsfile') settings = copy_default_settings() settings['package_name'] = package_name save_settings(settings, package_dir=package_dir, settings_filename=options.get('settings_filename')) settings = load_settings(package_dir=package_dir) if options.get('interactive'): settings['version'] = click.prompt('Version', default=settings.get( 'version', '0.0.1')) settings['description'] = click.prompt('Description', default=settings.get( 'description', '')) settings['name'] = click.prompt('Author', default=settings.get( 'name', os.environ.get('USER', ''))) settings['email'] = click.prompt('Author\'s email', default=settings.get('email', '')) settings['python_version'] = click.prompt( 'Python version (choose from: {})'.format( settings.get('pyversions')), default='3.5') settings['exclude_paths'] = click.prompt( "Exclude additional paths from kecpkg (eg. 'data, input')", default=settings.get('exclude_paths', ''), value_proc=process_additional_exclude_paths) save_settings(settings, package_dir=package_dir, settings_filename=options.get('settings_filename')) if options.get('set_key'): k, v = options.get('set_key') if options.get('verbose'): echo_info("Set the key '{}' to value '{}'".format(k, v)) settings[k] = v save_settings(settings, package_dir=package_dir, settings_filename=options.get('settings_filename')) if options.get('get_key'): echo_info( tabulate([ (options.get('get_key'), settings.get(options.get('get_key'))) ], headers=("key", "value"))) return if options.get('verbose'): echo_info(tabulate(settings.items(), headers=("key", "value"))) if not options.get('interactive'): echo_success('Settings file identified and correct')
def upload(package=None, url=None, username=None, password=None, token=None, scope=None, scope_id=None, kecpkg=None, **options): """ Upload built kecpkg to KE-chain. If no options are provided, the interactive mode is triggered. """ package_name = package or get_package_name() or click.prompt( 'Package name') settings = load_settings( package_dir=get_package_dir(package_name), settings_filename=options.get('settings_filename')) if not url or not ((username and password) or token): url = click.prompt('Url (incl http(s)://)', default=settings.get('url') or url) username = click.prompt('Username', default=settings.get('username') or username) password = click.prompt('Password', hide_input=True) # set the interactive world to True for continuation sake options['interactive'] = True elif not options.get('interactive'): url = url or settings.get('url') username = username or settings.get('username') token = token or settings.get('token') scope_id = scope_id or settings.get('scope_id') client = Client(url) client.login(username=username, password=password, token=token) # scope finder if not scope_id and settings.get('scope_id') and \ click.confirm("Do you wish to use the stored `scope_id` in settings: `{}`".format( settings.get('scope_id')), default=True): scope_id = settings.get('scope_id') if not scope_id: scopes = client.scopes() scope_matcher = [ dict(number=i, scope_id=scope.id, scope=scope.name) for i, scope in zip(range(1, len(scopes)), scopes) ] # nice UI echo_info('Choose from following scopes:') for match_dict in scope_matcher: echo_info( "{number} | {scope_id:.8} | {scope}".format(**match_dict)) scope_match = None while not scope_match: scope_guess = click.prompt('Row number, part of Id or Scope') scope_match = validate_scopes(scope_guess, scope_matcher) echo_success( "Scope selected: '{scope}' ({scope_id})".format(**scope_match)) scope_id = scope_match['scope_id'] scope_to_upload = get_project(url, username, password, token, scope_id=scope_id) # service reupload service_id = options.get('service_id') or settings.get('service_id') if options.get('reupload') and not service_id: echo_failure('Please provide a service id to reupload to.') elif service_id and not options.get('reupload') and options.get( 'interactive'): if click.confirm( "Do you wish to *replace* the previously uploaded service: `{}`" .format(service_id), default=True): service_id = service_id else: service_id = None # store to settings if options.get('store'): settings.update( dict(url=url, username=username, scope_id=str(scope_id))) if service_id: settings['service_id'] = str(service_id) save_settings(settings, settings_filename=options.get('settings_filename')) # do upload build_path = os.path.join(get_package_dir(package_name), settings.get('build_dir')) if not os.path.exists(build_path): echo_failure('Cannot find build path, please do `kecpkg build` first') sys.exit(400) upload_package(scope_to_upload, build_path, kecpkg, service_id=service_id, settings=settings, store_settings=options.get('store'), settings_filename=options.get('settings_filename'))