process_bot 0.1.2 → 0.1.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 +4 -4
- data/.rubocop.yml +1 -1
- data/.ruby-version +1 -0
- data/Gemfile +7 -2
- data/Gemfile.lock +54 -28
- data/README.md +19 -9
- data/exe/process_bot +0 -2
- data/lib/process_bot/capistrano/sidekiq.rake +14 -25
- data/lib/process_bot/capistrano/sidekiq.rb +0 -2
- data/lib/process_bot/capistrano/sidekiq_helpers.rb +42 -26
- data/lib/process_bot/capistrano.rb +1 -0
- data/lib/process_bot/client_socket.rb +31 -0
- data/lib/process_bot/control_socket.rb +31 -10
- data/lib/process_bot/logger.rb +20 -8
- data/lib/process_bot/options.rb +57 -2
- data/lib/process_bot/process/handlers/sidekiq.rb +12 -22
- data/lib/process_bot/process/runner.rb +58 -6
- data/lib/process_bot/process.rb +114 -13
- data/lib/process_bot/version.rb +1 -1
- data/lib/process_bot.rb +1 -0
- data/peak_flow.yml +19 -3
- data/process_bot.gemspec +2 -10
- metadata +10 -50
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b719a497be535319bf027f1669321e58466f845657bdebb82f94d78f6d6e984f
|
|
4
|
+
data.tar.gz: cbbbc984e75edacbcdf6e9f8579633e6ebac87cf4e0690b944a7e32b4424c4f1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fdbd9fd4a6196cf6d9183289ec7c9ee984d25e5e2af0af5c8213a5fe649865687aa70f0dcf5a49f91100c7ceae72298e9bb9bdcb9dac9861c86b50209528d1a6
|
|
7
|
+
data.tar.gz: 97942320a0a9354770aa8f4e155a620a1c232c5fd712d2269f525d76b1a00adc2480c6c8e51c79c8eb2047a391224c6597e4c9bc74bf4b3369aa0664b7418e1d
|
data/.rubocop.yml
CHANGED
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.7.8
|
data/Gemfile
CHANGED
|
@@ -5,9 +5,14 @@ source "https://rubygems.org"
|
|
|
5
5
|
# Specify your gem's dependencies in process_bot.gemspec
|
|
6
6
|
gemspec
|
|
7
7
|
|
|
8
|
+
gem "pry"
|
|
8
9
|
gem "rake"
|
|
9
10
|
gem "rspec"
|
|
10
|
-
gem "rubocop"
|
|
11
11
|
gem "string-cases"
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
group :development do
|
|
14
|
+
gem "rubocop"
|
|
15
|
+
gem "rubocop-performance"
|
|
16
|
+
gem "rubocop-rake"
|
|
17
|
+
gem "rubocop-rspec"
|
|
18
|
+
end
|
data/Gemfile.lock
CHANGED
|
@@ -1,63 +1,89 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
process_bot (0.1.
|
|
4
|
+
process_bot (0.1.3)
|
|
5
|
+
knjrbfw (>= 0.0.116)
|
|
5
6
|
|
|
6
7
|
GEM
|
|
7
8
|
remote: https://rubygems.org/
|
|
8
9
|
specs:
|
|
9
10
|
ast (2.4.2)
|
|
10
11
|
coderay (1.1.3)
|
|
12
|
+
datet (0.0.25)
|
|
11
13
|
diff-lcs (1.5.0)
|
|
12
|
-
|
|
14
|
+
http2 (0.0.36)
|
|
15
|
+
string-cases (~> 0)
|
|
16
|
+
json (2.6.3)
|
|
17
|
+
knjrbfw (0.0.116)
|
|
18
|
+
datet
|
|
19
|
+
http2
|
|
20
|
+
php4r
|
|
21
|
+
ruby_process
|
|
22
|
+
tsafe
|
|
23
|
+
wref (>= 0.0.8)
|
|
13
24
|
method_source (1.0.0)
|
|
14
|
-
parallel (1.
|
|
15
|
-
parser (3.
|
|
25
|
+
parallel (1.23.0)
|
|
26
|
+
parser (3.2.2.0)
|
|
16
27
|
ast (~> 2.4.1)
|
|
17
|
-
|
|
28
|
+
php4r (0.0.4)
|
|
29
|
+
datet
|
|
30
|
+
http2
|
|
31
|
+
string-strtr
|
|
32
|
+
pry (0.14.2)
|
|
18
33
|
coderay (~> 1.1)
|
|
19
34
|
method_source (~> 1.0)
|
|
20
35
|
rainbow (3.1.1)
|
|
21
36
|
rake (13.0.6)
|
|
22
|
-
regexp_parser (2.
|
|
37
|
+
regexp_parser (2.8.0)
|
|
23
38
|
rexml (3.2.5)
|
|
24
|
-
rspec (3.
|
|
25
|
-
rspec-core (~> 3.
|
|
26
|
-
rspec-expectations (~> 3.
|
|
27
|
-
rspec-mocks (~> 3.
|
|
28
|
-
rspec-core (3.
|
|
29
|
-
rspec-support (~> 3.
|
|
30
|
-
rspec-expectations (3.
|
|
39
|
+
rspec (3.12.0)
|
|
40
|
+
rspec-core (~> 3.12.0)
|
|
41
|
+
rspec-expectations (~> 3.12.0)
|
|
42
|
+
rspec-mocks (~> 3.12.0)
|
|
43
|
+
rspec-core (3.12.0)
|
|
44
|
+
rspec-support (~> 3.12.0)
|
|
45
|
+
rspec-expectations (3.12.0)
|
|
31
46
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
32
|
-
rspec-support (~> 3.
|
|
33
|
-
rspec-mocks (3.
|
|
47
|
+
rspec-support (~> 3.12.0)
|
|
48
|
+
rspec-mocks (3.12.0)
|
|
34
49
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
35
|
-
rspec-support (~> 3.
|
|
36
|
-
rspec-support (3.
|
|
37
|
-
rubocop (1.
|
|
50
|
+
rspec-support (~> 3.12.0)
|
|
51
|
+
rspec-support (3.12.0)
|
|
52
|
+
rubocop (1.50.2)
|
|
38
53
|
json (~> 2.3)
|
|
39
54
|
parallel (~> 1.10)
|
|
40
|
-
parser (>= 3.
|
|
55
|
+
parser (>= 3.2.0.0)
|
|
41
56
|
rainbow (>= 2.2.2, < 4.0)
|
|
42
57
|
regexp_parser (>= 1.8, < 3.0)
|
|
43
58
|
rexml (>= 3.2.5, < 4.0)
|
|
44
|
-
rubocop-ast (>= 1.
|
|
59
|
+
rubocop-ast (>= 1.28.0, < 2.0)
|
|
45
60
|
ruby-progressbar (~> 1.7)
|
|
46
|
-
unicode-display_width (>=
|
|
47
|
-
rubocop-ast (1.
|
|
48
|
-
parser (>= 3.
|
|
49
|
-
rubocop-
|
|
61
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
|
62
|
+
rubocop-ast (1.28.0)
|
|
63
|
+
parser (>= 3.2.1.0)
|
|
64
|
+
rubocop-capybara (2.18.0)
|
|
65
|
+
rubocop (~> 1.41)
|
|
66
|
+
rubocop-performance (1.17.1)
|
|
50
67
|
rubocop (>= 1.7.0, < 2.0)
|
|
51
68
|
rubocop-ast (>= 0.4.0)
|
|
52
69
|
rubocop-rake (0.6.0)
|
|
53
70
|
rubocop (~> 1.0)
|
|
54
|
-
rubocop-rspec (2.
|
|
55
|
-
rubocop (~> 1.
|
|
56
|
-
|
|
71
|
+
rubocop-rspec (2.20.0)
|
|
72
|
+
rubocop (~> 1.33)
|
|
73
|
+
rubocop-capybara (~> 2.17)
|
|
74
|
+
ruby-progressbar (1.13.0)
|
|
75
|
+
ruby_process (0.0.13)
|
|
76
|
+
string-cases
|
|
77
|
+
tsafe
|
|
78
|
+
wref
|
|
57
79
|
string-cases (0.0.4)
|
|
58
|
-
|
|
80
|
+
string-strtr (0.0.3)
|
|
81
|
+
tsafe (0.0.12)
|
|
82
|
+
unicode-display_width (2.4.2)
|
|
83
|
+
wref (0.0.8)
|
|
59
84
|
|
|
60
85
|
PLATFORMS
|
|
86
|
+
ruby
|
|
61
87
|
x86_64-linux
|
|
62
88
|
|
|
63
89
|
DEPENDENCIES
|
data/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# ProcessBot
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Run your app through ProcessBot for automatic restart if crashing, but still support normal deployment through Capistrano.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
In the future ProcessBot will also watch memory usage and restart processes if leaking memory automatically and gracefully.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -12,17 +12,27 @@ Add this line to your application's Gemfile:
|
|
|
12
12
|
gem 'process_bot'
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
Add to your `Capfile`:
|
|
16
|
+
```ruby
|
|
17
|
+
require "process_bot"
|
|
18
|
+
install_plugin ProcessBot::Capistrano::Sidekiq
|
|
19
|
+
install_plugin ProcessBot::Capistrano::Puma
|
|
20
|
+
```
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
Add to your `deploy.rb`:
|
|
23
|
+
```ruby
|
|
24
|
+
after "deploy:starting", "process_bot:sidekiq:graceful"
|
|
25
|
+
after "deploy:published", "process_bot:sidekiq:start"
|
|
26
|
+
after "deploy:failed", "process_bot:sidekiq:start"
|
|
27
|
+
```
|
|
22
28
|
|
|
23
29
|
## Usage
|
|
24
30
|
|
|
25
|
-
|
|
31
|
+
Run commands in the command line like this:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
cap production process_bot:sidekiq:graceful
|
|
35
|
+
```
|
|
26
36
|
|
|
27
37
|
## Development
|
|
28
38
|
|
data/exe/process_bot
CHANGED
|
@@ -14,62 +14,51 @@ namespace :load do
|
|
|
14
14
|
set :sidekiq_options_per_process, nil
|
|
15
15
|
set :sidekiq_user, nil
|
|
16
16
|
# Rbenv, Chruby, and RVM integration
|
|
17
|
-
set :rbenv_map_bins, fetch(:rbenv_map_bins).to_a
|
|
18
|
-
set :rvm_map_bins, fetch(:rvm_map_bins).to_a
|
|
19
|
-
set :chruby_map_bins, fetch(:chruby_map_bins).to_a
|
|
17
|
+
set :rbenv_map_bins, fetch(:rbenv_map_bins).to_a + ["sidekiq", "sidekiqctl"]
|
|
18
|
+
set :rvm_map_bins, fetch(:rvm_map_bins).to_a + ["sidekiq", "sidekiqctl"]
|
|
19
|
+
set :chruby_map_bins, fetch(:chruby_map_bins).to_a + ["sidekiq", "sidekiqctl"]
|
|
20
20
|
# Bundler integration
|
|
21
|
-
set :bundle_bins, fetch(:bundle_bins).to_a
|
|
21
|
+
set :bundle_bins, fetch(:bundle_bins).to_a + ["sidekiq", "sidekiqctl"]
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
namespace :process_bot do
|
|
26
26
|
namespace :sidekiq do
|
|
27
|
-
desc "
|
|
28
|
-
task :
|
|
27
|
+
desc "Stop Sidekiq and ProcessBot gracefully (stop fetching new tasks from Redis and then quit when nothing is running)"
|
|
28
|
+
task :graceful do
|
|
29
29
|
on roles fetch(:sidekiq_roles) do |role|
|
|
30
30
|
git_plugin.switch_user(role) do
|
|
31
|
-
git_plugin.
|
|
32
|
-
git_plugin.
|
|
31
|
+
git_plugin.running_process_bot_processes.each do |process_bot_process|
|
|
32
|
+
git_plugin.process_bot_command(process_bot_process, :graceful)
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
-
desc "Stop Sidekiq (graceful shutdown within timeout, put unfinished tasks back to Redis)"
|
|
38
|
+
desc "Stop Sidekiq and ProcessBot (graceful shutdown within timeout, put unfinished tasks back to Redis)"
|
|
39
39
|
task :stop do
|
|
40
40
|
on roles fetch(:sidekiq_roles) do |role|
|
|
41
41
|
git_plugin.switch_user(role) do
|
|
42
|
-
git_plugin.
|
|
43
|
-
git_plugin.
|
|
42
|
+
git_plugin.running_process_bot_processes.each do |process_bot_data|
|
|
43
|
+
git_plugin.process_bot_command(process_bot_data, :stop)
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
desc "
|
|
50
|
-
task :stop_after_time do
|
|
51
|
-
on roles fetch(:sidekiq_roles) do |role|
|
|
52
|
-
git_plugin.switch_user(role) do
|
|
53
|
-
git_plugin.running_sidekiq_processes.each do |sidekiq_process|
|
|
54
|
-
git_plugin.stop_sidekiq_after_time(pid: sidekiq_process.fetch(:pid), signal: "TERM")
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
desc "Start sidekiq"
|
|
49
|
+
desc "Start Sidekiq and ProcessBot"
|
|
61
50
|
task :start do
|
|
62
51
|
on roles fetch(:sidekiq_roles) do |role|
|
|
63
52
|
git_plugin.switch_user(role) do
|
|
64
53
|
fetch(:sidekiq_processes).times do |idx|
|
|
65
|
-
puts "Starting Sidekiq #{idx}"
|
|
54
|
+
puts "Starting Sidekiq with ProcessBot #{idx}"
|
|
66
55
|
git_plugin.start_sidekiq(idx)
|
|
67
56
|
end
|
|
68
57
|
end
|
|
69
58
|
end
|
|
70
59
|
end
|
|
71
60
|
|
|
72
|
-
desc "Restart
|
|
61
|
+
desc "Restart Sidekiq and ProcessBot"
|
|
73
62
|
task :restart do
|
|
74
63
|
invoke! "process_bot:sidekiq:stop"
|
|
75
64
|
invoke! "process_bot:sidekiq:start"
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
module ProcessBot::Capistrano::SidekiqHelpers # rubocop:disable Metrics/ModuleLength
|
|
2
4
|
def sidekiq_require
|
|
3
5
|
"--require #{fetch(:sidekiq_require)}" if fetch(:sidekiq_require)
|
|
4
6
|
end
|
|
@@ -30,41 +32,44 @@ module ProcessBot::Capistrano::SidekiqHelpers
|
|
|
30
32
|
end
|
|
31
33
|
end
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
raise "Invalid PID: #{pid}" unless pid.to_s.match?(/\A\d+\Z/)
|
|
36
|
-
raise "Invalid signal: #{signal}" unless VALID_SIGNALS.include?(signal)
|
|
37
|
-
|
|
38
|
-
backend.execute "kill -#{signal} #{pid}"
|
|
39
|
-
end
|
|
35
|
+
def process_bot_command(process_bot_data, command)
|
|
36
|
+
raise "No port in process bot data? #{process_bot_data}" unless process_bot_data["port"]
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
backend_command = "cd #{release_path} && " \
|
|
39
|
+
"#{SSHKit.config.command_map.prefix[:bundle].join(" ")} bundle exec process_bot " \
|
|
40
|
+
"--command #{command} " \
|
|
41
|
+
"--port #{process_bot_data.fetch("port")}"
|
|
44
42
|
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
if command == :graceful && !fetch(:process_bot_wait_for_gracefully_stopped).nil?
|
|
44
|
+
backend_command << " --wait-for-gracefully-stopped #{fetch(:process_bot_wait_for_gracefully_stopped)}"
|
|
45
|
+
end
|
|
47
46
|
|
|
48
|
-
backend.execute
|
|
47
|
+
backend.execute backend_command
|
|
49
48
|
end
|
|
50
49
|
|
|
51
|
-
def
|
|
50
|
+
def running_process_bot_processes
|
|
52
51
|
sidekiq_app_name = fetch(:sidekiq_app_name, fetch(:application))
|
|
53
52
|
raise "No :sidekiq_app_name was set" unless sidekiq_app_name
|
|
54
53
|
|
|
55
54
|
begin
|
|
56
|
-
processes_output = backend.capture("ps a |
|
|
55
|
+
processes_output = backend.capture("ps a | grep ProcessBot | grep sidekiq | grep -v '/usr/bin/SCREEN' | grep '#{Regexp.escape(sidekiq_app_name)}'")
|
|
57
56
|
rescue SSHKit::Command::Failed
|
|
58
57
|
# Fails when output is empty (when no processes found through grep)
|
|
59
|
-
puts "No Sidekiq processes found"
|
|
58
|
+
puts "No ProcessBot Sidekiq processes found"
|
|
60
59
|
return []
|
|
61
60
|
end
|
|
62
61
|
|
|
62
|
+
parse_process_bot_process_from_ps(processes_output)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def parse_process_bot_process_from_ps(processes_output)
|
|
63
66
|
processes = []
|
|
64
|
-
processes_output.scan(/^\s*(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(
|
|
65
|
-
|
|
67
|
+
processes_output.scan(/^\s*(\d+)\s+(\S+)\s+(\S+)\s+(\S+)\s+ProcessBot (\{([^\n]+?)\})$/).each do |process_output|
|
|
68
|
+
process_bot_data = JSON.parse(process_output[4])
|
|
69
|
+
process_bot_pid = process_output[0]
|
|
70
|
+
process_bot_data["process_bot_pid"] = process_bot_pid
|
|
66
71
|
|
|
67
|
-
processes <<
|
|
72
|
+
processes << process_bot_data
|
|
68
73
|
end
|
|
69
74
|
|
|
70
75
|
processes
|
|
@@ -86,7 +91,7 @@ module ProcessBot::Capistrano::SidekiqHelpers
|
|
|
86
91
|
backend.capture(:echo, SSHKit.config.command_map[:bundle]).strip
|
|
87
92
|
end
|
|
88
93
|
|
|
89
|
-
def start_sidekiq(idx = 0) # rubocop:disable Metrics/AbcSize
|
|
94
|
+
def start_sidekiq(idx = 0) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
90
95
|
releases = backend.capture(:ls, "-x", releases_path).split
|
|
91
96
|
releases << release_timestamp.to_s if release_timestamp
|
|
92
97
|
releases.uniq
|
|
@@ -95,13 +100,19 @@ module ProcessBot::Capistrano::SidekiqHelpers
|
|
|
95
100
|
raise "Invalid release timestamp: #{release_timestamp}" unless latest_release_version
|
|
96
101
|
|
|
97
102
|
args = [
|
|
103
|
+
"--command", "start",
|
|
98
104
|
"--id", "sidekiq-#{latest_release_version}-#{idx}",
|
|
105
|
+
"--application", fetch(:sidekiq_app_name, fetch(:application)),
|
|
99
106
|
"--handler", "sidekiq",
|
|
100
107
|
"--bundle-prefix", SSHKit.config.command_map.prefix[:bundle].join(" "),
|
|
101
108
|
"--sidekiq-environment", fetch(:sidekiq_env),
|
|
102
|
-
"--port",
|
|
109
|
+
"--port", idx + 7050,
|
|
110
|
+
"--release-path", release_path
|
|
103
111
|
]
|
|
104
|
-
|
|
112
|
+
|
|
113
|
+
# Use screen for logging everything which is why this is disabled
|
|
114
|
+
# args += ["--log-file-path", fetch(:sidekiq_log)] if fetch(:sidekiq_log)
|
|
115
|
+
|
|
105
116
|
args += ["--sidekiq-require", fetch(:sidekiq_require)] if fetch(:sidekiq_require)
|
|
106
117
|
args += ["--sidekiq-tag", fetch(:sidekiq_tag)] if fetch(:sidekiq_tag)
|
|
107
118
|
args += ["--sidekiq-queues", Array(fetch(:sidekiq_queue)).join(",")] if fetch(:sidekiq_queue)
|
|
@@ -112,13 +123,18 @@ module ProcessBot::Capistrano::SidekiqHelpers
|
|
|
112
123
|
end
|
|
113
124
|
args += fetch(:sidekiq_options) if fetch(:sidekiq_options)
|
|
114
125
|
|
|
115
|
-
screen_args = ["-dmS sidekiq
|
|
116
|
-
|
|
126
|
+
screen_args = ["-dmS process-bot--sidekiq--#{idx}-#{latest_release_version}"]
|
|
127
|
+
|
|
128
|
+
if (process_bot_sidekiq_log = fetch(:process_bot_sidekig_log))
|
|
129
|
+
screen_args << "-L -Logfile #{process_bot_sidekiq_log}"
|
|
130
|
+
elsif fetch(:sidekiq_log)
|
|
131
|
+
screen_args << "-L -Logfile #{fetch(:sidekiq_log)}"
|
|
132
|
+
end
|
|
117
133
|
|
|
118
134
|
process_bot_args = args.compact.map { |arg| "\"#{arg}\"" }
|
|
119
135
|
|
|
120
136
|
command = "/usr/bin/screen #{screen_args.join(" ")} " \
|
|
121
|
-
"bash -c 'cd #{release_path} && #{SSHKit.config.command_map.prefix[:bundle].join(" ")} bundle exec process_bot #{process_bot_args.join(" ")}'"
|
|
137
|
+
"bash -c 'cd #{release_path} && exec #{SSHKit.config.command_map.prefix[:bundle].join(" ")} bundle exec process_bot #{process_bot_args.join(" ")}'"
|
|
122
138
|
|
|
123
139
|
puts "WARNING: A known bug prevents Sidekiq from starting when pty is set (which it is)" if fetch(:pty)
|
|
124
140
|
puts "ProcessBot Sidekiq command: #{command}"
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require "socket"
|
|
2
|
+
|
|
3
|
+
class ProcessBot::ClientSocket
|
|
4
|
+
attr_reader :options
|
|
5
|
+
|
|
6
|
+
def initialize(options:)
|
|
7
|
+
@options = options
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def client
|
|
11
|
+
@client ||= TCPSocket.new("localhost", options.fetch(:port).to_i)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def close
|
|
15
|
+
client.close
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def send_command(data)
|
|
19
|
+
client.puts(JSON.generate(data))
|
|
20
|
+
response_raw = client.gets
|
|
21
|
+
|
|
22
|
+
# Happens if process is interrupted
|
|
23
|
+
return :nil if response_raw.nil?
|
|
24
|
+
|
|
25
|
+
response = JSON.parse(response_raw)
|
|
26
|
+
|
|
27
|
+
return :success if response.fetch("type") == "success"
|
|
28
|
+
|
|
29
|
+
raise "Command raised an error: #{response.fetch("message")}" if response.fetch("type") == "error"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require "socket"
|
|
2
|
+
|
|
1
3
|
class ProcessBot::ControlSocket
|
|
2
4
|
attr_reader :options, :process, :server
|
|
3
5
|
|
|
@@ -11,9 +13,16 @@ class ProcessBot::ControlSocket
|
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
def start
|
|
14
|
-
|
|
16
|
+
@server = TCPServer.new("localhost", port)
|
|
17
|
+
run_client_loop
|
|
18
|
+
|
|
19
|
+
puts "TCPServer started"
|
|
20
|
+
|
|
21
|
+
options.events.call(:on_socket_opened, port: port)
|
|
22
|
+
end
|
|
15
23
|
|
|
16
|
-
|
|
24
|
+
def stop
|
|
25
|
+
server.close
|
|
17
26
|
end
|
|
18
27
|
|
|
19
28
|
def run_client_loop
|
|
@@ -26,14 +35,26 @@ class ProcessBot::ControlSocket
|
|
|
26
35
|
end
|
|
27
36
|
end
|
|
28
37
|
|
|
29
|
-
def handle_client(client)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
38
|
+
def handle_client(client) # rubocop:disable Metrics/AbcSize
|
|
39
|
+
loop do
|
|
40
|
+
data = client.gets
|
|
41
|
+
break if data.nil? # Client disconnected
|
|
42
|
+
|
|
43
|
+
command = JSON.parse(data)
|
|
44
|
+
command_type = command.fetch("command")
|
|
45
|
+
|
|
46
|
+
if command_type == "graceful" || command_type == "stop"
|
|
47
|
+
begin
|
|
48
|
+
process.__send__(command_type)
|
|
49
|
+
client.puts(JSON.generate(type: "success"))
|
|
50
|
+
rescue => e # rubocop:disable Style/RescueStandardError
|
|
51
|
+
client.puts(JSON.generate(type: "error", message: e.message))
|
|
52
|
+
|
|
53
|
+
raise e
|
|
54
|
+
end
|
|
55
|
+
else
|
|
56
|
+
client.puts(JSON.generate(type: "error", message: "Unknown command: #{command_type}"))
|
|
57
|
+
end
|
|
37
58
|
end
|
|
38
59
|
end
|
|
39
60
|
end
|
data/lib/process_bot/logger.rb
CHANGED
|
@@ -1,22 +1,34 @@
|
|
|
1
1
|
class ProcessBot::Logger
|
|
2
|
-
attr_reader :
|
|
2
|
+
attr_reader :options
|
|
3
3
|
|
|
4
4
|
def initialize(options:)
|
|
5
5
|
@options = options
|
|
6
|
-
|
|
7
|
-
open_file
|
|
8
6
|
end
|
|
9
7
|
|
|
10
|
-
def log(output)
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
def log(output, type: :stdout)
|
|
9
|
+
if type == :stdout
|
|
10
|
+
$stdout.print output
|
|
11
|
+
elsif type == :stderr
|
|
12
|
+
$stderr.print output
|
|
13
|
+
else
|
|
14
|
+
raise "Unknown type: #{type}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
return unless log_to_file?
|
|
18
|
+
|
|
19
|
+
fp_log.write(output)
|
|
20
|
+
fp_log.flush
|
|
13
21
|
end
|
|
14
22
|
|
|
15
23
|
def log_file_path
|
|
16
24
|
options.fetch(:log_file_path)
|
|
17
25
|
end
|
|
18
26
|
|
|
19
|
-
def
|
|
20
|
-
|
|
27
|
+
def log_to_file?
|
|
28
|
+
options.present?(:log_file_path)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def fp_log
|
|
32
|
+
@fp_log ||= File.open(log_file_path, "a") if log_to_file?
|
|
21
33
|
end
|
|
22
34
|
end
|
data/lib/process_bot/options.rb
CHANGED
|
@@ -5,8 +5,59 @@ class ProcessBot::Options
|
|
|
5
5
|
@options = options
|
|
6
6
|
end
|
|
7
7
|
|
|
8
|
-
def
|
|
9
|
-
options
|
|
8
|
+
def [](key)
|
|
9
|
+
options[key]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def events
|
|
13
|
+
@events ||= begin
|
|
14
|
+
require "knjrbfw"
|
|
15
|
+
|
|
16
|
+
event_handler = ::Knj::Event_handler.new
|
|
17
|
+
event_handler.add_event(name: :on_process_started)
|
|
18
|
+
event_handler.add_event(name: :on_socket_opened)
|
|
19
|
+
event_handler
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def fetch(...)
|
|
24
|
+
options.fetch(...)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def application_basename
|
|
28
|
+
@application_basename ||= begin
|
|
29
|
+
app_path_parts = release_path.split("/")
|
|
30
|
+
|
|
31
|
+
if release_path.include?("/releases/")
|
|
32
|
+
app_path_parts.pop(2)
|
|
33
|
+
elsif release_path.end_with?("/current")
|
|
34
|
+
app_path_parts.pop
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
app_path_parts.last
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def possible_process_titles
|
|
42
|
+
possible_names = []
|
|
43
|
+
|
|
44
|
+
# Sidekiq name can by current Rails root base name
|
|
45
|
+
possible_names << application_basename
|
|
46
|
+
|
|
47
|
+
# Sidekiq name can be set tag name (but we wrongly read application for some reason?)
|
|
48
|
+
possible_names << options.fetch(:application)
|
|
49
|
+
|
|
50
|
+
possible_names
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def possible_process_titles_joined_regex
|
|
54
|
+
possible_process_titles_joined_regex = ""
|
|
55
|
+
possible_process_titles.each_with_index do |possible_name, index|
|
|
56
|
+
possible_process_titles_joined_regex << "|" if index >= 1
|
|
57
|
+
possible_process_titles_joined_regex << Regexp.escape(possible_name)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
possible_process_titles_joined_regex
|
|
10
61
|
end
|
|
11
62
|
|
|
12
63
|
def present?(key)
|
|
@@ -15,6 +66,10 @@ class ProcessBot::Options
|
|
|
15
66
|
false
|
|
16
67
|
end
|
|
17
68
|
|
|
69
|
+
def release_path
|
|
70
|
+
@release_path ||= fetch(:release_path)
|
|
71
|
+
end
|
|
72
|
+
|
|
18
73
|
def set(key, value)
|
|
19
74
|
options[key] = value
|
|
20
75
|
end
|
|
@@ -3,8 +3,6 @@ class ProcessBot::Process::Handlers::Sidekiq
|
|
|
3
3
|
|
|
4
4
|
def initialize(options)
|
|
5
5
|
@options = options
|
|
6
|
-
|
|
7
|
-
set_defaults
|
|
8
6
|
end
|
|
9
7
|
|
|
10
8
|
def fetch(*args, **opts)
|
|
@@ -21,35 +19,27 @@ class ProcessBot::Process::Handlers::Sidekiq
|
|
|
21
19
|
options.set(*args, **opts)
|
|
22
20
|
end
|
|
23
21
|
|
|
24
|
-
def
|
|
25
|
-
set :sidekiq_default_hooks, true
|
|
26
|
-
set :sidekiq_pid, -> { File.join(shared_path, "tmp", "pids", "sidekiq.pid") }
|
|
27
|
-
set :sidekiq_timeout, 10
|
|
28
|
-
set :sidekiq_roles, fetch(:sidekiq_role, :app)
|
|
29
|
-
set :sidekiq_processes, 1
|
|
30
|
-
set :sidekiq_options_per_process, nil
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
def command # rubocop:disable Metrics/AbcSize
|
|
22
|
+
def start_command # rubocop:disable Metrics/AbcSize
|
|
34
23
|
args = []
|
|
35
24
|
|
|
36
25
|
options.options.each do |key, value|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
else
|
|
45
|
-
args.push "--#{sidekiq_key} #{value}"
|
|
26
|
+
next unless (match = key.to_s.match(/\Asidekiq_(.+)\Z/))
|
|
27
|
+
|
|
28
|
+
sidekiq_key = match[1]
|
|
29
|
+
|
|
30
|
+
if sidekiq_key == "queue"
|
|
31
|
+
value.split(",").each do |queue|
|
|
32
|
+
args.push "--queue #{queue}"
|
|
46
33
|
end
|
|
34
|
+
else
|
|
35
|
+
args.push "--#{sidekiq_key} #{value}"
|
|
47
36
|
end
|
|
48
37
|
end
|
|
49
38
|
|
|
50
|
-
command = ""
|
|
39
|
+
command = "bash -c 'cd #{options.fetch(:release_path)} && exec "
|
|
51
40
|
command << "#{options.fetch(:bundle_prefix)} " if options.present?(:bundle_prefix)
|
|
52
41
|
command << "bundle exec sidekiq #{args.compact.join(' ')}"
|
|
42
|
+
command << "'"
|
|
53
43
|
command
|
|
54
44
|
end
|
|
55
45
|
end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
require "knjrbfw"
|
|
2
|
+
|
|
1
3
|
class ProcessBot::Process::Runner
|
|
2
|
-
attr_reader :command, :exit_status, :logger, :monitor, :options, :stop_time
|
|
4
|
+
attr_reader :command, :exit_status, :logger, :monitor, :options, :pid, :stop_time, :subprocess_pid
|
|
3
5
|
|
|
4
6
|
def initialize(command:, logger:, options:)
|
|
5
7
|
@command = command
|
|
@@ -9,8 +11,12 @@ class ProcessBot::Process::Runner
|
|
|
9
11
|
@output = []
|
|
10
12
|
end
|
|
11
13
|
|
|
12
|
-
def output(output:, type:)
|
|
13
|
-
logger.log(output)
|
|
14
|
+
def output(output:, type:)
|
|
15
|
+
logger.log(output, type: type)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def running?
|
|
19
|
+
!stop_time
|
|
14
20
|
end
|
|
15
21
|
|
|
16
22
|
def run # rubocop:disable Metrics/AbcSize
|
|
@@ -20,8 +26,8 @@ class ProcessBot::Process::Runner
|
|
|
20
26
|
require "pty"
|
|
21
27
|
|
|
22
28
|
PTY.spawn(command, err: stderr_writer.fileno) do |stdout, _stdin, pid|
|
|
23
|
-
@
|
|
24
|
-
logger.log "Command running with PID #{pid}: #{command}"
|
|
29
|
+
@subprocess_pid = pid
|
|
30
|
+
logger.log "Command running with PID #{pid}: #{command}\n"
|
|
25
31
|
|
|
26
32
|
stdout_reader_thread = Thread.new do
|
|
27
33
|
stdout.each_char do |chunk|
|
|
@@ -32,7 +38,7 @@ class ProcessBot::Process::Runner
|
|
|
32
38
|
rescue Errno::EIO
|
|
33
39
|
# Process done
|
|
34
40
|
ensure
|
|
35
|
-
status = Process::Status.wait(
|
|
41
|
+
status = Process::Status.wait(subprocess_pid, 0)
|
|
36
42
|
|
|
37
43
|
@exit_status = status.exitstatus
|
|
38
44
|
stderr_writer.close
|
|
@@ -46,10 +52,56 @@ class ProcessBot::Process::Runner
|
|
|
46
52
|
end
|
|
47
53
|
end
|
|
48
54
|
|
|
55
|
+
find_sidekiq_pid
|
|
56
|
+
|
|
49
57
|
stdout_reader_thread.join
|
|
50
58
|
stderr_reader_thread.join
|
|
51
59
|
|
|
52
60
|
@stop_time = Time.new
|
|
53
61
|
end
|
|
54
62
|
end
|
|
63
|
+
|
|
64
|
+
def subprocess_pgid
|
|
65
|
+
@subprocess_pgid ||= Process.getpgid(subprocess_pid)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def sidekiq_app_name
|
|
69
|
+
options.fetch(:application)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def find_sidekiq_pid # rubocop:disable Metrics/AbcSize
|
|
73
|
+
Thread.new do
|
|
74
|
+
while running? && !pid
|
|
75
|
+
Knj::Unix_proc.list("grep" => "sidekiq") do |process|
|
|
76
|
+
cmd = process.data.fetch("cmd")
|
|
77
|
+
|
|
78
|
+
if /sidekiq ([0-9]+\.[0-9]+\.[0-9]+) (#{options.possible_process_titles_joined_regex})/.match?(cmd)
|
|
79
|
+
sidekiq_pid = process.data.fetch("pid").to_i
|
|
80
|
+
|
|
81
|
+
begin
|
|
82
|
+
sidekiq_pgid = Process.getpgid(sidekiq_pid)
|
|
83
|
+
rescue Errno::ESRCH
|
|
84
|
+
# Process no longer running
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
if subprocess_pgid == sidekiq_pgid
|
|
88
|
+
puts "FOUND PID: #{sidekiq_pid}"
|
|
89
|
+
|
|
90
|
+
@pid = sidekiq_pid
|
|
91
|
+
options.events.call(:on_process_started, pid: pid)
|
|
92
|
+
|
|
93
|
+
break
|
|
94
|
+
else
|
|
95
|
+
puts "PGID didn't match - Sidekiq: #{sidekiq_pgid} Own: #{subprocess_pgid}"
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
unless pid
|
|
101
|
+
puts "Waiting 1 second before trying to find Sidekiq PID again"
|
|
102
|
+
sleep 1
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
55
107
|
end
|
data/lib/process_bot/process.rb
CHANGED
|
@@ -1,35 +1,68 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
1
3
|
class ProcessBot::Process
|
|
2
4
|
autoload :Handlers, "#{__dir__}/process/handlers"
|
|
3
5
|
autoload :Runner, "#{__dir__}/process/runner"
|
|
4
6
|
|
|
5
|
-
attr_reader :options, :stopped
|
|
7
|
+
attr_reader :current_pid, :current_process_title, :options, :port, :stopped
|
|
6
8
|
|
|
7
9
|
def initialize(options)
|
|
8
10
|
@options = options
|
|
9
11
|
@stopped = false
|
|
10
|
-
end
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
options.events.connect(:on_process_started, &method(:on_process_started)) # rubocop:disable Performance/MethodObjectAsBlock
|
|
14
|
+
options.events.connect(:on_socket_opened, &method(:on_socket_opened)) # rubocop:disable Performance/MethodObjectAsBlock
|
|
15
|
+
|
|
16
|
+
logger.log("Options: #{options.options}")
|
|
14
17
|
end
|
|
15
18
|
|
|
16
|
-
def
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
def execute!
|
|
20
|
+
command = options.fetch(:command)
|
|
21
|
+
|
|
22
|
+
if command == "start"
|
|
23
|
+
start
|
|
24
|
+
elsif command == "graceful" || command == "stop"
|
|
25
|
+
client.send_command(command: command)
|
|
26
|
+
else
|
|
27
|
+
raise "Unknown command: #{command}"
|
|
28
|
+
end
|
|
19
29
|
end
|
|
20
30
|
|
|
21
|
-
def
|
|
22
|
-
@
|
|
31
|
+
def client
|
|
32
|
+
@client ||= ProcessBot::ClientSocket.new(options: options)
|
|
23
33
|
end
|
|
24
34
|
|
|
25
35
|
def handler_class
|
|
26
36
|
@handler_class ||= begin
|
|
27
|
-
require_relative "process/handlers/#{
|
|
28
|
-
ProcessBot::Process::Handlers.const_get(StringCases.snake_to_camel(
|
|
37
|
+
require_relative "process/handlers/#{handler_name}"
|
|
38
|
+
ProcessBot::Process::Handlers.const_get(StringCases.snake_to_camel(handler_name))
|
|
29
39
|
end
|
|
30
40
|
end
|
|
31
41
|
|
|
32
|
-
def
|
|
42
|
+
def handler_name
|
|
43
|
+
@handler_name ||= options.fetch(:handler)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def logger
|
|
47
|
+
@logger ||= ProcessBot::Logger.new(options: options)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def on_process_started(_event_name, pid:)
|
|
51
|
+
@current_pid = pid
|
|
52
|
+
update_process_title
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def on_socket_opened(_event_name, port:)
|
|
56
|
+
@port = port
|
|
57
|
+
update_process_title
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def start_control_socket
|
|
61
|
+
@control_socket = ProcessBot::ControlSocket.new(options: options, process: self)
|
|
62
|
+
@control_socket.start
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def start
|
|
33
66
|
start_control_socket
|
|
34
67
|
|
|
35
68
|
loop do
|
|
@@ -44,9 +77,77 @@ class ProcessBot::Process
|
|
|
44
77
|
end
|
|
45
78
|
end
|
|
46
79
|
|
|
80
|
+
def graceful
|
|
81
|
+
@stopped = true
|
|
82
|
+
|
|
83
|
+
unless current_pid
|
|
84
|
+
warn "#{handler_name} not running with a PID"
|
|
85
|
+
return
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
Process.kill("TSTP", current_pid)
|
|
89
|
+
|
|
90
|
+
if options[:wait_for_gracefully_stopped] == "false"
|
|
91
|
+
Thread.new { wait_for_no_jobs_and_stop_sidekiq }
|
|
92
|
+
else
|
|
93
|
+
wait_for_no_jobs_and_stop_sidekiq
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def stop
|
|
98
|
+
@stopped = true
|
|
99
|
+
|
|
100
|
+
unless current_pid
|
|
101
|
+
warn "#{handler_name} not running with a PID"
|
|
102
|
+
return
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
Process.kill("TERM", current_pid)
|
|
106
|
+
end
|
|
107
|
+
|
|
47
108
|
def run
|
|
48
109
|
handler_instance = handler_class.new(options)
|
|
49
|
-
runner = ProcessBot::Process::Runner.new(command: handler_instance.
|
|
110
|
+
runner = ProcessBot::Process::Runner.new(command: handler_instance.start_command, logger: logger, options: options)
|
|
50
111
|
runner.run
|
|
51
112
|
end
|
|
113
|
+
|
|
114
|
+
def update_process_title
|
|
115
|
+
process_args = {application: options[:application], handler: handler_name, id: options[:id], pid: current_pid, port: port}
|
|
116
|
+
@current_process_title = "ProcessBot #{JSON.generate(process_args)}"
|
|
117
|
+
Process.setproctitle(current_process_title)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def wait_for_no_jobs # rubocop:disable Metrics/AbcSize
|
|
121
|
+
loop do
|
|
122
|
+
found_process = false
|
|
123
|
+
|
|
124
|
+
Knj::Unix_proc.list("grep" => current_pid) do |process|
|
|
125
|
+
process_command = process.data.fetch("cmd")
|
|
126
|
+
process_pid = process.data.fetch("pid").to_i
|
|
127
|
+
next unless process_pid == current_pid
|
|
128
|
+
|
|
129
|
+
found_process = true
|
|
130
|
+
sidekiq_regex = /\Asidekiq (\d+).(\d+).(\d+) (#{options.possible_process_titles_joined_regex}) \[(\d+) of (\d+)(\]|) (.+?)(\]|)\Z/
|
|
131
|
+
match = process_command.match(sidekiq_regex)
|
|
132
|
+
raise "Couldnt match Sidekiq command: #{process_command} with Sidekiq regex: #{sidekiq_regex}" unless match
|
|
133
|
+
|
|
134
|
+
running_jobs = match[5].to_i
|
|
135
|
+
|
|
136
|
+
puts "running_jobs: #{running_jobs}"
|
|
137
|
+
|
|
138
|
+
return if running_jobs.zero? # rubocop:disable Lint/NonLocalExitFromIterator
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
raise "Couldn't find running process with PID #{current_pid}" unless found_process
|
|
142
|
+
|
|
143
|
+
sleep 1
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def wait_for_no_jobs_and_stop_sidekiq
|
|
148
|
+
puts "Wait for no jobs and Stop sidekiq"
|
|
149
|
+
|
|
150
|
+
wait_for_no_jobs
|
|
151
|
+
stop
|
|
152
|
+
end
|
|
52
153
|
end
|
data/lib/process_bot/version.rb
CHANGED
data/lib/process_bot.rb
CHANGED
|
@@ -4,6 +4,7 @@ module ProcessBot
|
|
|
4
4
|
class Error < StandardError; end
|
|
5
5
|
|
|
6
6
|
autoload :Capistrano, "#{__dir__}/process_bot/capistrano"
|
|
7
|
+
autoload :ClientSocket, "#{__dir__}/process_bot/client_socket"
|
|
7
8
|
autoload :ControlSocket, "#{__dir__}/process_bot/control_socket"
|
|
8
9
|
autoload :Logger, "#{__dir__}/process_bot/logger"
|
|
9
10
|
autoload :Options, "#{__dir__}/process_bot/options"
|
data/peak_flow.yml
CHANGED
|
@@ -1,4 +1,20 @@
|
|
|
1
1
|
rvm: true
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
builds:
|
|
3
|
+
build_1:
|
|
4
|
+
environment:
|
|
5
|
+
RUBY_VERSION: 2.7.8
|
|
6
|
+
name: Ruby 2.7.8
|
|
7
|
+
script:
|
|
8
|
+
- bundle exec rspec
|
|
9
|
+
build_2:
|
|
10
|
+
environment:
|
|
11
|
+
RUBY_VERSION: 3.2.2
|
|
12
|
+
name: Ruby 3.2.2
|
|
13
|
+
script:
|
|
14
|
+
- bundle exec rspec
|
|
15
|
+
build_3:
|
|
16
|
+
environment:
|
|
17
|
+
RUBY_VERSION: 2.7.8
|
|
18
|
+
name: Rubocop
|
|
19
|
+
script:
|
|
20
|
+
- bundle exec rubocop
|
data/process_bot.gemspec
CHANGED
|
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
|
12
12
|
spec.description = "Run and control processes."
|
|
13
13
|
spec.homepage = "https://github.com/kaspernj/process_bot"
|
|
14
14
|
spec.license = "MIT"
|
|
15
|
-
spec.required_ruby_version = ">= 2.
|
|
15
|
+
spec.required_ruby_version = ">= 2.7.0"
|
|
16
16
|
|
|
17
17
|
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
18
18
|
|
|
@@ -31,15 +31,7 @@ Gem::Specification.new do |spec|
|
|
|
31
31
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
32
32
|
spec.require_paths = ["lib"]
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
# spec.add_dependency "example-gem", "~> 1.0"
|
|
34
|
+
spec.add_dependency "knjrbfw", ">= 0.0.116"
|
|
36
35
|
|
|
37
|
-
spec.add_development_dependency "rubocop"
|
|
38
|
-
spec.add_development_dependency "rubocop-performance"
|
|
39
|
-
spec.add_development_dependency "rubocop-rake"
|
|
40
|
-
spec.add_development_dependency "rubocop-rspec"
|
|
41
|
-
|
|
42
|
-
# For more information and examples about making a new gem, check out our
|
|
43
|
-
# guide at: https://bundler.io/guides/creating_gem.html
|
|
44
36
|
spec.metadata["rubygems_mfa_required"] = "true"
|
|
45
37
|
end
|
metadata
CHANGED
|
@@ -1,71 +1,29 @@
|
|
|
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.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- kaspernj
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2023-07-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: knjrbfw
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
20
|
-
type: :
|
|
19
|
+
version: 0.0.116
|
|
20
|
+
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
27
|
-
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: rubocop-performance
|
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
|
30
|
-
requirements:
|
|
31
|
-
- - ">="
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: '0'
|
|
34
|
-
type: :development
|
|
35
|
-
prerelease: false
|
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
-
requirements:
|
|
38
|
-
- - ">="
|
|
39
|
-
- !ruby/object:Gem::Version
|
|
40
|
-
version: '0'
|
|
41
|
-
- !ruby/object:Gem::Dependency
|
|
42
|
-
name: rubocop-rake
|
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
|
44
|
-
requirements:
|
|
45
|
-
- - ">="
|
|
46
|
-
- !ruby/object:Gem::Version
|
|
47
|
-
version: '0'
|
|
48
|
-
type: :development
|
|
49
|
-
prerelease: false
|
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
-
requirements:
|
|
52
|
-
- - ">="
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '0'
|
|
55
|
-
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: rubocop-rspec
|
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
|
58
|
-
requirements:
|
|
59
|
-
- - ">="
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: '0'
|
|
62
|
-
type: :development
|
|
63
|
-
prerelease: false
|
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
-
requirements:
|
|
66
|
-
- - ">="
|
|
67
|
-
- !ruby/object:Gem::Version
|
|
68
|
-
version: '0'
|
|
26
|
+
version: 0.0.116
|
|
69
27
|
description: Run and control processes.
|
|
70
28
|
email:
|
|
71
29
|
- k@spernj.org
|
|
@@ -76,6 +34,7 @@ extra_rdoc_files: []
|
|
|
76
34
|
files:
|
|
77
35
|
- ".rspec"
|
|
78
36
|
- ".rubocop.yml"
|
|
37
|
+
- ".ruby-version"
|
|
79
38
|
- CHANGELOG.md
|
|
80
39
|
- Gemfile
|
|
81
40
|
- Gemfile.lock
|
|
@@ -91,6 +50,7 @@ files:
|
|
|
91
50
|
- lib/process_bot/capistrano/sidekiq.rake
|
|
92
51
|
- lib/process_bot/capistrano/sidekiq.rb
|
|
93
52
|
- lib/process_bot/capistrano/sidekiq_helpers.rb
|
|
53
|
+
- lib/process_bot/client_socket.rb
|
|
94
54
|
- lib/process_bot/control_socket.rb
|
|
95
55
|
- lib/process_bot/logger.rb
|
|
96
56
|
- lib/process_bot/options.rb
|
|
@@ -119,14 +79,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
119
79
|
requirements:
|
|
120
80
|
- - ">="
|
|
121
81
|
- !ruby/object:Gem::Version
|
|
122
|
-
version: 2.
|
|
82
|
+
version: 2.7.0
|
|
123
83
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
84
|
requirements:
|
|
125
85
|
- - ">="
|
|
126
86
|
- !ruby/object:Gem::Version
|
|
127
87
|
version: '0'
|
|
128
88
|
requirements: []
|
|
129
|
-
rubygems_version: 3.
|
|
89
|
+
rubygems_version: 3.1.6
|
|
130
90
|
signing_key:
|
|
131
91
|
specification_version: 4
|
|
132
92
|
summary: Run and control processes.
|