From 38938e892b6086a77c255fdd7c459571e9d7b4b1 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 14 Mar 2022 23:50:09 +0100 Subject: [PATCH 1/8] [FR] [Plugin] Check if all plugins are really installed Fixes #2524 --- InvenTree/plugin/apps.py | 1 + InvenTree/plugin/registry.py | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/InvenTree/plugin/apps.py b/InvenTree/plugin/apps.py index 75063c6b3c..e86ffffc0d 100644 --- a/InvenTree/plugin/apps.py +++ b/InvenTree/plugin/apps.py @@ -30,6 +30,7 @@ class PluginAppConfig(AppConfig): if not registry.is_loading: # this is the first startup + registry.check_plugin_file() registry.collect_plugins() registry.load_plugins() diff --git a/InvenTree/plugin/registry.py b/InvenTree/plugin/registry.py index 40a4a4e2d9..98fb8038a3 100644 --- a/InvenTree/plugin/registry.py +++ b/InvenTree/plugin/registry.py @@ -10,6 +10,7 @@ import pathlib import logging from typing import OrderedDict from importlib import reload +import pkg_resources from django.apps import apps from django.conf import settings @@ -30,6 +31,7 @@ from maintenance_mode.core import get_maintenance_mode, set_maintenance_mode from .integration import IntegrationPluginBase from .helpers import handle_error, log_error, get_plugins, IntegrationPluginError +from InvenTree.config import get_plugin_file logger = logging.getLogger('inventree') @@ -211,6 +213,23 @@ class PluginsRegistry: # Log collected plugins logger.info(f'Collected {len(self.plugin_modules)} plugins!') logger.info(", ".join([a.__module__ for a in self.plugin_modules])) + + def check_plugin_file(self): + """ + Check if all plugins are installed in the current enviroment + """ + # many thanks to Asclepius + # https://stackoverflow.com/questions/16294819/check-if-my-python-has-all-required-packages/45474387#45474387 + + plugin_file = pathlib.Path(get_plugin_file()) + requirements = pkg_resources.parse_requirements(plugin_file.open()) + + for requirement in requirements: + try: + pkg_resources.require(str(requirement)) + except Exception as error: + handle_error(error, log_name='init', do_raise=False) + # endregion # region registry functions From d9d2f3907210f90b371268f963fd3a898cbabf0d Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 15 Mar 2022 01:24:58 +0100 Subject: [PATCH 2/8] just run install --- InvenTree/InvenTree/settings.py | 1 + InvenTree/plugin/apps.py | 2 +- InvenTree/plugin/registry.py | 30 ++++++++++++++++++------------ 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 279225355d..9688f90c12 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -900,3 +900,4 @@ if DEBUG or TESTING: PLUGIN_TESTING = get_setting('PLUGIN_TESTING', TESTING) # are plugins beeing tested? PLUGIN_TESTING_SETUP = get_setting('PLUGIN_TESTING_SETUP', False) # load plugins from setup hooks in testing? PLUGIN_RETRY = get_setting('PLUGIN_RETRY', 5) # how often should plugin loading be tried? +PLUGIN_FILE_CHECKED = False # Was the plugin file checked? diff --git a/InvenTree/plugin/apps.py b/InvenTree/plugin/apps.py index e86ffffc0d..202e1570e2 100644 --- a/InvenTree/plugin/apps.py +++ b/InvenTree/plugin/apps.py @@ -30,7 +30,7 @@ class PluginAppConfig(AppConfig): if not registry.is_loading: # this is the first startup - registry.check_plugin_file() + registry.install_plugin_file() registry.collect_plugins() registry.load_plugins() diff --git a/InvenTree/plugin/registry.py b/InvenTree/plugin/registry.py index 98fb8038a3..3a3e6f70ad 100644 --- a/InvenTree/plugin/registry.py +++ b/InvenTree/plugin/registry.py @@ -8,9 +8,10 @@ Registry for loading and managing multiple plugins at run-time import importlib import pathlib import logging +import os +import subprocess from typing import OrderedDict from importlib import reload -import pkg_resources from django.apps import apps from django.conf import settings @@ -214,21 +215,26 @@ class PluginsRegistry: logger.info(f'Collected {len(self.plugin_modules)} plugins!') logger.info(", ".join([a.__module__ for a in self.plugin_modules])) - def check_plugin_file(self): + def install_plugin_file(self): """ - Check if all plugins are installed in the current enviroment + Make sure all plugins are installed in the current enviroment """ - # many thanks to Asclepius - # https://stackoverflow.com/questions/16294819/check-if-my-python-has-all-required-packages/45474387#45474387 + + if settings.PLUGIN_FILE_CHECKED: + logger.info('Plugin file was already checked') + return plugin_file = pathlib.Path(get_plugin_file()) - requirements = pkg_resources.parse_requirements(plugin_file.open()) - - for requirement in requirements: - try: - pkg_resources.require(str(requirement)) - except Exception as error: - handle_error(error, log_name='init', do_raise=False) + try: + output = str(subprocess.check_output(['pip', 'install', '-r', str(plugin_file.absolute())], cwd=os.path.dirname(settings.BASE_DIR)), 'utf-8') + except subprocess.CalledProcessError as error: # pragma: no cover + logger.error(f'Ran into error while trying to install plugins!\n{str(error)}') + return False + + logger.info(f'plugin requirements were run\n{output}') + + # do not run again + settings.PLUGIN_FILE_CHECKED = True # endregion From e398d648854d2cc838a4980c3f500a8c5f06bcdd Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 15 Mar 2022 01:25:52 +0100 Subject: [PATCH 3/8] just use the setting --- InvenTree/plugin/registry.py | 4 +--- InvenTree/plugin/serializers.py | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/InvenTree/plugin/registry.py b/InvenTree/plugin/registry.py index 3a3e6f70ad..6283320aa2 100644 --- a/InvenTree/plugin/registry.py +++ b/InvenTree/plugin/registry.py @@ -32,7 +32,6 @@ from maintenance_mode.core import get_maintenance_mode, set_maintenance_mode from .integration import IntegrationPluginBase from .helpers import handle_error, log_error, get_plugins, IntegrationPluginError -from InvenTree.config import get_plugin_file logger = logging.getLogger('inventree') @@ -224,9 +223,8 @@ class PluginsRegistry: logger.info('Plugin file was already checked') return - plugin_file = pathlib.Path(get_plugin_file()) try: - output = str(subprocess.check_output(['pip', 'install', '-r', str(plugin_file.absolute())], cwd=os.path.dirname(settings.BASE_DIR)), 'utf-8') + output = str(subprocess.check_output(['pip', 'install', '-r', settings.PLUGIN_FILE], cwd=os.path.dirname(settings.BASE_DIR)), 'utf-8') except subprocess.CalledProcessError as error: # pragma: no cover logger.error(f'Ran into error while trying to install plugins!\n{str(error)}') return False diff --git a/InvenTree/plugin/serializers.py b/InvenTree/plugin/serializers.py index 14c2c11ec6..6965f398f0 100644 --- a/InvenTree/plugin/serializers.py +++ b/InvenTree/plugin/serializers.py @@ -16,7 +16,6 @@ from django.utils import timezone from rest_framework import serializers from plugin.models import PluginConfig, PluginSetting -from InvenTree.config import get_plugin_file from common.serializers import SettingsSerializer @@ -123,7 +122,7 @@ class PluginConfigInstallSerializer(serializers.Serializer): # save plugin to plugin_file if installed successfull if success: - with open(get_plugin_file(), "a") as plugin_file: + with open(settings.PLUGIN_FILE, "a") as plugin_file: plugin_file.write(f'{" ".join(install_name)} # Installed {timezone.now()} by {str(self.context["request"].user)}\n') return ret From 5f9a549918bb5f63d4f9e6a056833d63709b8a2e Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 15 Mar 2022 01:27:14 +0100 Subject: [PATCH 4/8] docs --- InvenTree/plugin/apps.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/InvenTree/plugin/apps.py b/InvenTree/plugin/apps.py index 202e1570e2..6ebaf2215a 100644 --- a/InvenTree/plugin/apps.py +++ b/InvenTree/plugin/apps.py @@ -30,7 +30,11 @@ class PluginAppConfig(AppConfig): if not registry.is_loading: # this is the first startup + + # make sure all plugins are installed registry.install_plugin_file() + + # get plugins and init them registry.collect_plugins() registry.load_plugins() From 51860a4e7bb3dcef8d933defaf918dc873d5e8a8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 15 Mar 2022 01:33:01 +0100 Subject: [PATCH 5/8] run with u flag --- InvenTree/plugin/registry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/plugin/registry.py b/InvenTree/plugin/registry.py index 6283320aa2..7a8e24abd0 100644 --- a/InvenTree/plugin/registry.py +++ b/InvenTree/plugin/registry.py @@ -224,7 +224,7 @@ class PluginsRegistry: return try: - output = str(subprocess.check_output(['pip', 'install', '-r', settings.PLUGIN_FILE], cwd=os.path.dirname(settings.BASE_DIR)), 'utf-8') + output = str(subprocess.check_output(['pip', 'install', '-U', '-r', settings.PLUGIN_FILE], cwd=os.path.dirname(settings.BASE_DIR)), 'utf-8') except subprocess.CalledProcessError as error: # pragma: no cover logger.error(f'Ran into error while trying to install plugins!\n{str(error)}') return False From 8f10ef817c6ecd258ae1ff68d9b7d74bd37e1bca Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 16 Mar 2022 14:47:27 +0100 Subject: [PATCH 6/8] add setting for reload --- InvenTree/common/models.py | 7 +++++++ InvenTree/plugin/apps.py | 6 ++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py index 2b84918e82..766ef16703 100644 --- a/InvenTree/common/models.py +++ b/InvenTree/common/models.py @@ -1002,6 +1002,13 @@ class InvenTreeSetting(BaseInvenTreeSetting): 'validator': bool, }, + 'PLUGIN_ON_STARTUP': { + 'name': _('Check plugins on startup'), + 'description': _('Check that all plugins are installed on startup - enable in container enviroments'), + 'default': False, + 'validator': bool, + 'requires_restart': True, + }, # Settings for plugin mixin features 'ENABLE_PLUGINS_URL': { 'name': _('Enable URL integration'), diff --git a/InvenTree/plugin/apps.py b/InvenTree/plugin/apps.py index 6ebaf2215a..93f1b89b6e 100644 --- a/InvenTree/plugin/apps.py +++ b/InvenTree/plugin/apps.py @@ -30,9 +30,11 @@ class PluginAppConfig(AppConfig): if not registry.is_loading: # this is the first startup + from common.models import InvenTreeSetting - # make sure all plugins are installed - registry.install_plugin_file() + if InvenTreeSetting.get_setting('PLUGIN_ON_STARTUP'): + # make sure all plugins are installed + registry.install_plugin_file() # get plugins and init them registry.collect_plugins() From 3606bd8fe45d7bf43f8009c173d856d7e8ba52d2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 16 Mar 2022 17:03:04 +0100 Subject: [PATCH 7/8] add try to settings test --- InvenTree/plugin/apps.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/InvenTree/plugin/apps.py b/InvenTree/plugin/apps.py index 93f1b89b6e..ff03fdd04e 100644 --- a/InvenTree/plugin/apps.py +++ b/InvenTree/plugin/apps.py @@ -30,11 +30,13 @@ class PluginAppConfig(AppConfig): if not registry.is_loading: # this is the first startup - from common.models import InvenTreeSetting - - if InvenTreeSetting.get_setting('PLUGIN_ON_STARTUP'): - # make sure all plugins are installed - registry.install_plugin_file() + try: + from common.models import InvenTreeSetting + if InvenTreeSetting.get_setting('PLUGIN_ON_STARTUP'): + # make sure all plugins are installed + registry.install_plugin_file() + except: + pass # get plugins and init them registry.collect_plugins() From eef51600c50f179efbc83e2fab483a29e7d8998e Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 20 Mar 2022 18:10:42 +0100 Subject: [PATCH 8/8] do not write to db --- InvenTree/plugin/apps.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/plugin/apps.py b/InvenTree/plugin/apps.py index ff03fdd04e..c611f6f8c1 100644 --- a/InvenTree/plugin/apps.py +++ b/InvenTree/plugin/apps.py @@ -32,7 +32,7 @@ class PluginAppConfig(AppConfig): # this is the first startup try: from common.models import InvenTreeSetting - if InvenTreeSetting.get_setting('PLUGIN_ON_STARTUP'): + if InvenTreeSetting.get_setting('PLUGIN_ON_STARTUP', create=False): # make sure all plugins are installed registry.install_plugin_file() except: