ruby-sh 2.1.4 → 2.2.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.editorconfig +9 -0
- data/.rubocop.yml +1 -0
- data/.ruby-version +1 -0
- data/Gemfile +17 -1
- data/Gemfile.lock +62 -11
- data/README.md +12 -35
- data/Rakefile +4 -5
- data/lib/rubsh/command.rb +2 -2
- data/lib/rubsh/running_command.rb +60 -35
- data/lib/rubsh/running_pipeline.rb +66 -41
- data/lib/rubsh/version.rb +1 -1
- data/lib/rubsh.rb +9 -0
- metadata +4 -4
- data/.rubocop +0 -0
- data/sig/rubsh.rbs +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ed7c44c0751c5a302ee4f576ff386d5e345599caf3d06e302d81c21d58bc8729
|
4
|
+
data.tar.gz: 16c410868acd95c98ddfd78db9883136aa3e1d4361358463b72c078a371830c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16924112923bd153dbd9c69fee521f58e3b2fd394846ac110142cf04152a5f3d3533c2d88984c9c9d0723c64743c5e3f1003ecd0fe4620edea3d4be199030fa4
|
7
|
+
data.tar.gz: b9434103597b613d4a6c6aaf82da6f72ca850dccd453eea9b1e5ec561ffd2a202894dfbdcfcd2acfb17229eee780c7c47c377546e985e36dd372a233b76d8999
|
data/.editorconfig
ADDED
data/.rubocop.yml
CHANGED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.1.1
|
data/Gemfile
CHANGED
@@ -5,6 +5,22 @@ source "https://rubygems.org"
|
|
5
5
|
# Specify your gem's dependencies in rubsh.gemspec
|
6
6
|
gemspec
|
7
7
|
|
8
|
+
# rake
|
8
9
|
gem "rake", "~> 13.0"
|
9
|
-
|
10
|
+
|
11
|
+
# lint
|
12
|
+
gem "rubocop", "~> 1.29"
|
10
13
|
gem "standard", "~> 1.12"
|
14
|
+
|
15
|
+
# test
|
16
|
+
gem "rspec", "~> 3.0"
|
17
|
+
|
18
|
+
# doc
|
19
|
+
gem "yard", "~> 0.9"
|
20
|
+
gem "redcarpet", "~> 3.5"
|
21
|
+
|
22
|
+
# lsp
|
23
|
+
gem "solargraph"
|
24
|
+
|
25
|
+
# misc
|
26
|
+
gem "overcommit", "~> 0.59"
|
data/Gemfile.lock
CHANGED
@@ -1,19 +1,44 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ruby-sh (2.
|
4
|
+
ruby-sh (2.2.6)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
9
|
ast (2.4.2)
|
10
|
+
backport (1.2.0)
|
11
|
+
benchmark (0.2.1)
|
12
|
+
childprocess (4.1.0)
|
10
13
|
diff-lcs (1.5.0)
|
14
|
+
e2mmap (0.1.0)
|
15
|
+
iniparse (1.5.0)
|
16
|
+
jaro_winkler (1.5.6)
|
17
|
+
json (2.6.2)
|
18
|
+
kramdown (2.4.0)
|
19
|
+
rexml
|
20
|
+
kramdown-parser-gfm (1.1.0)
|
21
|
+
kramdown (~> 2.0)
|
22
|
+
mini_portile2 (2.8.4)
|
23
|
+
nokogiri (1.15.4)
|
24
|
+
mini_portile2 (~> 2.8.2)
|
25
|
+
racc (~> 1.4)
|
26
|
+
nokogiri (1.15.4-x86_64-linux)
|
27
|
+
racc (~> 1.4)
|
28
|
+
overcommit (0.59.1)
|
29
|
+
childprocess (>= 0.6.3, < 5)
|
30
|
+
iniparse (~> 1.4)
|
31
|
+
rexml (~> 3.2)
|
11
32
|
parallel (1.22.1)
|
12
|
-
parser (3.1.2.
|
33
|
+
parser (3.1.2.1)
|
13
34
|
ast (~> 2.4.1)
|
35
|
+
racc (1.7.1)
|
14
36
|
rainbow (3.1.1)
|
15
37
|
rake (13.0.6)
|
38
|
+
redcarpet (3.5.1)
|
16
39
|
regexp_parser (2.5.0)
|
40
|
+
reverse_markdown (2.1.1)
|
41
|
+
nokogiri
|
17
42
|
rexml (3.2.5)
|
18
43
|
rspec (3.11.0)
|
19
44
|
rspec-core (~> 3.11.0)
|
@@ -28,35 +53,61 @@ GEM
|
|
28
53
|
diff-lcs (>= 1.2.0, < 2.0)
|
29
54
|
rspec-support (~> 3.11.0)
|
30
55
|
rspec-support (3.11.0)
|
31
|
-
rubocop (1.
|
56
|
+
rubocop (1.35.0)
|
57
|
+
json (~> 2.3)
|
32
58
|
parallel (~> 1.10)
|
33
|
-
parser (>= 3.1.
|
59
|
+
parser (>= 3.1.2.1)
|
34
60
|
rainbow (>= 2.2.2, < 4.0)
|
35
61
|
regexp_parser (>= 1.8, < 3.0)
|
36
62
|
rexml (>= 3.2.5, < 4.0)
|
37
|
-
rubocop-ast (>= 1.
|
63
|
+
rubocop-ast (>= 1.20.1, < 2.0)
|
38
64
|
ruby-progressbar (~> 1.7)
|
39
65
|
unicode-display_width (>= 1.4.0, < 3.0)
|
40
|
-
rubocop-ast (1.
|
66
|
+
rubocop-ast (1.21.0)
|
41
67
|
parser (>= 3.1.1.0)
|
42
|
-
rubocop-performance (1.
|
68
|
+
rubocop-performance (1.14.3)
|
43
69
|
rubocop (>= 1.7.0, < 2.0)
|
44
70
|
rubocop-ast (>= 0.4.0)
|
45
71
|
ruby-progressbar (1.11.0)
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
72
|
+
solargraph (0.48.0)
|
73
|
+
backport (~> 1.2)
|
74
|
+
benchmark
|
75
|
+
bundler (>= 1.17.2)
|
76
|
+
diff-lcs (~> 1.4)
|
77
|
+
e2mmap
|
78
|
+
jaro_winkler (~> 1.5)
|
79
|
+
kramdown (~> 2.3)
|
80
|
+
kramdown-parser-gfm (~> 1.1)
|
81
|
+
parser (~> 3.0)
|
82
|
+
reverse_markdown (>= 1.0.5, < 3)
|
83
|
+
rubocop (>= 0.52)
|
84
|
+
thor (~> 1.0)
|
85
|
+
tilt (~> 2.0)
|
86
|
+
yard (~> 0.9, >= 0.9.24)
|
87
|
+
standard (1.16.0)
|
88
|
+
rubocop (= 1.35.0)
|
89
|
+
rubocop-performance (= 1.14.3)
|
90
|
+
thor (1.2.2)
|
91
|
+
tilt (2.3.0)
|
92
|
+
unicode-display_width (2.2.0)
|
93
|
+
webrick (1.7.0)
|
94
|
+
yard (0.9.28)
|
95
|
+
webrick (~> 1.7.0)
|
50
96
|
|
51
97
|
PLATFORMS
|
52
98
|
ruby
|
53
99
|
x86_64-linux
|
54
100
|
|
55
101
|
DEPENDENCIES
|
102
|
+
overcommit (~> 0.59)
|
56
103
|
rake (~> 13.0)
|
104
|
+
redcarpet (~> 3.5)
|
57
105
|
rspec (~> 3.0)
|
106
|
+
rubocop (~> 1.29)
|
58
107
|
ruby-sh!
|
108
|
+
solargraph
|
59
109
|
standard (~> 1.12)
|
110
|
+
yard (~> 0.9)
|
60
111
|
|
61
112
|
BUNDLED WITH
|
62
113
|
2.3.8
|
data/README.md
CHANGED
@@ -5,7 +5,7 @@ Rubsh (a.k.a. ruby-sh) - Inspired by [python-sh], allows you to call any program
|
|
5
5
|
```ruby
|
6
6
|
require 'rubsh'
|
7
7
|
|
8
|
-
sh = Rubsh
|
8
|
+
sh = Rubsh.new
|
9
9
|
print(sh.cmd('ifconfig').call_with('wlan0').stdout_data)
|
10
10
|
```
|
11
11
|
|
@@ -34,34 +34,6 @@ When using this library you can:
|
|
34
34
|
* etc.
|
35
35
|
|
36
36
|
|
37
|
-
## Table of Contents
|
38
|
-
|
39
|
-
* [Installation](#installation)
|
40
|
-
* [Usage](#usage)
|
41
|
-
* [Basic Syntax](#basic-syntax)
|
42
|
-
* [Passing Arguments](#passing-arguments)
|
43
|
-
* [Exit Codes](#exit-codes)
|
44
|
-
* [Redirection](#redirection)
|
45
|
-
* [Incremental Iteration](#incremental-iteration)
|
46
|
-
* [Background Processes](#background-processes)
|
47
|
-
* [Baking](#baking)
|
48
|
-
* [Subcommands](#subcommands)
|
49
|
-
* [Piping](#piping)
|
50
|
-
* [Reference](#reference)
|
51
|
-
* [Special Kwargs](#special-kwargs)
|
52
|
-
* [FAQ](#faq)
|
53
|
-
* [Why doesn’t `*` work as a command argument?](#why-doesnt--work-as-a-command-argument)
|
54
|
-
* [How do I execute a bash builtin?](#how-do-i-execute-a-bash-builtin)
|
55
|
-
* [How do I call a program that isn’t in $PATH?](#how-do-i-call-a-program-that-isnt-in-path)
|
56
|
-
* [How do I run a command and connect it to stdout and stdin?](#how-do-i-run-a-command-and-connect-it-to-stdout-and-stdin)
|
57
|
-
* [How do I order keyword arguments?](#how-do-i-order-keyword-arguments)
|
58
|
-
* [Development](#development)
|
59
|
-
* [Contributing](#contributing)
|
60
|
-
* [Acknowledgements](#acknowledgements)
|
61
|
-
* [License](#license)
|
62
|
-
* [Code Of Conduct](#code-of-conduct)
|
63
|
-
|
64
|
-
|
65
37
|
## Installation
|
66
38
|
|
67
39
|
Add this line to your application's Gemfile:
|
@@ -85,7 +57,7 @@ Or install it yourself as:
|
|
85
57
|
|
86
58
|
```ruby
|
87
59
|
# Create a shell
|
88
|
-
sh = Rubsh
|
60
|
+
sh = Rubsh.new
|
89
61
|
|
90
62
|
# Create a command, use `command`/`cmd`
|
91
63
|
cmd = sh.cmd("ls")
|
@@ -218,6 +190,11 @@ sh.cmd('ssh').call_with("myserver.com", "-p 1393", "whoami")
|
|
218
190
|
myserver = sh.cmd('ssh').bake("myserver.com", p: 1393)
|
219
191
|
myserver.call_with('whoami')
|
220
192
|
myserver.call_with('pwd')
|
193
|
+
|
194
|
+
# With a special kwarg
|
195
|
+
sleep = sh.cmd('sleep').bake(_timeout: 2)
|
196
|
+
sleep.call(1) # => ok
|
197
|
+
sleep.call(3) # => a `CommandReturnFailureError` raised
|
221
198
|
```
|
222
199
|
|
223
200
|
### Subcommands
|
@@ -311,7 +288,7 @@ Glob expansion is a feature of a shell, like Bash, and is performed by the shell
|
|
311
288
|
### How do I execute a bash builtin?
|
312
289
|
|
313
290
|
```ruby
|
314
|
-
sh = Rubsh
|
291
|
+
sh = Rubsh.new
|
315
292
|
rawsh = sh.cmd('bash').bake('-c')
|
316
293
|
print(rawsh.call_with('echo Hello').stdout_data) # => "Hello\n"
|
317
294
|
```
|
@@ -321,14 +298,14 @@ print(rawsh.call_with('echo Hello').stdout_data) # => "Hello\n"
|
|
321
298
|
Use absolute binpath
|
322
299
|
|
323
300
|
```ruby
|
324
|
-
sh = Rubsh
|
301
|
+
sh = Rubsh.new
|
325
302
|
sh.cmd('/path/to/command').call()
|
326
303
|
```
|
327
304
|
|
328
|
-
|
305
|
+
Or use `Rubsh::Shell::Env#path`
|
329
306
|
|
330
307
|
```ruby
|
331
|
-
sh = Rubsh
|
308
|
+
sh = Rubsh.new
|
332
309
|
sh.env.path << "/dir/to/command/"
|
333
310
|
sh.cmd('command').call()
|
334
311
|
```
|
@@ -348,7 +325,7 @@ my-command --arg1=val1 arg2 --arg3=val3
|
|
348
325
|
Use:
|
349
326
|
|
350
327
|
```ruby
|
351
|
-
sh = Rubsh
|
328
|
+
sh = Rubsh.new
|
352
329
|
sh.cmd('my-command').call_with({ arg1: "val1" }, "args2", { arg3: "val3" })
|
353
330
|
```
|
354
331
|
|
data/Rakefile
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "bundler/gem_tasks"
|
4
|
-
require "rspec/core/rake_task"
|
5
|
-
|
6
|
-
RSpec::Core::RakeTask.new(:spec)
|
7
4
|
|
8
5
|
require "rubocop/rake_task"
|
9
|
-
|
10
6
|
RuboCop::RakeTask.new
|
11
7
|
|
12
|
-
|
8
|
+
require "rspec/core/rake_task"
|
9
|
+
RSpec::Core::RakeTask.new(:spec)
|
10
|
+
|
11
|
+
task default: %i[rubocop spec]
|
data/lib/rubsh/command.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
require "timeout"
|
2
|
-
|
3
1
|
module Rubsh
|
4
2
|
class RunningCommand
|
5
3
|
SPECIAL_KWARGS = %i[
|
@@ -39,6 +37,14 @@ module Rubsh
|
|
39
37
|
# @return [Number]
|
40
38
|
attr_reader :exit_code
|
41
39
|
|
40
|
+
# @!attribute [r] started_at
|
41
|
+
# @return [Time]
|
42
|
+
attr_reader :started_at
|
43
|
+
|
44
|
+
# @!attribute [r] finished_at
|
45
|
+
# @return [Time]
|
46
|
+
attr_reader :finished_at
|
47
|
+
|
42
48
|
# @!attribute [r] stdout_data
|
43
49
|
# @return [String]
|
44
50
|
attr_reader :stdout_data
|
@@ -57,6 +63,8 @@ module Rubsh
|
|
57
63
|
@prog_with_args = nil
|
58
64
|
@pid = nil
|
59
65
|
@exit_code = nil
|
66
|
+
@started_at = nil
|
67
|
+
@finished_at = nil
|
60
68
|
@stdout_data = "".force_encoding(::Encoding.default_external)
|
61
69
|
@stderr_data = "".force_encoding(::Encoding.default_external)
|
62
70
|
@in_rd = nil
|
@@ -109,42 +117,26 @@ module Rubsh
|
|
109
117
|
extract_opts(opts)
|
110
118
|
end
|
111
119
|
|
112
|
-
# @return [
|
113
|
-
def
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
if timeout
|
118
|
-
begin
|
119
|
-
::Timeout.timeout(timeout) { _, status = ::Process.wait2(@pid) }
|
120
|
-
rescue ::Timeout::Error
|
121
|
-
timeout_occurred = true
|
120
|
+
# @return [Numeric, nil]
|
121
|
+
def wall_time
|
122
|
+
@finished_at.nil? ? nil : @finished_at - @started_at
|
123
|
+
end
|
124
|
+
alias_method :execution_time, :wall_time
|
122
125
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
sleep 0.1
|
128
|
-
end
|
129
|
-
failure = @pid if status.nil?
|
130
|
-
failure && ::Process.kill("KILL", failure) # forceful stop
|
131
|
-
end
|
132
|
-
else
|
133
|
-
_, status = ::Process.wait2(@pid)
|
134
|
-
end
|
126
|
+
# @return [Boolean]
|
127
|
+
def ok?
|
128
|
+
@_ok_code.include?(@exit_code)
|
129
|
+
end
|
135
130
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
ensure
|
141
|
-
@out_rd_reader&.wait
|
142
|
-
@err_rd_reader&.wait
|
131
|
+
# @return [void]
|
132
|
+
def wait(timeout: nil)
|
133
|
+
wait2(timeout: timeout)
|
134
|
+
handle_return_code
|
143
135
|
end
|
144
136
|
|
145
137
|
# @return [String]
|
146
|
-
def
|
147
|
-
|
138
|
+
def to_s
|
139
|
+
@prog_with_args
|
148
140
|
end
|
149
141
|
|
150
142
|
# @!visibility private
|
@@ -296,6 +288,7 @@ module Rubsh
|
|
296
288
|
def spawn
|
297
289
|
args = __spawn_arguments
|
298
290
|
@pid = ::Process.spawn(*args)
|
291
|
+
@started_at = Time.now
|
299
292
|
@in_wr&.write(@_in_data) if @_in_data
|
300
293
|
@in_wr&.close
|
301
294
|
|
@@ -317,8 +310,41 @@ module Rubsh
|
|
317
310
|
@err_wr&.close
|
318
311
|
end
|
319
312
|
|
313
|
+
def wait2(timeout: nil)
|
314
|
+
timeout_occurred = false
|
315
|
+
_, status = nil, nil
|
316
|
+
|
317
|
+
if timeout
|
318
|
+
begin
|
319
|
+
::Timeout.timeout(timeout) { _, status = ::Process.wait2(@pid) }
|
320
|
+
rescue ::Timeout::Error
|
321
|
+
timeout_occurred = true
|
322
|
+
|
323
|
+
::Process.kill("TERM", @pid) # graceful stop
|
324
|
+
30.times do
|
325
|
+
_, status = ::Process.wait2(@pid, ::Process::WNOHANG | ::Process::WUNTRACED)
|
326
|
+
break if status
|
327
|
+
sleep 0.1
|
328
|
+
end
|
329
|
+
failure = @pid if status.nil?
|
330
|
+
failure && ::Process.kill("KILL", failure) # forceful stop
|
331
|
+
end
|
332
|
+
else
|
333
|
+
_, status = ::Process.wait2(@pid)
|
334
|
+
end
|
335
|
+
|
336
|
+
@exit_code = status&.exitstatus
|
337
|
+
@finished_at = Time.now
|
338
|
+
raise Exceptions::CommandTimeoutError, "execution expired" if timeout_occurred
|
339
|
+
rescue Errno::ECHILD, Errno::ESRCH
|
340
|
+
raise Exceptions::CommandTimeoutError, "execution expired" if timeout_occurred
|
341
|
+
ensure
|
342
|
+
@out_rd_reader&.wait
|
343
|
+
@err_rd_reader&.wait
|
344
|
+
end
|
345
|
+
|
320
346
|
def handle_return_code
|
321
|
-
return if
|
347
|
+
return if ok?
|
322
348
|
message = format("\n\n RAN: %s\n\n STDOUT:\n%s\n STDERR:\n%s\n", @prog_with_args, @stdout_data, @stderr_data)
|
323
349
|
raise Exceptions::CommandReturnFailureError.new(@exit_code, message)
|
324
350
|
end
|
@@ -331,7 +357,6 @@ module Rubsh
|
|
331
357
|
def run_in_foreground
|
332
358
|
spawn
|
333
359
|
wait(timeout: @_timeout)
|
334
|
-
handle_return_code
|
335
360
|
end
|
336
361
|
end
|
337
362
|
end
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require "open3"
|
2
|
-
|
3
1
|
module Rubsh
|
4
2
|
class RunningPipeline
|
5
3
|
SPECIAL_KWARGS = %i[
|
@@ -24,6 +22,14 @@ module Rubsh
|
|
24
22
|
# @return [Number]
|
25
23
|
attr_reader :exit_code
|
26
24
|
|
25
|
+
# @!attribute [r] started_at
|
26
|
+
# @return [Time]
|
27
|
+
attr_reader :started_at
|
28
|
+
|
29
|
+
# @!attribute [r] finished_at
|
30
|
+
# @return [Time]
|
31
|
+
attr_reader :finished_at
|
32
|
+
|
27
33
|
# @!attribute [r] stdout_data
|
28
34
|
# @return [String]
|
29
35
|
attr_reader :stdout_data
|
@@ -39,6 +45,8 @@ module Rubsh
|
|
39
45
|
# Runtime
|
40
46
|
@prog_with_args = nil
|
41
47
|
@exit_code = nil
|
48
|
+
@started_at = nil
|
49
|
+
@finished_at = nil
|
42
50
|
@stdout_data = "".force_encoding(::Encoding.default_external)
|
43
51
|
@stderr_data = "".force_encoding(::Encoding.default_external)
|
44
52
|
@in_rd = nil
|
@@ -73,48 +81,26 @@ module Rubsh
|
|
73
81
|
@_no_err = false
|
74
82
|
end
|
75
83
|
|
76
|
-
# @return [
|
77
|
-
def
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
if timeout
|
82
|
-
begin
|
83
|
-
::Timeout.timeout(timeout) { last_status = @waiters.map(&:value)[-1] }
|
84
|
-
rescue ::Timeout::Error
|
85
|
-
timeout_occurred = true
|
84
|
+
# @return [Numeric, nil]
|
85
|
+
def wall_time
|
86
|
+
@finished_at.nil? ? nil : @finished_at - @started_at
|
87
|
+
end
|
88
|
+
alias_method :execution_time, :wall_time
|
86
89
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
30.times do
|
92
|
-
_, status = ::Process.wait2(w.pid, ::Process::WNOHANG | ::Process::WUNTRACED)
|
93
|
-
break if status
|
94
|
-
sleep 0.1
|
95
|
-
rescue ::Errno::ECHILD, ::Errno::ESRCH
|
96
|
-
status = true
|
97
|
-
end
|
98
|
-
failures << w.pid if status.nil?
|
99
|
-
}
|
100
|
-
failures.each { |pid| ::Process.kill("KILL", pid) } # forceful stop
|
101
|
-
end
|
102
|
-
else
|
103
|
-
last_status = @waiters.map(&:value)[-1]
|
104
|
-
end
|
90
|
+
# @return [Boolean]
|
91
|
+
def ok?
|
92
|
+
@_ok_code.include?(@exit_code)
|
93
|
+
end
|
105
94
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
ensure
|
111
|
-
@out_rd_reader&.wait
|
112
|
-
@err_rd_reader&.wait
|
95
|
+
# @return [void]
|
96
|
+
def wait(timeout: nil)
|
97
|
+
wait2(timeout: timeout)
|
98
|
+
handle_return_code
|
113
99
|
end
|
114
100
|
|
115
101
|
# @return [String]
|
116
|
-
def
|
117
|
-
|
102
|
+
def to_s
|
103
|
+
@prog_with_args
|
118
104
|
end
|
119
105
|
|
120
106
|
# @!visibility private
|
@@ -203,6 +189,7 @@ module Rubsh
|
|
203
189
|
cmds = @rcmds.map { |r| r.__spawn_arguments(env: @_env, cwd: @_cwd, redirection_args: {}) }
|
204
190
|
@prog_with_args = @rcmds.map(&:__prog_with_args).join(" | ")
|
205
191
|
@waiters = ::Open3.pipeline_start(*cmds, compile_redirection_args)
|
192
|
+
@started_at = Time.now
|
206
193
|
@in_wr&.write(@_in_data) if @_in_data
|
207
194
|
@in_wr&.close
|
208
195
|
|
@@ -224,8 +211,47 @@ module Rubsh
|
|
224
211
|
@err_wr&.close
|
225
212
|
end
|
226
213
|
|
214
|
+
def wait2(timeout: nil)
|
215
|
+
timeout_occurred = false
|
216
|
+
last_status = nil
|
217
|
+
|
218
|
+
if timeout
|
219
|
+
begin
|
220
|
+
::Timeout.timeout(timeout) { last_status = @waiters.map(&:value)[-1] }
|
221
|
+
rescue ::Timeout::Error
|
222
|
+
timeout_occurred = true
|
223
|
+
|
224
|
+
failures = []
|
225
|
+
@waiters.each { |w| ::Process.kill("TERM", w.pid) } # graceful stop
|
226
|
+
@waiters.each { |w|
|
227
|
+
_, status = nil, nil
|
228
|
+
30.times do
|
229
|
+
_, status = ::Process.wait2(w.pid, ::Process::WNOHANG | ::Process::WUNTRACED)
|
230
|
+
break if status
|
231
|
+
sleep 0.1
|
232
|
+
rescue ::Errno::ECHILD, ::Errno::ESRCH
|
233
|
+
status = true
|
234
|
+
end
|
235
|
+
failures << w.pid if status.nil?
|
236
|
+
}
|
237
|
+
failures.each { |pid| ::Process.kill("KILL", pid) } # forceful stop
|
238
|
+
end
|
239
|
+
else
|
240
|
+
last_status = @waiters.map(&:value)[-1]
|
241
|
+
end
|
242
|
+
|
243
|
+
@exit_code = last_status&.exitstatus
|
244
|
+
@finished_at = Time.now
|
245
|
+
raise Exceptions::CommandTimeoutError, "execution expired" if timeout_occurred
|
246
|
+
rescue ::Errno::ECHILD, ::Errno::ESRCH
|
247
|
+
raise Exceptions::CommandTimeoutError, "execution expired" if timeout_occurred
|
248
|
+
ensure
|
249
|
+
@out_rd_reader&.wait
|
250
|
+
@err_rd_reader&.wait
|
251
|
+
end
|
252
|
+
|
227
253
|
def handle_return_code
|
228
|
-
return if
|
254
|
+
return if ok?
|
229
255
|
message = format("\n\n RAN: %s\n\n STDOUT:\n%s\n STDERR:\n%s\n", @prog_with_args, @stdout_data, @stderr_data)
|
230
256
|
raise Exceptions::CommandReturnFailureError.new(@exit_code, message)
|
231
257
|
end
|
@@ -237,7 +263,6 @@ module Rubsh
|
|
237
263
|
def run_in_foreground
|
238
264
|
spawn
|
239
265
|
wait(timeout: @_timeout)
|
240
|
-
handle_return_code
|
241
266
|
end
|
242
267
|
end
|
243
268
|
end
|
data/lib/rubsh/version.rb
CHANGED
data/lib/rubsh.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "open3"
|
4
|
+
require "timeout"
|
5
|
+
|
3
6
|
require_relative "rubsh/argument"
|
4
7
|
require_relative "rubsh/command"
|
5
8
|
require_relative "rubsh/exceptions"
|
@@ -10,3 +13,9 @@ require_relative "rubsh/shell/env"
|
|
10
13
|
require_relative "rubsh/shell"
|
11
14
|
require_relative "rubsh/stream_reader"
|
12
15
|
require_relative "rubsh/version"
|
16
|
+
|
17
|
+
module Rubsh
|
18
|
+
def self.new
|
19
|
+
Shell.new
|
20
|
+
end
|
21
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-sh
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Doe
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-09-26 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Rubsh (a.k.a. ruby-sh) - Inspired by python-sh, allows you to call any
|
14
14
|
program as if it were a function.
|
@@ -18,10 +18,11 @@ executables: []
|
|
18
18
|
extensions: []
|
19
19
|
extra_rdoc_files: []
|
20
20
|
files:
|
21
|
+
- ".editorconfig"
|
21
22
|
- ".overcommit.yml"
|
22
23
|
- ".rspec"
|
23
|
-
- ".rubocop"
|
24
24
|
- ".rubocop.yml"
|
25
|
+
- ".ruby-version"
|
25
26
|
- ".yardopts"
|
26
27
|
- CODE_OF_CONDUCT.md
|
27
28
|
- Gemfile
|
@@ -41,7 +42,6 @@ files:
|
|
41
42
|
- lib/rubsh/stream_reader.rb
|
42
43
|
- lib/rubsh/version.rb
|
43
44
|
- rubsh.gemspec
|
44
|
-
- sig/rubsh.rbs
|
45
45
|
homepage: https://github.com/souk4711/rubsh
|
46
46
|
licenses:
|
47
47
|
- MIT
|
data/.rubocop
DELETED
File without changes
|
data/sig/rubsh.rbs
DELETED