Skip to content

Commit 49e3484

Browse files
author
Kevin Paulisse
committed
Construct temporary copy of script to execute
1 parent ff1348d commit 49e3484

2 files changed

Lines changed: 57 additions & 6 deletions

File tree

lib/octocatalog-diff/util/scriptrunner.rb

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
require 'fileutils'
66
require 'open3'
77
require 'shellwords'
8+
require 'tempfile'
89

910
module OctocatalogDiff
1011
module Util
@@ -13,7 +14,7 @@ class ScriptRunner
1314
# For an exception running the script
1415
class ScriptException < RuntimeError; end
1516

16-
attr_reader :script, :logger, :stdout, :stderr, :exitcode
17+
attr_reader :script, :script_src, :logger, :stdout, :stderr, :exitcode
1718

1819
# Create the object - the object is a configured script, which can be executed multiple
1920
# times with different environment varibles.
@@ -24,7 +25,8 @@ class ScriptException < RuntimeError; end
2425
# opts[:override_script_path] (Optional) Directory where a similarly-named script MAY exist
2526
def initialize(opts = {})
2627
@logger = opts.fetch(:logger)
27-
@script = find_script(opts.fetch(:default_script), opts[:override_script_path])
28+
@script_src = find_script(opts.fetch(:default_script), opts[:override_script_path])
29+
@script = temp_script(@script_src)
2830
@stdout = nil
2931
@stderr = nil
3032
@exitcode = nil
@@ -48,7 +50,7 @@ def run(opts = {})
4850
env['PWD'] = working_dir
4951
env['PATH'] ||= ENV['PATH']
5052

51-
cmdline = [@script, argv].flatten.compact.map { |x| Shellwords.escape(x) }.join(' ')
53+
cmdline = [script, argv].flatten.compact.map { |x| Shellwords.escape(x) }.join(' ')
5254
@logger.debug "Execute: #{cmdline}"
5355

5456
@stdout, @stderr, status = Open3.capture3(env, cmdline, unsetenv_others: true, chdir: working_dir)
@@ -62,6 +64,24 @@ def run(opts = {})
6264

6365
private
6466

67+
# PRIVATE: Create a temporary file with the contents of the script and mark the script executable.
68+
# This is to avoid changing ownership or permissions on any user-supplied file.
69+
#
70+
# @param script [String] Path to script
71+
# @return [String] Path to tempfile containing script
72+
def temp_script(script)
73+
unless File.file?(script)
74+
raise Errno::ENOENT, "Script '#{script}' not found"
75+
end
76+
script_name, extension = script.split('.', 2)
77+
tempfile = ::Tempfile.new([File.basename(script_name), ".#{extension}"])
78+
tempfile.write(File.read(script))
79+
tempfile.close
80+
FileUtils.chmod 0o755, tempfile.path
81+
at_exit { FileUtils.rm_f tempfile.path }
82+
tempfile.path
83+
end
84+
6585
# PRIVATE: Determine the path to the script to execute, taking into account the default script
6686
# location and the optional override script path.
6787
#

spec/octocatalog-diff/tests/util/scriptrunner_spec.rb

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
logger: @logger
2828
}
2929
obj = described_class.new(opts)
30-
expect(obj.script).to eq(__FILE__)
30+
expect(obj.script_src).to eq(__FILE__)
3131
expect(@logger_str.string).to match(/Selecting.+scriptrunner_spec.rb from override script path/)
3232
end
3333

@@ -40,7 +40,7 @@
4040
}
4141
obj = described_class.new(opts)
4242
answer = File.expand_path('../../../../scripts/env/env.sh', File.dirname(__FILE__))
43-
expect(obj.script).to eq(answer)
43+
expect(obj.script_src).to eq(answer)
4444
expect(@logger_str.string).to match(/Did not find.+env.sh in override script path/)
4545
end
4646

@@ -52,7 +52,7 @@
5252
}
5353
obj = described_class.new(opts)
5454
answer = File.expand_path('../../../../scripts/env/env.sh', File.dirname(__FILE__))
55-
expect(obj.script).to eq(answer)
55+
expect(obj.script_src).to eq(answer)
5656
expect(@logger_str.string).to eq('')
5757
end
5858
end
@@ -90,5 +90,36 @@
9090
expect(@described_obj.stderr).to eq(stderr)
9191
expect(@described_obj.exitcode).to eq(42)
9292
end
93+
94+
context 'testing the environment' do
95+
before(:each) do
96+
ENV['THIS_SHOULD_NOT_BE_PASSED'] = 'foo'
97+
end
98+
99+
after(:each) do
100+
ENV.delete('THIS_SHOULD_NOT_BE_PASSED')
101+
end
102+
103+
it 'should set only the defined environment' do
104+
opts = {
105+
:working_dir => File.dirname(__FILE__),
106+
:argv => %w(foo bar),
107+
'HELLO_WORLD' => 'booyah'
108+
}
109+
result = @described_obj.run(opts)
110+
expect(result).to match(/HELLO_WORLD=booyah/)
111+
112+
env_home = Regexp.escape(ENV['HOME'])
113+
expect(result).to match(/HOME=#{env_home}/)
114+
115+
env_path = Regexp.escape(ENV['PATH'])
116+
expect(result).to match(/PATH=#{env_path}/)
117+
118+
env_pwd = Regexp.escape(opts[:working_dir])
119+
expect(result).to match(/PWD=#{env_pwd}/)
120+
121+
expect(result).not_to match(/THIS_SHOULD_NOT_BE_PASSED/)
122+
end
123+
end
93124
end
94125
end

0 commit comments

Comments
 (0)