process_bot 0.1.24 → 0.1.27
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/CHANGELOG.md +2 -0
- data/Gemfile.lock +1 -1
- data/README.md +3 -1
- data/Rakefile +3 -3
- data/lib/process_bot/process/handlers/custom.rb +8 -1
- data/lib/process_bot/process.rb +79 -2
- data/lib/process_bot/version.rb +1 -1
- data/lib/tasks/release.rake +171 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 016c72db98675857edb0e34b274af5cc835d0cd5496d4bb6fccb7ebdaf05c555
|
|
4
|
+
data.tar.gz: 51a18efde6ff957df0dfe6f8e07e9df2f341a1519b3f17356de1ef7422f21e85
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f2387a26b91b32675f6679af84f4e4096bde512f62f344a962a0312abea804395e6013b88f268c20094a04724f27c71680c421f63893c785f47586b472f68ff8
|
|
7
|
+
data.tar.gz: '0491239c4e11851cfef5704cbbc9e79611c356595192f5187199fd3a7c898f7e61e5885a302ed84cdedb53692a4bf7e89369791fe69a169663273d95b5f4ebd8'
|
data/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
- Guard stop-related process scanning when subprocess PID/PGID is unavailable and fail stop loudly.
|
|
10
10
|
- Wait briefly for subprocess PID assignment during stop; raise if PID is still missing so stop cannot silently succeed.
|
|
11
11
|
|
|
12
|
+
- Require an active runner for custom stop commands to avoid constructing a fresh runner with no PID.
|
|
13
|
+
|
|
12
14
|
## [0.1.0] - 2022-04-03
|
|
13
15
|
|
|
14
16
|
- Initial release
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -113,7 +113,9 @@ bundle exec process_bot --command start --log-file-path /var/log/process_bot.log
|
|
|
113
113
|
|
|
114
114
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
115
115
|
|
|
116
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
116
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
117
|
+
|
|
118
|
+
To bump the patch version, run bundle, commit the version bump, build, and push the gem, run `bundle exec rake release:patch`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
117
119
|
|
|
118
120
|
## Contributing
|
|
119
121
|
|
data/Rakefile
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
require "bundler/gem_tasks"
|
|
4
4
|
require "rspec/core/rake_task"
|
|
5
|
-
|
|
6
|
-
RSpec::Core::RakeTask.new(:spec)
|
|
7
|
-
|
|
8
5
|
require "rubocop/rake_task"
|
|
9
6
|
|
|
7
|
+
RSpec::Core::RakeTask.new(:spec)
|
|
10
8
|
RuboCop::RakeTask.new
|
|
11
9
|
|
|
10
|
+
Dir[File.expand_path("../lib/tasks/**/*.rake", __FILE__)].each { |f| load f }
|
|
11
|
+
|
|
12
12
|
task default: %i[spec rubocop]
|
|
@@ -48,7 +48,14 @@ class ProcessBot::Process::Handlers::Custom
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def stop(**_args)
|
|
51
|
+
runner = process.active_runner
|
|
52
|
+
|
|
53
|
+
unless runner
|
|
54
|
+
logger.logs "No active runner to stop"
|
|
55
|
+
return
|
|
56
|
+
end
|
|
57
|
+
|
|
51
58
|
logger.logs "Stop related processes"
|
|
52
|
-
|
|
59
|
+
runner.stop_related_processes
|
|
53
60
|
end
|
|
54
61
|
end
|
data/lib/process_bot/process.rb
CHANGED
|
@@ -3,7 +3,7 @@ require "json"
|
|
|
3
3
|
require "monitor"
|
|
4
4
|
require "string-cases"
|
|
5
5
|
|
|
6
|
-
class ProcessBot::Process
|
|
6
|
+
class ProcessBot::Process # rubocop:disable Metrics/ClassLength
|
|
7
7
|
extend Forwardable
|
|
8
8
|
|
|
9
9
|
def_delegator :handler_instance, :graceful
|
|
@@ -131,8 +131,15 @@ class ProcessBot::Process
|
|
|
131
131
|
def send_control_command(command, **command_options)
|
|
132
132
|
logger.logs "Sending #{command} command"
|
|
133
133
|
response = client.send_command(command: command, options: options.options.merge(command_options))
|
|
134
|
-
|
|
134
|
+
|
|
135
|
+
if response == :nil
|
|
136
|
+
handle_missing_control_command_response(command)
|
|
137
|
+
return if options[:ignore_no_process_bot]
|
|
138
|
+
|
|
139
|
+
raise "No response from ProcessBot while sending #{command}"
|
|
140
|
+
end
|
|
135
141
|
rescue Errno::ECONNREFUSED => e
|
|
142
|
+
handle_missing_control_command_response(command)
|
|
136
143
|
raise e unless options[:ignore_no_process_bot]
|
|
137
144
|
end
|
|
138
145
|
|
|
@@ -140,6 +147,17 @@ class ProcessBot::Process
|
|
|
140
147
|
current_runner_instance&.runner || @runner ||= build_runner
|
|
141
148
|
end
|
|
142
149
|
|
|
150
|
+
def active_runner
|
|
151
|
+
current_runner_instance&.runner
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def active_runner!
|
|
155
|
+
runner_instance = active_runner
|
|
156
|
+
return runner_instance if runner_instance
|
|
157
|
+
|
|
158
|
+
raise "Unable to stop custom process because no active runner is available. Ensure the custom command runs in foreground."
|
|
159
|
+
end
|
|
160
|
+
|
|
143
161
|
def update_process_title
|
|
144
162
|
process_args = {application: options[:application], handler: handler_name, id: options[:id], pid: current_pid, port: port}
|
|
145
163
|
@current_process_title = "ProcessBot #{JSON.generate(process_args)}"
|
|
@@ -263,6 +281,65 @@ class ProcessBot::Process
|
|
|
263
281
|
start_runner_instance
|
|
264
282
|
end
|
|
265
283
|
|
|
284
|
+
def handle_missing_control_command_response(command)
|
|
285
|
+
return unless command == "stop"
|
|
286
|
+
|
|
287
|
+
matching_processes = matching_process_bot_processes
|
|
288
|
+
log_missing_control_response_diagnostics(matching_processes)
|
|
289
|
+
force_stop_process_bot_if_configured(matching_processes)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def log_missing_control_response_diagnostics(matching_processes)
|
|
293
|
+
logger.logs "Control command response missing; attempting diagnostics for application=#{options[:application].inspect} id=#{options[:id].inspect}"
|
|
294
|
+
logger.logs "Matching process_bot lines:\n#{matching_process_bot_processes_text(matching_processes)}"
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def matching_process_bot_processes_text(lines)
|
|
298
|
+
return "(none)" if lines.empty?
|
|
299
|
+
|
|
300
|
+
lines.join("\n")
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def matching_process_bot_processes
|
|
304
|
+
ps_output = Knj::Os.shellcmd("ps -eo pid,args")
|
|
305
|
+
|
|
306
|
+
ps_output
|
|
307
|
+
.to_s
|
|
308
|
+
.split("\n")
|
|
309
|
+
.select { |line| process_bot_process_line_matches?(line) }
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
def process_bot_process_line_matches?(line)
|
|
313
|
+
line.include?("ProcessBot {") &&
|
|
314
|
+
line.include?("\"application\":\"#{options[:application]}\"") &&
|
|
315
|
+
line.include?("\"id\":\"#{options[:id]}\"")
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def force_stop_process_bot_if_configured(matching_processes)
|
|
319
|
+
return unless truthy_option?(:force_stop_on_no_response)
|
|
320
|
+
|
|
321
|
+
matching_processes.each do |line|
|
|
322
|
+
pid = line.strip.split(/\s+/, 2).first
|
|
323
|
+
next unless pid&.match?(/\A\d+\z/)
|
|
324
|
+
|
|
325
|
+
logger.logs "Force-stopping unresponsive process_bot PID #{pid}"
|
|
326
|
+
Process.kill("TERM", Integer(pid, 10))
|
|
327
|
+
rescue Errno::ESRCH
|
|
328
|
+
logger.logs "Process bot PID #{pid} already gone during force stop"
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
def truthy_option?(key)
|
|
333
|
+
value = options[key]
|
|
334
|
+
return false if value.nil?
|
|
335
|
+
return value if value == true || value == false
|
|
336
|
+
|
|
337
|
+
normalized = value.to_s.strip.downcase
|
|
338
|
+
return false if normalized == "false" || normalized == "0" || normalized == ""
|
|
339
|
+
|
|
340
|
+
true
|
|
341
|
+
end
|
|
342
|
+
|
|
266
343
|
private
|
|
267
344
|
|
|
268
345
|
attr_reader :current_runner_instance, :runner_events, :runner_monitor
|
data/lib/process_bot/version.rb
CHANGED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
require "English"
|
|
2
|
+
require "fileutils"
|
|
3
|
+
require "pathname"
|
|
4
|
+
require "rubygems/version"
|
|
5
|
+
require "shellwords"
|
|
6
|
+
|
|
7
|
+
class ProcessBotRubygemsRelease # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
8
|
+
VERSION_FILE = Pathname.new(File.expand_path("../process_bot/version.rb", __dir__)) unless const_defined?(:VERSION_FILE)
|
|
9
|
+
|
|
10
|
+
def call
|
|
11
|
+
ensure_clean_worktree!
|
|
12
|
+
checkout_master!
|
|
13
|
+
fetch!
|
|
14
|
+
merge!
|
|
15
|
+
|
|
16
|
+
next_version = determine_next_version
|
|
17
|
+
|
|
18
|
+
bump_version!(next_version)
|
|
19
|
+
commit!(next_version)
|
|
20
|
+
push!
|
|
21
|
+
gem_file = build_gem!(next_version)
|
|
22
|
+
push_gem!(gem_file)
|
|
23
|
+
delete_gem_file!(gem_file)
|
|
24
|
+
rescue StandardError
|
|
25
|
+
warn "Release failed."
|
|
26
|
+
raise
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def ensure_clean_worktree!
|
|
32
|
+
dirty_entries = git_status_lines.grep_v(%r{\A\?\? process_bot-[^/]+\.gem\z})
|
|
33
|
+
return if dirty_entries.empty?
|
|
34
|
+
|
|
35
|
+
raise "Working tree must be clean before releasing:\n#{dirty_entries.join("\n")}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def checkout_master!
|
|
39
|
+
run!("git", "checkout", "master")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def fetch!
|
|
43
|
+
run!("git", "fetch", remote_name)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def merge!
|
|
47
|
+
run!("git", "merge", "--ff-only", "#{remote_name}/master")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def determine_next_version
|
|
51
|
+
requested_version || bumped_version
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def requested_version
|
|
55
|
+
version = ENV["VERSION"]&.strip
|
|
56
|
+
return if version.to_s.empty?
|
|
57
|
+
|
|
58
|
+
Gem::Version.new(version)
|
|
59
|
+
version
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def bumped_version
|
|
63
|
+
case bump_type
|
|
64
|
+
when "major"
|
|
65
|
+
[version_segments[0] + 1, 0, 0].join(".")
|
|
66
|
+
when "minor"
|
|
67
|
+
[version_segments[0], version_segments[1] + 1, 0].join(".")
|
|
68
|
+
when "patch"
|
|
69
|
+
[version_segments[0], version_segments[1], version_segments[2] + 1].join(".")
|
|
70
|
+
else
|
|
71
|
+
raise "Unsupported BUMP=#{bump_type.inspect}. Use patch, minor, major, or VERSION=x.y.z."
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def version_segments
|
|
76
|
+
@version_segments ||= begin
|
|
77
|
+
segments = Gem::Version.new(current_version).segments
|
|
78
|
+
segments << 0 while segments.length < 3
|
|
79
|
+
segments
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def current_version
|
|
84
|
+
@current_version ||= VERSION_FILE.read[/VERSION = "([^"]+)"\.freeze/, 1] || raise("Could not find current version")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def bump_version!(next_version)
|
|
88
|
+
raise "Next version must differ from current version" if next_version == current_version
|
|
89
|
+
|
|
90
|
+
VERSION_FILE.write(
|
|
91
|
+
VERSION_FILE.read.sub(
|
|
92
|
+
/VERSION = "[^"]+"\.freeze/,
|
|
93
|
+
%(VERSION = "#{next_version}".freeze)
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
run!("git", "add", VERSION_FILE.to_s)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def commit!(next_version)
|
|
101
|
+
run!("git", "commit", "-m", "Release #{next_version}")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def push!
|
|
105
|
+
run!("git", "push", remote_name, "master")
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def build_gem!(next_version)
|
|
109
|
+
gem_file = "process_bot-#{next_version}.gem"
|
|
110
|
+
run!("gem", "build", "process_bot.gemspec")
|
|
111
|
+
gem_file
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def push_gem!(gem_file)
|
|
115
|
+
run!("gem", "push", gem_file)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def delete_gem_file!(gem_file)
|
|
119
|
+
FileUtils.rm_f(gem_file)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def git_status_lines
|
|
123
|
+
capture!("git", "status", "--porcelain").split("\n").reject(&:empty?)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def bump_type
|
|
127
|
+
ENV.fetch("BUMP", "patch")
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def remote_name
|
|
131
|
+
ENV.fetch("REMOTE", "origin")
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def capture!(*command)
|
|
135
|
+
output = `#{command.map { |part| Shellwords.escape(part) }.join(" ")}`
|
|
136
|
+
raise "Command failed: #{command.join(' ')}" unless $CHILD_STATUS&.success?
|
|
137
|
+
|
|
138
|
+
output
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def run!(*command)
|
|
142
|
+
return if system(*command)
|
|
143
|
+
|
|
144
|
+
raise "Command failed: #{command.join(' ')}"
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
namespace :release do
|
|
149
|
+
desc "Release a patch version from master by fetching, fast-forward merging, bumping version, pushing, and publishing"
|
|
150
|
+
task :patch do
|
|
151
|
+
ENV["BUMP"] = "patch"
|
|
152
|
+
ProcessBotRubygemsRelease.new.call
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
desc "Release a minor version from master by fetching, fast-forward merging, bumping version, pushing, and publishing"
|
|
156
|
+
task :minor do
|
|
157
|
+
ENV["BUMP"] = "minor"
|
|
158
|
+
ProcessBotRubygemsRelease.new.call
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
desc "Release a major version from master by fetching, fast-forward merging, bumping version, pushing, and publishing"
|
|
162
|
+
task :major do
|
|
163
|
+
ENV["BUMP"] = "major"
|
|
164
|
+
ProcessBotRubygemsRelease.new.call
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
desc "Release the gem from master by fetching, fast-forward merging, bumping version, pushing, and publishing"
|
|
168
|
+
task :rubygems do
|
|
169
|
+
ProcessBotRubygemsRelease.new.call
|
|
170
|
+
end
|
|
171
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: process_bot
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.27
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- kaspernj
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-04-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: knjrbfw
|
|
@@ -175,6 +175,7 @@ files:
|
|
|
175
175
|
- lib/process_bot/process/runner.rb
|
|
176
176
|
- lib/process_bot/process/runner_instance.rb
|
|
177
177
|
- lib/process_bot/version.rb
|
|
178
|
+
- lib/tasks/release.rake
|
|
178
179
|
- peak_flow.yml
|
|
179
180
|
- process_bot.gemspec
|
|
180
181
|
homepage: https://github.com/kaspernj/process_bot
|