ruby-sh 2.1.4 → 2.2.6
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/.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