qbash 0.4.1 → 0.4.2
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/.gitignore +2 -0
- data/lib/qbash.rb +57 -64
- data/qbash.gemspec +1 -1
- data/test/test__helper.rb +2 -2
- data/test/test_qbash.rb +20 -2
- 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: 72371631590029a1533bf6e899fa51833c924fd89e070c2747bea4bd1257a906
|
4
|
+
data.tar.gz: 5855c06aa2c9444779f214783d35d56925fed96ed010c1fb187f4549618e63f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 81fe1f8e49f5a706a8a357b5a071513f2f14e83d072e18300e7ec3fd7c15e18280302b77817b0b5f29d123bca871d136df086310cef6c1f5183410a35ff18309
|
7
|
+
data.tar.gz: 3baeb218d6423e04a2ccb86d02e5717c48c92af3e30833d2ff909ae480681c28db79e5586c7f5fa84691142d94960eeec7f1b2a9a2e26325bcd5e2ca10219036
|
data/.gitignore
CHANGED
data/lib/qbash.rb
CHANGED
@@ -84,84 +84,77 @@ module Kernel
|
|
84
84
|
def qbash(cmd, stdin: '', env: {}, log: Loog::NULL, accept: [0], both: false, level: Logger::DEBUG)
|
85
85
|
env.each { |k, v| raise "env[#{k}] is nil" if v.nil? }
|
86
86
|
cmd = cmd.reject { |a| a.nil? || (a.is_a?(String) && a.empty?) }.join(' ') if cmd.is_a?(Array)
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
87
|
+
logit =
|
88
|
+
lambda do |msg|
|
89
|
+
mtd =
|
90
|
+
case level
|
91
|
+
when Logger::DEBUG
|
92
|
+
:debug
|
93
|
+
when Logger::INFO
|
94
|
+
:info
|
95
|
+
when Logger::WARN
|
96
|
+
:warn
|
97
|
+
when Logger::ERROR
|
98
|
+
:error
|
99
|
+
else
|
100
|
+
raise "Unknown log level #{level}"
|
101
|
+
end
|
102
|
+
if log.nil?
|
103
|
+
# nothing to print
|
104
|
+
elsif log.respond_to?(mtd)
|
105
|
+
log.__send__(mtd, msg)
|
106
|
+
else
|
107
|
+
log.print("#{msg}\n")
|
108
|
+
end
|
99
109
|
end
|
100
|
-
|
101
|
-
# nothing to print
|
102
|
-
elsif log.respond_to?(mtd)
|
103
|
-
log.__send__(mtd, "+ #{cmd}")
|
104
|
-
else
|
105
|
-
log.print("+ #{cmd}\n")
|
106
|
-
end
|
110
|
+
logit["+ #{cmd}"]
|
107
111
|
buf = ''
|
108
112
|
e = 1
|
109
113
|
start = Time.now
|
110
114
|
Open3.popen2e(env, "/bin/bash -c #{Shellwords.escape(cmd)}") do |sin, sout, ctrl|
|
115
|
+
consume =
|
116
|
+
lambda do
|
117
|
+
loop do
|
118
|
+
break if sout.eof?
|
119
|
+
ln = sout.gets
|
120
|
+
next if ln.nil?
|
121
|
+
next if ln.empty?
|
122
|
+
buf += ln
|
123
|
+
ln = "##{ctrl.pid}: #{ln}"
|
124
|
+
logit[ln]
|
125
|
+
rescue IOError => e
|
126
|
+
logit[e.message]
|
127
|
+
break
|
128
|
+
end
|
129
|
+
end
|
111
130
|
sin.write(stdin)
|
112
131
|
sin.close
|
113
132
|
if block_given?
|
114
|
-
|
115
|
-
watch =
|
116
|
-
Thread.new do
|
117
|
-
until closed
|
118
|
-
begin
|
119
|
-
ln = sout.gets
|
120
|
-
rescue IOError => e
|
121
|
-
ln = Backtrace.new(e).to_s
|
122
|
-
end
|
123
|
-
next if ln.nil?
|
124
|
-
next if ln.empty?
|
125
|
-
ln = "##{ctrl.pid}: #{ln}"
|
126
|
-
if log.nil?
|
127
|
-
# no logging
|
128
|
-
elsif log.respond_to?(mtd)
|
129
|
-
log.__send__(mtd, ln)
|
130
|
-
else
|
131
|
-
log.print(ln)
|
132
|
-
end
|
133
|
-
buf += ln
|
134
|
-
end
|
135
|
-
end
|
133
|
+
watch = Thread.new { consume.call }
|
134
|
+
watch.abort_on_exception = true
|
136
135
|
pid = ctrl.pid
|
137
136
|
yield pid
|
137
|
+
sout.close
|
138
|
+
watch.join(0.01)
|
139
|
+
watch.kill if watch.alive?
|
138
140
|
begin
|
139
|
-
Process.
|
140
|
-
|
141
|
-
# simply ignore it
|
142
|
-
end
|
143
|
-
closed = true
|
144
|
-
watch.join
|
145
|
-
else
|
146
|
-
until sout.eof?
|
141
|
+
Process.getpgid(pid) # should be dead already (raising Errno::ESRCH)
|
142
|
+
Process.kill('TERM', pid) # let's try to kill it
|
147
143
|
begin
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
if log.nil?
|
153
|
-
# no logging
|
154
|
-
elsif log.respond_to?(mtd)
|
155
|
-
log.__send__(mtd, ln)
|
156
|
-
else
|
157
|
-
log.print(ln)
|
144
|
+
Process.getpgid(pid) # should be dead now (raising Errno::ESRCH)
|
145
|
+
raise "Process ##{pid} did not terminate after SIGTERM"
|
146
|
+
rescue Errno::ESRCH
|
147
|
+
logit["Process ##{pid} killed with SIGTERM"]
|
158
148
|
end
|
159
|
-
|
160
|
-
|
161
|
-
e = ctrl.value.to_i
|
162
|
-
if !accept.nil? && !accept.include?(e)
|
163
|
-
raise "The command '#{cmd}' failed with exit code ##{e} in #{start.ago}\n#{buf}"
|
149
|
+
rescue Errno::ESRCH
|
150
|
+
logit["Process ##{pid} exited gracefully"]
|
164
151
|
end
|
152
|
+
else
|
153
|
+
consume.call
|
154
|
+
end
|
155
|
+
e = ctrl.value.to_i
|
156
|
+
if !accept.nil? && !accept.include?(e)
|
157
|
+
raise "The command '#{cmd}' failed with exit code ##{e} in #{start.ago}\n#{buf}"
|
165
158
|
end
|
166
159
|
end
|
167
160
|
return [buf, e] if both
|
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.2'
|
13
13
|
s.license = 'MIT'
|
14
14
|
s.summary = 'Quick Executor of a BASH Command'
|
15
15
|
s.description =
|
data/test/test__helper.rb
CHANGED
@@ -15,8 +15,8 @@ unless SimpleCov.running || ENV['PICKS']
|
|
15
15
|
SimpleCov::Formatter::CoberturaFormatter
|
16
16
|
]
|
17
17
|
)
|
18
|
-
SimpleCov.minimum_coverage
|
19
|
-
SimpleCov.minimum_coverage_by_file
|
18
|
+
SimpleCov.minimum_coverage 1
|
19
|
+
SimpleCov.minimum_coverage_by_file 1
|
20
20
|
SimpleCov.start do
|
21
21
|
add_filter 'test/'
|
22
22
|
add_filter 'vendor/'
|
data/test/test_qbash.rb
CHANGED
@@ -81,7 +81,7 @@ class TestQbash < Minitest::Test
|
|
81
81
|
|
82
82
|
def test_kills_in_thread
|
83
83
|
Thread.new do
|
84
|
-
qbash('sleep 9999') do |pid|
|
84
|
+
qbash('sleep 9999', accept: nil) do |pid|
|
85
85
|
Process.kill('KILL', pid)
|
86
86
|
end
|
87
87
|
end.join
|
@@ -92,9 +92,10 @@ class TestQbash < Minitest::Test
|
|
92
92
|
buf = Loog::Buffer.new
|
93
93
|
Dir.mktmpdir do |home|
|
94
94
|
flag = File.join(home, 'started.txt')
|
95
|
+
cmd = "while true; do date; touch #{Shellwords.escape(flag)}; sleep 0.001; done"
|
95
96
|
Thread.new do
|
96
97
|
stdout =
|
97
|
-
qbash(
|
98
|
+
qbash(cmd, log: buf, accept: nil) do |pid|
|
98
99
|
loop { break if File.exist?(flag) }
|
99
100
|
Process.kill('KILL', pid)
|
100
101
|
end
|
@@ -113,4 +114,21 @@ class TestQbash < Minitest::Test
|
|
113
114
|
refute_empty(stdout)
|
114
115
|
end
|
115
116
|
end
|
117
|
+
|
118
|
+
def test_exists_after_background_stop
|
119
|
+
stop = false
|
120
|
+
t =
|
121
|
+
Thread.new do
|
122
|
+
qbash('trap "" TERM; tail -f /dev/null', accept: nil) do
|
123
|
+
loop { break if stop }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
t.abort_on_exception = true
|
127
|
+
sleep(0.1)
|
128
|
+
stop = true
|
129
|
+
t.join(0.01)
|
130
|
+
t.kill
|
131
|
+
sleep(0.01)
|
132
|
+
refute_predicate(t, :alive?)
|
133
|
+
end
|
116
134
|
end
|