Skip to content

Commit

Permalink
feat: cleanup flag (#1086)
Browse files Browse the repository at this point in the history
This will add a `-c` / `--clean` flag to the save command and ensure that the env directory is deleted if it exists.

Close #967
  • Loading branch information
CodeWithEmad committed Aug 27, 2024
1 parent 6cdeddd commit 46b4016
Show file tree
Hide file tree
Showing 10 changed files with 63 additions and 10 deletions.
1 change: 1 addition & 0 deletions changelog.d/20240629_120748_codewithemad_cleanup_flag.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- [Feature] Added `-c` or `--clean` option to tutor config save: For plugin developers and advanced users, this option cleans the `env/` folder before saving, ensuring a fresh environment for testing and development. (by @CodeWithEmad)
2 changes: 1 addition & 1 deletion docs/tutorials/edx-platform.rst
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ You should then re-build the "openedx" Docker image to pick up your changes::

tutor images build openedx-dev

Then, whenever you run ``tutor dev start``, the "lms" and "cms" container should automatically hot-reload your changes.
Then, whenever you run ``tutor dev start``, the "lms" and "cms" containers should automatically hot-reload your changes.

To push your changes in production, you should do the same with ``tutor local`` and the "openedx" image::

Expand Down
5 changes: 5 additions & 0 deletions tests/commands/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ def test_config_save(self) -> None:
self.assertFalse(result.exception)
self.assertEqual(0, result.exit_code)

def test_config_save_cleanup_env_dir(self) -> None:
result = self.invoke(["config", "save", "-c"])
self.assertFalse(result.exception)
self.assertEqual(0, result.exit_code)

def test_config_save_interactive(self) -> None:
result = self.invoke(["config", "save", "-i"])
self.assertFalse(result.exception)
Expand Down
2 changes: 1 addition & 1 deletion tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def mock_prompt(*_args: None, **kwargs: str) -> str:
with patch.object(click, "prompt", new=mock_prompt):
with patch.object(click, "confirm", new=mock_prompt):
config = tutor_config.load_minimal(rootdir)
interactive.ask_questions(config)
interactive.ask_questions(config, rootdir)

self.assertIn("MYSQL_ROOT_PASSWORD", config)
self.assertEqual(8, len(get_typed(config, "MYSQL_ROOT_PASSWORD", str)))
Expand Down
12 changes: 9 additions & 3 deletions tutor/commands/compose.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,12 +187,18 @@ def interactive_upgrade(


def interactive_configuration(
context: click.Context, interactive: bool, run_for_prod: t.Optional[bool] = None
context: click.Context,
interactive: bool,
run_for_prod: t.Optional[bool] = None,
) -> None:
click.echo(fmt.title("Interactive platform configuration"))
config = tutor_config.load_minimal(context.obj.root)
if interactive:
interactive_config.ask_questions(config, run_for_prod=run_for_prod)
click.echo(fmt.title("Interactive platform configuration"))
interactive_config.ask_questions(
config,
context.obj.root,
run_for_prod=run_for_prod,
)
tutor_config.save_config_file(context.obj.root, config)
config = tutor_config.load_full(context.obj.root)
tutor_env.save(context.obj.root, config)
Expand Down
12 changes: 11 additions & 1 deletion tutor/commands/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,13 @@ def _candidate_config_items(self) -> t.Iterable[tuple[str, ConfigValue]]:
@click.option(
"-e", "--env-only", "env_only", is_flag=True, help="Skip updating config.yml"
)
@click.option(
"-c",
"--clean",
"clean_env",
is_flag=True,
help="Remove everything in the env directory before save",
)
@click.pass_obj
def save(
context: Context,
Expand All @@ -145,10 +152,13 @@ def save(
remove_vars: list[tuple[str, t.Any]],
unset_vars: list[str],
env_only: bool,
clean_env: bool,
) -> None:
config = tutor_config.load_minimal(context.root)
if interactive:
interactive_config.ask_questions(config)
interactive_config.ask_questions(config, context.root, clean_env_prompt=True)
if clean_env:
env.delete_env_dir(context.root)
if set_vars:
for key, value in set_vars:
config[key] = env.render_unknown(config, value)
Expand Down
4 changes: 2 additions & 2 deletions tutor/commands/k8s.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,10 +225,10 @@ def launch(context: click.Context, non_interactive: bool) -> None:
from_release=tutor_env.get_env_release(context.obj.root),
)

click.echo(fmt.title("Interactive platform configuration"))
config = tutor_config.load_minimal(context.obj.root)
if not non_interactive:
interactive_config.ask_questions(config, run_for_prod=True)
click.echo(fmt.title("Interactive platform configuration"))
interactive_config.ask_questions(config, context.obj.root, run_for_prod=True)
tutor_config.save_config_file(context.obj.root, config)
config = tutor_config.load_full(context.obj.root)
tutor_env.save(context.obj.root, config)
Expand Down
2 changes: 1 addition & 1 deletion tutor/commands/plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def plugins_command() -> None:

@click.command(
short_help="Print the location of file-based plugins",
help=f"""Print the location of yaml-based plugins: nboth python v1 and yaml v0 plugins. This location can be manually
help=f"""Print the location of yaml-based plugins: both python v1 and yaml v0 plugins. This location can be manually
defined by setting the {PLUGINS_ROOT_ENV_VAR_NAME} environment variable""",
)
def printroot() -> None:
Expand Down
14 changes: 14 additions & 0 deletions tutor/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,20 @@ def root_dir(root: str) -> str:
return os.path.abspath(root)


def delete_env_dir(root: str) -> None:
env_path = base_dir(root)

try:
shutil.rmtree(env_path)
fmt.echo_alert(f"Removed existing Tutor environment at: {env_path}")
except PermissionError:
raise exceptions.TutorError(
f"Permission Denied while trying to remove existing Tutor environment at: {env_path}"
)
except FileNotFoundError:
fmt.echo_info(f"No existing Tutor environment to remove at: {env_path}")


@hooks.Actions.PLUGIN_UNLOADED.add()
def _delete_plugin_templates(plugin: str, root: str, _config: Config) -> None:
"""
Expand Down
19 changes: 18 additions & 1 deletion tutor/interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,23 @@
from .types import Config, get_typed


def ask_questions(config: Config, run_for_prod: Optional[bool] = None) -> None:
def ask_questions(
config: Config,
root: str,
run_for_prod: Optional[bool] = None,
clean_env_prompt: bool = False,
) -> None:
"""
Interactively ask questions to collect configuration values from the user.
Arguments:
config: Existing (or minimal) configuration. Modified in-place.
run_for_prod: Whether platform should be configured for production.
If None, then ask the user.
clean_env_prompt: Whether to show the clean environment prompt before running.
defaults to False.
Returns:
None
"""
defaults = tutor_config.get_defaults()
if run_for_prod is None:
Expand Down Expand Up @@ -148,6 +157,14 @@ def ask_questions(config: Config, run_for_prod: Optional[bool] = None) -> None:
config,
defaults,
)
if clean_env_prompt:
run_clean = click.confirm(
fmt.question("Remove existing Tutor environment directory?"),
prompt_suffix=" ",
default=True,
)
if run_clean:
env.delete_env_dir(root)

hooks.Actions.CONFIG_INTERACTIVE.do(config)

Expand Down

0 comments on commit 46b4016

Please sign in to comment.