process_bot 0.1.19 → 0.1.21
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/AGENTS.md +1 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile.lock +2 -2
- data/lib/process_bot/client_socket.rb +31 -12
- data/lib/process_bot/control_socket.rb +44 -1
- data/lib/process_bot/logger.rb +24 -0
- data/lib/process_bot/options.rb +1 -0
- data/lib/process_bot/version.rb +1 -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: f9caad47714b9ab413bc33e85beb54b250455b6831ace86d058e6e63e84939bb
|
|
4
|
+
data.tar.gz: f5d294e5d78cf4bafa60d4ddca38cea9887310ed3cc517d0fbe91ff95673c5b4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ec5cdb7e07944681062b07f44408c0eb17962f4667a8215eead66a5b62ba6d0c78ffb521c431c94199fcff597bee5e5755df540687e79396cbb53df1daca5034
|
|
7
|
+
data.tar.gz: 3e21dba2ec87ef787ce3c4bf3b1d6f81273896862a04807ee48f81cc86b8e769712c1de043106c9e64cb16b6ac47f52f0a99d025614c0861ba60f4728fa1fb97
|
data/AGENTS.md
CHANGED
|
@@ -13,3 +13,4 @@
|
|
|
13
13
|
- Added `graceful_no_wait` command and Capistrano task for non-blocking graceful shutdowns.
|
|
14
14
|
- Always add or update tests for new/changed functionality, and run them.
|
|
15
15
|
- Added coverage for graceful_no_wait and Capistrano wait defaults.
|
|
16
|
+
- Bumped version to 0.1.20 for log streaming updates.
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
- Stop accepting new control commands during shutdown so in-flight responses complete reliably.
|
|
3
|
+
- Stream ProcessBot logs to connected control clients for Capistrano output.
|
|
4
|
+
- Sanitize broadcast log output to keep JSON encoding safe.
|
|
5
|
+
- Bump version to 0.1.20.
|
|
6
|
+
- Flush log output immediately so Capistrano can stream it.
|
|
7
|
+
- Bump version to 0.1.21.
|
|
3
8
|
|
|
4
9
|
## [0.1.0] - 2022-04-03
|
|
5
10
|
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
process_bot (0.1.
|
|
4
|
+
process_bot (0.1.21)
|
|
5
5
|
knjrbfw (>= 0.0.116)
|
|
6
6
|
pry
|
|
7
7
|
rake
|
|
@@ -83,7 +83,7 @@ GEM
|
|
|
83
83
|
rubocop-rake (0.7.1)
|
|
84
84
|
lint_roller (~> 1.1)
|
|
85
85
|
rubocop (>= 1.72.1)
|
|
86
|
-
rubocop-rspec (3.
|
|
86
|
+
rubocop-rspec (3.9.0)
|
|
87
87
|
lint_roller (~> 1.1)
|
|
88
88
|
rubocop (~> 1.81)
|
|
89
89
|
ruby-progressbar (1.13.0)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require "json"
|
|
1
2
|
require "socket"
|
|
2
3
|
|
|
3
4
|
class ProcessBot::ClientSocket
|
|
@@ -26,23 +27,41 @@ class ProcessBot::ClientSocket
|
|
|
26
27
|
logger.logs "Sending: #{data}"
|
|
27
28
|
begin
|
|
28
29
|
client.puts(JSON.generate(data))
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
end
|
|
30
|
+
loop do
|
|
31
|
+
response_raw = client.gets
|
|
32
|
+
return :nil if response_raw.nil?
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
return :nil if response_raw.nil?
|
|
34
|
+
response = JSON.parse(response_raw)
|
|
36
35
|
|
|
37
|
-
|
|
36
|
+
case response.fetch("type")
|
|
37
|
+
when "log"
|
|
38
|
+
write_log_output(response)
|
|
39
|
+
when "success"
|
|
40
|
+
return :success
|
|
41
|
+
when "error"
|
|
42
|
+
error = RuntimeError.new("Command raised an error: #{response.fetch("message")}")
|
|
43
|
+
error.set_backtrace(response.fetch("backtrace") + Thread.current.backtrace)
|
|
38
44
|
|
|
39
|
-
|
|
45
|
+
raise error
|
|
46
|
+
else
|
|
47
|
+
raise "Unknown response type: #{response.fetch("type")}"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
rescue Errno::ECONNRESET, Errno::EPIPE
|
|
51
|
+
:nil
|
|
52
|
+
end
|
|
53
|
+
end
|
|
40
54
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
55
|
+
def write_log_output(response)
|
|
56
|
+
output = response["output"].to_s
|
|
57
|
+
stream = response.fetch("stream", "stdout")
|
|
44
58
|
|
|
45
|
-
|
|
59
|
+
if stream == "stderr"
|
|
60
|
+
$stderr.print output
|
|
61
|
+
$stderr.flush
|
|
62
|
+
else
|
|
63
|
+
$stdout.print output
|
|
64
|
+
$stdout.flush
|
|
46
65
|
end
|
|
47
66
|
end
|
|
48
67
|
end
|
|
@@ -3,12 +3,14 @@ require "json"
|
|
|
3
3
|
require "knjrbfw"
|
|
4
4
|
|
|
5
5
|
class ProcessBot::ControlSocket
|
|
6
|
-
attr_reader :options, :port, :process, :server
|
|
6
|
+
attr_reader :clients, :clients_mutex, :options, :port, :process, :server
|
|
7
7
|
|
|
8
8
|
def initialize(options:, process:)
|
|
9
9
|
@options = options
|
|
10
10
|
@process = process
|
|
11
11
|
@port = options.fetch(:port).to_i
|
|
12
|
+
@clients = []
|
|
13
|
+
@clients_mutex = Mutex.new
|
|
12
14
|
end
|
|
13
15
|
|
|
14
16
|
def logger
|
|
@@ -20,6 +22,9 @@ class ProcessBot::ControlSocket
|
|
|
20
22
|
run_client_loop
|
|
21
23
|
logger.logs "TCPServer started"
|
|
22
24
|
options.events.call(:on_socket_opened, port: @port)
|
|
25
|
+
options.events.connect(:on_log) do |event_name, output:, type:|
|
|
26
|
+
broadcast_log(event_name, output: output, type: type)
|
|
27
|
+
end
|
|
23
28
|
end
|
|
24
29
|
|
|
25
30
|
def start_tcp_server
|
|
@@ -70,6 +75,8 @@ class ProcessBot::ControlSocket
|
|
|
70
75
|
end
|
|
71
76
|
|
|
72
77
|
def handle_client(client) # rubocop:disable Metrics/AbcSize
|
|
78
|
+
add_client(client)
|
|
79
|
+
|
|
73
80
|
loop do
|
|
74
81
|
data = client.gets
|
|
75
82
|
break if data.nil? # Client disconnected
|
|
@@ -105,6 +112,42 @@ class ProcessBot::ControlSocket
|
|
|
105
112
|
client.puts(JSON.generate(type: "error", message: "Unknown command: #{command_type}", backtrace: Thread.current.backtrace))
|
|
106
113
|
end
|
|
107
114
|
end
|
|
115
|
+
ensure
|
|
116
|
+
remove_client(client)
|
|
117
|
+
client.close unless client.closed?
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def broadcast_log(_event_name, output:, type:)
|
|
121
|
+
safe_output = normalize_output(output)
|
|
122
|
+
payload = JSON.generate(type: "log", stream: type.to_s, output: safe_output)
|
|
123
|
+
|
|
124
|
+
clients_snapshot.each do |client|
|
|
125
|
+
client.puts(payload)
|
|
126
|
+
rescue IOError, Errno::EPIPE, Errno::ECONNRESET
|
|
127
|
+
remove_client(client)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def add_client(client)
|
|
132
|
+
clients_mutex.synchronize do
|
|
133
|
+
clients << client
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def remove_client(client)
|
|
138
|
+
clients_mutex.synchronize do
|
|
139
|
+
clients.delete(client)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def clients_snapshot
|
|
144
|
+
clients_mutex.synchronize do
|
|
145
|
+
clients.dup
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def normalize_output(output)
|
|
150
|
+
output.to_s.encode("UTF-8", invalid: :replace, undef: :replace, replace: "?")
|
|
108
151
|
end
|
|
109
152
|
|
|
110
153
|
def symbolize_keys(hash)
|
data/lib/process_bot/logger.rb
CHANGED
|
@@ -11,6 +11,7 @@ class ProcessBot::Logger
|
|
|
11
11
|
|
|
12
12
|
def log(output, type: :stdout)
|
|
13
13
|
write_output(output, type)
|
|
14
|
+
broadcast_log(output, type)
|
|
14
15
|
|
|
15
16
|
return unless log_to_file?
|
|
16
17
|
return if type == :debug && !debug_enabled?
|
|
@@ -38,12 +39,16 @@ class ProcessBot::Logger
|
|
|
38
39
|
case type
|
|
39
40
|
when :stdout
|
|
40
41
|
$stdout.print output
|
|
42
|
+
$stdout.flush
|
|
41
43
|
when :stderr
|
|
42
44
|
$stderr.print output
|
|
45
|
+
$stderr.flush
|
|
43
46
|
when :info
|
|
44
47
|
$stdout.print output if logging_enabled?
|
|
48
|
+
$stdout.flush if logging_enabled?
|
|
45
49
|
when :debug
|
|
46
50
|
$stdout.print output if debug_enabled?
|
|
51
|
+
$stdout.flush if debug_enabled?
|
|
47
52
|
else
|
|
48
53
|
raise "Unknown type: #{type}"
|
|
49
54
|
end
|
|
@@ -54,6 +59,25 @@ class ProcessBot::Logger
|
|
|
54
59
|
fp_log.flush
|
|
55
60
|
end
|
|
56
61
|
|
|
62
|
+
def broadcast_log(output, type)
|
|
63
|
+
return unless should_broadcast?(type)
|
|
64
|
+
|
|
65
|
+
options.events.call(:on_log, output: output, type: type)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def should_broadcast?(type)
|
|
69
|
+
case type
|
|
70
|
+
when :stdout, :stderr
|
|
71
|
+
true
|
|
72
|
+
when :info
|
|
73
|
+
logging_enabled?
|
|
74
|
+
when :debug
|
|
75
|
+
debug_enabled?
|
|
76
|
+
else
|
|
77
|
+
false
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
57
81
|
def debug_enabled?
|
|
58
82
|
truthy_option?(:debug)
|
|
59
83
|
end
|
data/lib/process_bot/options.rb
CHANGED
data/lib/process_bot/version.rb
CHANGED