Skip to content

Commit

Permalink
Add workspace context variables to templates
Browse files Browse the repository at this point in the history
  • Loading branch information
jacksmith15 committed Dec 1, 2021
1 parent d268d30 commit f429ca0
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 11 deletions.
18 changes: 18 additions & 0 deletions docs/docs/templates.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,21 @@ Created new project my-new-library at libs/my-new-library.
```

> 💡 Ensure cookiecutter is installed with `pip install workspace-cli[cookiecutter]`.
## Default variables

Some additional variables can be automatically made available to project templates:

- `{{ workspace_project_path }}` is the relative path of the project from the workspace root.
- `{{ workspace_project_name }}` is the name of the project in the workspace.

To make these available, include them in the template's `cookiecutter.json` file, e.g.

```json
{
"workspace_project_path": null,
"workspace_project_name": null
}
```

> ⚠️ Encoding these in a project creates coupling between the project and the workspace.
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@
"project_slug": null,
"project_name": "{{ cookiecutter.project_slug | replace('-', ' ') | title }}",
"package_name": "{{ cookiecutter.project_slug | replace('-', '_') }}",
"python_version": null
"python_version": null,
"workspace_project_path": null,
"workspace_project_name": null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"path": "{{ cookiecutter.workspace_project_path }}",
"name": "{{ cookiecutter.workspace_project_name }}"
}
15 changes: 12 additions & 3 deletions tests/cli/commands/test_new.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import json
import os
import subprocess
from pathlib import Path

import pytest

Expand Down Expand Up @@ -101,13 +103,20 @@ def should_create_project_from_template():
popen.stdin.flush()
output = popen.communicate()[0].decode().strip()
# AND the exit code is 0
assert popen.returncode == 0
assert popen.returncode == 0, output

# AND the expected message is displayed
assert output == "Created new project my-library at libs/my-library."
# AND the project is created with detected type
project = Workspace.from_path(WORKSPACE_ROOT)
assert project.projects["my-library"].type == "pipenv"
workspace = Workspace.from_path(WORKSPACE_ROOT)
assert workspace.projects["my-library"].type == "pipenv"

# AND additional context about the project was available to the template
context = json.loads((WORKSPACE_ROOT / "libs/my-library/context.json").read_text())
assert context == {
"path": "libs/my-library",
"name": "my-library",
}

@staticmethod
def should_fail_when_non_existing_template_is_specified():
Expand Down
8 changes: 5 additions & 3 deletions workspace/cli/commands/new.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def new(path: Path, type: str = None, name: str = None, template: str = None):
)

try:
_initialise_template(workspace, path=path.resolve(), type=type, template=template)
_initialise_template(workspace, path=path.resolve(), type=type, name=name, template=template)
type = type or utils.detect_type(workspace, path.resolve())
if not type:
valid_type_list = "\n".join([f" - <b>{name}</b>" for name in get_adapters()])
Expand Down Expand Up @@ -107,10 +107,12 @@ def new(path: Path, type: str = None, name: str = None, template: str = None):
sys.exit(0)


def _initialise_template(workspace: Workspace, path: Path, type: str = None, template: str = None):
def _initialise_template(workspace: Workspace, path: Path, name: str, type: str = None, template: str = None):
"""Initialise the project directory."""
if template:
workspace.templates.create(template, path)
workspace.templates.create(
template, path=path, name=name
)
else:
if not type:
raise WorkspaceCLIError("<e>Must specify at least one of <b>--type</b> and <b>--template</b> options.</e>")
Expand Down
30 changes: 26 additions & 4 deletions workspace/core/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def __contains__(self, template_name: str) -> bool:
except KeyError:
return False

def create(self, template_name: str, path: Path):
def create(self, template_name: str, path: Path, name: str):
try:
template_path = self[template_name]
except KeyError:
Expand All @@ -56,15 +56,31 @@ def create(self, template_name: str, path: Path):
raise WorkspaceTemplateError(
"Cookiecutter is not installed - run 'pip install workspace-cli[cookiecutter]'."
)
context = _build_context(template_path, path, self._build_project_context(path, name))
cookiecutter(
template=str(template_path),
output_dir=path.parent,
extra_context=_build_context(template_path, path),
extra_context=context,
no_input=True,
)

def _build_project_context(self, path: Path, name: str):
"""These are additional context variables made available in project templates.
def _build_context(template_path: Path, target_directory: Path) -> str:
They allow templating the relative path of the project from the workspace root, as
well as the project name.
"""
return {
"workspace_project_path": str(path.relative_to(self.workspace.path)),
"workspace_project_name": name,
}


def _build_context(
template_path: Path,
target_directory: Path,
default_context: dict,
) -> dict:
"""Build the cookiecutter context.
We need to combine the target directory and prompt the user for all other fields. By
Expand All @@ -83,7 +99,13 @@ def _build_context(template_path: Path, target_directory: Path) -> str:
context = generate_context(
context_file=template_path / "cookiecutter.json",
)
extra_context = _prompt_for_config(context, {directory_field: target_directory.name})
extra_context = _prompt_for_config(
context,
{
**default_context,
**{directory_field: target_directory.name},
}
)

# Return the rendered context
return extra_context
Expand Down

0 comments on commit f429ca0

Please sign in to comment.