qbash 0.4.4 → 0.4.5
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/lib/qbash.rb +27 -21
- data/qbash.gemspec +1 -1
- data/test/test_qbash.rb +48 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1ca64f77c050b51b02164ffab0c532d6d915fe0ba76ed4d311894b150eac963f
|
4
|
+
data.tar.gz: 4082a2f3aadf655a8fe6a6b7df67a85212f0fe9b8221bf64a26329abdfb8218a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 80535d4feebc803f62b1da69da63f2412330cc8f21a8ca9a5cfc344535b2c15c6816df65fcaa16db40efff702b8628dcd7817a4b93003a423b8928ea3df82d25
|
7
|
+
data.tar.gz: 609155111977c724663a6ba5c6a90718252d0c520f608c3dcadf0abc80dc0bf5d135f27a1204cfc9a002371461c9ff2e8cff85b9862dc6404fc2baf0a9a70932
|
data/lib/qbash.rb
CHANGED
@@ -75,17 +75,19 @@ module Kernel
|
|
75
75
|
#
|
76
76
|
# @param [String, Array] cmd The command to run (String or Array of arguments)
|
77
77
|
# @param [String] stdin The +stdin+ to provide to the command
|
78
|
+
# @param [Array] opts List of bash options, like "--login" and "--noprofile"
|
78
79
|
# @param [Hash] env Hash of environment variables
|
79
80
|
# @param [Loog|IO] log Logging facility with +.debug()+ method (or +$stdout+, or nil if should go to +/dev/null+)
|
80
81
|
# @param [Array] accept List of accepted exit codes (accepts all if the list is +nil+)
|
81
82
|
# @param [Boolean] both If set to TRUE, the function returns an array +(stdout, code)+
|
82
83
|
# @param [Integer] level Logging level (use +Logger::DEBUG+, +Logger::INFO+, +Logger::WARN+, or +Logger::ERROR+)
|
83
84
|
# @return [String] Everything that was printed to the +stdout+ by the command
|
84
|
-
def qbash(cmd, stdin: '', env: {}, log: Loog::NULL, accept: [0], both: false, level: Logger::DEBUG)
|
85
|
+
def qbash(cmd, opts: [], stdin: '', env: {}, log: Loog::NULL, accept: [0], both: false, level: Logger::DEBUG)
|
85
86
|
env.each { |k, v| raise "env[#{k}] is nil" if v.nil? }
|
86
87
|
cmd = cmd.reject { |a| a.nil? || (a.is_a?(String) && a.empty?) }.join(' ') if cmd.is_a?(Array)
|
87
88
|
logit =
|
88
89
|
lambda do |msg|
|
90
|
+
msg = msg.gsub(/\n$/, '')
|
89
91
|
mtd =
|
90
92
|
case level
|
91
93
|
when Logger::DEBUG
|
@@ -104,19 +106,21 @@ module Kernel
|
|
104
106
|
elsif log.respond_to?(mtd)
|
105
107
|
log.__send__(mtd, msg)
|
106
108
|
else
|
107
|
-
log.print(msg)
|
109
|
+
log.print("#{msg}\n")
|
108
110
|
end
|
109
111
|
end
|
110
|
-
logit["+ #{cmd}"]
|
111
112
|
buf = ''
|
112
113
|
e = 1
|
113
114
|
start = Time.now
|
114
|
-
|
115
|
+
bash = ['exec', '/bin/bash'] + opts + ['-c', Shellwords.escape(cmd)]
|
116
|
+
Open3.popen2e(env, bash.join(' ')) do |sin, sout, ctrl|
|
117
|
+
pid = ctrl.pid
|
118
|
+
logit["+ #{cmd} /##{pid}"]
|
115
119
|
consume =
|
116
120
|
lambda do
|
117
121
|
loop do
|
118
122
|
break if sout.eof?
|
119
|
-
ln = sout.gets
|
123
|
+
ln = sout.gets # together with the \n at the end
|
120
124
|
next if ln.nil?
|
121
125
|
next if ln.empty?
|
122
126
|
buf += ln
|
@@ -132,22 +136,24 @@ module Kernel
|
|
132
136
|
if block_given?
|
133
137
|
watch = Thread.new { consume.call }
|
134
138
|
watch.abort_on_exception = true
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
139
|
+
begin
|
140
|
+
yield pid
|
141
|
+
ensure
|
142
|
+
sout.close
|
143
|
+
watch.join(0.01)
|
144
|
+
watch.kill if watch.alive?
|
145
|
+
attempt = 1
|
146
|
+
since = Time.now
|
147
|
+
loop do
|
148
|
+
Process.kill(0, pid) # should be dead already (raising Errno::ESRCH)
|
149
|
+
Process.kill('TERM', pid) # let's try to kill it
|
150
|
+
logit["Tried to stop ##{pid} with SIGTERM (attempt no.#{attempt}, #{since.ago}): #{cmd}"]
|
151
|
+
sleep(0.1)
|
152
|
+
attempt += 1
|
153
|
+
rescue Errno::ESRCH
|
154
|
+
logit["Process ##{pid} reacted to SIGTERM, after #{attempt} attempts and #{since.ago}"] if attempt > 1
|
155
|
+
break
|
156
|
+
end
|
151
157
|
end
|
152
158
|
else
|
153
159
|
consume.call
|
data/qbash.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version=
|
10
10
|
s.required_ruby_version = '>=3.2'
|
11
11
|
s.name = 'qbash'
|
12
|
-
s.version = '0.4.
|
12
|
+
s.version = '0.4.5'
|
13
13
|
s.license = 'MIT'
|
14
14
|
s.summary = 'Quick Executor of a BASH Command'
|
15
15
|
s.description =
|
data/test/test_qbash.rb
CHANGED
@@ -79,6 +79,14 @@ class TestQbash < Minitest::Test
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
|
+
def test_lets_exception_float_up
|
83
|
+
assert_raises(StandardError) do
|
84
|
+
qbash('sleep 767676', accept: nil, log: $stdout) do
|
85
|
+
raise 'intentional'
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
82
90
|
def test_kills_in_thread
|
83
91
|
Thread.new do
|
84
92
|
qbash('sleep 9999', accept: nil) do |pid|
|
@@ -87,6 +95,21 @@ class TestQbash < Minitest::Test
|
|
87
95
|
end.join
|
88
96
|
end
|
89
97
|
|
98
|
+
def test_truly_kills
|
99
|
+
qbash('sleep 9876543', accept: nil) do
|
100
|
+
sleep(0.1)
|
101
|
+
end
|
102
|
+
refute_empty(qbash('ps ax | grep -v 9876543'))
|
103
|
+
end
|
104
|
+
|
105
|
+
def test_executes_without_sh_only_bash
|
106
|
+
qbash('sleep 89898989', accept: nil) do
|
107
|
+
refute_empty(qbash('ps ax | grep -v "sh -c exec /bin/bash -c sleep 89898989" | grep -v grep'))
|
108
|
+
refute_empty(qbash('ps ax | grep "sleep 89898989" | grep -v grep'))
|
109
|
+
sleep(0.1)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
90
113
|
def test_logs_in_background
|
91
114
|
stdout = nil
|
92
115
|
buf = Loog::Buffer.new
|
@@ -114,7 +137,17 @@ class TestQbash < Minitest::Test
|
|
114
137
|
sleep(0.1)
|
115
138
|
pid = i
|
116
139
|
end
|
117
|
-
assert_equal(buf.to_s, "+ echo one; echo two\n##{pid}: one\n##{pid}: two\n")
|
140
|
+
assert_equal(buf.to_s, "+ echo one; echo two /##{pid}\n##{pid}: one\n##{pid}: two\n")
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_logs_multi_line_to_console
|
144
|
+
console = FakeConsole.new
|
145
|
+
pid = nil
|
146
|
+
qbash('echo one; echo two', log: console, accept: nil) do |i|
|
147
|
+
sleep(0.1)
|
148
|
+
pid = i
|
149
|
+
end
|
150
|
+
assert_equal(console.to_s, "+ echo one; echo two /##{pid}\n##{pid}: one\n##{pid}: two\n")
|
118
151
|
end
|
119
152
|
|
120
153
|
def test_with_both
|
@@ -141,4 +174,18 @@ class TestQbash < Minitest::Test
|
|
141
174
|
refute(t.join(0.1))
|
142
175
|
t.kill
|
143
176
|
end
|
177
|
+
|
178
|
+
class FakeConsole
|
179
|
+
def initialize
|
180
|
+
@buf = ''
|
181
|
+
end
|
182
|
+
|
183
|
+
def to_s
|
184
|
+
@buf
|
185
|
+
end
|
186
|
+
|
187
|
+
def print(ln)
|
188
|
+
@buf += ln
|
189
|
+
end
|
190
|
+
end
|
144
191
|
end
|