diff --git a/src/octoprint/plugin/__init__.py b/src/octoprint/plugin/__init__.py index dc5700cc..4dde566f 100644 --- a/src/octoprint/plugin/__init__.py +++ b/src/octoprint/plugin/__init__.py @@ -281,6 +281,7 @@ class PluginSettings(object): defaults = dict() self.defaults = dict(plugins=dict()) self.defaults["plugins"][plugin_key] = defaults + self.defaults["plugins"][plugin_key]["_config_version"] = None if get_preprocessors is None: get_preprocessors = dict() diff --git a/src/octoprint/plugin/types.py b/src/octoprint/plugin/types.py index 842e68f9..caabeed4 100644 --- a/src/octoprint/plugin/types.py +++ b/src/octoprint/plugin/types.py @@ -830,6 +830,41 @@ class SettingsPlugin(OctoPrintPlugin): """ return dict(), dict() + def get_settings_version(self): + """ + Retrieves the settings format version of the plugin. + + Use this to have OctoPrint trigger your migration function if it detects an outdated settings version in + config.yaml. + + Returns: + int or None: an int signifying the current settings format, should be incremented by plugins whenever there + are backwards incompatible changes. Returning None here disables the version tracking for the + plugin's configuration. + """ + return None + + def on_settings_migrate(self, target, current): + """ + Called by OctoPrint if it detects that the installed version of the plugin necessitates a higher settings version + than the one currently stored in _config.yaml. Will also be called if the settings data stored in config.yaml + doesn't have version information, in which case the ``current`` parameter will be None. + + Your plugin's implementation should take care of migrating any data by utilizing self._settings. OctoPrint + will take care of saving any changes to disk by calling `self._settings.save()` after returning from this method. + + This method will be called before your plugin's :func:`initialize` method, but with all injections already + having taken place. You can therefore depend on the configuration having been migrated by the time :func:`initialize` + is called. + + Arguments: + target (int): The settings format version the plugin requires, this should always be the same value as + returned by :func:`get_settings_version`. + current (int or None): The settings format version as currently stored in config.yaml. May be None if + no version information can be found. + """ + pass + class EventHandlerPlugin(OctoPrintPlugin): """ diff --git a/src/octoprint/server/__init__.py b/src/octoprint/server/__init__.py index 26687855..d94ba4a4 100644 --- a/src/octoprint/server/__init__.py +++ b/src/octoprint/server/__init__.py @@ -210,7 +210,22 @@ class Server(): set_preprocessors=set_preprocessors) return dict(settings=plugin_settings) + def settings_plugin_pre_init(name, implementation): + if not isinstance(implementation, octoprint.plugin.SettingsPlugin): + return + + settings_version = implementation.get_settings_version() + settings_migrator = implementation.on_settings_migrate + + if settings_version is not None and settings_migrator is not None: + stored_version = implementation._settings.get_int(["_config_version"]) + if stored_version is None or stored_version < settings_version: + settings_migrator(settings_version, stored_version) + implementation._settings.set_int(["_config_version"], settings_version) + implementation._settings.save() + pluginManager.implementation_inject_factories=[octoprint_plugin_inject_factory, settings_plugin_inject_factory] + pluginManager.implementation_pre_inits=[settings_plugin_pre_init] pluginManager.initialize_implementations() pluginManager.log_all_plugins()