qbash 0.4.1 → 0.4.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5bcb7505bb5d037e9270a685ccc8c2b55fffe4ba0de40744a71e67261d176c65
4
- data.tar.gz: 5cf3716ada0456b85887c95f09d653441ee903c5a8146ff7aef5f9e04989fd1c
3
+ metadata.gz: df6e6d3493e78bba7aa90acd7e51c84b8b869e5fac3e1fe696ed67e29a50219c
4
+ data.tar.gz: 7b5b9e9ae374085166d01b17ddf16c7ba740b50f5f0c4dfb407bb9da2ffcc6bc
5
5
  SHA512:
6
- metadata.gz: 3221e4ce357d1bc544ef9bd9235f975576442f0f6ae390f9e62845f49856629b74921b26ef28254a2c795990a105096fed31f86e7532f4b240994643b5d71e12
7
- data.tar.gz: 731fc1acd072b6087947895431d40e8dd44c1cc635a59ce9e0aac900c8e670b7cb7a9a2ef99da570f204d07eab5004dfeaf48b818f3bc1f0b67367dc3d66fbc7
6
+ metadata.gz: 348fabb99cb7a962b51d354b0b668e08e009f9c82db7433c91fa738336e19f0e51c9cd319d0dae522a73813e7a80344a54e717cca09d9f67ad1068e403058966
7
+ data.tar.gz: 3c72c3b7a39d5e3523cda49ed38be201147e07d15f8cae5b69bfd74b5f2cec6b8a8ac3f4d7d78ffc321a415893ed1c46005372030aa651a15679d8686523eac3
data/.gitignore CHANGED
@@ -9,3 +9,5 @@ node_modules/
9
9
  rdoc/
10
10
  temp/
11
11
  vendor/
12
+
13
+ **/.claude/settings.local.json
data/Gemfile CHANGED
@@ -12,6 +12,7 @@ gem 'minitest-reporters', '~>1.7', require: false
12
12
  gem 'net-ping', '~>2.0', require: false
13
13
  gem 'rake', '~>13.2', require: false
14
14
  gem 'random-port', '~>0.0', require: false
15
+ gem 'rdoc', '~>6.13', require: false
15
16
  gem 'rubocop', '~>1.73', require: false
16
17
  gem 'rubocop-minitest', '>0', require: false
17
18
  gem 'rubocop-performance', '>0', require: false
data/Gemfile.lock CHANGED
@@ -45,6 +45,7 @@ GEM
45
45
  cucumber-messages (> 19, < 28)
46
46
  cucumber-messages (22.0.0)
47
47
  cucumber-tag-expressions (6.1.2)
48
+ date (3.4.1)
48
49
  diff-lcs (1.6.1)
49
50
  docile (1.4.1)
50
51
  elapsed (0.0.1)
@@ -94,12 +95,17 @@ GEM
94
95
  ast (~> 2.4.1)
95
96
  racc
96
97
  prism (1.4.0)
98
+ psych (5.2.5)
99
+ date
100
+ stringio
97
101
  public_suffix (6.0.2)
98
102
  racc (1.8.1)
99
103
  rainbow (3.1.1)
100
104
  rake (13.2.1)
101
105
  random-port (0.7.5)
102
106
  tago (> 0)
107
+ rdoc (6.13.1)
108
+ psych (>= 4.0.0)
103
109
  regexp_parser (2.10.0)
104
110
  rexml (3.4.1)
105
111
  rubocop (1.75.5)
@@ -137,6 +143,7 @@ GEM
137
143
  simplecov (~> 0.19)
138
144
  simplecov-html (0.13.1)
139
145
  simplecov_json_formatter (0.1.4)
146
+ stringio (3.1.7)
140
147
  sys-uname (1.3.1)
141
148
  ffi (~> 1.1)
142
149
  tago (0.1.0)
@@ -171,6 +178,7 @@ DEPENDENCIES
171
178
  qbash!
172
179
  rake (~> 13.2)
173
180
  random-port (~> 0.0)
181
+ rdoc (~> 6.13)
174
182
  rubocop (~> 1.73)
175
183
  rubocop-minitest (> 0)
176
184
  rubocop-performance (> 0)
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
- mtd =
88
- case level
89
- when Logger::DEBUG
90
- :debug
91
- when Logger::INFO
92
- :info
93
- when Logger::WARN
94
- :warn
95
- when Logger::ERROR
96
- :error
97
- else
98
- raise "Unknown log level #{level}"
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
- if log.nil?
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
- closed = false
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
138
- begin
139
- Process.kill('TERM', pid)
137
+ sout.close
138
+ watch.join(0.01)
139
+ watch.kill if watch.alive?
140
+ attempt = 1
141
+ since = Time.now
142
+ loop do
143
+ Process.kill(0, pid) # should be dead already (raising Errno::ESRCH)
144
+ Process.kill('TERM', pid) # let's try to kill it
145
+ logit["Tried to stop ##{pid} with SIGTERM (attempt no.#{attempt}, #{since.ago}): #{cmd}"]
146
+ sleep(0.1)
147
+ attempt += 1
140
148
  rescue Errno::ESRCH
141
- # simply ignore it
149
+ logit["Process ##{pid} exited gracefully"]
150
+ break
142
151
  end
143
- closed = true
144
- watch.join
145
152
  else
146
- until sout.eof?
147
- begin
148
- ln = sout.gets
149
- rescue IOError => e
150
- ln = Backtrace.new(e).to_s
151
- end
152
- if log.nil?
153
- # no logging
154
- elsif log.respond_to?(mtd)
155
- log.__send__(mtd, ln)
156
- else
157
- log.print(ln)
158
- end
159
- buf += ln
160
- end
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}"
164
- end
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.1'
12
+ s.version = '0.4.3'
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 90
19
- SimpleCov.minimum_coverage_by_file 90
18
+ SimpleCov.minimum_coverage 95
19
+ SimpleCov.minimum_coverage_by_file 95
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("while true; do date; touch #{Shellwords.escape(flag)}; sleep 0.001; done", log: buf) do |pid|
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
+ pid = nil
121
+ t =
122
+ Thread.new do
123
+ qbash('trap "" TERM; sleep 10', accept: nil) do |id|
124
+ pid = id
125
+ loop { break if stop }
126
+ end
127
+ end
128
+ t.abort_on_exception = true
129
+ sleep(0.01)
130
+ stop = true
131
+ refute(t.join(0.1))
132
+ t.kill
133
+ end
116
134
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qbash
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yegor Bugayenko