6
0
mirror of https://github.com/ChristianLight/tutor.git synced 2025-01-24 14:08:23 +00:00

feat: cleanup flag (#1086)

This will add a `-c` / `--clean` flag to the save command and ensure that the env directory is deleted if it exists.

Close https://github.com/overhangio/tutor/issues/967
This commit is contained in:
Emad Rad 2024-08-28 01:49:52 +03:30 committed by GitHub
parent 6cdedddd22
commit 46b401690c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 63 additions and 10 deletions

View File

@ -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)

View File

@ -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::

View File

@ -17,6 +17,11 @@ class ConfigTests(unittest.TestCase, TestCommandMixin):
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)

View File

@ -48,7 +48,7 @@ class ConfigTests(unittest.TestCase):
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)))

View File

@ -187,12 +187,18 @@ Press enter when you are ready to continue"""
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)

View File

@ -136,6 +136,13 @@ class ConfigListKeyValParamType(ConfigKeyValParamType):
@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,
@ -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)

View File

@ -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)

View File

@ -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:

View File

@ -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:
"""

View File

@ -7,7 +7,12 @@ from . import env, exceptions, fmt, hooks
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.
@ -15,6 +20,10 @@ def ask_questions(config: Config, run_for_prod: Optional[bool] = None) -> None:
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:
@ -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)