kamal-backup 0.3.0.beta6 → 0.3.0.beta8

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: 68d3201dc26c940938479dc76d1c0e17b206676ae69610d736a69879a78de21c
4
- data.tar.gz: dd58ee38da83d4652515b108869e5fe441a25d80697b7db3ea1c606240a44766
3
+ metadata.gz: d920c58d8ff83794df465f0549d8599ea6a5e815b800221fa850ce024241abfa
4
+ data.tar.gz: feb8374a19862349d1fb0b3d55e3e697b45df66d58fc0c0f63ddba4767ed01f9
5
5
  SHA512:
6
- metadata.gz: 37ca5f6463fe6eb76c2f6994a783e2180f3eecff24e834550743c1f9130e6ad3019ce1e296dc44ac523a36c065f1e05ac00aaaec7c7224254daea0c2d683d7a0
7
- data.tar.gz: '0985cd5e2eb8a52db5dcc4265271eddde1513c8c8db80d0aec21ee3ca1be0bd42e2adcd60ffeb942aa7bb2d926cbad69f5d89051485ce64145f3f5c821abe3bb'
6
+ metadata.gz: 9e36489f2557dca0695be87b779a9b20f96148b141e67a9422bc8933c925ea68a17a73d221c56f4a51585258194752f0db4f0b6fed4a4845783334e6dc0d8e0e
7
+ data.tar.gz: f8bb63eb69f7da8bdfcc001e07c95495ac60fa6996eb4b4188cfb2a8bc453979176985703f897c8e5ac4a9b02faec5cf3d76aad15862b0c964d6c961c2910556
@@ -129,11 +129,13 @@ module KamalBackup
129
129
 
130
130
  def print_remote_version_status
131
131
  status = remote_version == VERSION ? "in sync" : "out of sync"
132
+ status_color = status == "in sync" ? :green : :red
133
+ status_output = CommandOutput.new(io: $stdout, env: command_env)
132
134
 
133
135
  puts("local: #{VERSION}")
134
136
  puts("remote: #{remote_version}")
135
- puts("status: #{status}")
136
- puts("fix: #{accessory_reboot_command}") if status == "out of sync"
137
+ puts("status: #{status_output.decorate(status, status_color, :bold)}")
138
+ puts("fix: #{status_output.decorate(accessory_reboot_command, :yellow, :bold)}") if status == "out of sync"
137
139
  end
138
140
 
139
141
  def validate_deploy_config
@@ -399,14 +401,17 @@ module KamalBackup
399
401
 
400
402
  def self.start(argv = ARGV, env: ENV)
401
403
  self.command_env = env
402
- Command.with_output(CommandOutput.new(io: $stderr)) do
404
+ output = CommandOutput.new(io: $stderr, env: env)
405
+ Command.with_output(output) do
403
406
  super(normalize_global_options(argv))
404
407
  end
405
408
  rescue Error => e
406
- warn("kamal-backup: #{Redactor.new(env: env).redact_string(e.message)}")
409
+ output ||= CommandOutput.new(io: $stderr, env: env)
410
+ output.error("(#{e.class}): #{e.message}", redactor: Redactor.new(env: env))
407
411
  exit(1)
408
412
  rescue Interrupt
409
- warn("kamal-backup: interrupted")
413
+ output ||= CommandOutput.new(io: $stderr, env: env)
414
+ output.error("(Interrupt): interrupted", redactor: Redactor.new(env: env))
410
415
  exit(130)
411
416
  ensure
412
417
  self.command_env = nil
@@ -27,14 +27,57 @@ module KamalBackup
27
27
  CommandResult = Struct.new(:stdout, :stderr, :status, :streamed, keyword_init: true)
28
28
 
29
29
  class CommandOutput
30
- def initialize(io: $stdout)
30
+ LEVELS = {
31
+ "DEBUG" => 0,
32
+ "INFO" => 1,
33
+ "WARN" => 2,
34
+ "ERROR" => 3,
35
+ "FATAL" => 4
36
+ }.freeze
37
+ LEVEL_COLORS = {
38
+ "DEBUG" => :black,
39
+ "INFO" => :blue,
40
+ "WARN" => :yellow,
41
+ "ERROR" => :red,
42
+ "FATAL" => :red
43
+ }.freeze
44
+ COLOR_CODES = {
45
+ black: 30,
46
+ red: 31,
47
+ green: 32,
48
+ yellow: 33,
49
+ blue: 34,
50
+ magenta: 35,
51
+ cyan: 36,
52
+ white: 37,
53
+ light_black: 90,
54
+ light_red: 91,
55
+ light_green: 92,
56
+ light_yellow: 93,
57
+ light_blue: 94,
58
+ light_magenta: 95,
59
+ light_cyan: 96,
60
+ light_white: 97
61
+ }.freeze
62
+
63
+ def initialize(io: $stdout, env: ENV, verbosity: :info)
31
64
  @io = io
65
+ @env = env
66
+ @verbosity = LEVELS.fetch(verbosity.to_s.upcase)
32
67
  @mutex = Mutex.new
33
68
  @buffers = {}
34
69
  end
35
70
 
36
71
  def info(message, redactor:)
37
- write_line(" INFO #{redactor.redact_string(message)}")
72
+ write_message("INFO", redactor.redact_string(message))
73
+ end
74
+
75
+ def error(message, redactor:)
76
+ write_message("ERROR", colorize(redactor.redact_string(message), :red, :bold))
77
+ end
78
+
79
+ def decorate(value, color, mode = nil)
80
+ colorize(value, color, mode)
38
81
  end
39
82
 
40
83
  def command_start(spec, redactor:)
@@ -42,8 +85,8 @@ module KamalBackup
42
85
  started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
43
86
  display = spec.display(redactor)
44
87
 
45
- write_line(" INFO [#{id}] Running #{display} locally")
46
- write_line(" DEBUG [#{id}] Command: #{display}")
88
+ write_message("INFO", "Running #{colorize(display, :yellow, :bold)} #{local_target}", id)
89
+ write_message("DEBUG", "Command: #{colorize(display, :blue)}", id)
47
90
 
48
91
  { id: id, started_at: started_at, redactor: redactor }
49
92
  end
@@ -51,6 +94,7 @@ module KamalBackup
51
94
  def command_output(context, _stream, data, redactor:)
52
95
  raw = data.to_s
53
96
  return if raw.empty?
97
+ return unless log_level?("DEBUG")
54
98
 
55
99
  synchronize do
56
100
  key = [context.fetch(:id), _stream]
@@ -62,16 +106,23 @@ module KamalBackup
62
106
  def command_exit(context, status)
63
107
  runtime = Process.clock_gettime(Process::CLOCK_MONOTONIC) - context.fetch(:started_at)
64
108
  result = status.to_i.zero? ? "successful" : "failed"
109
+ result_color = status.to_i.zero? ? :green : :red
65
110
 
66
111
  synchronize do
67
112
  flush_output_buffers(context)
68
- @io.puts(" INFO [#{context.fetch(:id)}] Finished in #{format("%.3f seconds", runtime)} with exit status #{status} (#{result}).")
113
+ write_message_unlocked("INFO", "Finished in #{format("%.3f seconds", runtime)} with exit status #{status} (#{colorize(result, result_color, :bold)}).", context.fetch(:id))
69
114
  end
70
115
  end
71
116
 
72
117
  private
73
- def write_line(message)
74
- synchronize { @io.puts(message) }
118
+ def write_message(level, message, id = nil)
119
+ return unless log_level?(level)
120
+
121
+ synchronize { write_message_unlocked(level, message, id) }
122
+ end
123
+
124
+ def write_message_unlocked(level, message, id = nil)
125
+ @io.puts(format_message(level, message, id)) if log_level?(level)
75
126
  end
76
127
 
77
128
  def synchronize(&block)
@@ -87,7 +138,7 @@ module KamalBackup
87
138
  end
88
139
 
89
140
  @buffers[key] = buffer
90
- write_output(context, output, redactor: redactor) unless output.empty?
141
+ write_output(context, output, redactor: redactor, stream: key.last) unless output.empty?
91
142
  end
92
143
 
93
144
  def flush_output_buffers(context)
@@ -98,17 +149,50 @@ module KamalBackup
98
149
  output = @buffers.delete(key)
99
150
  next if output.to_s.empty?
100
151
 
101
- write_output(context, output, redactor: context.fetch(:redactor))
102
- @io.puts unless output.end_with?("\n")
152
+ write_output(context, output, redactor: context.fetch(:redactor), stream: key.last)
103
153
  end
104
154
  end
105
155
 
106
- def write_output(context, output, redactor:)
156
+ def write_output(context, output, redactor:, stream: nil)
157
+ color = stream == :stderr ? :red : :green
158
+
107
159
  redactor.redact_string(output).each_line do |line|
108
- @io.print(" DEBUG [#{context.fetch(:id)}] \t#{line}")
160
+ write_message_unlocked("DEBUG", colorize("\t#{line}".chomp, color), context.fetch(:id))
109
161
  end
110
162
  @io.flush if @io.respond_to?(:flush)
111
163
  end
164
+
165
+ def format_message(level, message, id = nil)
166
+ message = "[#{colorize(id, :green)}] #{message}" if id
167
+ "#{colorize(level.rjust(6), LEVEL_COLORS.fetch(level))} #{message}"
168
+ end
169
+
170
+ def local_target
171
+ user = @env["USER"].to_s.empty? ? @env["USERNAME"].to_s : @env["USER"].to_s
172
+
173
+ if user.empty?
174
+ "on #{colorize("localhost", :blue)}"
175
+ else
176
+ "as #{colorize(user, :blue)}@#{colorize("localhost", :blue)}"
177
+ end
178
+ end
179
+
180
+ def log_level?(level)
181
+ LEVELS.fetch(level) >= @verbosity
182
+ end
183
+
184
+ def colorize(value, color, mode = nil)
185
+ string = value.to_s
186
+ return string unless colorize?
187
+ return string unless COLOR_CODES.key?(color)
188
+
189
+ prefix = mode == :bold ? "\e[1;" : "\e[0;"
190
+ "#{prefix}#{COLOR_CODES.fetch(color)};49m#{string}\e[0m"
191
+ end
192
+
193
+ def colorize?
194
+ @env["SSHKIT_COLOR"] || (@io.respond_to?(:tty?) && @io.tty?)
195
+ end
112
196
  end
113
197
 
114
198
  class Command
@@ -215,9 +215,10 @@ module KamalBackup
215
215
  end
216
216
 
217
217
  def capture_kamal(argv, stream: false)
218
- spec = CommandSpec.new(argv: argv)
218
+ spec = CommandSpec.new(argv: argv, env: kamal_stream_env(stream))
219
219
  options = {
220
220
  redactor: @redactor,
221
+ log: !stream,
221
222
  log_output: false,
222
223
  tee_stdout: stream ? @stdout : nil,
223
224
  tee_stderr: stream ? @stderr : nil
@@ -230,6 +231,20 @@ module KamalBackup
230
231
  end
231
232
  end
232
233
 
234
+ def kamal_stream_env(stream)
235
+ return {} unless stream
236
+
237
+ if @env["SSHKIT_COLOR"].to_s.empty?
238
+ stream_color? ? { "SSHKIT_COLOR" => "1" } : {}
239
+ else
240
+ { "SSHKIT_COLOR" => @env["SSHKIT_COLOR"] }
241
+ end
242
+ end
243
+
244
+ def stream_color?
245
+ [@stdout, @stderr].any? { |io| io.respond_to?(:tty?) && io.tty? }
246
+ end
247
+
233
248
  def parse_version_line(output)
234
249
  output.to_s.lines.map(&:strip).reverse.find { |line| line.match?(VERSION_LINE_PATTERN) }.to_s
235
250
  end
@@ -1,3 +1,3 @@
1
1
  module KamalBackup
2
- VERSION = "0.3.0.beta6"
2
+ VERSION = "0.3.0.beta8"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kamal-backup
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0.beta6
4
+ version: 0.3.0.beta8
5
5
  platform: ruby
6
6
  authors:
7
7
  - crmne