diff --git a/README.md b/README.md
index 206d591..9297722 100644
--- a/README.md
+++ b/README.md
@@ -14,6 +14,8 @@
[license]: https://github.com/{{cookiecutter.github_user}}/{{cookiecutter.project_name}}/blob/main/LICENSE
[coveralls]: https://coveralls.io/github/coordt/cookie-composer?branch=master
+https://coordt.github.io/cookie-composer/
+
Cookie composer builds on the [cookie cutter](https://github.com/cookiecutter/cookiecutter) project to generate projects based on one or more cookiecutter templates.
@@ -24,6 +26,52 @@ Cookie composer builds on the [cookie cutter](https://github.com/cookiecutter/co
- Add new capabilities to an existing repository by applying a template
- Apply template updates to the generated project
+## Introduction
+
+Cookie Cutter treats templates like sandwiches. There are templates for hamburgers, clubs, and any other kind of sandwich you can dream up. You might have options and defaults on a template, like `Hold the mustard?[False]:` or `Mustard type [dijon]:`, but those are decided by the template author.
+
+
+
+
+If you look closely at the sandwiches (templates), there is usually many things in common. What if we treated the templates as compositions of other templates:
+
+
+
+You now can manage several smaller and specialized templates that provide functionality. Each template's options will be specific to what that template needs.
+
+
+
+Cookie Composer uses a composition file to describe the layers required, and even override a template's default answers.
+
+```yaml
+template: bottom-bun
+context:
+ toasting_level: light
+ buttered: False
+---
+template: burger
+---
+template: cheese
+context:
+ kind: swiss
+---
+template: bacon
+context:
+ cooking_level: crispy
+---
+template: ketchup
+---
+template: mustard
+context:
+ type: yellow
+---
+template: top-bun
+context:
+ toasting_level: light
+ buttered: False
+```
+
+We have created [a repo of highly composable templates](https://github.com/coordt/cookiecomposer-templates) as examples or reference. However, Cookie Composer is designed to handle any Cookie Cutter template.
## Purpose
diff --git a/cookie_composer/cc_overrides.py b/cookie_composer/cc_overrides.py
index df8e5ee..b664ae9 100644
--- a/cookie_composer/cc_overrides.py
+++ b/cookie_composer/cc_overrides.py
@@ -146,6 +146,6 @@ def prompt_for_config(prompts: dict, existing_config: Context, no_input=False) -
context[key] = val
except UndefinedError as err:
msg = f"Unable to render variable '{key}'"
- raise UndefinedVariableInTemplate(msg, err, context)
+ raise UndefinedVariableInTemplate(msg, err, context) from err
return context
diff --git a/cookie_composer/cli.py b/cookie_composer/cli.py
index 629ec27..f1fcd43 100644
--- a/cookie_composer/cli.py
+++ b/cookie_composer/cli.py
@@ -23,6 +23,40 @@ def cli():
@cli.command()
@click_log.simple_verbosity_option(logger)
+@click.option(
+ "--no-input",
+ is_flag=True,
+ help="Do not prompt for parameters and only use cookiecutter.json file content. "
+ "Defaults to deleting any cached resources and redownloading them.",
+)
+@click.option(
+ "-c",
+ "--checkout",
+ help="branch, tag or commit to checkout after git clone",
+)
+@click.option(
+ "--directory",
+ help="Directory within repo that holds cookiecutter.json file "
+ "for advanced repositories with multi templates in it",
+)
+@click.option(
+ "-f",
+ "--overwrite-if-exists",
+ is_flag=True,
+ help="Overwrite the contents of the output directory if it already exists",
+)
+@click.option(
+ "-s",
+ "--skip-if-file-exists",
+ is_flag=True,
+ help="Skip the files in the corresponding directories if they already exist",
+ default=False,
+)
+@click.option(
+ "--default-config",
+ is_flag=True,
+ help="Do not load a config file. Use the defaults instead",
+)
@click.argument("path_or_url", type=str, required=True)
@click.argument(
"output_dir",
@@ -30,19 +64,79 @@ def cli():
default=lambda: Path.cwd(),
type=click.Path(exists=True, dir_okay=True, file_okay=False, resolve_path=True),
)
-def create(path_or_url: str, output_dir: Optional[Path]):
+def create(
+ no_input: bool,
+ checkout: str,
+ directory: str,
+ overwrite_if_exists: bool,
+ skip_if_file_exists: bool,
+ default_config: bool,
+ path_or_url: str,
+ output_dir: Optional[Path],
+):
"""Create a project from a template or configuration."""
- create_cmd(path_or_url, output_dir)
+ create_cmd(
+ path_or_url,
+ output_dir,
+ no_input,
+ checkout,
+ directory,
+ overwrite_if_exists,
+ skip_if_file_exists,
+ default_config,
+ )
@cli.command()
@click_log.simple_verbosity_option(logger)
-@click.option("--no-input", is_flag=True)
-@click.argument("path_or_url", type=str, required=True)
+@click.option(
+ "--no-input",
+ is_flag=True,
+ help="Do not prompt for parameters and only use cookiecutter.json file content. "
+ "Defaults to deleting any cached resources and redownloading them.",
+)
+@click.option(
+ "-c",
+ "--checkout",
+ help="branch, tag or commit to checkout after git clone",
+)
+@click.option(
+ "--directory",
+ help="Directory within repo that holds cookiecutter.json file "
+ "for advanced repositories with multi templates in it",
+)
+@click.option(
+ "-f",
+ "--overwrite-if-exists",
+ is_flag=True,
+ help="Overwrite the contents of the output directory if it already exists",
+)
+@click.option(
+ "-s",
+ "--skip-if-file-exists",
+ is_flag=True,
+ help="Skip the files in the corresponding directories if they already exist",
+ default=False,
+)
+@click.option(
+ "--default-config",
+ is_flag=True,
+ help="Do not load a config file. Use the defaults instead",
+)
+@click.argument("path_or_url", type=str, required=True, help="The template or composition path or URL")
@click.argument(
"destination", required=False, type=click.Path(exists=True, file_okay=False, writable=True, path_type=Path)
)
-def add(no_input: bool, path_or_url: str, destination: Optional[Path]):
+def add(
+ no_input: bool,
+ checkout: str,
+ directory: str,
+ overwrite_if_exists: bool,
+ skip_if_file_exists: bool,
+ default_config: bool,
+ path_or_url: str,
+ destination: Optional[Path],
+):
"""Add a template or configuration to an existing project."""
destination = destination or Path(".")
try:
diff --git a/cookie_composer/commands/add.py b/cookie_composer/commands/add.py
index 703e997..e74a617 100644
--- a/cookie_composer/commands/add.py
+++ b/cookie_composer/commands/add.py
@@ -29,6 +29,11 @@ def add_cmd(
path_or_url: str,
destination_dir: Optional[Path] = None,
no_input: bool = False,
+ checkout: Optional[str] = None,
+ directory: Optional[str] = None,
+ overwrite_if_exists: bool = False,
+ skip_if_file_exists: bool = False,
+ default_config: bool = False,
):
"""
Add a template or configuration to an existing project.
@@ -37,6 +42,11 @@ def add_cmd(
path_or_url: A URL or string to add the template or configuration
destination_dir: The project directory to add the layer to
no_input: If ``True`` force each layer's ``no_input`` attribute to ``True``
+ checkout: The branch, tag or commit to check out after git clone
+ directory: Directory within repo that holds cookiecutter.json file
+ overwrite_if_exists: Overwrite the contents of the output directory if it already exists
+ skip_if_file_exists: Skip the files in the corresponding directories if they already exist
+ default_config: Do not load a config file. Use the defaults instead
Raises:
GitError: If the destination_dir is not a git repository
@@ -57,7 +67,15 @@ def add_cmd(
addl_composition = read_composition(path_or_url)
logger.info(f"Adding composition {path_or_url} to {output_dir}.")
else:
- tmpl = LayerConfig(template=path_or_url)
+ overwrite_rules = ["*"] if overwrite_if_exists else []
+ tmpl = LayerConfig(
+ template=path_or_url,
+ directory=directory,
+ checkout=checkout,
+ no_input=no_input or default_config,
+ skip_if_file_exists=skip_if_file_exists,
+ overwrite=overwrite_rules,
+ )
addl_composition = Composition(layers=[tmpl])
logger.info(f"Adding template {path_or_url} to {output_dir}.")
diff --git a/cookie_composer/commands/create.py b/cookie_composer/commands/create.py
index e0788f4..334dbd5 100644
--- a/cookie_composer/commands/create.py
+++ b/cookie_composer/commands/create.py
@@ -22,6 +22,11 @@ def create_cmd(
path_or_url: str,
output_dir: Optional[Path] = None,
no_input: bool = False,
+ checkout: Optional[str] = None,
+ directory: Optional[str] = None,
+ overwrite_if_exists: bool = False,
+ skip_if_file_exists: bool = False,
+ default_config: bool = False,
) -> Path:
"""
Generate a new project from a composition file, local template or remote template.
@@ -30,6 +35,11 @@ def create_cmd(
path_or_url: The path or url to the composition file or template
output_dir: Where to generate the project
no_input: If ``True`` force each layer's ``no_input`` attribute to ``True``
+ checkout: The branch, tag or commit to check out after git clone
+ directory: Directory within repo that holds cookiecutter.json file
+ overwrite_if_exists: Overwrite the contents of the output directory if it already exists
+ skip_if_file_exists: Skip the files in the corresponding directories if they already exist
+ default_config: Do not load a config file. Use the defaults instead
Returns:
The path to the generated project.
@@ -39,8 +49,16 @@ def create_cmd(
composition = read_composition(path_or_url)
logger.info(f"Rendering composition {path_or_url} to {output_dir}.")
else:
- tmpl = LayerConfig(template=path_or_url)
- composition = Composition(layers=[tmpl], destination=output_dir)
+ overwrite_rules = ["*"] if overwrite_if_exists else []
+ tmpl = LayerConfig(
+ template=path_or_url,
+ directory=directory,
+ checkout=checkout,
+ no_input=no_input or default_config,
+ skip_if_file_exists=skip_if_file_exists,
+ overwrite=overwrite_rules,
+ )
+ composition = Composition(layers=[tmpl])
logger.info(f"Rendering template {path_or_url} to {output_dir}.")
rendered_layers = render_layers(composition.layers, output_dir, no_input=no_input)
rendered_composition = RenderedComposition(
diff --git a/cookie_composer/composition.py b/cookie_composer/composition.py
index 9ec9eba..99d2c46 100644
--- a/cookie_composer/composition.py
+++ b/cookie_composer/composition.py
@@ -249,7 +249,7 @@ def write_composition(layers: List[LayerConfig], destination: Union[str, Path]):
def write_rendered_composition(composition: RenderedComposition):
"""
- Write the composition file using the rendered layers to the appropriate.
+ Write the composition file using the rendered layers to the appropriate place.
Args:
composition: The rendered composition object to export
diff --git a/docsrc/_static/css/custom.css b/docsrc/_static/css/custom.css
index ab06996..65abbe5 100644
--- a/docsrc/_static/css/custom.css
+++ b/docsrc/_static/css/custom.css
@@ -1,4 +1,4 @@
-.sig-prename.descclassname {
+dt.sig.py > .sig-prename.descclassname {
display: none;
}
.subheading {
diff --git a/docsrc/_static/img/composer-logo.svg b/docsrc/_static/img/composer-logo.svg
new file mode 100644
index 0000000..9c42e0d
--- /dev/null
+++ b/docsrc/_static/img/composer-logo.svg
@@ -0,0 +1 @@
+
diff --git a/docsrc/cli.rst b/docsrc/cli.rst
index 721c227..2c27a44 100644
--- a/docsrc/cli.rst
+++ b/docsrc/cli.rst
@@ -2,5 +2,5 @@ Command-line Interface
======================
.. click:: cookie_composer.cli:cli
- :prog: cookie_composer
+ :prog: cookie-composer
:nested: full
diff --git a/docsrc/conf.py b/docsrc/conf.py
index f66015e..57dd1d8 100644
--- a/docsrc/conf.py
+++ b/docsrc/conf.py
@@ -65,8 +65,31 @@
# -- Options for HTML output -------------------------------------------
+html_title = "Cookie Composer"
+html_logo = "_static/img/composer-logo.svg"
html_theme = "furo"
html_static_path = ["_static"]
html_css_files = [
"css/custom.css",
]
+html_theme_options = {
+ "top_of_page_button": "edit",
+ "navigation_with_keys": True,
+ "footer_icons": [
+ {
+ "name": "GitHub",
+ "url": "https://github.com/coordt/cookie-composer",
+ "html": '",
+ "class": "",
+ },
+ ],
+}