diff --git a/CHANGELOG.md b/CHANGELOG.md index 79ab42ab..23d8fcb3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ appear at the top. * Add your entries below here, remember to credit yourself however you want to be credited! + * Export environment variables and execute command in a subshell. + [PR #273](https://github.com/capistrano/sshkit/pull/273) + @kuon * Introduce `log_command_start`, `log_command_data`, `log_command_exit` methods on `Formatter` [PR #257](https://github.com/capistrano/sshkit/pull/257) @robd diff --git a/lib/sshkit/command.rb b/lib/sshkit/command.rb index 2406f638..95340582 100644 --- a/lib/sshkit/command.rb +++ b/lib/sshkit/command.rb @@ -163,7 +163,7 @@ def environment_string def with(&block) return yield unless environment_hash.any? - "( #{environment_string} %s )" % yield + "( export #{environment_string} ; %s )" % yield end def user(&block) diff --git a/test/functional/backends/test_netssh.rb b/test/functional/backends/test_netssh.rb index aa2f343e..c6fc042c 100644 --- a/test/functional/backends/test_netssh.rb +++ b/test/functional/backends/test_netssh.rb @@ -38,7 +38,7 @@ def test_simple_netssh Command: /usr/bin/env ls -l Command: if test ! -d /tmp; then echo \"Directory does not exist '/tmp'\" 1>&2; false; fi Command: if ! sudo -u root whoami > /dev/null; then echo \"You cannot switch to user 'root' using sudo, please check the sudoers file\" 1>&2; false; fi - Command: cd /tmp && ( RAILS_ENV="production" sudo -u root RAILS_ENV="production" -- sh -c '/usr/bin/env touch restart.txt' ) + Command: cd /tmp && ( export RAILS_ENV="production" ; sudo -u root RAILS_ENV="production" -- sh -c '/usr/bin/env touch restart.txt' ) EOEXPECTED end @@ -62,6 +62,16 @@ def test_ssh_option_merge assert_equal({ forward_agent: false, paranoid: true }, host_ssh_options) end + def test_env_vars_substituion_in_subshell + captured_command_result = nil + Netssh.new(a_host) do |host| + with some_env_var: :some_value do + captured_command_result = capture(:echo, '$SOME_ENV_VAR') + end + end.run + assert_equal "some_value", captured_command_result + end + def test_execute_raises_on_non_zero_exit_status_and_captures_stdout_and_stderr err = assert_raises SSHKit::Command::Failed do Netssh.new(a_host) do |host| diff --git a/test/unit/test_command.rb b/test/unit/test_command.rb index 69598158..a973466a 100644 --- a/test/unit/test_command.rb +++ b/test/unit/test_command.rb @@ -28,44 +28,44 @@ def test_using_a_heredoc def test_including_the_env SSHKit.config = nil c = Command.new(:rails, 'server', env: {rails_env: :production}) - assert_equal %{( RAILS_ENV="production" /usr/bin/env rails server )}, c.to_command + assert_equal %{( export RAILS_ENV="production" ; /usr/bin/env rails server )}, c.to_command end def test_including_the_env_with_multiple_keys SSHKit.config = nil c = Command.new(:rails, 'server', env: {rails_env: :production, foo: 'bar'}) - assert_equal %{( RAILS_ENV="production" FOO="bar" /usr/bin/env rails server )}, c.to_command + assert_equal %{( export RAILS_ENV="production" FOO="bar" ; /usr/bin/env rails server )}, c.to_command end def test_including_the_env_with_string_keys SSHKit.config = nil c = Command.new(:rails, 'server', env: {'FACTER_env' => :production, foo: 'bar'}) - assert_equal %{( FACTER_env="production" FOO="bar" /usr/bin/env rails server )}, c.to_command + assert_equal %{( export FACTER_env="production" FOO="bar" ; /usr/bin/env rails server )}, c.to_command end def test_double_quotes_are_escaped_in_env SSHKit.config = nil c = Command.new(:rails, 'server', env: {foo: 'asdf"hjkl'}) - assert_equal %{( FOO="asdf\\\"hjkl" /usr/bin/env rails server )}, c.to_command + assert_equal %{( export FOO="asdf\\\"hjkl" ; /usr/bin/env rails server )}, c.to_command end def test_including_the_env_doesnt_addressively_escape SSHKit.config = nil c = Command.new(:rails, 'server', env: {path: '/example:$PATH'}) - assert_equal %{( PATH="/example:$PATH" /usr/bin/env rails server )}, c.to_command + assert_equal %{( export PATH="/example:$PATH" ; /usr/bin/env rails server )}, c.to_command end def test_global_env SSHKit.config = nil SSHKit.config.default_env = { default: 'env' } c = Command.new(:rails, 'server', env: {}) - assert_equal %{( DEFAULT="env" /usr/bin/env rails server )}, c.to_command + assert_equal %{( export DEFAULT="env" ; /usr/bin/env rails server )}, c.to_command end def test_default_env_is_overwritten_with_locally_defined SSHKit.config.default_env = { foo: 'bar', over: 'under' } c = Command.new(:rails, 'server', env: { over: 'write'}) - assert_equal %{( FOO="bar" OVER="write" /usr/bin/env rails server )}, c.to_command + assert_equal %{( export FOO="bar" OVER="write" ; /usr/bin/env rails server )}, c.to_command end def test_working_in_a_given_directory @@ -75,7 +75,7 @@ def test_working_in_a_given_directory def test_working_in_a_given_directory_with_env c = Command.new(:ls, '-l', in: "/opt/sites", env: {a: :b}) - assert_equal %{cd /opt/sites && ( A="b" /usr/bin/env ls -l )}, c.to_command + assert_equal %{cd /opt/sites && ( export A="b" ; /usr/bin/env ls -l )}, c.to_command end def test_having_a_host_passed @@ -120,7 +120,7 @@ def test_umask_with_working_directory_and_user def test_umask_with_env_and_working_directory_and_user SSHKit.config.umask = '007' c = Command.new(:touch, 'somefile', user: 'bob', env: {a: 'b'}, in: '/var') - assert_equal %{cd /var && umask 007 && ( A="b" sudo -u bob A="b" -- sh -c '/usr/bin/env touch somefile' )}, c.to_command + assert_equal %{cd /var && umask 007 && ( export A="b" ; sudo -u bob A="b" -- sh -c '/usr/bin/env touch somefile' )}, c.to_command end def test_verbosity_defaults_to_logger_info