def test_expand_abbreviations(): template = 'gh:audreyr/cookiecutter-pypackage' # This is not a valid repo url just yet! # First `main.expand_abbreviations` needs to translate it assert is_repo_url(template) is False expanded_template = expand_abbreviations(template, {}) assert is_repo_url(expanded_template) is True
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 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 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_abbreviation_expansion_not_an_abbreviation(): input_dir = main.expand_abbreviations( 'baz', {'abbreviations': {'foo': 'bar'}} ) assert input_dir == 'baz'
def test_abbreviation_expansion_not_an_abbreviation(): input_dir = main.expand_abbreviations('baz', {'abbreviations': { 'foo': 'bar' }}) assert input_dir == 'baz'
def test_abbreviation_expansion(self): template = main.expand_abbreviations('foo', {'abbreviations': { 'foo': 'bar' }}) self.assertEqual(template, 'bar')
def test_abbreviation_expansion_prefix_not_0_in_braces(): with pytest.raises(IndexError): main.expand_abbreviations('xx:a', {'abbreviations': {'xx': '{1}'}})
def test_abbreviation_expansion_override_builtin(): input_dir = main.expand_abbreviations('gh:a', {'abbreviations': { 'gh': '<{0}>' }}) assert input_dir == '<a>'
def test_abbreviation_expansion_override_builtin(self): input_dir = main.expand_abbreviations( 'gh:a', {'abbreviations': { 'gh': '<{0}>' }}) self.assertEqual(input_dir, '<a>')
def test_abbreviation_expansion_override_builtin(): input_dir = main.expand_abbreviations( 'gh:a', {'abbreviations': {'gh': '<{0}>'}} ) assert input_dir == '<a>'
def test_abbreviation_expansion_not_an_abbreviation(self): input_dir = main.expand_abbreviations( 'baz', {'abbreviations': { 'foo': 'bar' }}) self.assertEqual(input_dir, 'baz')
def test_abbreviation_expansion_builtin(self): input_dir = main.expand_abbreviations('gh:a', {}) self.assertEqual(input_dir, 'https://github.com/a.git')
def test_abbreviation_expansion_prefix(): input_dir = main.expand_abbreviations('xx:a', {'abbreviations': { 'xx': '<{0}>' }}) assert input_dir == '<a>'
def test_abbreviation_expansion_prefix_ignores_suffix(): input_dir = main.expand_abbreviations('xx:a', {'abbreviations': { 'xx': '<>' }}) assert input_dir == '<>'
def test_abbreviation_expansion_prefix(): input_dir = main.expand_abbreviations( 'xx:a', {'abbreviations': {'xx': '<{0}>'}} ) assert input_dir == '<a>'
def Command(path=None, template=None, flavor=None, prompt=False, author=None, license_years=None, module_version=None, website=None): """Initialise a module by populating a directory Will populate specified path (or current path if not specified) with OpenERP/Odoo modules files and directory structure. Usage: %(std_usage)s %(surcmd)s [PATH] [--template=TEMPLATE] [--flavor=FLAVOR] [--prompt|-p] [--author=AUTHOREMAIL] [--license-years=YEARS] [--module-version VERSION] [--website WEBSITE] Options: %(std_options)s PATH If not specified, equivalent to "." --template TEMPLATE Advanced cookiecutter template. --flavor FLAVOR Target template version (ie: 8.0, 7.0) (default: master) --prompt, -p Prompt for all values, except those provided on the command line. And values from config files. --author AUTHOREMAIL Author name and email. Default taken in config. (ie: 'Robert Dubois <*****@*****.**>') --license-years YEARS License applicable years. (ie: 2010-2012, 2013) (defaults to current year) --module-version VERSION Starting version number --website WEBSITE Website of the module. """ cfg = kids.cfg.load() path = os.getcwd() if path is None else os.path.abspath(path) root = common.find_root(path) if root: msg.die("Module %r already initialized." % root) name = os.path.basename(path) output_dir = os.path.dirname(path) if not os.path.isdir(output_dir): msg.die("Destination directory %r doesn't exists." % (output_dir, )) if template is None: template = mdict(cfg).get("init.template", DEFAULT_TEMPLATE) ## Command line provided values cli_values = {"name": name} default_values = {} dct = default_values if author is None else cli_values if author is None: author = mdict(cfg).get("author") if author is None and not os.environ.get("NO_GIT_CONFIG", ""): author = get_git_author(path) if author is None: msg.die( "No author found on command line nor in config files.\n" "Please provide an author with '--author=', for instance:\n\n" " oem init --author='Robert Dubois <*****@*****.**>'" "\n\nor set a default author in your config file before " "running this command:\n\n" " oem config set author 'Robert Dubois <*****@*****.**>'" "\n\nor set a default author in your git config like this:" "\n\n" " git config --global user.name 'Robert Dubois'\n" " git config --global user.email '*****@*****.**'\n") match = AUTHOR_REGEX.search(author) if not match: msg.die("Your value %r for 'author' doesn't match specs.\n" "You should try to match this example:\n\n" " Robert Dubois <*****@*****.**>" % (author, )) match = match.groupdict() dct["author"] = match["name"].strip() dct["email"] = match["email"] if license_years is None: license_years = sact.epoch.Time.now().strftime('%Y') default_values["license_years"] = license_years else: cli_values["license_years"] = license_years if module_version is None: module_version = '0.1' default_values["version"] = module_version else: cli_values["version"] = module_version if website is not None: cli_values["website"] = website ## A big part of the following code comes directly from ## cookiecutter, and I would be glad that some of the ## functionality I implemented would come in a way or another ## in cookiecutter itslef. # Get user config from ~/.cookiecutterrc or equivalent # If no config file, sensible defaults from config.DEFAULT_CONFIG are used config_dict = cc.get_user_config() template = cc.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 = cc.clone(repo_url=template, checkout=flavor, clone_to_dir=config_dict['cookiecutters_dir'], no_input=True) else: # If it's a local repo, no need to clone or copy to your # cookiecutters_dir repo_dir = template context_file = cc.find_cfg_file(repo_dir) default_context = config_dict.get('default_context', {}) if default_values: default_context.update(default_values) context = cc.generate_context( context_file=context_file, default_context=default_context, extra_context=config_dict, ) context = cc.prompt_for_config(context, no_input=False, values=cli_values, only_missing=not prompt, with_optional=prompt) ## XXXvlab: missing no-overwrite mode # Create project from local context and project template. if not os.environ.get("OEM_DRY_RUN", ""): cc.generate_files(repo_dir=repo_dir, context=context, output_dir=output_dir)
def test_abbreviation_expansion_builtin(): input_dir = main.expand_abbreviations( 'gh:a', {} ) assert input_dir == 'https://github.com/a.git'
def test_abbreviation_expansion_prefix_ignores_suffix(): input_dir = main.expand_abbreviations( 'xx:a', {'abbreviations': {'xx': '<>'}} ) assert input_dir == '<>'
def test_abbreviation_expansion_prefix_ignores_suffix(self): input_dir = main.expand_abbreviations('xx:a', {'abbreviations': {'xx': '<>'}}) self.assertEqual(input_dir, '<>')
def test_abbreviation_expansion_prefix_ignores_suffix(self): input_dir = main.expand_abbreviations('xx:a', {'abbreviations': { 'xx': '<>' }}) self.assertEqual(input_dir, '<>')
def test_abbreviation_expansion_builtin(): input_dir = main.expand_abbreviations('gh:a', {}) assert input_dir == 'https://github.com/a.git'
def test_abbreviation_expansion_not_an_abbreviation(self): input_dir = main.expand_abbreviations('baz', {'abbreviations': {'foo': 'bar'}}) self.assertEqual(input_dir, 'baz')
def test_abbreviation_expansion_builtin(self): template = main.expand_abbreviations('gh:a', {}) self.assertEqual(template, 'https://github.com/a.git')
def test_abbreviation_expansion_override_builtin(self): input_dir = main.expand_abbreviations('gh:a', {'abbreviations': {'gh': '<{0}>'}}) self.assertEqual(input_dir, '<a>')
def test_abbreviation_expansion_override_builtin(self): template = main.expand_abbreviations( 'gh:a', {'abbreviations': { 'gh': '<{0}>' }}) self.assertEqual(template, '<a>')
def test_abbreviation_expansion_prefix_ignores_suffix(self): template = main.expand_abbreviations('xx:a', {'abbreviations': { 'xx': '<>' }}) self.assertEqual(template, '<>')
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)