-
Notifications
You must be signed in to change notification settings - Fork 501
feat: Add ENV_SAVED Action #1302
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
tutor/env.py
Outdated
| save_all_from(src, os.path.join(root_env, dst), config) | ||
|
|
||
| upgrade_obsolete(root) | ||
| # print("asdasd") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left over print statement
tutor/env.py
Outdated
|
|
||
| upgrade_obsolete(root) | ||
| # print("asdasd") | ||
| hooks.Actions.ENV_SAVED.do(root_env, config) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docs you add mention that modifying this config variable won't result in the configuration being resaved and that it should be treated as read-only.
However, the config object being passed into this function is handed to this function. I'm not sure what the lifetime is of this variable outside of the function, but presumably, the configuration could be modified by this function and affect later code execution, even if the result isn't saved.
Should we add in a bit of enforcement here, by cloning the object before passing it? If so, might we do an equality check between the config before and after the change and add a warning saying the changes will not be saved?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Kelketek You're right that someone modifying the data here could cause issues so I've made it create a deepcopy. That said, I think it can still cause issues since the object will be cloned once and then passed to all hooks. If hook1 modifies it, hook2 might see the modified version, which isn't ideal. However, I don't see how I could change this in a backwards-compatible way.
Generally, wherever you want the value to be affected, we'd use a filter, and actions are like event handlers that can't affect the event data. If someone is modifying the config then they are asking for trouble.
I thought adding a warning would be overkill; however, it could lead to detecting subtle bugs earlier, so I think it would be nice to add that. I'll add that tomorrow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Kelketek I've added a check for changes to the config and also realized that the simple theme plugin is violating this since it created a copy of the config but not a deep copy so deeper modifications were being persisted. I've fixed that as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome. Glad to see this is already proving helpful :)
Adds an ENV_SAVED Action hook that allows hooking into the save mechanism and running custom code after saving an environemnt.
6225baa to
538a574
Compare
|
Update: Just noticed one thing after initial comment. Please remove the unused function I commented on. Consider the approval conditional on this change and the tests passing. 👍
|
tutor/env.py
Outdated
| ) | ||
|
|
||
|
|
||
| def deepcompare(val1, val2): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@xitij2000 Just saw this when I noticed the CI failing when it found this. It looks like it's unused, so I think it can be removed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Kelketek Yeah, I started implementing a more thorough compare mechanism that would show an actual diff, but realised it isn't worth it currently.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Got it, thanks! I see it's gone now and the build is green, so you're good to go, then :)
|
@regisb Could you have a look at this and see if this is something you'd want in tutor? For our purposes, we are using this to run tasks that add additional content to the environment that can't be effectively managed using templates. Currently we're adding an enhanced save command that internally calls the tutor save command, however this way you can hook into the standard save command, and keep changes in sync when the config changes outside the save command. |
|
Hi @xitij2000, thanks for the PR. I have looked at the code changes and I have some questions like what is the exact usecase for which we are modifying the tutor save command.
If you can provide more context, we might be able to provide a solution for your usecase. |
One use-case can be seen here: https://gitlab.com/opencraft/dev/tutor-contrib-grove/-/blob/main/tutorgrove/plugins/config/commands/grove_config_save.py?ref_type=heads#L59 The use cases I see are as following:
For our simple theme plugin people can configure multiple themes in Here are other example use cases I can envision:
|
|
@xitij2000 Thanks for explaining the use cases. That said, I have concerns about implementing this behavior by hooking into tutor save command via an ENV_SAVED action. As From a design standpoint, this feels like something that should be explicit and opt-in, rather than automatically triggered as part of save command. Personally, I would be more comfortable with this functionality being exposed as a separate command (or at least an explicitly invoked action), so users clearly understand when these extra steps are executed(as we will have no warnings for env folder change). I think the idea itself is valuable — I just don’t think modifying the save command via an added action is the safest place to implement it. Happy to continue discussing alternative approaches if you’d like. |
|
@Faraz32123 Thanks for your detailed feedback! I understand your concerns. I think there are two ways I see to do what we'd like without using the hook and we are already doing one of those. The first is to add another CLI command to do whatever operations we want. For instance, adding The other option would be to evaluate what operations we need to perform on save and whether those can be accomplished with other new hooks or expanding the functionality of hooks themselves. I'll look into this a bit more and continue this conversation. |
|
@Faraz32123 I've looked at the places where we use this feature and for one of them (theming) it would require larger modifications to the way templates work in order to be implemented without this. Re the concerns:
I understand, however this would not change that, it would only allow doing additional operations that a plugin might need to do in order to persist it's own configuration. In fact what we are aiming to do by hooking into save is to persist config in ways that default tutor is not capable of doing, such as pushing config to s3. For example if a plugin is designed to download certain assets from S3 and build them into the platform images, one would expect to configure the plugin's settings, and use launch or save and start to set up the instance with the plugin. However, currently you'd need to run a custom command. The mechanics of this command will be different depending on circumstances. For example if you want to do
I feel that the purpose of every plugin is to introduce side effects. These should be side effects that the user wants, such as installing xblocks or setting up a theme, or even starting a completely new service. Yes they could be surprising and risky if a plugin is poorly designed, however that is true of all plugin hooks.
It is explicit and opt-in. A user needs to install a plugin and enable it. Plugins can already hook into the That said I can understand that the existence of existing hooks might not justify the creation of even more hooks. If this still feels risky then I think the best path for us will be to continue using wrapper commands as we do now. |
|
Just to chime in here: One thing I requested when reviewing this PR is a loud warning if the config was changed by a script in the hook in any way, as using this hook to modify the config is considered a bug. This allows the sanctity of the config to be preserved while still allowing the hooks to read the final result for outside actions. @xitij2000 implemented this in the PR and immediately found a relevant bug that would have violated this principle in our plugin. So we know it works for mitigating unintended config changes. This could be upgraded to an error, if need be, or we could change something about hooks to force deep cloning of the config for the save action, so that any changes would automatically be discarded. My preference is to keep it as a warning, or make it an error as a fallback. |
|
@Kelketek I agree, however I can also imagine it being useful to modify the config before save. For instance, if a plugin's schema changes they might want to automatically migrate the config to the new scheme. That said if we enforce no changes to the config, plugin authors can just print a message asking users to upgrade the config and print out the updated config to copy and paste. |
Adds an ENV_SAVED Action hook that allows hooking into the save mechanism and running custom code after saving an environemnt.