]> git.basschouten.com Git - openhab-addons.git/commitdiff
[jrubyscripting] check if gems are installed before installing them (#13151)
authorCody Cutrer <cody@cutrer.us>
Sun, 7 Aug 2022 16:02:34 +0000 (10:02 -0600)
committerGitHub <noreply@github.com>
Sun, 7 Aug 2022 16:02:34 +0000 (18:02 +0200)
* [jrubyscripting] check if gems are installed before installing them

this avoids unecessary downloads and overwrites, or even more importantly
errors if the GEM_HOME is not writable by the openhab user.

this also adds a configuration option for if you want to check for
updates every time configuration is applied (or openhab restarts). this
is useful if your OpenHAB system can't access the internet regularly.

Signed-off-by: Cody Cutrer <cody@cutrer.us>
bundles/org.openhab.automation.jrubyscripting/README.md
bundles/org.openhab.automation.jrubyscripting/src/main/java/org/openhab/automation/jrubyscripting/internal/JRubyScriptEngineConfiguration.java
bundles/org.openhab.automation.jrubyscripting/src/main/resources/OH-INF/config/config.xml
bundles/org.openhab.automation.jrubyscripting/src/main/resources/OH-INF/i18n/jruby.properties

index eeea0cb3882ec1e74a9e7db12924aa9390795dc4..6cdaaa104e4cb69bfbd63d4c6128a8ea479f7acf 100644 (file)
@@ -16,6 +16,7 @@ Alternatively, JRuby configuration parameters may be set by creating a `jruby.cf
 | org.openhab.automation.jrubyscripting:local_variables | transient                               | Defines how variables are shared between Ruby and Java. See [this](https://github.com/jruby/jruby/wiki/RedBridge#local-variable-behavior-options) for options and details                                   |
 | org.openhab.automation.jrubyscripting:gems            |                                         | A comma separated list of [Ruby Gems](https://rubygems.org/) to install.                                                                                                                                    |
 | org.openhab.automation.jrubyscripting:require         |                                         | A comma separated list of script names to be required by the JRuby Scripting Engine at the beginning of user scripts.                                                                                       |
+| org.openhab.automation.jrubyscripting:check_update    | true                                    | Check RubyGems for updates to the above gems when OpenHAB starts or JRuby settings are changed. Otherwise it will try to fulfil the requirements with locally installed gems, and you can manage them yourself with an external Ruby by setting the same GEM_HOME. |
 
 ## Ruby Gems
 
index d9f0e29450b1ae6f64312c976bb67ca63fabcd13..f0a9a24a025a7c3a6503848a3596afd411db382d 100644 (file)
@@ -52,6 +52,7 @@ public class JRubyScriptEngineConfiguration {
     private static final String RUBYLIB = "rubylib";
     private static final String GEMS = "gems";
     private static final String REQUIRE = "require";
+    private static final String CHECK_UPDATE = "check_update";
 
     // Map of configuration parameters
     private static final Map<String, OptionalConfigurationElement> CONFIGURATION_PARAMETERS = Map.ofEntries(
@@ -74,7 +75,10 @@ public class JRubyScriptEngineConfiguration {
             Map.entry(GEMS, new OptionalConfigurationElement.Builder(OptionalConfigurationElement.Type.GEM).build()),
 
             Map.entry(REQUIRE,
-                    new OptionalConfigurationElement.Builder(OptionalConfigurationElement.Type.REQUIRE).build()));
+                    new OptionalConfigurationElement.Builder(OptionalConfigurationElement.Type.REQUIRE).build()),
+
+            Map.entry(CHECK_UPDATE,
+                    new OptionalConfigurationElement.Builder(OptionalConfigurationElement.Type.CHECK_UPDATE).build()));
 
     private static final Map<OptionalConfigurationElement.Type, List<OptionalConfigurationElement>> CONFIGURATION_TYPE_MAP = CONFIGURATION_PARAMETERS
             .values().stream().collect(Collectors.groupingBy(v -> v.type));
@@ -153,12 +157,19 @@ public class JRubyScriptEngineConfiguration {
         if (gemsConfigElement == null || !gemsConfigElement.getValue().isPresent()) {
             return;
         }
+        boolean checkUpdate = true;
+        OptionalConfigurationElement updateConfigElement = CONFIGURATION_PARAMETERS.get(CHECK_UPDATE);
+        if (updateConfigElement != null && updateConfigElement.getValue().isPresent()) {
+            checkUpdate = updateConfigElement.getValue().get().equals("true");
+        }
 
         String[] gems = gemsConfigElement.getValue().get().split(",");
+        String gemCommand = "require 'bundler/inline'\nrequire 'openssl'\n\ngemfile(" + checkUpdate + ") do\n"
+                + "  source 'https://rubygems.org/'\n";
+        int validGems = 0;
         for (String gem : gems) {
             gem = gem.trim();
             String version = "";
-            String gemCommand;
             if (gem.contains("=")) {
                 String[] gemParts = gem.split("=");
                 gem = gemParts[0].trim();
@@ -167,23 +178,29 @@ public class JRubyScriptEngineConfiguration {
 
             if (gem.isEmpty()) {
                 continue;
-            } else if (version.isEmpty()) {
-                gemCommand = "Gem.install('" + gem + "')\n";
-            } else {
-                gemCommand = "Gem.install('" + gem + "', '" + version + "')\n";
             }
 
-            try {
-                logger.debug("Installing Gem: {}", gem);
-                logger.trace("Gem install code:\n{}\n", gemCommand);
-                engine.eval(gemCommand);
-            } catch (ScriptException e) {
-                logger.warn("Error installing Gem: {}", e.getMessage());
-            } catch (BootstrapMethodError e) {
-                logger.warn("Error while checking/installing gems: {}. You may need to restart OpenHAB",
-                        e.getMessage());
-                logger.debug("Error in configureGems", e);
+            gemCommand += "  gem '" + gem + "'";
+            if (!version.isEmpty()) {
+                gemCommand += ", '" + version + "'";
             }
+            gemCommand += ", require: false\n";
+            validGems += 1;
+        }
+        if (validGems == 0) {
+            return;
+        }
+        gemCommand += "end\n";
+
+        try {
+            logger.debug("Installing Gems");
+            logger.trace("Gem install code:\n{}", gemCommand);
+            engine.eval(gemCommand);
+        } catch (ScriptException e) {
+            logger.warn("Error installing Gems: {}", e.getMessage());
+        } catch (BootstrapMethodError e) {
+            logger.warn("Error while checking/installing gems: {}. You may need to restart OpenHAB", e.getMessage());
+            logger.debug("Error in configureGems", e);
         }
     }
 
@@ -310,7 +327,8 @@ public class JRubyScriptEngineConfiguration {
             SYSTEM_PROPERTY,
             RUBY_ENVIRONMENT,
             GEM,
-            REQUIRE
+            REQUIRE,
+            CHECK_UPDATE,
         }
 
         private static class Builder {
index 4d9427e17d071101ef50c309f381e8c9f6cf403c..ef80653a3fcd10cb49e6c69af7556a05e93888fe 100644 (file)
 
                <parameter name="gems" type="text" required="false" groupName="gems">
                        <label>Ruby Gems</label>
-                       <description>A comma separated list of Ruby Gems to install.</description>
+                       <description><![CDATA[A comma separated list of Ruby Gems to install. Versions may be constrained by separating with an
+                           <tt>=</tt> and then the standard RubyGems version constraint, such as "<tt>openhab-scripting=~>4.0</tt>".
+                               ]]></description>
+               </parameter>
+
+               <parameter name="check_update" type="boolean" required="true" groupName="gems">
+                       <label>Check for Gem Updates</label>
+                       <description>Check RubyGems for updates to the above gems when OpenHAB starts or JRuby settings are changed.
+                               Otherwise it will try to fulfill the requirements with locally installed gems, and you can manage them yourself with
+                               an external Ruby by setting the same GEM_HOME.</description>
+                       <options>
+                               <option value="true">Check For Updates</option>
+                               <option value="false">Do Not Check For Updates</option>
+                       </options>
+                       <default>true</default>
                </parameter>
 
                <parameter name="require" type="text" required="false" groupName="gems">
 
                <parameter name="gem_home" type="text" required="false" groupName="environment">
                        <label>GEM_HOME</label>
-                       <description>Location Ruby Gems will be installed and loaded, directory will be created if missing and gem installs
-                               are specified. Defaults to "OPENHAB_CONF/scripts/lib/ruby/gem_home" when not specified.</description>
+                       <description><![CDATA[Location Ruby Gems will be installed and loaded, directory will be created if missing and gem
+                           installs are specified. Defaults to "<tt>OPENHAB_CONF/scripts/lib/ruby/gem_home</tt>" when not specified.
+                               ]]></description>
                </parameter>
 
                <parameter name="rubylib" type="text" required="false" groupName="environment">
                        <label>RUBYLIB</label>
-                       <description>Search path for user libraries. Separate each path with a colon (semicolon in Windows). Defaults to
-                               "OPENHAB_CONF/automation/lib/ruby" when not specified.</description>
+                       <description><![CDATA[Search path for user libraries. Separate each path with a colon (semicolon in Windows). Defaults to
+                               "<tt>OPENHAB_CONF/automation/lib/ruby</tt>" when not specified.]]></description>
                </parameter>
 
                <parameter name="local_context" type="text" required="false" groupName="system">
                        <label>Context Instance Type</label>
-                       <description>The local context holds Ruby runtime, name-value pairs for sharing variables between Java and Ruby. See
-                               https://github.com/jruby/jruby/wiki/RedBridge#Context_Instance_Type for options and details.</description>
+                       <description><![CDATA[The local context holds Ruby runtime, name-value pairs for sharing variables between Java and Ruby. See
+                               <a href="https://github.com/jruby/jruby/wiki/RedBridge#Context_Instance_Type">the documentation</a> for options and details.
+                               ]]></description>
                        <default>singlethread</default>
                        <options>
                                <option value="singleton">Singleton</option>
@@ -60,8 +76,9 @@
 
                <parameter name="local_variable" type="text" required="false" groupName="system">
                        <label>Local Variable Behavior</label>
-                       <description>Defines how variables are shared between Ruby and Java. See
-                               https://github.com/jruby/jruby/wiki/RedBridge#local-variable-behavior-options for options and details.</description>
+                       <description><![CDATA[Defines how variables are shared between Ruby and Java. See
+                               <a href="https://github.com/jruby/jruby/wiki/RedBridge#local-variable-behavior-options">the documentation</a> for options and details.
+                               ]]></description>
                        <default>transient</default>
                        <options>
                                <option value="transient">Transient</option>
index 989c8e5e9c793e55013f8ed37fcd7aa7c42e040d..8e54c06f6569917c2b23845b7c4116ffc72eb2d0 100644 (file)
@@ -1,7 +1,11 @@
+automation.config.jruby.check_update.label = Check for Gem Updates
+automation.config.jruby.check_update.description = Check RubyGems for updates to the above gems when OpenHAB starts or JRuby settings are changed. Otherwise it will try to fulfill the requirements with locally installed gems, and you can manage them yourself with an external Ruby by setting the same GEM_HOME.
+automation.config.jruby.check_update.option.true = Check For Updates
+automation.config.jruby.check_update.option.false = Do Not Check For Updates
 automation.config.jruby.gem_home.label = GEM_HOME
-automation.config.jruby.gem_home.description = Location Ruby Gems will be installed and loaded, directory will be created if missing and gem installs are specified. Defaults to "OPENHAB_CONF/scripts/lib/ruby/gem_home" when not specified.
+automation.config.jruby.gem_home.description = Location Ruby Gems will be installed and loaded, directory will be created if missing and gem installs are specified. Defaults to "<tt>OPENHAB_CONF/scripts/lib/ruby/gem_home</tt>" when not specified.
 automation.config.jruby.gems.label = Ruby Gems
-automation.config.jruby.gems.description = A comma separated list of Ruby Gems to install.
+automation.config.jruby.gems.description = A comma separated list of Ruby Gems to install. Versions may be constrained by separating with an <tt>=</tt> and then the standard RubyGems version constraint, such as "<tt>openhab-scripting=~>4.0</tt>".
 automation.config.jruby.group.environment.label = Ruby Environment
 automation.config.jruby.group.environment.description = This group defines Ruby's environment.
 automation.config.jruby.group.gems.label = Ruby Gems
@@ -9,20 +13,20 @@ automation.config.jruby.group.gems.description = This group defines the list of
 automation.config.jruby.group.system.label = System Properties
 automation.config.jruby.group.system.description = This group defines JRuby system properties.
 automation.config.jruby.local_context.label = Context Instance Type
-automation.config.jruby.local_context.description = The local context holds Ruby runtime, name-value pairs for sharing variables between Java and Ruby. See https://github.com/jruby/jruby/wiki/RedBridge#Context_Instance_Type for options and details.
+automation.config.jruby.local_context.description = The local context holds Ruby runtime, name-value pairs for sharing variables between Java and Ruby. See <a href="https://github.com/jruby/jruby/wiki/RedBridge#Context_Instance_Type">the documentation</a> for options and details.
 automation.config.jruby.local_context.option.singleton = Singleton
 automation.config.jruby.local_context.option.threadsafe = ThreadSafe
 automation.config.jruby.local_context.option.singlethread = SingleThread
 automation.config.jruby.local_context.option.concurrent = Concurrent
 automation.config.jruby.local_variable.label = Local Variable Behavior
-automation.config.jruby.local_variable.description = Defines how variables are shared between Ruby and Java. See https://github.com/jruby/jruby/wiki/RedBridge#local-variable-behavior-options for options and details.
+automation.config.jruby.local_variable.description = Defines how variables are shared between Ruby and Java. See <a href="https://github.com/jruby/jruby/wiki/RedBridge#local-variable-behavior-options">the documentation</a> for options and details.
 automation.config.jruby.local_variable.option.transient = Transient
 automation.config.jruby.local_variable.option.persistent = Persistent
 automation.config.jruby.local_variable.option.global = Global
 automation.config.jruby.require.label = Require Scripts
 automation.config.jruby.require.description = A comma separated list of script names to be required by the JRuby Scripting Engine before running user scripts.
 automation.config.jruby.rubylib.label = RUBYLIB
-automation.config.jruby.rubylib.description = Search path for user libraries. Separate each path with a colon (semicolon in Windows). Defaults to "OPENHAB_CONF/automation/lib/ruby" when not specified.
+automation.config.jruby.rubylib.description = Search path for user libraries. Separate each path with a colon (semicolon in Windows). Defaults to "<tt>OPENHAB_CONF/automation/lib/ruby</tt>" when not specified.
 
 # service