def test_get_user_config_invalid(user_config_path): """ Get config from an invalid ~/.cookiecutterrc file """ shutil.copy('tests/test-config/invalid-config.yaml', user_config_path) with pytest.raises(InvalidConfiguration): config.get_user_config()
def test_get_user_config_valid(user_config_path): """ Get config from a valid ~/.cookiecutterrc file """ shutil.copy(VALID_CONFIG_PATH, user_config_path) conf = config.get_user_config() assert conf == VALID_CONFIG
def generate_cookiecutter_context( template_git_url: str, cookiecutter_template_dir: Path, config_file: Optional[Path] = None, default_config: bool = False, extra_context: Optional[Dict[str, Any]] = None, no_input: bool = False, ) -> CookiecutterContext: _validate_cookiecutter(cookiecutter_template_dir) context_file = cookiecutter_template_dir / "cookiecutter.json" config_dict = get_user_config( config_file=str(config_file) if config_file else None, default_config=default_config) context = generate_context( context_file=context_file, default_context=config_dict["default_context"], extra_context=extra_context, ) # prompt the user to manually configure at the command line. # except when 'no-input' flag is set context["cookiecutter"] = prompt_for_config(context, no_input) context["cookiecutter"]["_template"] = template_git_url return context
def test_default_config_from_env_variable(monkeypatch, custom_config_path, custom_config): """Validate app configuration. User config path should be parsed from sys env.""" monkeypatch.setenv('COOKIECUTTER_CONFIG', custom_config_path) user_config = config.get_user_config() assert user_config == custom_config
def copy_cookiecutter_resume(template_name='cookiecutter-easydata'): """Make a copy of the cookiecutter replay file in the generated project. By default, cookiecutter creates a replay directory in a user's ~/.cookiecutter directory. This hook creates a YAML version of those values in the generated project. This can be used to regenerate the project by doing a: >>> cookiecutter --config_file path/to/cookiecutter-easydata.yaml cookiecutter-easydata """ config_obj = get_user_config() config_dir = pathlib.Path(config_obj['replay_dir']) src_path = config_dir / f'{template_name}.json' yml_path = f'.{template_name}.yml' # relative to root of generated project logger.debug(f"Reading cookiecutter replay data from {src_path}") with open(src_path) as f: cookiecutter_opts = json.load(f) yaml_opts = {k:v for k,v in sorted(cookiecutter_opts['cookiecutter'].items()) if not k.startswith('_')} yaml = YAML() yaml.default_flow_style=False yaml.width=4096 yaml.indent(offset=4) logger.debug(f"Dumping cookiecutter replay (YAML) info to {yml_path}") with open(yml_path, 'w') as fw: yaml.dump({'default_context': yaml_opts}, fw)
def test_specify_config_path(mocker, custom_config_path, custom_config): spy_get_config = mocker.spy(config, 'get_config') user_config = config.get_user_config(custom_config_path) spy_get_config.assert_called_once_with(custom_config_path) assert user_config == custom_config
def test_force_default_config(mocker): spy_get_config = mocker.spy(config, 'get_config') user_config = config.get_user_config(None) assert user_config == config.DEFAULT_CONFIG assert not spy_get_config.called
def get_cookiecutter_config(template, default_config=None, version=None): """Obtains the configuration used for cookiecutter templating Args: template: Path to the template default_config (dict, optional): The default configuration version (str, optional): The git SHA or branch to use when checking out template. Defaults to latest version Returns: tuple: The cookiecutter repo directory and the config dict """ default_config = default_config or {} config_dict = cc_config.get_user_config() repo_dir, _ = cc_repository.determine_repo_dir( template=template, abbreviations=config_dict['abbreviations'], clone_to_dir=config_dict['cookiecutters_dir'], checkout=version, no_input=True) context_file = os.path.join(repo_dir, 'cookiecutter.json') context = cc_generate.generate_context( context_file=context_file, default_context={ **config_dict['default_context'], **default_config }) return repo_dir, cc_prompt.prompt_for_config(context)
def piecutter(template, checkout=None, no_input=False, overwrite_if_exists=False, output_dir='.', user_config_file=None, default_config=False): """ :param template: A directory containing a project template directory, or a URL to a git repository. :param checkout: The branch, tag or commit ID to checkout after clone. :param no_input: Prompt the user at command line for manual configuration? :param: overwrite_if_exists: Overwrite the contents of output directory if it exists. :param output_dir: Where to output the generated project dir. :param user_config_file: User configuration file path. :param default_config: Use default values rather than a config file. """ config_dict = get_user_config( config_file=user_config_file, default_config=default_config, ) repo_dir, cleanup = determine_repo_dir( template=template, abbreviations=config_dict['abbreviations'], clone_to_dir=config_dict['cookiecutters_dir'], checkout=checkout, no_input=no_input, ) template_name = os.path.basename(os.path.abspath(repo_dir)) prompt_config_file = os.path.join(repo_dir, constants.PROMPT_CONFIG_FILE) msg = u'prompt_config_file is %s, loading module and calling prompt_config function' logger.debug(msg, prompt_config_file) spec = importlib.util.spec_from_file_location('cookiecutter_config', prompt_config_file) config_module = importlib.util.module_from_spec(spec) spec.loader.exec_module(config_module) prompt_config = config_module.prompt_config() # TODO add some validation here that each item has needed fields, a valid prompt_type, etc template_context = OrderedDict([]) template_context['cookiecutter'] = prompt_user_for_config( prompt_config, no_input) # TODO add template dir or url to template_context['_template'] and dump # template_context to replay_dir # TODO write to .piecutter.json file (or something) before generating files result = generate_files(repo_dir=repo_dir, context=template_context, overwrite_if_exists=overwrite_if_exists, output_dir=output_dir) # Cleanup (if required) if cleanup: rmtree(repo_dir) return result
def test_get_user_config_no_rc(user_config_path): """ Do NOT get config from a valid ~/.cookiecutterrc file """ shutil.copy(VALID_CONFIG_PATH, user_config_path) for rc_file in (None, '', 'this-will-not-ever-exist'): conf = config.get_user_config(rc_file) assert conf == config.DEFAULT_CONFIG
def main(): repo_dir = sys.argv[1] user_config_path = sys.argv[2] user_config_path = user_config_path if os.path.isfile(user_config_path) else None context_file = os.path.join(repo_dir, 'cookiecutter.json') config_dict = get_user_config(user_config_path) context = generate_context(context_file, config_dict['default_context']) print(json.dumps(context))
def test_expand_vars_for_directories_in_config(monkeypatch): monkeypatch.setenv("COOKIES", "Users/bob/cookies") config_file = "tests/test-config/config-expand-vars.yaml" user_config = config.get_user_config(config_file) assert user_config["replay_dir"] == "Users/bob/cookies/replay-files" assert user_config["cookiecutters_dir"] == "Users/bob/cookies/templates"
def test_specify_config_path(mocker, custom_config_path, custom_config): """Validate provided custom config path should be respected and parsed.""" spy_get_config = mocker.spy(config, 'get_config') user_config = config.get_user_config(custom_config_path) spy_get_config.assert_called_once_with(custom_config_path) assert user_config == custom_config
def test_expand_vars_for_directories_in_config(monkeypatch): monkeypatch.setenv('COOKIES', 'Users/bob/cookies') config_file = 'tests/test-config/config-expand-vars.yaml' user_config = config.get_user_config(config_file) assert user_config['replay_dir'] == 'Users/bob/cookies/replay-files' assert user_config['cookiecutters_dir'] == 'Users/bob/cookies/templates'
def test_get_user_config_valid(user_config_path, custom_config): """ Get config from a valid ~/.cookiecutterrc file """ shutil.copy('tests/test-config/valid-config.yaml', user_config_path) conf = config.get_user_config() assert conf == custom_config
def test_force_default_config(mocker, custom_config_path): """Validate `default_config=True` should ignore provided custom user config.""" spy_get_config = mocker.spy(config, 'get_config') user_config = config.get_user_config(custom_config_path, default_config=True) assert user_config == config.DEFAULT_CONFIG assert not spy_get_config.called
def create( template_git_url: str, output_dir: str = ".", config_file: Optional[str] = None, default_config: bool = False, extra_context: Optional[dict] = None, no_input: bool = False, overwrite_if_exists: bool = False, ) -> str: """Expand a Git based Cookiecutter template into a new project on disk.""" with TemporaryDirectory() as cookiecutter_template_dir_str: cookiecutter_template_dir = Path(cookiecutter_template_dir_str) try: repo = Repo.clone_from(template_git_url, cookiecutter_template_dir) last_commit = repo.head.object.hexsha except Exception as e: raise InvalidCookiecutterRepository(e) main_cookiecutter_directory: Optional[Path] = None for dir_item in cookiecutter_template_dir.glob("*cookiecutter.*"): if dir_item.is_dir( ) and "{{" in dir_item.name and "}}" in dir_item.name: main_cookiecutter_directory = dir_item break if not main_cookiecutter_directory: # pragma: no cover raise UnableToFindCookiecutterTemplate(cookiecutter_template_dir) context_file = cookiecutter_template_dir / "cookiecutter.json" config_dict = get_user_config(config_file=config_file, default_config=default_config) context = generate_context( context_file=str(context_file), default_context=config_dict["default_context"], extra_context=extra_context, ) # prompt the user to manually configure at the command line. # except when 'no-input' flag is set context["cookiecutter"] = prompt_for_config(context, no_input) context["cookiecutter"]["_template"] = template_git_url (main_cookiecutter_directory / ".cruft.json").write_text( json_dumps({ "template": template_git_url, "commit": last_commit, "context": context })) return generate_files( repo_dir=cookiecutter_template_dir, context=context, overwrite_if_exists=overwrite_if_exists, output_dir=output_dir, )
def _custom_cookiecutter(template, output_dir, project_slug, package_slug): config_dict = get_user_config( config_file=None, default_config=False, ) template_name = os.path.splitext(os.path.basename(template))[0] repo_dir = os.path.join(config_dict['cookiecutters_dir'], template_name) if os.path.isdir(repo_dir): purge_dir(repo_dir) repo_dir, _ = determine_repo_dir( template=template, abbreviations=config_dict['abbreviations'], clone_to_dir=config_dict['cookiecutters_dir'], checkout=None, no_input=False, password=None, directory=None, ) context_file = os.path.join(repo_dir, 'cookiecutter.json') context = generate_context( context_file=context_file, default_context=config_dict['default_context'], extra_context=None, ) answers = dialogus( ADD_REPORT_QUESTIONS, 'Add report to existing project', intro=REPORT_ADD_WIZARD_INTRO, summary=REPORT_SUMMARY, finish_text='Add', previous_text='Back', ) if not answers: raise ClickException('Aborted by user input') context['cookiecutter']['_template'] = template context['cookiecutter']['project_slug'] = project_slug context['cookiecutter']['package_slug'] = package_slug context['cookiecutter']['initial_report_name'] = answers[ 'initial_report_name'] context['cookiecutter']['initial_report_slug'] = answers[ 'initial_report_slug'] context['cookiecutter']['initial_report_description'] = answers[ 'initial_report_description'] context['cookiecutter']['initial_report_renderer'] = answers[ 'initial_report_renderer'] result = _generate_files(context, output_dir, repo_dir) return result, context['cookiecutter']['initial_report_slug']
def test_expand_user_for_directories_in_config(monkeypatch): def _expanduser(path): return path.replace('~', 'Users/bob') monkeypatch.setattr('os.path.expanduser', _expanduser) config_file = 'tests/test-config/config-expand-user.yaml' user_config = config.get_user_config(config_file) assert user_config['replay_dir'] == 'Users/bob/replay-files' assert user_config['cookiecutters_dir'] == 'Users/bob/templates'
def main(template: str): config_dict = get_user_config() repo_dir, cleanup = determine_repo_dir( template=template, abbreviations=config_dict['abbreviations'], clone_to_dir=config_dict['cookiecutters_dir'], checkout=None, no_input=False, ) print("repo_dir: ", repo_dir) print("cleanup: ", cleanup)
def test_expand_user_for_directories_in_config(monkeypatch): def _expanduser(path): return path.replace("~", "Users/bob") monkeypatch.setattr("os.path.expanduser", _expanduser) config_file = "tests/test-config/config-expand-user.yaml" user_config = config.get_user_config(config_file) assert user_config["replay_dir"] == "Users/bob/replay-files" assert user_config["cookiecutters_dir"] == "Users/bob/templates"
def main(template, extra_context, no_input, checkout, verbose, replay, overwrite_if_exists, output_dir, config_file, default_config, debug_file): """Create a project from a Cookiecutter project template (TEMPLATE). Cookiecutter is free and open source software, developed and managed by volunteers. If you would like to help out or fund the project, please get in touch at https://github.com/audreyr/cookiecutter. """ # If you _need_ to support a local template in a directory # called 'help', use a qualified path to the directory. if template == u'help': click.echo(click.get_current_context().get_help()) sys.exit(0) if template == u'ls': config_dict = get_user_config(config_file=config_file, default_config=default_config) clone_to_dir = config_dict['cookiecutters_dir'] import glob directories = "\n".join(glob.glob1(clone_to_dir, "*")) click.echo('Existing templates:\n' + directories) sys.exit(0) configure_logger( stream_level='DEBUG' if verbose else 'INFO', debug_file=debug_file, ) try: cookiecutter(template, checkout, no_input, extra_context=extra_context, replay=replay, overwrite_if_exists=overwrite_if_exists, output_dir=output_dir, config_file=config_file, default_config=default_config, password=os.environ.get('COOKIECUTTER_REPO_PASSWORD')) except (OutputDirExistsException, InvalidModeException, FailedHookException, UnknownExtension, InvalidZipRepository, RepositoryNotFound, RepositoryCloneFailed) as e: click.echo(e) sys.exit(1) except UndefinedVariableInTemplate as undefined_err: click.echo('{}'.format(undefined_err.message)) click.echo('Error message: {}'.format(undefined_err.error.message)) context_str = json.dumps(undefined_err.context, indent=4, sort_keys=True) click.echo('Context: {}'.format(context_str)) sys.exit(1)
def test_get_user_config_valid(self): """ Get config from a valid ~/.cookiecutterrc file """ shutil.copy('tests/test-config/valid-config.yaml', self.user_config_path) conf = config.get_user_config() expected_conf = { 'cookiecutters_dir': '/home/example/some-path-to-templates', 'default_context': { "full_name": "Firstname Lastname", "email": "*****@*****.**", "github_username": "******" } } self.assertEqual(conf, expected_conf)
def template_gen(template_name, config_file, no_input, extra_context={}): print('\n*** Gather information to generate Nextflow %s ***' % template_name.replace('-', ' ')) project_dir = cookiecutter(os.path.join(GEN_HOME, template_name), config_file=config_file, no_input=no_input, extra_context=extra_context) config_dict = get_user_config(config_file=config_file) project_context = load(config_dict['replay_dir'], template_name) print('Template generated in: %s\n' % project_dir) return project_dir, project_context
def cookiecutter(template, checkout=None, no_input=False, extra_context=None): """ Replacement for cookiecutter's own cookiecutter. The difference with cookiecutter's cookiecutter function is that this one doesn't automatically str() all the values passed along to the template. :param template: A directory containing a project template directory, or a URL to a git repository. :param checkout: The branch, tag or commit ID to checkout after clone. :param no_input: Prompt the user at command line for manual configuration? :param extra_context: A dictionary of context that overrides default and user configuration. """ # Get user config from ~/.cookiecutterrc or equivalent # If no config file, sensible defaults from config.DEFAULT_CONFIG are used config_dict = get_user_config() template = expand_abbreviations(template, config_dict) # TODO: find a better way to tell if it's a repo URL if 'git@' in template or 'https://' in template: repo_dir = clone( repo_url=template, checkout=checkout, clone_to_dir=config_dict['cookiecutters_dir'], no_input=no_input ) else: # If it's a local repo, no need to clone or copy to your # cookiecutters_dir repo_dir = template context_file = os.path.join(repo_dir, 'cookiecutter.json') logging.debug('context_file is {0}'.format(context_file)) context = generate_context( context_file=context_file, default_context=config_dict['default_context'], extra_context=extra_context, ) # Create project from local context and project template. generate_files( repo_dir=repo_dir, context=context )
def test_get_user_config_valid(user_config_path): """ Get config from a valid ~/.cookiecutterrc file """ shutil.copy('tests/test-config/valid-config.yaml', user_config_path) conf = config.get_user_config() expected_conf = { 'cookiecutters_dir': '/home/example/some-path-to-templates', 'default_context': { 'full_name': 'Firstname Lastname', 'email': '*****@*****.**', 'github_username': '******' } } assert conf == expected_conf
def main(): repo_dir = sys.argv[1] user_config_path = sys.argv[2] user_config_path = user_config_path if os.path.isfile(user_config_path) else None output_folder_path = sys.argv[3] context_json_path = sys.argv[4] extra_context = json.load(open(context_json_path, 'r')) context_file = os.path.join(repo_dir, 'cookiecutter.json') config_dict = get_user_config(user_config_path) context = generate_context(context_file, config_dict['default_context'], extra_context=extra_context) rendered_context = render_context(context, output_folder_path) print(json.dumps(rendered_context))
def __init__(self, run_id='run_0001', model=None, template=None, checkout=None, settings=None, output_dir=None): self.model = model self.run_id = run_id self.template = template self.checkout = checkout self.output_dir = output_dir self.staging_dir = self.output_dir + '/' + self.run_id # The following code is mostly lifted from https://github.com/cookiecutter/cookiecutter/blob/master/cookiecutter/main.py # Load cookie-cutter config - see https://cookiecutter.readthedocs.io/en/1.7.0/advanced/user_config.html config_dict = cc_config.get_user_config( config_file=None, default_config=False, ) self._repo_dir, cleanup = cc_repository.determine_repo_dir( template=template, abbreviations=config_dict['abbreviations'], clone_to_dir=config_dict['cookiecutters_dir'], checkout=checkout, no_input=True) context_file = os.path.join(self._repo_dir, 'cookiecutter.json') logger.debug('context_file is {}'.format(context_file)) context = cc_generate.generate_context( context_file=context_file, default_context=config_dict['default_context'], extra_context=settings) print('Default context: (from cookiecutter.json)') pprint.pprint(context['cookiecutter']) self.default_context = context['cookiecutter'] if settings is None: self.settings = {} self.settings['_template'] = template self.settings['_checkout'] = checkout self.settings['model'] = model if not os.path.isdir(self.staging_dir): os.makedirs(self.staging_dir + '/downloads')
def recurse_submodule(template): commit = False # get the cloned repo config_dict = get_user_config() print(config_dict) repo_dir, cleanup = determine_repo_dir( template=template, checkout=None, no_input=True, abbreviations=config_dict['abbreviations'], clone_to_dir=config_dict['cookiecutters_dir'] ) # run a git submodule update print("repo_dir: ", repo_dir) # check any submodule not initialzed result = subprocess.run(["git", "submodule", "status"], cwd=repo_dir , stdout=subprocess.PIPE) output = result.stdout.decode() print(output) if (output[0] != ' ') : subprocess.run(["git", "submodule", "sync", "--recursive"], cwd=repo_dir) subprocess.run(["git", "submodule", "update", "--init", "--recursive"], cwd=repo_dir) # remove this folder if it is empty ( because it was created with uninitialized submodule ) submodule_dir = PROJECT_DIRECTORY+'/meerkat_adminlte' try: os.rmdir(submodule_dir) except OSError as ex: if ex.errno == errno.ENOTEMPTY: print("directory not empty") exit(1) # replay cookiecutter(template,replay=True, overwrite_if_exists=True, output_dir="../") commit = False else : commit = True return commit
def copy_cookiecutter_resume(template_name='cookiecutter-easydata'): config_obj = get_user_config() config_dir = pathlib.Path(config_obj['replay_dir']) src_path = config_dir / f'{template_name}.json' dst_path = f'{template_name}.json' # relative to root of generated project logger.debug(f"Reading cookiecutter replay data from {src_path}") with open(src_path) as f: cookiecutter_opts = json.load(f) logger.info(f"Dumping cookiecutter replay info to {dst_path}") with open(dst_path, 'w') as fw: json.dump(cookiecutter_opts.get('cookiecutter', {}), fw, sort_keys=True, indent=4)
def list_installed_templates(default_config, passed_config_file): """List installed (locally cloned) templates. Use cookiecutter --list-installed.""" config = get_user_config(passed_config_file, default_config) cookiecutter_folder = config.get('cookiecutters_dir') if not os.path.exists(cookiecutter_folder): click.echo( 'Error: Cannot list installed templates. Folder does not exist: ' '{}'.format(cookiecutter_folder)) sys.exit(-1) template_names = [ folder for folder in os.listdir(cookiecutter_folder) if os.path.exists( os.path.join(cookiecutter_folder, folder, 'cookiecutter.json')) ] click.echo('{} installed templates: '.format(len(template_names))) for name in template_names: click.echo(' * {}'.format(name))
def cookiepatch(): parser = argparse.ArgumentParser(description='Tool to apply / create patch from ' 'cookiecutter templates') parser.add_argument('--template', type=str, help='an integer for the accumulator') parser.add_argument('--diff', type=str, nargs='+', help='versions passed for git diff') parser.add_argument('--show', action='store_true', help='Just print diff') args = parser.parse_args() conf_file = None if os.path.exists(CONF_PATH): with open(CONF_PATH) as f: conf_file = json.load(f) if args.template: template = args.template elif conf_file and 'template' in conf_file: template = conf_file['template'] else: template = input('Input template repository url: ') if args.diff: diff = args.diff elif conf_file and 'revision' in conf_file: diff = [conf_file['revision']] else: cur = input('Input template version applied currently: ') to = input('Input version to follow [master]: ') or 'master' diff = [cur, to] no_input = False config_dict = get_user_config(config_file=USER_CONFIG_PATH) parsed_template = expand_abbreviations(template, config_dict) repo_dir = clone(repo_url=parsed_template, clone_to_dir=config_dict['cookiecutters_dir'], checkout=None, no_input=no_input) patch_bytes = subprocess.check_output(['git', 'diff'] + diff + ['--', '{{cookiecutter.repo_name}}'], cwd=repo_dir) patch_str = patch_bytes.decode() context_file = os.path.join(repo_dir, 'cookiecutter.json') context = generate_context( context_file=context_file, default_context=config_dict['default_context'], extra_context={}, ) if conf_file: context['cookiecutter'] = conf_file['variables'] else: # prompt the user to manually configure at the command line. # except when 'no-input' flag is set context['cookiecutter'] = prompt_for_config(context, no_input) rendered = Template(patch_str).render(**context) if args.show: print(rendered) return p = subprocess.Popen(['patch', '-Np1', '--no-backup-if-mismatch'], stdin=subprocess.PIPE, cwd='..') p.communicate(rendered.encode()) # Generate cookiepatcher JSON if len(diff) == 1: rev = 'HEAD' else: rev = diff[-1] revision_bytes = subprocess.check_output(['git', 'rev-parse'] + [rev], cwd=repo_dir) revision_str = revision_bytes.decode().rstrip('\n') json_content = { 'revision': revision_str, 'variables': context['cookiecutter'], 'template': template } with open(CONF_PATH, 'w') as f: json.dump(json_content, f, ensure_ascii=False, indent=2, sort_keys=True)
def test_get_user_config_nonexistent(self): """ Get config from a nonexistent ~/.cookiecutterrc file """ self.assertEqual(config.get_user_config(), config.DEFAULT_CONFIG)
def test_get_user_config_nonexistent(): """ Get config from a nonexistent ~/.cookiecutterrc file """ assert config.get_user_config() == config.DEFAULT_CONFIG
def test_get_user_config_from_path(): """ Get config from a valid ~/.cookiecutterrc file directly """ conf = config.get_user_config(VALID_CONFIG_PATH) assert conf == VALID_CONFIG
try: from ConfigParser import SafeConfigParser except ImportError: from configparser import SafeConfigParser import os from .loaders import ( URLLoader, FileSystemLoader, GitLoader, HgLoader, InstalledLoader ) from .utils import clean_dict, recursive_update from cookiecutter.config import get_user_config COOKIECUTTER_CONFIG = get_user_config() DEFAULTS = { 'support_dir': os.path.expanduser('~/.cookiejar'), 'config_file': os.path.expanduser('~/.cookiejar/cookiejarrc'), 'index': 'https://raw.github.com/fcurella/cookiejar-channel/master/index.json', 'template-loaders': ( URLLoader, GitLoader, HgLoader, FileSystemLoader, InstalledLoader, ), 'templates_dir': os.path.join(COOKIECUTTER_CONFIG['cookiecutters_dir'], ''), }
def test_default_config_from_env_variable( monkeypatch, custom_config_path, custom_config): monkeypatch.setenv('COOKIECUTTER_CONFIG', custom_config_path) user_config = config.get_user_config() assert user_config == custom_config