Skip to content

Commit

Permalink
Merge pull request #11 from coordt/doc-updates
Browse files Browse the repository at this point in the history
Documentation updates
  • Loading branch information
coordt authored Sep 6, 2022
2 parents 21e0f15 + a95d6c6 commit 7b7ceb8
Show file tree
Hide file tree
Showing 10 changed files with 214 additions and 12 deletions.
48 changes: 48 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/

<!-- end-badges -->

Cookie composer builds on the [cookie cutter](https://github.com/cookiecutter/cookiecutter) project to generate projects based on one or more cookiecutter templates.
Expand All @@ -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.


<img src="https://raw.githubusercontent.com/coordt/cookie-composer/master/docsrc/_static/img/sandwiches.png" alt="Templates are treated like finished sandwiches" style="zoom:50%;" />

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:

<img src="https://raw.githubusercontent.com/coordt/cookie-composer/master/docsrc/_static/img/compositions.png" alt="Sandwiches as a composition of layers" style="zoom:50%;" />

You now can manage several smaller and specialized templates that provide functionality. Each template's options will be specific to what that template needs.

<img src="https://raw.githubusercontent.com/coordt/cookie-composer/master/docsrc/_static/img/layers.png" alt="Templates broken out as layers on a sandwich" style="zoom:50%;" />

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
Expand Down
2 changes: 1 addition & 1 deletion cookie_composer/cc_overrides.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
104 changes: 99 additions & 5 deletions cookie_composer/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,120 @@ 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",
required=False,
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:
Expand Down
20 changes: 19 additions & 1 deletion cookie_composer/commands/add.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand All @@ -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}.")

Expand Down
22 changes: 20 additions & 2 deletions cookie_composer/commands/create.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand All @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion cookie_composer/composition.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docsrc/_static/css/custom.css
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.sig-prename.descclassname {
dt.sig.py > .sig-prename.descclassname {
display: none;
}
.subheading {
Expand Down
1 change: 1 addition & 0 deletions docsrc/_static/img/composer-logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion docsrc/cli.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ Command-line Interface
======================

.. click:: cookie_composer.cli:cli
:prog: cookie_composer
:prog: cookie-composer
:nested: full
23 changes: 23 additions & 0 deletions docsrc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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": '<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="0 0 16 16">'
'<path fill-rule="evenodd" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 '
"7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-"
".23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 "
"1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 "
"0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 "
"2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 "
"1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 "
'2.2 0 .21.15.46.55.38A8.013 8.013 0 0 0 16 8c0-4.42-3.58-8-8-8z"></path>'
"</svg>",
"class": "",
},
],
}

0 comments on commit 7b7ceb8

Please sign in to comment.