From 8bd9187140938c36a459aa0f493483a0f2d977eb Mon Sep 17 00:00:00 2001 From: Jimmy Tanagra Date: Sun, 2 Nov 2025 01:04:11 +1000 Subject: [PATCH 1/5] [jrubyscripting] Set .bundle directory to $USERDATA/cache/automation/ruby/.bundle BUNDLE_USER_HOME=$USERDATA/cache/automation/ruby/.bundle Signed-off-by: Jimmy Tanagra --- .../JRubyScriptEngineConfiguration.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) 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 81a431e306be8..33d2b67965d5a 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 @@ -49,6 +49,8 @@ public class JRubyScriptEngineConfiguration { public static final Path HOME_PATH = Path.of("automation", "ruby"); public static final Path HOME_PATH_ABS = Path.of(OpenHAB.getConfigFolder()).resolve(HOME_PATH); private static final Path DEFAULT_GEMFILE_PATH = HOME_PATH_ABS.resolve("Gemfile"); + private static final Path BUNDLE_USER_HOME = Path.of(OpenHAB.getUserDataFolder(), "cache", "automation", "ruby", + ".bundle"); private static final Logger LOGGER = LoggerFactory.getLogger(JRubyScriptEngineConfiguration.class); @@ -84,6 +86,18 @@ public static class JRubyScriptingConfiguration { private String specificGemHome = ""; private File bundleGemfile = DEFAULT_GEMFILE_PATH.toFile(); + JRubyScriptEngineConfiguration() { + File dir = BUNDLE_USER_HOME.toFile(); + if (!dir.exists()) { + boolean created = dir.mkdirs(); + if (created) { + LOGGER.debug("Created directory for Ruby Bundler user path: {}", dir); + } else { + LOGGER.warn("Could not create directory for Ruby Bundler user path: {}", dir); + } + } + } + /** * Update configuration * @@ -241,7 +255,7 @@ public File getGemfile() { /** * Run bundle install or update. - * + * * This is to be called at start up or configuration change, * so that gems are available when user scripts are run. * @@ -302,7 +316,7 @@ public void bundlerSetup(ScriptEngine engine) { /** * Install a gems in ScriptEngine - * + * * @param engine Engine to install gems */ synchronized void configureGems(ScriptEngine engine, boolean update) { @@ -415,6 +429,7 @@ public static void setEnvironmentVariable(ScriptEngine engine, String key, @Null public void configureRubyEnvironment(ScriptEngine scriptEngine) { setEnvironmentVariable(scriptEngine, "GEM_HOME", getSpecificGemHome()); setEnvironmentVariable(scriptEngine, "RUBYLIB", configuration.rubylib); + setEnvironmentVariable(scriptEngine, "BUNDLE_USER_HOME", BUNDLE_USER_HOME.toString()); if (bundleGemfile.exists()) { setEnvironmentVariable(scriptEngine, "BUNDLE_GEMFILE", bundleGemfile.toString()); } From 4a18e6ff7baafa7b66069a7835ee55f9a1ad659d Mon Sep 17 00:00:00 2001 From: Jimmy Tanagra Date: Sun, 2 Nov 2025 10:54:34 +1000 Subject: [PATCH 2/5] set BUNDLE_USER_CONFIG as a sibling to Gemfile Signed-off-by: Jimmy Tanagra --- .../internal/JRubyScriptEngineConfiguration.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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 33d2b67965d5a..71eff84fe45f0 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 @@ -241,6 +241,16 @@ private File resolveGemfile() { "The Gemfile setting is set to '{}' which is a directory. It should be set to a file. Setting it to '{}'", gemfilePath, gemfile); } + + File bundleUserConfigDir = gemfilePath.resolveSibling(".bundle").toFile(); + if (!bundleUserConfigDir.exists()) { + boolean created = bundleUserConfigDir.mkdirs(); + if (created) { + LOGGER.debug("Created directory for Ruby Bundler user config path: {}", bundleUserConfigDir); + } else { + LOGGER.warn("Could not create directory for Ruby Bundler user config path: {}", bundleUserConfigDir); + } + } return gemfile; } @@ -432,6 +442,8 @@ public void configureRubyEnvironment(ScriptEngine scriptEngine) { setEnvironmentVariable(scriptEngine, "BUNDLE_USER_HOME", BUNDLE_USER_HOME.toString()); if (bundleGemfile.exists()) { setEnvironmentVariable(scriptEngine, "BUNDLE_GEMFILE", bundleGemfile.toString()); + setEnvironmentVariable(scriptEngine, "BUNDLE_USER_CONFIG", + bundleGemfile.toPath().resolveSibling(".bundle").resolve("config").toString()); } configureRubyLib(scriptEngine); From dc64f4f7f7c42dc9fffb3d2d497fb9d2747059bb Mon Sep 17 00:00:00 2001 From: Jimmy Tanagra Date: Sun, 2 Nov 2025 12:04:30 +1000 Subject: [PATCH 3/5] check for bundle config dir in each run Signed-off-by: Jimmy Tanagra --- .../JRubyScriptEngineConfiguration.java | 43 +++++++------------ 1 file changed, 15 insertions(+), 28 deletions(-) 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 71eff84fe45f0..ace81b989448b 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 @@ -87,15 +87,7 @@ public static class JRubyScriptingConfiguration { private File bundleGemfile = DEFAULT_GEMFILE_PATH.toFile(); JRubyScriptEngineConfiguration() { - File dir = BUNDLE_USER_HOME.toFile(); - if (!dir.exists()) { - boolean created = dir.mkdirs(); - if (created) { - LOGGER.debug("Created directory for Ruby Bundler user path: {}", dir); - } else { - LOGGER.warn("Could not create directory for Ruby Bundler user path: {}", dir); - } - } + ensureDirectoryExists(BUNDLE_USER_HOME.toString(), "Ruby Bundler user path"); } /** @@ -214,12 +206,15 @@ private boolean ensureGemHomeExists(String gemHome) { return false; } - File gemHomeDirectory = new File(gemHome); - if (!gemHomeDirectory.exists()) { - LOGGER.debug("gem_home directory '{}' does not exist, creating", gemHome); - if (!gemHomeDirectory.mkdirs()) { - LOGGER.warn("Error creating gem_home directory: {}", gemHome); - return false; + return ensureDirectoryExists(gemHome, "gem_home directory"); + } + + private boolean ensureDirectoryExists(String dir, String description) { + File directory = new File(dir); + if (!directory.exists()) { + LOGGER.debug("{} '{}' does not exist, creating", description, dir); + if (!directory.mkdirs()) { + LOGGER.warn("Error creating {}: {}", description, dir); } } return true; @@ -241,16 +236,6 @@ private File resolveGemfile() { "The Gemfile setting is set to '{}' which is a directory. It should be set to a file. Setting it to '{}'", gemfilePath, gemfile); } - - File bundleUserConfigDir = gemfilePath.resolveSibling(".bundle").toFile(); - if (!bundleUserConfigDir.exists()) { - boolean created = bundleUserConfigDir.mkdirs(); - if (created) { - LOGGER.debug("Created directory for Ruby Bundler user config path: {}", bundleUserConfigDir); - } else { - LOGGER.warn("Could not create directory for Ruby Bundler user config path: {}", bundleUserConfigDir); - } - } return gemfile; } @@ -439,11 +424,13 @@ public static void setEnvironmentVariable(ScriptEngine engine, String key, @Null public void configureRubyEnvironment(ScriptEngine scriptEngine) { setEnvironmentVariable(scriptEngine, "GEM_HOME", getSpecificGemHome()); setEnvironmentVariable(scriptEngine, "RUBYLIB", configuration.rubylib); - setEnvironmentVariable(scriptEngine, "BUNDLE_USER_HOME", BUNDLE_USER_HOME.toString()); if (bundleGemfile.exists()) { - setEnvironmentVariable(scriptEngine, "BUNDLE_GEMFILE", bundleGemfile.toString()); + Path bundleUserConfigDir = bundleGemfile.toPath().resolveSibling(".bundle"); + ensureDirectoryExists(bundleUserConfigDir.toString(), "Ruby Bundler user config path"); + setEnvironmentVariable(scriptEngine, "BUNDLE_USER_HOME", BUNDLE_USER_HOME.toString()); setEnvironmentVariable(scriptEngine, "BUNDLE_USER_CONFIG", - bundleGemfile.toPath().resolveSibling(".bundle").resolve("config").toString()); + bundleUserConfigDir.resolve("config").toString()); + setEnvironmentVariable(scriptEngine, "BUNDLE_GEMFILE", bundleGemfile.toString()); } configureRubyLib(scriptEngine); From a55a522b42474595d2f6dcb73e07519588e6d92f Mon Sep 17 00:00:00 2001 From: Jimmy Tanagra Date: Sun, 2 Nov 2025 17:18:55 +1000 Subject: [PATCH 4/5] override Gem.user_home Signed-off-by: Jimmy Tanagra --- .../internal/JRubyScriptEngineConfiguration.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) 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 ace81b989448b..ccb20f0267ffb 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 @@ -49,8 +49,7 @@ public class JRubyScriptEngineConfiguration { public static final Path HOME_PATH = Path.of("automation", "ruby"); public static final Path HOME_PATH_ABS = Path.of(OpenHAB.getConfigFolder()).resolve(HOME_PATH); private static final Path DEFAULT_GEMFILE_PATH = HOME_PATH_ABS.resolve("Gemfile"); - private static final Path BUNDLE_USER_HOME = Path.of(OpenHAB.getUserDataFolder(), "cache", "automation", "ruby", - ".bundle"); + private static final Path BUNDLE_USER_HOME = Path.of(OpenHAB.getUserDataFolder(), "cache", "automation", "ruby"); private static final Logger LOGGER = LoggerFactory.getLogger(JRubyScriptEngineConfiguration.class); @@ -266,6 +265,8 @@ public void bundlerInit(ScriptEngine engine, boolean update) { require "bundler" require "bundler/cli" + Gem.instance_variable_set(:@user_home, ENV['BUNDLE_USER_HOME']) + Bundler::CLI.start(["%s"]) """.formatted(operation); @@ -294,6 +295,8 @@ public void bundlerSetup(ScriptEngine engine) { JRuby.runtime.instance_config.update_native_env_enabled = false require "bundler" + Gem.instance_variable_set(:@user_home, ENV['BUNDLE_USER_HOME']) + Bundler.settings.temporary(auto_install: true) do require "bundler/setup" Bundler.require @@ -360,6 +363,8 @@ synchronized void configureGems(ScriptEngine engine, boolean update) { require 'bundler/inline' require 'openssl' + Gem.instance_variable_set(:@user_home, ENV['BUNDLE_USER_HOME']) + gemfile(%b) do source 'https://rubygems.org/' %s @@ -424,10 +429,10 @@ public static void setEnvironmentVariable(ScriptEngine engine, String key, @Null public void configureRubyEnvironment(ScriptEngine scriptEngine) { setEnvironmentVariable(scriptEngine, "GEM_HOME", getSpecificGemHome()); setEnvironmentVariable(scriptEngine, "RUBYLIB", configuration.rubylib); + setEnvironmentVariable(scriptEngine, "BUNDLE_USER_HOME", BUNDLE_USER_HOME.toString()); if (bundleGemfile.exists()) { Path bundleUserConfigDir = bundleGemfile.toPath().resolveSibling(".bundle"); ensureDirectoryExists(bundleUserConfigDir.toString(), "Ruby Bundler user config path"); - setEnvironmentVariable(scriptEngine, "BUNDLE_USER_HOME", BUNDLE_USER_HOME.toString()); setEnvironmentVariable(scriptEngine, "BUNDLE_USER_CONFIG", bundleUserConfigDir.resolve("config").toString()); setEnvironmentVariable(scriptEngine, "BUNDLE_GEMFILE", bundleGemfile.toString()); From 191d22c6d862078b68c1abba1a8d6f292be5f7d1 Mon Sep 17 00:00:00 2001 From: Jimmy Tanagra Date: Wed, 5 Nov 2025 00:50:03 +1000 Subject: [PATCH 5/5] override unbundle_env using prepend Signed-off-by: Jimmy Tanagra --- .../JRubyScriptEngineConfiguration.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) 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 ccb20f0267ffb..713a0eaa32459 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 @@ -216,7 +216,7 @@ private boolean ensureDirectoryExists(String dir, String description) { LOGGER.warn("Error creating {}: {}", description, dir); } } - return true; + return directory.exists(); } private File resolveGemfile() { @@ -265,8 +265,6 @@ public void bundlerInit(ScriptEngine engine, boolean update) { require "bundler" require "bundler/cli" - Gem.instance_variable_set(:@user_home, ENV['BUNDLE_USER_HOME']) - Bundler::CLI.start(["%s"]) """.formatted(operation); @@ -295,8 +293,6 @@ public void bundlerSetup(ScriptEngine engine) { JRuby.runtime.instance_config.update_native_env_enabled = false require "bundler" - Gem.instance_variable_set(:@user_home, ENV['BUNDLE_USER_HOME']) - Bundler.settings.temporary(auto_install: true) do require "bundler/setup" Bundler.require @@ -357,13 +353,22 @@ synchronized void configureGems(ScriptEngine engine, boolean update) { } // Set update_native_env_enabled to false so that bundler doesn't leak into other script engines + // Override unbundle_env to reinsert BUNDLE_USER_HOME into the environment, + // so that the inline bundler creates the bundle cache in BUNDLE_USER_HOME instead of in ~openhab/.bundle String gemCommand = """ require 'jruby' JRuby.runtime.instance_config.update_native_env_enabled = false + require 'bundler' require 'bundler/inline' require 'openssl' - Gem.instance_variable_set(:@user_home, ENV['BUNDLE_USER_HOME']) + module BundlerEnvironmentOverride + def unbundle_env(...) + bundle_user_home = ENV['BUNDLE_USER_HOME'] + super.tap { |env| env['BUNDLE_USER_HOME'] = bundle_user_home } + end + end + Bundler.singleton_class.prepend(BundlerEnvironmentOverride) gemfile(%b) do source 'https://rubygems.org/'