Skip to content

Commit

Permalink
feat: Add context to template for commands
Browse files Browse the repository at this point in the history
  • Loading branch information
nickderobertis committed Jan 28, 2023
1 parent 3e7b3d4 commit 34a6672
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 13 deletions.
7 changes: 5 additions & 2 deletions flexlate_dev/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from flexlate_dev.ignore import IgnoreSpecification
from flexlate_dev.user_command import UserCommand
from flexlate_dev.user_runner import (
CommandContext,
RunConfiguration,
UserRootRunConfiguration,
UserRunConfiguration,
Expand Down Expand Up @@ -63,9 +64,11 @@ class FullRunConfiguration:
def __post_init__(self):
self._ignore_spec = IgnoreSpecification(ignore_list=self._use_ignore)

def to_jinja_data(self) -> Dict[str, Any]:
def to_jinja_data(self, context: CommandContext) -> Dict[str, Any]:
return dict(
config=self.config.dict(), data=self.data.dict() if self.data else {}
config=self.config.dict(),
data=self.data.dict() if self.data else {},
context=context.dict(),
)

@property
Expand Down
13 changes: 12 additions & 1 deletion flexlate_dev/ext_flexlate.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from pathlib import Path
from typing import List

from flexlate.config import TemplateSource
from flexlate.config_manager import ConfigManager
from flexlate.exc import CannotParseCommitMessageFlexlateTransaction
from flexlate.ext_git import get_commits_between_two_commits
Expand All @@ -13,14 +14,24 @@


def get_render_relative_root_in_template_from_project_path(project_path: Path) -> Path:
ts = _get_template_source_from_project_path(project_path)
return ts.render_relative_root_in_template


def get_template_path_from_project_path(project_path: Path) -> str:
ts = _get_template_source_from_project_path(project_path)
return ts.path


def _get_template_source_from_project_path(project_path: Path) -> TemplateSource:
config_manager = ConfigManager()
config = config_manager.load_config(project_path)
if len(config.template_sources) != 1:
raise ValueError(
"Must have only a single template source to extract relative root"
)
ts = config.template_sources[0]
return ts.render_relative_root_in_template
return ts


def get_non_flexlate_commits_between_commits(
Expand Down
46 changes: 41 additions & 5 deletions flexlate_dev/project_ops.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@
FullRunConfiguration,
)
from flexlate_dev.dirutils import directory_has_files_or_directories
from flexlate_dev.ext_flexlate import get_template_path_from_project_path
from flexlate_dev.gitutils import stage_and_commit_all
from flexlate_dev.render import create_jinja_environment
from flexlate_dev.styles import ACTION_REQUIRED_STYLE, INFO_STYLE, print_styled
from flexlate_dev.user_runner import RunnerHookType, run_user_hook
from flexlate_dev.user_runner import CommandContext, RunnerHookType, run_user_hook

fxt = Flexlate()

Expand Down Expand Up @@ -64,7 +65,19 @@ def init_project() -> str:
out_path = out_root / folder
if not out_path.exists():
out_path.mkdir()
run_user_hook(RunnerHookType.PRE_CHECK, out_path, run_config, config, jinja_env)

context = CommandContext.create(
template_root=template_path,
out_root=out_root,
no_input=no_input,
save=save,
abort_on_conflict=abort_on_conflict,
auto_commit=auto_commit,
)
run_user_hook(
RunnerHookType.PRE_CHECK, out_path, run_config, config, jinja_env, context
)

if out_path.exists() and directory_has_files_or_directories(out_path):
print_styled(f"{out_path} exists, updating project", INFO_STYLE)
update_project(
Expand Down Expand Up @@ -106,7 +119,16 @@ def initialize_project_get_folder(
default_folder_name=default_folder_name,
)
out_path = out_root / folder
run_user_hook(RunnerHookType.POST_INIT, out_path, run_config, config, jinja_env)

context = CommandContext.create(
template_root=template_path,
out_root=out_root,
no_input=no_input,
save=save,
)
run_user_hook(
RunnerHookType.POST_INIT, out_path, run_config, config, jinja_env, context
)
if save:
if run_config.data:
run_config.data.folder_name = folder
Expand Down Expand Up @@ -143,7 +165,19 @@ def run_update_check_was_aborted() -> bool:
)
return True

run_user_hook(RunnerHookType.PRE_UPDATE, out_path, run_config, config, jinja_env)
template_path = Path(get_template_path_from_project_path(out_path))
context = CommandContext.create(
template_root=template_path,
out_root=out_path,
no_input=no_input,
save=save,
abort_on_conflict=abort_on_conflict,
auto_commit=auto_commit,
)
run_user_hook(
RunnerHookType.PRE_UPDATE, out_path, run_config, config, jinja_env, context
)

try:
aborted = run_update_check_was_aborted()
except flexlate_exc.GitRepoDirtyException:
Expand All @@ -166,7 +200,9 @@ def run_update_check_was_aborted() -> bool:
# If update was not successful, skip post update hook and saving config
return

run_user_hook(RunnerHookType.POST_UPDATE, out_path, run_config, config, jinja_env)
run_user_hook(
RunnerHookType.POST_UPDATE, out_path, run_config, config, jinja_env, context
)
if save:
_save_config(out_path, config, run_config)

Expand Down
56 changes: 53 additions & 3 deletions flexlate_dev/user_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,20 +92,66 @@ def get_run_config(self, command: ExternalCLICommandType) -> UserRunConfiguratio
return config


class PathsContext(BaseModel):
template_root: str
out_root: str


class OptionsContext(BaseModel):
no_input: bool
save: bool
abort_on_conflict: Optional[bool] = None
auto_commit: Optional[bool] = None


class CommandContext(BaseModel):
"""
Represents the context of a command that will be provided for the user to
use in templated commands
"""

paths: PathsContext
options: OptionsContext

@classmethod
def create(
cls,
template_root: Path,
out_root: Path,
no_input: bool,
save: bool,
abort_on_conflict: Optional[bool] = None,
auto_commit: Optional[bool] = None,
):
return cls(
paths=PathsContext(
template_root=str(template_root.absolute()),
out_root=str(out_root.absolute()),
),
options=OptionsContext(
no_input=no_input,
save=save,
abort_on_conflict=abort_on_conflict,
auto_commit=auto_commit,
),
)


def run_user_hook(
hook_type: RunnerHookType,
out_path: Path,
run_config: "FullRunConfiguration",
config: "FlexlateDevConfig",
jinja_env: jinja2.Environment,
context: CommandContext,
):
"""
Runs a hook of the given type.
"""
hook: Optional[List[Runnable]] = getattr(run_config.config, hook_type.value)
if hook is not None:
commands = _create_command_list_resolving_references(hook, config)
rendered_commands = _render_commands(commands, run_config, jinja_env)
rendered_commands = _render_commands(commands, run_config, jinja_env, context)
print_styled(f"Running {hook_type.value} commands", INFO_STYLE)
with change_directory_to(out_path):
run_command_or_command_strs(rendered_commands)
Expand Down Expand Up @@ -135,22 +181,26 @@ def _render_commands(
commands: List[UserCommand],
run_config: "FullRunConfiguration",
jinja_env: jinja2.Environment,
context: CommandContext,
) -> List[UserCommand]:
"""
Renders the given commands using the given jinja environment, returning new commands.
"""
return [_render_command(command, run_config, jinja_env) for command in commands]
return [
_render_command(command, run_config, jinja_env, context) for command in commands
]


def _render_command(
command: UserCommand,
run_config: "FullRunConfiguration",
jinja_env: jinja2.Environment,
context: CommandContext,
) -> UserCommand:
"""
Renders the given command using the given jinja environment, returning a new command.
"""
data = run_config.to_jinja_data()
data = run_config.to_jinja_data(context)
update_dict: Dict[str, Any] = {}
for attr in ["run", "name"]:
value = getattr(command, attr)
Expand Down
11 changes: 9 additions & 2 deletions tests/gen_configs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
)
from flexlate_dev.user_command import UserCommand
from flexlate_dev.user_runner import UserRootRunConfiguration, UserRunConfiguration
from tests.config import GENERATED_FILES_DIR, INPUT_CONFIGS_DIR
from tests.config import INPUT_CONFIGS_DIR


def gen_config_with_user_commands():
Expand Down Expand Up @@ -147,8 +147,15 @@ def gen_config_with_templated_commands():
data_config = UserDataConfiguration(data=dict(q1="a1", q2=2), folder_name="a")
data_templated_command = UserCommand(run="touch {{ data.data.q2 }}.txt")
config_templated_command = UserCommand(run="touch {{ config.data_name }}.txt")
context_templated_command = UserCommand(
run="echo '{{ context.paths.template_root }}' > context_path.txt"
)
run_config = UserRootRunConfiguration(
post_init=[data_templated_command, config_templated_command],
post_init=[
data_templated_command,
config_templated_command,
context_templated_command,
],
data_name="my-data",
)
run_configs = {"my-run-config": run_config, **create_default_run_configs()}
Expand Down
1 change: 1 addition & 0 deletions tests/input_files/configs/with_templated_commands.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,4 @@ run_configs:
post_init:
- run: touch {{ data.data.q2 }}.txt
- run: touch {{ config.data_name }}.txt
- run: echo '{{ context.paths.template_root }}' > context_path.txt
3 changes: 3 additions & 0 deletions tests/test_publish.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,9 @@ def test_publish_creates_output_with_templated_commands(copier_one_template_path
assert (project_path / "2.txt").exists()
assert (project_path / "my-data.txt").exists()

context_path_result = (project_path / "context_path.txt").read_text().strip()
assert context_path_result == str(template_path.absolute())


def test_publish_pre_check_can_alter_whether_init_or_update(
copier_one_template_path: Path,
Expand Down

0 comments on commit 34a6672

Please sign in to comment.