From add6f799dfdc0c39efb0802f13dc4ad1af669668 Mon Sep 17 00:00:00 2001 From: Michal Gritzbach Date: Tue, 18 Aug 2026 10:10:56 +0200 Subject: [PATCH] Fixes #39641 - Support remote_execution_remote_working_dir in push mode --- .../runners/script_runner.rb | 9 ++- test/script_runner_test.rb | 55 +++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 test/script_runner_test.rb diff --git a/lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb b/lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb index e6bf843..6c064f3 100644 --- a/lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb +++ b/lib/smart_proxy_remote_execution_ssh/runners/script_runner.rb @@ -113,7 +113,7 @@ def initialize(options, user_method, suspended_action: nil) @client_private_key_file = settings.ssh_identity_key_file @local_working_dir = options.fetch(:local_working_dir, settings.local_working_dir) - @remote_working_dir = options.fetch(:remote_working_dir, settings.remote_working_dir.shellescape) + @remote_working_dir = absolute_path!(options.fetch(:remote_working_dir, settings.remote_working_dir)).shellescape @socket_working_dir = options.fetch(:socket_working_dir, settings.socket_working_dir) @cleanup_working_dirs = options.fetch(:cleanup_working_dirs, settings.cleanup_working_dirs) @first_execution = options.fetch(:first_execution, false) @@ -275,6 +275,13 @@ def indent_multiline(string) string.lines.map { |line| " | #{line}" }.join end + def absolute_path!(path) + path = path.to_s.strip + raise "Remote working directory '#{path}' is not an absolute path" unless Pathname.new(path).absolute? + + path + end + def should_cleanup? @process_manager && @cleanup_working_dirs end diff --git a/test/script_runner_test.rb b/test/script_runner_test.rb new file mode 100644 index 0000000..9c81e48 --- /dev/null +++ b/test/script_runner_test.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'test_helper' +require 'smart_proxy_remote_execution_ssh/runners/script_runner' + +module Proxy::RemoteExecution::Ssh::Runners + class ScriptRunnerTest < Minitest::Test + def build_runner(options = {}) + ScriptRunner.new( + { hostname: 'somehost.example.com', script: 'echo hello' }.merge(options), + NoopUserMethod.new + ) + end + + def remote_working_dir_of(runner) + runner.instance_variable_get(:@remote_working_dir) + end + + def stub_setting(value) + Proxy::RemoteExecution::Ssh::Plugin.settings.stubs(:remote_working_dir).returns(value) + end + + def test_remote_working_dir_falls_back_to_the_local_setting + stub_setting('/var/tmp') + assert_equal '/var/tmp', remote_working_dir_of(build_runner) + end + + def test_remote_working_dir_prefers_the_value_from_the_options + stub_setting('/var/tmp') + assert_equal '/opt/rex', remote_working_dir_of(build_runner(remote_working_dir: '/opt/rex')) + end + + def test_remote_working_dir_from_the_options_is_shell_escaped + stub_setting('/var/tmp') + runner = build_runner(remote_working_dir: '/opt/my rex dir') + assert_equal '/opt/my\\ rex\\ dir', remote_working_dir_of(runner) + end + + def test_remote_working_dir_from_the_local_setting_is_shell_escaped + stub_setting('/opt/my rex dir') + assert_equal '/opt/my\\ rex\\ dir', remote_working_dir_of(build_runner) + end + + def test_remote_working_dir_rejects_a_relative_path_from_the_options + stub_setting('/var/tmp') + error = assert_raises(RuntimeError) { build_runner(remote_working_dir: 'opt/rex') } + assert_match(/not an absolute path/, error.message) + end + + def test_remote_working_dir_rejects_an_empty_path_from_the_options + stub_setting('/var/tmp') + assert_raises(RuntimeError) { build_runner(remote_working_dir: ' ') } + end + end +end