terrapin 0.6.0.alpha → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +30 -0
- data/.travis.yml +4 -1
- data/Gemfile +0 -4
- data/NEWS.md +15 -0
- data/README.md +34 -27
- data/lib/terrapin/command_line/runners.rb +0 -1
- data/lib/terrapin/command_line.rb +14 -9
- data/lib/terrapin/version.rb +1 -1
- data/spec/spec_helper.rb +0 -4
- data/spec/support/nonblocking_examples.rb +2 -2
- data/spec/support/stub_os.rb +5 -5
- data/spec/terrapin/command_line/runners/backticks_runner_spec.rb +2 -2
- data/spec/terrapin/command_line/runners/fake_runner_spec.rb +4 -4
- data/spec/terrapin/command_line/runners/popen_runner_spec.rb +2 -2
- data/spec/terrapin/command_line/runners/process_runner_spec.rb +2 -2
- data/spec/terrapin/command_line_spec.rb +39 -27
- data/spec/terrapin/errors_spec.rb +13 -16
- data/spec/terrapin/os_detector_spec.rb +4 -4
- data/spec/terrapin/runners_spec.rb +11 -24
- data/terrapin.gemspec +17 -21
- metadata +12 -66
- data/lib/terrapin/command_line/runners/posix_runner.rb +0 -49
- data/spec/support/unsetting_exitstatus.rb +0 -7
- data/spec/terrapin/command_line/runners/posix_runner_spec.rb +0 -40
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 63194360423231cdc4e8a9d8029d6a13cefb7adaf18393e9c440c5bd78d9cda9
|
4
|
+
data.tar.gz: df7766c516b75cf3e4a3cdc9e34db75f71b445401412f2fa0d56731642bcf62a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 605061255a57c1565265f4b80c14fe246cdaea034c17148ac499022e6cbe34612551c455d506429c8ba5b071449a2d0b4137140757135b3a2c983af404bb0514
|
7
|
+
data.tar.gz: a609c3fb7fbca55d3382a8c8f41c93de29761aa19221a2768100e80107afe789d6984f46945b48eadea49def31a59bf7eab1812304fb66c1add37784f489e263
|
@@ -0,0 +1,30 @@
|
|
1
|
+
name: CI
|
2
|
+
on:
|
3
|
+
- push
|
4
|
+
- pull_request
|
5
|
+
|
6
|
+
jobs:
|
7
|
+
build:
|
8
|
+
name: Ruby ${{ matrix.ruby }}
|
9
|
+
if: github.event_name == 'push' || github.event.pull_request.head.repo.full_name != github.repository
|
10
|
+
strategy:
|
11
|
+
fail-fast: false
|
12
|
+
matrix:
|
13
|
+
ruby:
|
14
|
+
- jruby-9.4.1.0
|
15
|
+
- "2.7"
|
16
|
+
- "3.0"
|
17
|
+
- "3.1"
|
18
|
+
- "3.2"
|
19
|
+
|
20
|
+
runs-on: 'ubuntu-latest'
|
21
|
+
|
22
|
+
steps:
|
23
|
+
- uses: actions/checkout@v2
|
24
|
+
- uses: ruby/setup-ruby@v1
|
25
|
+
with:
|
26
|
+
ruby-version: ${{ matrix.ruby }}
|
27
|
+
- name: Setup project
|
28
|
+
run: bundle install
|
29
|
+
- name: Run test
|
30
|
+
run: bundle exec rake
|
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/NEWS.md
CHANGED
@@ -1,3 +1,18 @@
|
|
1
|
+
New for 1.0.0:
|
2
|
+
|
3
|
+
* Terrapin::CommandLine::PosixRunner was removed. You can replace any usage of this with Terrapin::CommandLine::ProcessRunner, which uses Ruby’s builtin Process.spawn.
|
4
|
+
* Moved CI from Travis to GH Actions.
|
5
|
+
|
6
|
+
New for 0.6.0:
|
7
|
+
|
8
|
+
* Rename the project to Terrapin
|
9
|
+
|
10
|
+
New for 0.5.8:
|
11
|
+
|
12
|
+
* Improvement: Ensure that argument interpolations can be turned into Strings
|
13
|
+
* Feature: Save STDOUT and STDERR for inspection when the command completes
|
14
|
+
* Bug fix: Properly interpolate at the end of the line
|
15
|
+
|
1
16
|
New for 0.5.7:
|
2
17
|
|
3
18
|
* Feature: Allow collection of both STDOUT and STDERR.
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Terrapin [](http://travis-ci.org/thoughtbot/terrapin)
|
2
2
|
|
3
|
-
|
3
|
+
Run shell commands safely, even with user-supplied values
|
4
4
|
|
5
5
|
[API reference](http://rubydoc.info/gems/terrapin/)
|
6
6
|
|
@@ -40,7 +40,15 @@ passed into `new` (see 'Security' below):
|
|
40
40
|
```ruby
|
41
41
|
line = Terrapin::CommandLine.new("echo", "haha`whoami`")
|
42
42
|
line.command # => "echo haha`whoami`"
|
43
|
-
line.run # => "hahawebserver"
|
43
|
+
line.run # => "hahawebserver\n"
|
44
|
+
```
|
45
|
+
|
46
|
+
This is the right way:
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
line = Terrapin::CommandLine.new("echo", "haha:whoami")
|
50
|
+
line.command(whoami: "`whoami`") # => "echo haha'`whoami`'"
|
51
|
+
line.run(whoami: "`whoami`") # => "haha`whoami`\n"
|
44
52
|
```
|
45
53
|
|
46
54
|
You can ignore the result:
|
@@ -97,11 +105,11 @@ line.command # => "lolwut", but it looks in /opt/bin for it.
|
|
97
105
|
You can even give it a bunch of places to look:
|
98
106
|
|
99
107
|
```ruby
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
108
|
+
FileUtils.rm("/opt/bin/lolwut")
|
109
|
+
File.open('/usr/local/bin/lolwut') { |f| f.write('echo Hello') }
|
110
|
+
Terrapin::CommandLine.path = ["/opt/bin", "/usr/local/bin"]
|
111
|
+
line = Terrapin::CommandLine.new("lolwut")
|
112
|
+
line.run # => prints 'Hello', because it searches the path
|
105
113
|
```
|
106
114
|
|
107
115
|
Or just put it in the command:
|
@@ -111,7 +119,8 @@ line = Terrapin::CommandLine.new("/opt/bin/lolwut")
|
|
111
119
|
line.command # => "/opt/bin/lolwut"
|
112
120
|
```
|
113
121
|
|
114
|
-
You can see what's getting run. The 'Command' part it logs is in green for
|
122
|
+
You can see what's getting run. The 'Command' part it logs is in green for
|
123
|
+
visibility! (where applicable)
|
115
124
|
|
116
125
|
```ruby
|
117
126
|
line = Terrapin::CommandLine.new("echo", ":var", logger: Logger.new(STDOUT))
|
@@ -136,23 +145,18 @@ second argument of `new`. Terrapin assumes that you will not be manually
|
|
136
145
|
passing user-generated data to that argument and will be using it as a template
|
137
146
|
for your command line's structure.
|
138
147
|
|
139
|
-
##
|
148
|
+
## Runners
|
140
149
|
|
141
|
-
|
142
|
-
|
143
|
-
application's heap from being copied when forking command line
|
144
|
-
processes. For applications with large heaps the gain can be
|
145
|
-
significant. To include `posix-spawn`, simply add it to your `Gemfile` or,
|
146
|
-
if you don't use bundler, install the gem.
|
150
|
+
Terrapin will choose from among a couple different ways of running commands.
|
151
|
+
The simplest is `Process.spawn`, which is also the default. Terrapin can also just use [backticks], so if for some reason you'd prefer that, you can ask Terrapin to use that:
|
147
152
|
|
148
|
-
|
153
|
+
```ruby
|
154
|
+
Terrapin::CommandLine.runner = Terrapin::CommandLine::BackticksRunner.new
|
155
|
+
```
|
156
|
+
|
157
|
+
And if you really want to, you can define your own Runner, though I can't imagine why you would.
|
149
158
|
|
150
|
-
|
151
|
-
The simplest is using backticks, and is the default in 1.8. In Ruby 1.9, it
|
152
|
-
will attempt to use `Process.spawn`. And, as mentioned above, if the
|
153
|
-
`posix-spawn` gem is installed, it will attempt to use that. If for some reason
|
154
|
-
one of the `.spawn` runners don't work for you, you can override them manually
|
155
|
-
by setting a new runner, like so:
|
159
|
+
[backticks]: https://ruby-doc.org/3.2.1/Kernel.html#method-i-60
|
156
160
|
|
157
161
|
```ruby
|
158
162
|
Terrapin::CommandLine.runner = Terrapin::CommandLine::BackticksRunner.new
|
@@ -173,8 +177,9 @@ http://jira.codehaus.org/browse/JRUBY-6162. You *will* want to use the
|
|
173
177
|
|
174
178
|
#### Spawn warning
|
175
179
|
|
176
|
-
If you get `unsupported spawn option: out` warning (like in [issue
|
177
|
-
try to use
|
180
|
+
If you get `unsupported spawn option: out` warning (like in [issue
|
181
|
+
38](https://github.com/thoughtbot/terrapin/issues/38)), try to use
|
182
|
+
`PopenRunner`:
|
178
183
|
|
179
184
|
```ruby
|
180
185
|
Terrapin::CommandLine.runner = Terrapin::CommandLine::PopenRunner.new
|
@@ -199,17 +204,19 @@ Question? Idea? Problem? Bug? Comment? Concern? Like using question marks?
|
|
199
204
|
|
200
205
|
## Credits
|
201
206
|
|
202
|
-
Thank you to all [the
|
207
|
+
Thank you to all [the
|
208
|
+
contributors](https://github.com/thoughtbot/terrapin/graphs/contributors)!
|
203
209
|
|
204
210
|

|
205
211
|
|
206
|
-
Terrapin is maintained and funded by [thoughtbot,
|
212
|
+
Terrapin is maintained and funded by [thoughtbot,
|
213
|
+
inc](http://thoughtbot.com/community)
|
207
214
|
|
208
215
|
The names and logos for thoughtbot are trademarks of thoughtbot, inc.
|
209
216
|
|
210
217
|
## License
|
211
218
|
|
212
|
-
Copyright 2011-
|
219
|
+
Copyright 2011-2018 Jon Yurek and thoughtbot, inc. This is free software, and
|
213
220
|
may be redistributed under the terms specified in the
|
214
221
|
[LICENSE](https://github.com/thoughtbot/terrapin/blob/master/LICENSE)
|
215
222
|
file.
|
@@ -2,6 +2,5 @@
|
|
2
2
|
|
3
3
|
require 'terrapin/command_line/runners/backticks_runner'
|
4
4
|
require 'terrapin/command_line/runners/process_runner'
|
5
|
-
require 'terrapin/command_line/runners/posix_runner'
|
6
5
|
require 'terrapin/command_line/runners/popen_runner'
|
7
6
|
require 'terrapin/command_line/runners/fake_runner'
|
@@ -38,7 +38,7 @@ module Terrapin
|
|
38
38
|
private
|
39
39
|
|
40
40
|
def best_runner
|
41
|
-
[
|
41
|
+
[ProcessRunner, BackticksRunner].detect do |runner|
|
42
42
|
runner.supported?
|
43
43
|
end.new
|
44
44
|
end
|
@@ -49,15 +49,20 @@ module Terrapin
|
|
49
49
|
attr_reader :exit_status, :runner
|
50
50
|
|
51
51
|
def initialize(binary, params = "", options = {})
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
@
|
52
|
+
if options.nil?
|
53
|
+
raise ArgumentError, "3rd argument to CommandLine.new should be a" \
|
54
|
+
"hash of values that will be interpolated into the command line"
|
55
|
+
end
|
56
|
+
|
57
|
+
@options = options.dup
|
58
|
+
@binary = binary.dup
|
59
|
+
@params = params.to_s.dup
|
60
|
+
@runner = @options.delete(:runner) || self.class.runner
|
61
|
+
@logger = @options.delete(:logger) || self.class.logger
|
62
|
+
@swallow_stderr = @options.delete(:swallow_stderr)
|
58
63
|
@expected_outcodes = @options.delete(:expected_outcodes) || [0]
|
59
|
-
@environment
|
60
|
-
@runner_options
|
64
|
+
@environment = @options.delete(:environment) || {}
|
65
|
+
@runner_options = @options.delete(:runner_options) || {}
|
61
66
|
end
|
62
67
|
|
63
68
|
def command(interpolations = {})
|
data/lib/terrapin/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
1
|
require 'rspec'
|
2
|
-
require 'mocha/api'
|
3
|
-
require 'bourne'
|
4
2
|
require 'terrapin'
|
5
3
|
require 'timeout'
|
6
4
|
require 'tempfile'
|
@@ -14,10 +12,8 @@ begin; require 'active_support/buffered_logger'; rescue LoadError; end
|
|
14
12
|
Dir[File.dirname(__FILE__) + "/support/**.rb"].each{|support_file| require support_file }
|
15
13
|
|
16
14
|
RSpec.configure do |config|
|
17
|
-
config.mock_with :mocha
|
18
15
|
config.include WithExitstatus
|
19
16
|
config.include StubOS
|
20
|
-
config.include UnsettingExitstatus
|
21
17
|
end
|
22
18
|
|
23
19
|
def best_logger
|
@@ -5,8 +5,8 @@ shared_examples_for "a command that does not block" do
|
|
5
5
|
|
6
6
|
Timeout.timeout(5) do
|
7
7
|
output = subject.call("cat '#{garbage_file.path}'")
|
8
|
-
$?.exitstatus.
|
9
|
-
output.output.length.
|
8
|
+
expect($?.exitstatus).to eq(0)
|
9
|
+
expect(output.output.length).to eq(10 * 1024 * 1024)
|
10
10
|
end
|
11
11
|
|
12
12
|
garbage_file.close
|
data/spec/support/stub_os.rb
CHANGED
@@ -1,25 +1,25 @@
|
|
1
1
|
module StubOS
|
2
2
|
def on_windows!
|
3
3
|
stub_os('mswin')
|
4
|
-
Terrapin::OS.
|
4
|
+
allow(Terrapin::OS).to receive(:path_separator).and_return(";")
|
5
5
|
end
|
6
6
|
|
7
7
|
def on_unix!
|
8
8
|
stub_os('darwin11.0.0')
|
9
|
-
Terrapin::OS.
|
9
|
+
allow(Terrapin::OS).to receive(:path_separator).and_return(":")
|
10
10
|
end
|
11
11
|
|
12
12
|
def on_mingw!
|
13
13
|
stub_os('mingw')
|
14
|
-
Terrapin::OS.
|
14
|
+
allow(Terrapin::OS).to receive(:path_separator).and_return(";")
|
15
15
|
end
|
16
16
|
|
17
17
|
def on_java!
|
18
|
-
Terrapin::OS.
|
18
|
+
allow(Terrapin::OS).to receive(:arch).and_return("universal-java1.7")
|
19
19
|
end
|
20
20
|
|
21
21
|
def stub_os(host_string)
|
22
22
|
# http://blog.emptyway.com/2009/11/03/proper-way-to-detect-windows-platform-in-ruby/
|
23
|
-
RbConfig::CONFIG.
|
23
|
+
allow(RbConfig::CONFIG).to receive(:[]).with('host_os').and_return(host_string)
|
24
24
|
end
|
25
25
|
end
|
@@ -16,9 +16,9 @@ describe Terrapin::CommandLine::BackticksRunner do
|
|
16
16
|
|
17
17
|
it 'sets the exitstatus when a command completes' do
|
18
18
|
subject.call("ruby -e 'exit 0'")
|
19
|
-
$?.exitstatus.
|
19
|
+
expect($?.exitstatus).to eq(0)
|
20
20
|
subject.call("ruby -e 'exit 5'")
|
21
|
-
$?.exitstatus.
|
21
|
+
expect($?.exitstatus).to eq(5)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -4,19 +4,19 @@ describe Terrapin::CommandLine::FakeRunner do
|
|
4
4
|
it 'records commands' do
|
5
5
|
subject.call("some command", :environment)
|
6
6
|
subject.call("other command", :other_environment)
|
7
|
-
subject.commands.
|
7
|
+
expect(subject.commands).to eq [["some command", :environment], ["other command", :other_environment]]
|
8
8
|
end
|
9
9
|
|
10
10
|
it 'can tell if a command was run' do
|
11
11
|
subject.call("some command", :environment)
|
12
12
|
subject.call("other command", :other_environment)
|
13
|
-
subject.ran?("some command").
|
14
|
-
subject.ran?("no command").
|
13
|
+
expect(subject.ran?("some command")).to eq true
|
14
|
+
expect(subject.ran?("no command")).to eq false
|
15
15
|
end
|
16
16
|
|
17
17
|
it 'can tell if a command was run even if shell options were set' do
|
18
18
|
subject.call("something 2>/dev/null", :environment)
|
19
|
-
subject.ran?("something").
|
19
|
+
expect(subject.ran?("something")).to eq true
|
20
20
|
end
|
21
21
|
|
22
22
|
end
|
@@ -16,9 +16,9 @@ describe Terrapin::CommandLine::PopenRunner do
|
|
16
16
|
|
17
17
|
it 'sets the exitstatus when a command completes' do
|
18
18
|
subject.call("ruby -e 'exit 0'")
|
19
|
-
$?.exitstatus.
|
19
|
+
expect($?.exitstatus).to eq(0)
|
20
20
|
subject.call("ruby -e 'exit 5'")
|
21
|
-
$?.exitstatus.
|
21
|
+
expect($?.exitstatus).to eq(5)
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -21,9 +21,9 @@ describe Terrapin::CommandLine::ProcessRunner do
|
|
21
21
|
|
22
22
|
it 'sets the exitstatus when a command completes' do
|
23
23
|
subject.call("ruby -e 'exit 0'")
|
24
|
-
$?.exitstatus.
|
24
|
+
expect($?.exitstatus).to eq(0)
|
25
25
|
subject.call("ruby -e 'exit 5'")
|
26
|
-
$?.exitstatus.
|
26
|
+
expect($?.exitstatus).to eq(5)
|
27
27
|
end
|
28
28
|
|
29
29
|
it "runs the command it's given and allows access to stderr afterwards" do
|
@@ -6,37 +6,49 @@ describe Terrapin::CommandLine do
|
|
6
6
|
on_unix! # Assume we're on unix unless otherwise specified.
|
7
7
|
end
|
8
8
|
|
9
|
+
describe ".new" do
|
10
|
+
it "treats nil params as blank" do
|
11
|
+
cmd = Terrapin::CommandLine.new("echo", nil)
|
12
|
+
expect(cmd.run).to eq("\n")
|
13
|
+
end
|
14
|
+
|
15
|
+
it "raises when given nil options" do
|
16
|
+
expect { Terrapin::CommandLine.new("echo", "", nil) }
|
17
|
+
.to raise_error(ArgumentError)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
9
21
|
it "takes a command and parameters and produces a Bash command line" do
|
10
22
|
cmd = Terrapin::CommandLine.new("convert", "a.jpg b.png", :swallow_stderr => false)
|
11
|
-
cmd.command.
|
23
|
+
expect(cmd.command).to eq("convert a.jpg b.png")
|
12
24
|
end
|
13
25
|
|
14
26
|
it "specifies the $PATH where the command can be found on unix" do
|
15
27
|
Terrapin::CommandLine.path = ["/path/to/command/dir", "/"]
|
16
28
|
cmd = Terrapin::CommandLine.new("ls")
|
17
|
-
cmd.command.
|
29
|
+
expect(cmd.command).to eq("PATH=/path/to/command/dir:/:$PATH; ls")
|
18
30
|
end
|
19
31
|
|
20
32
|
it "specifies the %PATH% where the command can be found on windows" do
|
21
33
|
on_windows!
|
22
34
|
Terrapin::CommandLine.path = ['C:\system32', 'D:\\']
|
23
35
|
cmd = Terrapin::CommandLine.new("dir")
|
24
|
-
cmd.command.
|
36
|
+
expect(cmd.command).to eq('SET PATH=C:\system32;D:\;%PATH% & dir')
|
25
37
|
end
|
26
38
|
|
27
39
|
it "specifies more than one path where the command can be found" do
|
28
40
|
Terrapin::CommandLine.path = ["/path/to/command/dir", "/some/other/path"]
|
29
41
|
cmd = Terrapin::CommandLine.new("ruby", "-e 'puts ENV[%{PATH}]'")
|
30
42
|
output = cmd.run
|
31
|
-
output.
|
32
|
-
output.
|
43
|
+
expect(output).to match(%r{/path/to/command/dir})
|
44
|
+
expect(output).to match(%r{/some/other/path})
|
33
45
|
end
|
34
46
|
|
35
47
|
it "temporarily changes specified environment variables" do
|
36
48
|
Terrapin::CommandLine.environment['TEST'] = 'Hello, world!'
|
37
49
|
cmd = Terrapin::CommandLine.new("ruby", "-e 'puts ENV[%{TEST}]'")
|
38
50
|
output = cmd.run
|
39
|
-
output.
|
51
|
+
expect(output).to match(%r{Hello, world!})
|
40
52
|
end
|
41
53
|
|
42
54
|
it 'changes environment variables for the command line' do
|
@@ -45,13 +57,13 @@ describe Terrapin::CommandLine do
|
|
45
57
|
"-e 'puts ENV[%{TEST}]'",
|
46
58
|
:environment => {'TEST' => 'Hej hej'})
|
47
59
|
output = cmd.run
|
48
|
-
output.
|
60
|
+
expect(output).to match(%r{Hej hej})
|
49
61
|
end
|
50
62
|
|
51
63
|
it 'passes the existing environment variables through to the runner' do
|
52
64
|
command = Terrapin::CommandLine.new('echo', '$HOME')
|
53
65
|
output = command.run
|
54
|
-
output.chomp.
|
66
|
+
expect(output.chomp).not_to eq('')
|
55
67
|
end
|
56
68
|
|
57
69
|
it "can interpolate quoted variables into the command line's parameters" do
|
@@ -60,7 +72,7 @@ describe Terrapin::CommandLine do
|
|
60
72
|
:swallow_stderr => false)
|
61
73
|
|
62
74
|
command_string = cmd.command(:one => "a.jpg", :two => "b.png")
|
63
|
-
command_string.
|
75
|
+
expect(command_string).to eq("convert 'a.jpg' 'b.png'")
|
64
76
|
end
|
65
77
|
|
66
78
|
it 'does not over-interpolate in a command line' do
|
@@ -69,17 +81,17 @@ describe Terrapin::CommandLine do
|
|
69
81
|
:swallow_stderr => false)
|
70
82
|
|
71
83
|
command_string = cmd.command(:hell => "a.jpg", :two => "b.png", :hello => "c.tiff")
|
72
|
-
command_string.
|
84
|
+
expect(command_string).to eq("convert 'a.jpg' 'b.png' 'c.tiff'")
|
73
85
|
end
|
74
86
|
|
75
87
|
it "interpolates when running a command" do
|
76
88
|
command = Terrapin::CommandLine.new("echo", ":hello_world")
|
77
|
-
command.run(:hello_world => "Hello, world").
|
89
|
+
expect(command.run(:hello_world => "Hello, world")).to match(/Hello, world/)
|
78
90
|
end
|
79
91
|
|
80
92
|
it "interpolates any Array arguments when running a command" do
|
81
93
|
command = Terrapin::CommandLine.new("echo", "Hello :worlds and :dwarfs")
|
82
|
-
command.command(:worlds => %w[mercury venus earth], :dwarfs => "pluto").
|
94
|
+
expect(command.command(:worlds => %w[mercury venus earth], :dwarfs => "pluto")).to eq("echo Hello 'mercury' 'venus' 'earth' and 'pluto'")
|
83
95
|
end
|
84
96
|
|
85
97
|
it "quotes command line options differently if we're on windows" do
|
@@ -88,7 +100,7 @@ describe Terrapin::CommandLine do
|
|
88
100
|
":one :{two}",
|
89
101
|
:swallow_stderr => false)
|
90
102
|
command_string = cmd.command(:one => "a.jpg", :two => "b.png")
|
91
|
-
command_string.
|
103
|
+
expect(command_string).to eq('convert "a.jpg" "b.png"')
|
92
104
|
end
|
93
105
|
|
94
106
|
it "can quote and interpolate dangerous variables" do
|
@@ -96,12 +108,12 @@ describe Terrapin::CommandLine do
|
|
96
108
|
":one :two",
|
97
109
|
:swallow_stderr => false)
|
98
110
|
command_string = cmd.command(:one => "`rm -rf`.jpg", :two => "ha'ha.png'")
|
99
|
-
command_string.
|
111
|
+
expect(command_string).to eq("convert '`rm -rf`.jpg' 'ha'\\''ha.png'\\'''")
|
100
112
|
end
|
101
113
|
|
102
114
|
it 'cannot recursively introduce a place where user-supplied commands can run' do
|
103
115
|
cmd = Terrapin::CommandLine.new('convert', ':foo :bar')
|
104
|
-
cmd.command(:foo => ':bar', :bar => '`rm -rf`').
|
116
|
+
expect(cmd.command(:foo => ':bar', :bar => '`rm -rf`')).to eq('convert \':bar\' \'`rm -rf`\'')
|
105
117
|
end
|
106
118
|
|
107
119
|
it "can quote and interpolate dangerous variables even on windows" do
|
@@ -110,7 +122,7 @@ describe Terrapin::CommandLine do
|
|
110
122
|
":one :two",
|
111
123
|
:swallow_stderr => false)
|
112
124
|
command_string = cmd.command(:one => "`rm -rf`.jpg", :two => "ha'ha.png")
|
113
|
-
command_string.
|
125
|
+
expect(command_string).to eq(%{convert "`rm -rf`.jpg" "ha'ha.png"})
|
114
126
|
end
|
115
127
|
|
116
128
|
it "quotes blank values into the command line's parameters" do
|
@@ -118,18 +130,18 @@ describe Terrapin::CommandLine do
|
|
118
130
|
"-X POST -d :data :url",
|
119
131
|
:swallow_stderr => false)
|
120
132
|
command_string = cmd.command(:data => "", :url => "http://localhost:9000")
|
121
|
-
command_string.
|
133
|
+
expect(command_string).to eq("curl -X POST -d '' 'http://localhost:9000'")
|
122
134
|
end
|
123
135
|
|
124
136
|
it "allows colons in parameters" do
|
125
137
|
cmd = Terrapin::CommandLine.new("convert", "'a.jpg' xc:black 'b.jpg'", :swallow_stderr => false)
|
126
|
-
cmd.command.
|
138
|
+
expect(cmd.command).to eq("convert 'a.jpg' xc:black 'b.jpg'")
|
127
139
|
end
|
128
140
|
|
129
141
|
it 'handles symbols in user supplied values' do
|
130
142
|
cmd = Terrapin::CommandLine.new("echo", ":foo")
|
131
143
|
command_string = cmd.command(:foo => :bar)
|
132
|
-
command_string.
|
144
|
+
expect(command_string).to eq("echo 'bar'")
|
133
145
|
end
|
134
146
|
|
135
147
|
it "can redirect stderr to the bit bucket if requested" do
|
@@ -137,7 +149,7 @@ describe Terrapin::CommandLine do
|
|
137
149
|
"a.jpg b.png",
|
138
150
|
:swallow_stderr => true)
|
139
151
|
|
140
|
-
cmd.command.
|
152
|
+
expect(cmd.command).to eq("convert a.jpg b.png 2>/dev/null")
|
141
153
|
end
|
142
154
|
|
143
155
|
it "can redirect stderr to the bit bucket on windows" do
|
@@ -146,7 +158,7 @@ describe Terrapin::CommandLine do
|
|
146
158
|
"a.jpg b.png",
|
147
159
|
:swallow_stderr => true)
|
148
160
|
|
149
|
-
cmd.command.
|
161
|
+
expect(cmd.command).to eq("convert a.jpg b.png 2>NUL")
|
150
162
|
end
|
151
163
|
|
152
164
|
it "runs the command it's given and returns the output" do
|
@@ -163,33 +175,33 @@ describe Terrapin::CommandLine do
|
|
163
175
|
it "colorizes the output to a tty" do
|
164
176
|
logger = FakeLogger.new(:tty => true)
|
165
177
|
Terrapin::CommandLine.new("echo", "'Logging!' :foo", :logger => logger).run(:foo => "bar")
|
166
|
-
logger.entries.
|
178
|
+
expect(logger.entries).to include("\e[32mCommand\e[0m :: echo 'Logging!' 'bar'")
|
167
179
|
end
|
168
180
|
|
169
181
|
it 'can still take something that does not respond to tty as a logger' do
|
170
182
|
output_buffer = StringIO.new
|
171
183
|
logger = best_logger.new(output_buffer)
|
172
|
-
logger.
|
184
|
+
expect(logger).not_to respond_to(:tty?)
|
173
185
|
Terrapin::CommandLine.new("echo", "'Logging!' :foo", :logger => logger).run(:foo => "bar")
|
174
186
|
output_buffer.rewind
|
175
|
-
output_buffer.read.
|
187
|
+
expect(output_buffer.read).to eq("Command :: echo 'Logging!' 'bar'\n")
|
176
188
|
end
|
177
189
|
|
178
190
|
it "logs the command to a supplied logger" do
|
179
191
|
logger = FakeLogger.new
|
180
192
|
Terrapin::CommandLine.new("echo", "'Logging!' :foo", :logger => logger).run(:foo => "bar")
|
181
|
-
logger.entries.
|
193
|
+
expect(logger.entries).to include("Command :: echo 'Logging!' 'bar'")
|
182
194
|
end
|
183
195
|
|
184
196
|
it "logs the command to a default logger" do
|
185
197
|
Terrapin::CommandLine.logger = FakeLogger.new
|
186
198
|
Terrapin::CommandLine.new("echo", "'Logging!'").run
|
187
|
-
Terrapin::CommandLine.logger.entries.
|
199
|
+
expect(Terrapin::CommandLine.logger.entries).to include("Command :: echo 'Logging!'")
|
188
200
|
end
|
189
201
|
|
190
202
|
it "is fine if no logger is supplied" do
|
191
203
|
Terrapin::CommandLine.logger = nil
|
192
204
|
cmd = Terrapin::CommandLine.new("echo", "'Logging!'", :logger => nil)
|
193
|
-
|
205
|
+
expect { cmd.run }.not_to raise_error
|
194
206
|
end
|
195
207
|
end
|
@@ -3,38 +3,36 @@ require 'spec_helper'
|
|
3
3
|
describe "When an error happens" do
|
4
4
|
it "raises a CommandLineError if the result code command isn't expected" do
|
5
5
|
cmd = Terrapin::CommandLine.new("echo", "hello")
|
6
|
-
cmd.
|
6
|
+
expect(cmd).to receive(:execute)
|
7
7
|
with_exitstatus_returning(1) do
|
8
|
-
|
8
|
+
expect { cmd.run }.to raise_error(Terrapin::CommandLineError)
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
12
|
it "does not raise if the result code is expected, even if nonzero" do
|
13
13
|
cmd = Terrapin::CommandLine.new("echo", "hello", expected_outcodes: [0, 1])
|
14
|
-
cmd.
|
14
|
+
expect(cmd).to receive(:execute)
|
15
15
|
with_exitstatus_returning(1) do
|
16
|
-
|
16
|
+
expect { cmd.run }.not_to raise_error
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
20
|
it "adds command output to exception message if the result code is nonzero" do
|
21
21
|
cmd = Terrapin::CommandLine.new("echo", "hello")
|
22
22
|
error_output = "Error 315"
|
23
|
-
cmd.
|
24
|
-
stubs(:execute).
|
25
|
-
returns(Terrapin::CommandLine::Output.new("", error_output))
|
23
|
+
expect(cmd).to receive(:execute).and_return(Terrapin::CommandLine::Output.new("", error_output))
|
26
24
|
with_exitstatus_returning(1) do
|
27
25
|
begin
|
28
26
|
cmd.run
|
29
27
|
rescue Terrapin::ExitStatusError => e
|
30
|
-
e.message.
|
28
|
+
expect(e.message).to match(/STDERR:\s+#{error_output}/)
|
31
29
|
end
|
32
30
|
end
|
33
31
|
end
|
34
32
|
|
35
33
|
it 'passes the error message to the exception when command is not found' do
|
36
34
|
cmd = Terrapin::CommandLine.new('test', '')
|
37
|
-
cmd.
|
35
|
+
expect(cmd).to receive(:execute).and_raise(Errno::ENOENT.new("not found"))
|
38
36
|
begin
|
39
37
|
cmd.run
|
40
38
|
rescue Terrapin::CommandNotFoundError => e
|
@@ -44,19 +42,18 @@ describe "When an error happens" do
|
|
44
42
|
|
45
43
|
it "should keep result code in #exitstatus" do
|
46
44
|
cmd = Terrapin::CommandLine.new("convert")
|
47
|
-
cmd.
|
45
|
+
expect(cmd).to receive(:execute).with("convert").and_return(:correct_value)
|
48
46
|
with_exitstatus_returning(1) do
|
49
47
|
cmd.run rescue nil
|
50
48
|
end
|
51
|
-
cmd.exit_status.
|
49
|
+
expect(cmd.exit_status).to eq(1)
|
52
50
|
end
|
53
51
|
|
54
52
|
it "does not blow up if running the command errored before execution" do
|
55
|
-
|
56
|
-
command
|
57
|
-
command.stubs(:command).raises("An Error")
|
53
|
+
cmd = Terrapin::CommandLine.new("echo", ":hello_world")
|
54
|
+
expect(cmd).to receive(:command).and_raise("An Error")
|
58
55
|
|
59
|
-
|
60
|
-
|
56
|
+
expect{ cmd.run }.to raise_error("An Error")
|
57
|
+
expect(cmd.exit_status).to eq 0
|
61
58
|
end
|
62
59
|
end
|
@@ -3,21 +3,21 @@ require 'spec_helper'
|
|
3
3
|
describe Terrapin::OSDetector do
|
4
4
|
it "detects that the system is unix" do
|
5
5
|
on_unix!
|
6
|
-
Terrapin::OS.
|
6
|
+
expect(Terrapin::OS).to be_unix
|
7
7
|
end
|
8
8
|
|
9
9
|
it "detects that the system is windows" do
|
10
10
|
on_windows!
|
11
|
-
Terrapin::OS.
|
11
|
+
expect(Terrapin::OS).to be_windows
|
12
12
|
end
|
13
13
|
|
14
14
|
it "detects that the system is windows (mingw)" do
|
15
15
|
on_mingw!
|
16
|
-
Terrapin::OS.
|
16
|
+
expect(Terrapin::OS).to be_windows
|
17
17
|
end
|
18
18
|
|
19
19
|
it "detects that the current Ruby is on Java" do
|
20
20
|
on_java!
|
21
|
-
Terrapin::OS.
|
21
|
+
expect(Terrapin::OS).to be_java
|
22
22
|
end
|
23
23
|
end
|
@@ -2,49 +2,37 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe "When picking a Runner" do
|
4
4
|
it "uses the BackticksRunner by default" do
|
5
|
-
Terrapin::CommandLine::ProcessRunner.
|
6
|
-
Terrapin::CommandLine::PosixRunner.stubs(:supported?).returns(false)
|
5
|
+
expect(Terrapin::CommandLine::ProcessRunner).to receive(:supported?).and_return(false)
|
7
6
|
|
8
7
|
cmd = Terrapin::CommandLine.new("echo", "hello")
|
9
8
|
|
10
|
-
cmd.runner.class.
|
9
|
+
expect(cmd.runner.class).to eq(Terrapin::CommandLine::BackticksRunner)
|
11
10
|
end
|
12
11
|
|
13
12
|
it "uses the ProcessRunner on 1.9 and it's available" do
|
14
|
-
Terrapin::CommandLine::ProcessRunner.
|
15
|
-
Terrapin::CommandLine::PosixRunner.stubs(:supported?).returns(false)
|
13
|
+
expect(Terrapin::CommandLine::ProcessRunner).to receive(:supported?).and_return(true)
|
16
14
|
|
17
15
|
cmd = Terrapin::CommandLine.new("echo", "hello")
|
18
|
-
cmd.runner.class.
|
16
|
+
expect(cmd.runner.class).to eq(Terrapin::CommandLine::ProcessRunner)
|
19
17
|
end
|
20
18
|
|
21
|
-
it "uses the
|
22
|
-
Terrapin::CommandLine::PosixRunner.stubs(:supported?).returns(true)
|
23
|
-
|
24
|
-
cmd = Terrapin::CommandLine.new("echo", "hello")
|
25
|
-
cmd.runner.class.should == Terrapin::CommandLine::PosixRunner
|
26
|
-
end
|
27
|
-
|
28
|
-
it "uses the BackticksRunner if the PosixRunner is available, but we told it to use Backticks all the time" do
|
29
|
-
Terrapin::CommandLine::PosixRunner.stubs(:supported?).returns(true)
|
19
|
+
it "uses the BackticksRunner if we told it to use Backticks all the time" do
|
30
20
|
Terrapin::CommandLine.runner = Terrapin::CommandLine::BackticksRunner.new
|
31
21
|
|
32
22
|
cmd = Terrapin::CommandLine.new("echo", "hello")
|
33
|
-
cmd.runner.class.
|
23
|
+
expect(cmd.runner.class).to eq(Terrapin::CommandLine::BackticksRunner)
|
34
24
|
end
|
35
25
|
|
36
|
-
it "uses the BackticksRunner if
|
37
|
-
Terrapin::CommandLine::PosixRunner.stubs(:supported?).returns(true)
|
38
|
-
|
26
|
+
it "uses the BackticksRunner, if we told it to use Backticks" do
|
39
27
|
cmd = Terrapin::CommandLine.new("echo", "hello", :runner => Terrapin::CommandLine::BackticksRunner.new)
|
40
|
-
cmd.runner.class.
|
28
|
+
expect(cmd.runner.class).to eq(Terrapin::CommandLine::BackticksRunner)
|
41
29
|
end
|
42
30
|
|
43
31
|
it "can go into 'Fake' mode" do
|
44
32
|
Terrapin::CommandLine.fake!
|
45
33
|
|
46
34
|
cmd = Terrapin::CommandLine.new("echo", "hello")
|
47
|
-
cmd.runner.class.
|
35
|
+
expect(cmd.runner.class).to eq Terrapin::CommandLine::FakeRunner
|
48
36
|
end
|
49
37
|
|
50
38
|
it "can turn off Fake mode" do
|
@@ -52,14 +40,14 @@ describe "When picking a Runner" do
|
|
52
40
|
Terrapin::CommandLine.unfake!
|
53
41
|
|
54
42
|
cmd = Terrapin::CommandLine.new("echo", "hello")
|
55
|
-
cmd.runner.class.
|
43
|
+
expect(cmd.runner.class).not_to eq Terrapin::CommandLine::FakeRunner
|
56
44
|
end
|
57
45
|
|
58
46
|
it "can use a FakeRunner even if not in Fake mode" do
|
59
47
|
Terrapin::CommandLine.unfake!
|
60
48
|
|
61
49
|
cmd = Terrapin::CommandLine.new("echo", "hello", :runner => Terrapin::CommandLine::FakeRunner.new)
|
62
|
-
cmd.runner.class.
|
50
|
+
expect(cmd.runner.class).to eq Terrapin::CommandLine::FakeRunner
|
63
51
|
end
|
64
52
|
end
|
65
53
|
|
@@ -78,7 +66,6 @@ describe 'When running an executable in the supplemental path' do
|
|
78
66
|
[
|
79
67
|
Terrapin::CommandLine::BackticksRunner,
|
80
68
|
Terrapin::CommandLine::PopenRunner,
|
81
|
-
Terrapin::CommandLine::PosixRunner,
|
82
69
|
Terrapin::CommandLine::ProcessRunner
|
83
70
|
].each do |runner_class|
|
84
71
|
if runner_class.supported?
|
data/terrapin.gemspec
CHANGED
@@ -1,28 +1,24 @@
|
|
1
1
|
$LOAD_PATH.push File.expand_path("../lib", __FILE__)
|
2
|
-
require
|
2
|
+
require "terrapin/version"
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
|
-
s.name
|
6
|
-
s.version
|
7
|
-
s.platform
|
8
|
-
s.author
|
9
|
-
s.email
|
10
|
-
s.homepage
|
11
|
-
s.summary
|
12
|
-
s.description
|
13
|
-
s.license
|
5
|
+
s.name = "terrapin"
|
6
|
+
s.version = Terrapin::VERSION.dup
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.author = "Jon Yurek"
|
9
|
+
s.email = "jyurek@thoughtbot.com"
|
10
|
+
s.homepage = "https://github.com/thoughtbot/terrapin"
|
11
|
+
s.summary = "Run shell commands safely, even with user-supplied values"
|
12
|
+
s.description = "Run shell commands safely, even with user-supplied values"
|
13
|
+
s.license = "MIT"
|
14
14
|
|
15
|
-
s.files
|
16
|
-
s.
|
17
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
18
17
|
s.require_paths = ["lib"]
|
19
18
|
|
20
|
-
s.add_dependency(
|
21
|
-
s.add_development_dependency(
|
22
|
-
s.add_development_dependency(
|
23
|
-
s.add_development_dependency(
|
24
|
-
s.add_development_dependency(
|
25
|
-
s.add_development_dependency('activesupport', ">= 3.0.0", "< 5.0")
|
26
|
-
s.add_development_dependency('pry')
|
19
|
+
s.add_dependency("climate_control")
|
20
|
+
s.add_development_dependency("rspec")
|
21
|
+
s.add_development_dependency("rake")
|
22
|
+
s.add_development_dependency("activesupport", ">= 3.0.0", "< 5.0")
|
23
|
+
s.add_development_dependency("pry")
|
27
24
|
end
|
28
|
-
|
metadata
CHANGED
@@ -1,57 +1,23 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: terrapin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Yurek
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: climate_control
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 0.0.3
|
20
|
-
- - "<"
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
version: '1.0'
|
23
|
-
type: :runtime
|
24
|
-
prerelease: false
|
25
|
-
version_requirements: !ruby/object:Gem::Requirement
|
26
|
-
requirements:
|
27
|
-
- - ">="
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: 0.0.3
|
30
|
-
- - "<"
|
31
|
-
- !ruby/object:Gem::Version
|
32
|
-
version: '1.0'
|
33
|
-
- !ruby/object:Gem::Dependency
|
34
|
-
name: rspec
|
35
|
-
requirement: !ruby/object:Gem::Requirement
|
36
|
-
requirements:
|
37
|
-
- - ">="
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version: '0'
|
40
|
-
type: :development
|
41
|
-
prerelease: false
|
42
|
-
version_requirements: !ruby/object:Gem::Requirement
|
43
16
|
requirements:
|
44
17
|
- - ">="
|
45
18
|
- !ruby/object:Gem::Version
|
46
19
|
version: '0'
|
47
|
-
|
48
|
-
name: bourne
|
49
|
-
requirement: !ruby/object:Gem::Requirement
|
50
|
-
requirements:
|
51
|
-
- - ">="
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
version: '0'
|
54
|
-
type: :development
|
20
|
+
type: :runtime
|
55
21
|
prerelease: false
|
56
22
|
version_requirements: !ruby/object:Gem::Requirement
|
57
23
|
requirements:
|
@@ -59,7 +25,7 @@ dependencies:
|
|
59
25
|
- !ruby/object:Gem::Version
|
60
26
|
version: '0'
|
61
27
|
- !ruby/object:Gem::Dependency
|
62
|
-
name:
|
28
|
+
name: rspec
|
63
29
|
requirement: !ruby/object:Gem::Requirement
|
64
30
|
requirements:
|
65
31
|
- - ">="
|
@@ -126,6 +92,7 @@ executables: []
|
|
126
92
|
extensions: []
|
127
93
|
extra_rdoc_files: []
|
128
94
|
files:
|
95
|
+
- ".github/workflows/ci.yml"
|
129
96
|
- ".gitignore"
|
130
97
|
- ".travis.yml"
|
131
98
|
- GOALS
|
@@ -142,7 +109,6 @@ files:
|
|
142
109
|
- lib/terrapin/command_line/runners/backticks_runner.rb
|
143
110
|
- lib/terrapin/command_line/runners/fake_runner.rb
|
144
111
|
- lib/terrapin/command_line/runners/popen_runner.rb
|
145
|
-
- lib/terrapin/command_line/runners/posix_runner.rb
|
146
112
|
- lib/terrapin/command_line/runners/process_runner.rb
|
147
113
|
- lib/terrapin/exceptions.rb
|
148
114
|
- lib/terrapin/os_detector.rb
|
@@ -152,13 +118,11 @@ files:
|
|
152
118
|
- spec/support/have_output.rb
|
153
119
|
- spec/support/nonblocking_examples.rb
|
154
120
|
- spec/support/stub_os.rb
|
155
|
-
- spec/support/unsetting_exitstatus.rb
|
156
121
|
- spec/support/with_exitstatus.rb
|
157
122
|
- spec/terrapin/command_line/output_spec.rb
|
158
123
|
- spec/terrapin/command_line/runners/backticks_runner_spec.rb
|
159
124
|
- spec/terrapin/command_line/runners/fake_runner_spec.rb
|
160
125
|
- spec/terrapin/command_line/runners/popen_runner_spec.rb
|
161
|
-
- spec/terrapin/command_line/runners/posix_runner_spec.rb
|
162
126
|
- spec/terrapin/command_line/runners/process_runner_spec.rb
|
163
127
|
- spec/terrapin/command_line_spec.rb
|
164
128
|
- spec/terrapin/errors_spec.rb
|
@@ -169,7 +133,7 @@ homepage: https://github.com/thoughtbot/terrapin
|
|
169
133
|
licenses:
|
170
134
|
- MIT
|
171
135
|
metadata: {}
|
172
|
-
post_install_message:
|
136
|
+
post_install_message:
|
173
137
|
rdoc_options: []
|
174
138
|
require_paths:
|
175
139
|
- lib
|
@@ -180,30 +144,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
180
144
|
version: '0'
|
181
145
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
182
146
|
requirements:
|
183
|
-
- - "
|
147
|
+
- - ">="
|
184
148
|
- !ruby/object:Gem::Version
|
185
|
-
version:
|
149
|
+
version: '0'
|
186
150
|
requirements: []
|
187
|
-
|
188
|
-
|
189
|
-
signing_key:
|
151
|
+
rubygems_version: 3.4.10
|
152
|
+
signing_key:
|
190
153
|
specification_version: 4
|
191
154
|
summary: Run shell commands safely, even with user-supplied values
|
192
|
-
test_files:
|
193
|
-
- spec/spec_helper.rb
|
194
|
-
- spec/support/fake_logger.rb
|
195
|
-
- spec/support/have_output.rb
|
196
|
-
- spec/support/nonblocking_examples.rb
|
197
|
-
- spec/support/stub_os.rb
|
198
|
-
- spec/support/unsetting_exitstatus.rb
|
199
|
-
- spec/support/with_exitstatus.rb
|
200
|
-
- spec/terrapin/command_line/output_spec.rb
|
201
|
-
- spec/terrapin/command_line/runners/backticks_runner_spec.rb
|
202
|
-
- spec/terrapin/command_line/runners/fake_runner_spec.rb
|
203
|
-
- spec/terrapin/command_line/runners/popen_runner_spec.rb
|
204
|
-
- spec/terrapin/command_line/runners/posix_runner_spec.rb
|
205
|
-
- spec/terrapin/command_line/runners/process_runner_spec.rb
|
206
|
-
- spec/terrapin/command_line_spec.rb
|
207
|
-
- spec/terrapin/errors_spec.rb
|
208
|
-
- spec/terrapin/os_detector_spec.rb
|
209
|
-
- spec/terrapin/runners_spec.rb
|
155
|
+
test_files: []
|
@@ -1,49 +0,0 @@
|
|
1
|
-
# coding: UTF-8
|
2
|
-
|
3
|
-
module Terrapin
|
4
|
-
class CommandLine
|
5
|
-
class PosixRunner
|
6
|
-
def self.available?
|
7
|
-
return @available unless @available.nil?
|
8
|
-
|
9
|
-
@available = posix_spawn_gem_available?
|
10
|
-
end
|
11
|
-
|
12
|
-
def self.supported?
|
13
|
-
available? && !OS.java?
|
14
|
-
end
|
15
|
-
|
16
|
-
def supported?
|
17
|
-
self.class.supported?
|
18
|
-
end
|
19
|
-
|
20
|
-
def call(command, env = {}, options = {})
|
21
|
-
pipe = MultiPipe.new
|
22
|
-
pid = spawn(env, command, options.merge(pipe.pipe_options))
|
23
|
-
pipe.read_and_then do
|
24
|
-
waitpid(pid)
|
25
|
-
end
|
26
|
-
pipe.output
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def spawn(*args)
|
32
|
-
POSIX::Spawn.spawn(*args)
|
33
|
-
end
|
34
|
-
|
35
|
-
def waitpid(pid)
|
36
|
-
Process.waitpid(pid)
|
37
|
-
end
|
38
|
-
|
39
|
-
def self.posix_spawn_gem_available?
|
40
|
-
require 'posix/spawn'
|
41
|
-
true
|
42
|
-
rescue LoadError
|
43
|
-
false
|
44
|
-
end
|
45
|
-
|
46
|
-
private_class_method :posix_spawn_gem_available?
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Terrapin::CommandLine::PosixRunner do
|
4
|
-
if Terrapin::CommandLine::PosixRunner.supported?
|
5
|
-
it_behaves_like 'a command that does not block'
|
6
|
-
|
7
|
-
it 'runs the command given and captures the output' do
|
8
|
-
output = subject.call("echo hello")
|
9
|
-
expect(output).to have_output "hello\n"
|
10
|
-
end
|
11
|
-
|
12
|
-
it 'runs the command given and captures the error output' do
|
13
|
-
output = subject.call("echo hello 1>&2")
|
14
|
-
expect(output).to have_error_output "hello\n"
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'modifies the environment and runs the command given' do
|
18
|
-
output = subject.call("echo $yes", {"yes" => "no"})
|
19
|
-
expect(output).to have_output "no\n"
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'sets the exitstatus when a command completes' do
|
23
|
-
subject.call("ruby -e 'exit 0'")
|
24
|
-
$?.exitstatus.should == 0
|
25
|
-
subject.call("ruby -e 'exit 5'")
|
26
|
-
$?.exitstatus.should == 5
|
27
|
-
end
|
28
|
-
|
29
|
-
it "runs the command it's given and allows access to stderr afterwards" do
|
30
|
-
cmd = Terrapin::CommandLine.new(
|
31
|
-
"ruby",
|
32
|
-
"-e '$stdout.puts %{hello}; $stderr.puts %{goodbye}'",
|
33
|
-
:swallow_stderr => false
|
34
|
-
)
|
35
|
-
cmd.run
|
36
|
-
expect(cmd.command_output).to eq "hello\n"
|
37
|
-
expect(cmd.command_error_output).to eq "goodbye\n"
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|