From 2d5771dd9d6546d52e6d2fdbb3e9c5ee7a455c2b Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Wed, 20 Jul 2022 15:28:10 -0600 Subject: [PATCH 1/2] [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 --- .../README.md | 3 ++ .../JRubyScriptEngineConfiguration.java | 52 +++++++++++++------ .../main/resources/OH-INF/config/config.xml | 35 +++++++++---- .../resources/OH-INF/i18n/jruby.properties | 14 +++-- 4 files changed, 73 insertions(+), 31 deletions(-) diff --git a/bundles/org.openhab.automation.jrubyscripting/README.md b/bundles/org.openhab.automation.jrubyscripting/README.md index eeea0cb3882ec..194bc3b96b56e 100644 --- a/bundles/org.openhab.automation.jrubyscripting/README.md +++ b/bundles/org.openhab.automation.jrubyscripting/README.md @@ -16,6 +16,9 @@ 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 fulfill the requirements with locally installed gems, and you can manage them yourself with + an external Ruby by setting the same GEM_HOME. | ## Ruby Gems diff --git a/bundles/org.openhab.automation.jrubyscripting/src/main/java/org/openhab/automation/jrubyscripting/internal/JRubyScriptEngineConfiguration.java b/bundles/org.openhab.automation.jrubyscripting/src/main/java/org/openhab/automation/jrubyscripting/internal/JRubyScriptEngineConfiguration.java index d9f0e29450b1a..f0a9a24a025a7 100644 --- a/bundles/org.openhab.automation.jrubyscripting/src/main/java/org/openhab/automation/jrubyscripting/internal/JRubyScriptEngineConfiguration.java +++ b/bundles/org.openhab.automation.jrubyscripting/src/main/java/org/openhab/automation/jrubyscripting/internal/JRubyScriptEngineConfiguration.java @@ -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 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> CONFIGURATION_TYPE_MAP = CONFIGURATION_PARAMETERS .values().stream().collect(Collectors.groupingBy(v -> v.type)); @@ -153,12 +157,19 @@ private synchronized void configureGems(ScriptEngine engine) { 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 @@ private synchronized void configureGems(ScriptEngine engine) { 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 @@ private enum Type { SYSTEM_PROPERTY, RUBY_ENVIRONMENT, GEM, - REQUIRE + REQUIRE, + CHECK_UPDATE, } private static class Builder { diff --git a/bundles/org.openhab.automation.jrubyscripting/src/main/resources/OH-INF/config/config.xml b/bundles/org.openhab.automation.jrubyscripting/src/main/resources/OH-INF/config/config.xml index 4d9427e17d071..ef80653a3fcd1 100644 --- a/bundles/org.openhab.automation.jrubyscripting/src/main/resources/OH-INF/config/config.xml +++ b/bundles/org.openhab.automation.jrubyscripting/src/main/resources/OH-INF/config/config.xml @@ -23,7 +23,21 @@ - A comma separated list of Ruby Gems to install. + = and then the standard RubyGems version constraint, such as "openhab-scripting=~>4.0". + ]]> + + + + + 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. + + + + + true @@ -34,20 +48,22 @@ - 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. + OPENHAB_CONF/scripts/lib/ruby/gem_home" when not specified. + ]]> - Search path for user libraries. Separate each path with a colon (semicolon in Windows). Defaults to - "OPENHAB_CONF/automation/lib/ruby" when not specified. + OPENHAB_CONF/automation/lib/ruby" when not specified.]]> - 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. + the documentation for options and details. + ]]> singlethread @@ -60,8 +76,9 @@ - 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. + the documentation for options and details. + ]]> transient diff --git a/bundles/org.openhab.automation.jrubyscripting/src/main/resources/OH-INF/i18n/jruby.properties b/bundles/org.openhab.automation.jrubyscripting/src/main/resources/OH-INF/i18n/jruby.properties index 989c8e5e9c793..8e54c06f65699 100644 --- a/bundles/org.openhab.automation.jrubyscripting/src/main/resources/OH-INF/i18n/jruby.properties +++ b/bundles/org.openhab.automation.jrubyscripting/src/main/resources/OH-INF/i18n/jruby.properties @@ -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 "OPENHAB_CONF/scripts/lib/ruby/gem_home" 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 = and then the standard RubyGems version constraint, such as "openhab-scripting=~>4.0". 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 the documentation 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 the documentation 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 "OPENHAB_CONF/automation/lib/ruby" when not specified. # service From 3b4f65f0c6aef63f4ac35f3dc5a74dc2c21718b3 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Sat, 6 Aug 2022 17:31:18 -0600 Subject: [PATCH 2/2] Update bundles/org.openhab.automation.jrubyscripting/README.md Co-authored-by: jimtng <2554958+jimtng@users.noreply.github.com> --- bundles/org.openhab.automation.jrubyscripting/README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/bundles/org.openhab.automation.jrubyscripting/README.md b/bundles/org.openhab.automation.jrubyscripting/README.md index 194bc3b96b56e..6cdaaa104e4cb 100644 --- a/bundles/org.openhab.automation.jrubyscripting/README.md +++ b/bundles/org.openhab.automation.jrubyscripting/README.md @@ -16,9 +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 fulfill the requirements with locally installed gems, and you can manage them yourself with - an external Ruby by setting the same GEM_HOME. | +| 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