mirror of
https://github.com/ChristianLight/tutor.git
synced 2025-01-26 22:48:25 +00:00
Add config render
command
This is going to be useful for using custom themes with user-defined variables.
This commit is contained in:
parent
c4c12b0ab8
commit
72e23f3f96
@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
Note: Breaking changes between versions are indicated by "💥".
|
Note: Breaking changes between versions are indicated by "💥".
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
|
||||||
|
- [Feature] Add `config render` command
|
||||||
|
|
||||||
## 3.11.0 (2020-01-14)
|
## 3.11.0 (2020-01-14)
|
||||||
|
|
||||||
- [Feature] Add support for simple, YAML-based plugins
|
- [Feature] Add support for simple, YAML-based plugins
|
||||||
|
@ -25,6 +25,14 @@ class EnvTests(unittest.TestCase):
|
|||||||
self.assertIn(template_name, renderer.environment.loader.list_templates())
|
self.assertIn(template_name, renderer.environment.loader.list_templates())
|
||||||
self.assertNotIn(template_name, templates)
|
self.assertNotIn(template_name, templates)
|
||||||
|
|
||||||
|
def test_is_binary_file(self):
|
||||||
|
self.assertTrue(env.is_binary_file("/home/somefile.ico"))
|
||||||
|
|
||||||
|
def test_find_path(self):
|
||||||
|
renderer = env.Renderer({}, [env.TEMPLATES_ROOT])
|
||||||
|
path = renderer.find_path("local/docker-compose.yml")
|
||||||
|
self.assertTrue(os.path.exists(path))
|
||||||
|
|
||||||
def test_pathjoin(self):
|
def test_pathjoin(self):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
"/tmp/env/target/dummy", env.pathjoin("/tmp", "target", "dummy")
|
"/tmp/env/target/dummy", env.pathjoin("/tmp", "target", "dummy")
|
||||||
|
@ -59,6 +59,28 @@ def save(context, interactive, set_, unset):
|
|||||||
env.save(context.root, config)
|
env.save(context.root, config)
|
||||||
|
|
||||||
|
|
||||||
|
@click.command(help="Render a template folder with eventual extra configuration files")
|
||||||
|
@click.option(
|
||||||
|
"-x",
|
||||||
|
"--extra-config",
|
||||||
|
"extra_configs",
|
||||||
|
multiple=True,
|
||||||
|
type=click.Path(exists=True, resolve_path=True, dir_okay=False),
|
||||||
|
help="Load extra configuration file (can be used multiple times)",
|
||||||
|
)
|
||||||
|
@click.argument("src", type=click.Path(exists=True, resolve_path=True))
|
||||||
|
@click.argument("dst")
|
||||||
|
@click.pass_obj
|
||||||
|
def render(context, extra_configs, src, dst):
|
||||||
|
config = tutor_config.load(context.root)
|
||||||
|
for extra_config in extra_configs:
|
||||||
|
tutor_config.merge(config, tutor_config.load_file(extra_config), force=True)
|
||||||
|
|
||||||
|
renderer = env.Renderer(config, [src])
|
||||||
|
renderer.render_all_to(dst)
|
||||||
|
fmt.echo_info("Templates rendered to {}".format(dst))
|
||||||
|
|
||||||
|
|
||||||
@click.command(help="Print the project root")
|
@click.command(help="Print the project root")
|
||||||
@click.pass_obj
|
@click.pass_obj
|
||||||
def printroot(context):
|
def printroot(context):
|
||||||
@ -77,5 +99,6 @@ def printvalue(context, key):
|
|||||||
|
|
||||||
|
|
||||||
config_command.add_command(save)
|
config_command.add_command(save)
|
||||||
|
config_command.add_command(render)
|
||||||
config_command.add_command(printroot)
|
config_command.add_command(printroot)
|
||||||
config_command.add_command(printvalue)
|
config_command.add_command(printvalue)
|
||||||
|
@ -56,6 +56,11 @@ def load_defaults():
|
|||||||
return serialize.load(env.read("config.yml"))
|
return serialize.load(env.read("config.yml"))
|
||||||
|
|
||||||
|
|
||||||
|
def load_file(path):
|
||||||
|
with open(path) as f:
|
||||||
|
return serialize.load(f.read())
|
||||||
|
|
||||||
|
|
||||||
def load_current(root, defaults):
|
def load_current(root, defaults):
|
||||||
"""
|
"""
|
||||||
Load the configuration currently stored on disk.
|
Load the configuration currently stored on disk.
|
||||||
|
36
tutor/env.py
36
tutor/env.py
@ -14,6 +14,7 @@ from .__about__ import __version__
|
|||||||
|
|
||||||
TEMPLATES_ROOT = pkg_resources.resource_filename("tutor", "templates")
|
TEMPLATES_ROOT = pkg_resources.resource_filename("tutor", "templates")
|
||||||
VERSION_FILENAME = "version"
|
VERSION_FILENAME = "version"
|
||||||
|
BIN_FILE_EXTENSIONS = [".ico", ".ttf", ".png", ".jpg"]
|
||||||
|
|
||||||
|
|
||||||
class Renderer:
|
class Renderer:
|
||||||
@ -38,6 +39,7 @@ class Renderer:
|
|||||||
|
|
||||||
def __init__(self, config, template_roots):
|
def __init__(self, config, template_roots):
|
||||||
self.config = deepcopy(config)
|
self.config = deepcopy(config)
|
||||||
|
self.template_roots = template_roots
|
||||||
|
|
||||||
# Create environment
|
# Create environment
|
||||||
environment = jinja2.Environment(
|
environment = jinja2.Environment(
|
||||||
@ -68,6 +70,13 @@ class Renderer:
|
|||||||
"""
|
"""
|
||||||
yield from self.iter_templates_in(subdir + "/")
|
yield from self.iter_templates_in(subdir + "/")
|
||||||
|
|
||||||
|
def find_path(self, path):
|
||||||
|
for templates_root in self.template_roots:
|
||||||
|
full_path = os.path.join(templates_root, path)
|
||||||
|
if os.path.exists(full_path):
|
||||||
|
return full_path
|
||||||
|
raise ValueError("Template path does not exist")
|
||||||
|
|
||||||
def patch(self, name, separator="\n", suffix=""):
|
def patch(self, name, separator="\n", suffix=""):
|
||||||
"""
|
"""
|
||||||
Render calls to {{ patch("...") }} in environment templates from plugin patches.
|
Render calls to {{ patch("...") }} in environment templates from plugin patches.
|
||||||
@ -93,11 +102,17 @@ class Renderer:
|
|||||||
return self.__render(template)
|
return self.__render(template)
|
||||||
|
|
||||||
def render_file(self, path):
|
def render_file(self, path):
|
||||||
|
if is_binary_file(path):
|
||||||
|
# Don't try to render binary files
|
||||||
|
with open(self.find_path(path), "rb") as f:
|
||||||
|
return f.read()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
template = self.environment.get_template(path)
|
template = self.environment.get_template(path)
|
||||||
except Exception:
|
except Exception:
|
||||||
fmt.echo_error("Error loading template " + path)
|
fmt.echo_error("Error loading template " + path)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
try:
|
try:
|
||||||
return self.__render(template)
|
return self.__render(template)
|
||||||
except (jinja2.exceptions.TemplateError, exceptions.TutorError):
|
except (jinja2.exceptions.TemplateError, exceptions.TutorError):
|
||||||
@ -107,6 +122,12 @@ class Renderer:
|
|||||||
fmt.echo_error("Unknown error rendering template " + path)
|
fmt.echo_error("Unknown error rendering template " + path)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
def render_all_to(self, root):
|
||||||
|
for template in self.iter_templates_in():
|
||||||
|
rendered = self.render_file(template)
|
||||||
|
dst = os.path.join(root, template)
|
||||||
|
write_to(rendered, dst)
|
||||||
|
|
||||||
def __render(self, template):
|
def __render(self, template):
|
||||||
try:
|
try:
|
||||||
return template.render(**self.config)
|
return template.render(**self.config)
|
||||||
@ -165,8 +186,14 @@ def save_all_from(prefix, root, config):
|
|||||||
|
|
||||||
|
|
||||||
def write_to(content, path):
|
def write_to(content, path):
|
||||||
|
"""
|
||||||
|
Content can be either str or bytes.
|
||||||
|
"""
|
||||||
|
open_mode = "w"
|
||||||
|
if isinstance(content, bytes):
|
||||||
|
open_mode += "b"
|
||||||
utils.ensure_file_directory_exists(path)
|
utils.ensure_file_directory_exists(path)
|
||||||
with open(path, "w") as of:
|
with open(path, open_mode) as of:
|
||||||
of.write(content)
|
of.write(content)
|
||||||
|
|
||||||
|
|
||||||
@ -262,10 +289,15 @@ def is_part_of_env(path):
|
|||||||
is_excluded = False
|
is_excluded = False
|
||||||
is_excluded = is_excluded or basename.startswith(".") or basename.endswith(".pyc")
|
is_excluded = is_excluded or basename.startswith(".") or basename.endswith(".pyc")
|
||||||
is_excluded = is_excluded or basename == "__pycache__"
|
is_excluded = is_excluded or basename == "__pycache__"
|
||||||
is_excluded = is_excluded or "partials" in parts
|
is_excluded = is_excluded or "partials" in parts or ".git" in parts
|
||||||
return not is_excluded
|
return not is_excluded
|
||||||
|
|
||||||
|
|
||||||
|
def is_binary_file(path):
|
||||||
|
ext = os.path.splitext(path)[1]
|
||||||
|
return ext in BIN_FILE_EXTENSIONS
|
||||||
|
|
||||||
|
|
||||||
def template_path(*path, templates_root=TEMPLATES_ROOT):
|
def template_path(*path, templates_root=TEMPLATES_ROOT):
|
||||||
"""
|
"""
|
||||||
Return the template file's absolute path.
|
Return the template file's absolute path.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user