mux_tf 0.15.0 → 0.16.0
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/lib/mux_tf/cli/current/plan_command.rb +123 -0
- data/lib/mux_tf/cli/current.rb +92 -171
- data/lib/mux_tf/cli/mux.rb +168 -5
- data/lib/mux_tf/cli/plan_summary.rb +12 -21
- data/lib/mux_tf/error_handling_methods.rb +79 -0
- data/lib/mux_tf/handlers/plan_handler.rb +8 -0
- data/lib/mux_tf/handlers.rb +6 -0
- data/lib/mux_tf/plan_formatter.rb +285 -257
- data/lib/mux_tf/plan_summary_handler.rb +36 -29
- data/lib/mux_tf/plan_utils.rb +20 -4
- data/lib/mux_tf/resource_tokenizer.rb +1 -1
- data/lib/mux_tf/stderr_line_handler.rb +145 -0
- data/lib/mux_tf/terraform_helpers.rb +44 -6
- data/lib/mux_tf/tmux.rb +55 -6
- data/lib/mux_tf/version.rb +1 -1
- data/lib/mux_tf/version_check.rb +1 -1
- data/lib/mux_tf.rb +6 -12
- data/mux_tf.gemspec +7 -1
- metadata +69 -8
@@ -33,7 +33,7 @@ module MuxTf
|
|
33
33
|
case action
|
34
34
|
when "create", "add"
|
35
35
|
:green
|
36
|
-
when "update", "change"
|
36
|
+
when "update", "change", "import-update"
|
37
37
|
:yellow
|
38
38
|
when "delete", "remove"
|
39
39
|
:red
|
@@ -64,6 +64,10 @@ module MuxTf
|
|
64
64
|
"±"
|
65
65
|
when "read"
|
66
66
|
">"
|
67
|
+
when "import"
|
68
|
+
"→"
|
69
|
+
when "import-update"
|
70
|
+
"↗︎"
|
67
71
|
else
|
68
72
|
action
|
69
73
|
end
|
@@ -94,7 +98,7 @@ module MuxTf
|
|
94
98
|
end
|
95
99
|
end
|
96
100
|
|
97
|
-
def initialize(data)
|
101
|
+
def initialize(data)
|
98
102
|
@parts = []
|
99
103
|
|
100
104
|
data["output_changes"]&.each do |output_name, v|
|
@@ -138,6 +142,14 @@ module MuxTf
|
|
138
142
|
case v["change"]["actions"]
|
139
143
|
when ["no-op"]
|
140
144
|
# do nothing
|
145
|
+
if v["change"]["importing"]
|
146
|
+
parts << {
|
147
|
+
type: "resource",
|
148
|
+
action: "import",
|
149
|
+
address: v["address"],
|
150
|
+
deps: find_deps(data, v["address"])
|
151
|
+
}
|
152
|
+
end
|
141
153
|
when ["create"]
|
142
154
|
parts << {
|
143
155
|
type: "resource",
|
@@ -146,9 +158,10 @@ module MuxTf
|
|
146
158
|
deps: find_deps(data, v["address"])
|
147
159
|
}
|
148
160
|
when ["update"]
|
161
|
+
# ap [v["change"]["actions"], v["change"]["importing"]]
|
149
162
|
parts << {
|
150
163
|
type: "resource",
|
151
|
-
action: "update",
|
164
|
+
action: v["change"]["importing"] ? "import-update" : "update",
|
152
165
|
address: v["address"],
|
153
166
|
deps: find_deps(data, v["address"])
|
154
167
|
}
|
@@ -198,7 +211,7 @@ module MuxTf
|
|
198
211
|
parts.select { |part| part[:type] == "output" }
|
199
212
|
end
|
200
213
|
|
201
|
-
def summary
|
214
|
+
def summary
|
202
215
|
# resources
|
203
216
|
resource_summary = {}
|
204
217
|
resource_parts.each do |part|
|
@@ -233,11 +246,9 @@ module MuxTf
|
|
233
246
|
end
|
234
247
|
|
235
248
|
def flat_summary
|
236
|
-
|
237
|
-
|
238
|
-
result << "[#{self.class.format_action(part[:action])}] #{self.class.format_address(part[:address])}"
|
249
|
+
resource_parts.map do |part|
|
250
|
+
"[#{self.class.format_action(part[:action])}] #{self.class.format_address(part[:address])}"
|
239
251
|
end
|
240
|
-
result
|
241
252
|
end
|
242
253
|
|
243
254
|
def sensitive_summary(before_value, after_value)
|
@@ -265,7 +276,20 @@ module MuxTf
|
|
265
276
|
result
|
266
277
|
end
|
267
278
|
|
268
|
-
def
|
279
|
+
def simple_summary(&printer)
|
280
|
+
printer = method(:puts) if printer.nil?
|
281
|
+
|
282
|
+
flat_summary.each do |line|
|
283
|
+
printer.call line
|
284
|
+
end
|
285
|
+
output_summary.each do |line|
|
286
|
+
printer.call line
|
287
|
+
end
|
288
|
+
printer.call ""
|
289
|
+
printer.call summary
|
290
|
+
end
|
291
|
+
|
292
|
+
def nested_summary
|
269
293
|
result = []
|
270
294
|
parts = resource_parts.deep_dup
|
271
295
|
until parts.empty?
|
@@ -305,8 +329,7 @@ module MuxTf
|
|
305
329
|
if result.empty?
|
306
330
|
throw :abort, "nothing selected"
|
307
331
|
else
|
308
|
-
|
309
|
-
MuxTf::Cli::Current.run_plan(targets: result)
|
332
|
+
result
|
310
333
|
end
|
311
334
|
end
|
312
335
|
|
@@ -314,22 +337,6 @@ module MuxTf
|
|
314
337
|
|
315
338
|
attr_reader :parts
|
316
339
|
|
317
|
-
def create_plan(filename, targets: [])
|
318
|
-
log "Preparing Plan ...", depth: 1
|
319
|
-
exit_code, meta = PlanFormatter.pretty_plan(filename, targets: targets)
|
320
|
-
case exit_code
|
321
|
-
when 0
|
322
|
-
[:ok, meta]
|
323
|
-
when 1
|
324
|
-
[:error, meta]
|
325
|
-
when 2
|
326
|
-
[:changes, meta]
|
327
|
-
else
|
328
|
-
log pastel.yellow("terraform plan exited with an unknown exit code: #{exit_code}")
|
329
|
-
[:unknown, meta]
|
330
|
-
end
|
331
|
-
end
|
332
|
-
|
333
340
|
def prune_unchanged_deps(_parts)
|
334
341
|
valid_addresses = resource_parts.map { |part| part[:address] }
|
335
342
|
|
@@ -338,7 +345,7 @@ module MuxTf
|
|
338
345
|
end
|
339
346
|
end
|
340
347
|
|
341
|
-
def find_deps(data, address)
|
348
|
+
def find_deps(data, address)
|
342
349
|
result = []
|
343
350
|
|
344
351
|
m = address.match(/\[(.+)\]$/)
|
@@ -358,7 +365,7 @@ module MuxTf
|
|
358
365
|
resource, parent_address = find_config(data["configuration"], "root_module", address, [])
|
359
366
|
if resource
|
360
367
|
deps = []
|
361
|
-
resource["expressions"]&.
|
368
|
+
resource["expressions"]&.each_value do |v|
|
362
369
|
deps << v["references"] if v.is_a?(Hash) && v["references"]
|
363
370
|
end
|
364
371
|
result += deps.map { |s| (parent_address + [s]).join(".") }
|
data/lib/mux_tf/plan_utils.rb
CHANGED
@@ -21,7 +21,7 @@ module MuxTf
|
|
21
21
|
puts msg.join(" - ")
|
22
22
|
end
|
23
23
|
|
24
|
-
def update_placeholders(dst, src, placeholder)
|
24
|
+
def update_placeholders(dst, src, placeholder)
|
25
25
|
return unless src
|
26
26
|
|
27
27
|
case src
|
@@ -104,7 +104,10 @@ module MuxTf
|
|
104
104
|
else
|
105
105
|
false
|
106
106
|
end
|
107
|
-
rescue Psych::
|
107
|
+
rescue Psych::DisallowedClass => e
|
108
|
+
ap e
|
109
|
+
false
|
110
|
+
rescue Psych::SyntaxError => e # rubocop:disable Lint/DuplicateBranch
|
108
111
|
ap e
|
109
112
|
false
|
110
113
|
end
|
@@ -115,6 +118,10 @@ module MuxTf
|
|
115
118
|
pastel.green(symbol)
|
116
119
|
when "~"
|
117
120
|
pastel.yellow(symbol)
|
121
|
+
when "-"
|
122
|
+
pastel.red(symbol)
|
123
|
+
when "?"
|
124
|
+
pastel.orange(symbol)
|
118
125
|
else
|
119
126
|
warning "Unknown symbol: #{symbol.inspect}"
|
120
127
|
symbol
|
@@ -143,7 +150,7 @@ module MuxTf
|
|
143
150
|
}.join("\n")
|
144
151
|
end
|
145
152
|
|
146
|
-
def in_display_representation(value)
|
153
|
+
def in_display_representation(value)
|
147
154
|
if valid_json?(value)
|
148
155
|
json_body = JSON.pretty_generate(JSON.parse(value))
|
149
156
|
wrap(json_body, prefix: "json(", suffix: ")", color: :gray)
|
@@ -251,12 +258,21 @@ module MuxTf
|
|
251
258
|
|
252
259
|
def get_pretty_action_and_symbol(actions)
|
253
260
|
case actions
|
261
|
+
when ["delete"]
|
262
|
+
pretty_action = "delete"
|
263
|
+
symbol = "-"
|
254
264
|
when ["update"]
|
255
265
|
pretty_action = "updated in-place"
|
256
266
|
symbol = "~"
|
257
267
|
when ["create"]
|
258
268
|
pretty_action = "created"
|
259
269
|
symbol = "+"
|
270
|
+
when %w[delete create]
|
271
|
+
pretty_action = "replaced (delete first)"
|
272
|
+
symbol = "±"
|
273
|
+
when ["read"]
|
274
|
+
pretty_action = "read"
|
275
|
+
symbol = ">"
|
260
276
|
else
|
261
277
|
warning "Unknown action: #{actions.inspect}"
|
262
278
|
pretty_action = actions.inspect
|
@@ -286,7 +302,7 @@ module MuxTf
|
|
286
302
|
# EOT
|
287
303
|
# # (12 unchanged attributes hidden)
|
288
304
|
# }
|
289
|
-
def tf_show_json_resource(resource)
|
305
|
+
def tf_show_json_resource(resource)
|
290
306
|
pretty_action, symbol = get_pretty_action_and_symbol(resource["change"]["actions"])
|
291
307
|
|
292
308
|
output = []
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "English"
|
4
|
+
module MuxTf
|
5
|
+
class StderrLineHandler
|
6
|
+
include PiotrbCliUtils::Util
|
7
|
+
include Coloring
|
8
|
+
|
9
|
+
include ErrorHandlingMethods
|
10
|
+
|
11
|
+
attr_reader :meta
|
12
|
+
|
13
|
+
def initialize(operation: nil)
|
14
|
+
@operation = operation
|
15
|
+
@held_messages = []
|
16
|
+
@parser = StatefulParser.new(normalizer: pastel.method(:strip))
|
17
|
+
@meta = {}
|
18
|
+
setup_error_handling(@parser, from_states: [:none])
|
19
|
+
end
|
20
|
+
|
21
|
+
def transform_paths!(hash, key)
|
22
|
+
return unless hash[key]
|
23
|
+
|
24
|
+
if key == "prefix"
|
25
|
+
hash[key].strip!
|
26
|
+
hash[key].gsub!(/^\[/, "")
|
27
|
+
hash[key].gsub!(/\]$/, "")
|
28
|
+
end
|
29
|
+
|
30
|
+
hash[key].gsub!("#{Dir.getwd}/", "")
|
31
|
+
hash[key].gsub!(Dir.getwd, "")
|
32
|
+
|
33
|
+
hash[key].gsub!($LAST_MATCH_INFO[1], "<cache>/") if hash[key].match(%r{(\.terragrunt-cache/[^/]+/[^/]+/)})
|
34
|
+
|
35
|
+
hash
|
36
|
+
end
|
37
|
+
|
38
|
+
def handle(raw_line)
|
39
|
+
return if raw_line.strip.empty?
|
40
|
+
|
41
|
+
if raw_line =~ /Error when retrieving token from sso: Token has expired and refresh failed/
|
42
|
+
log "#{pastel.red('error')}: SSO Session expired.", depth: 2
|
43
|
+
return
|
44
|
+
end
|
45
|
+
|
46
|
+
# [✘] error when retrieving credentials from custom process. please login using 'granted sso login --sso-start-url https://janeapp.awsapps.com/start --sso-region us-east-1'
|
47
|
+
if raw_line =~ /error when retrieving credentials from custom process. please login using '([^']+)'/
|
48
|
+
unless @sso_expired
|
49
|
+
@sso_expired = true
|
50
|
+
log "#{pastel.red('error')}: SSO Session expired.", depth: 2
|
51
|
+
log "#{pastel.red('error')}: Run: #{$LAST_MATCH_INFO[1]}", depth: 2
|
52
|
+
end
|
53
|
+
return
|
54
|
+
end
|
55
|
+
|
56
|
+
if raw_line.strip[0] == "{" && raw_line.strip[-1] == "}"
|
57
|
+
begin
|
58
|
+
# assuming that stderr is JSON and TG logs
|
59
|
+
parsed_line = JSON.parse(raw_line)
|
60
|
+
transform_paths!(parsed_line, "msg")
|
61
|
+
transform_paths!(parsed_line, "prefix")
|
62
|
+
parsed_line["msg"].gsub!("#{Dir.getwd}/", "")
|
63
|
+
parsed_line["prefix"]&.strip!&.gsub!(/^\[/, "")&.gsub!(/\]$/, "")
|
64
|
+
parsed_line["prefix"]&.gsub!(Dir.getwd, "")
|
65
|
+
if @operation == :plan
|
66
|
+
handle_plan_json(parsed_line)
|
67
|
+
else
|
68
|
+
log format_tg_log_line(parsed_line), depth: 2
|
69
|
+
end
|
70
|
+
rescue JSON::ParserError => e
|
71
|
+
log "#{pastel.red('error')}: failed to parse JSON: #{e.message}", depth: 2
|
72
|
+
log raw_line.rstrip, depth: 2
|
73
|
+
end
|
74
|
+
else
|
75
|
+
@parser.parse(raw_line.rstrip) do |state, line|
|
76
|
+
# log raw_line.rstrip, depth: 2
|
77
|
+
log_unhandled_line(state, line, reason: "unexpected state in StderrLineHandler") unless handle_error_states(@meta, state, line)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def handle_plan_json(parsed_line)
|
83
|
+
if parsed_line["msg"] =~ /terraform invocation failed in/
|
84
|
+
@held_messages << format_tg_log_line(parsed_line)
|
85
|
+
elsif parsed_line["msg"] =~ /1 error occurred/ && parsed_line["msg"] =~ /exit status 2\n/
|
86
|
+
# 2 = Succeeded with non-empty diff (changes present)
|
87
|
+
# clear the held messages and swallow up this message too
|
88
|
+
@held_messages = []
|
89
|
+
else
|
90
|
+
# flush
|
91
|
+
log format_tg_log_line(parsed_line), depth: 2
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def flush
|
96
|
+
@held_messages.each do |msg|
|
97
|
+
log msg, depth: 2
|
98
|
+
end
|
99
|
+
@held_messages = []
|
100
|
+
end
|
101
|
+
|
102
|
+
def do_print_errors
|
103
|
+
print_errors(@meta) if @meta[:errors] && !@meta[:errors].empty?
|
104
|
+
end
|
105
|
+
|
106
|
+
def merge_meta_into(other_meta)
|
107
|
+
[:errors, :warnings].each do |type|
|
108
|
+
if @meta[type]
|
109
|
+
other_meta[type] ||= []
|
110
|
+
other_meta[type] += @meta[type]
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
extra_keys = @meta.keys - [:errors, :warnings]
|
115
|
+
return unless extra_keys.any?
|
116
|
+
|
117
|
+
log "Unhandled keys in stderr_handler.meta: #{extra_keys.inspect}"
|
118
|
+
log @meta.inspect
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def format_tg_log_line(line_data)
|
124
|
+
# {
|
125
|
+
# "level"=>"error",
|
126
|
+
# "msg"=>"terraform invocation failed in /Users/piotr/Work/janepods/accounts/eks-dev/unstable-1/kluster/.terragrunt-cache/Gqer3b7TGI4swB-Nw7Pe5DUIrus/JkQqfrQedXyGMwcl4yYfGocMcvk/modules/kluster",
|
127
|
+
# "prefix"=>"[/Users/piotr/Work/janepods/accounts/eks-dev/unstable-1/kluster] ",
|
128
|
+
# "time"=>"2024-02-28T16:14:28-08:00"
|
129
|
+
# }
|
130
|
+
|
131
|
+
msg = ""
|
132
|
+
msg += case line_data["level"]
|
133
|
+
when "info"
|
134
|
+
pastel.cyan(line_data["level"])
|
135
|
+
when "error"
|
136
|
+
pastel.red(line_data["level"])
|
137
|
+
else
|
138
|
+
pastel.orange(line_data["level"])
|
139
|
+
end
|
140
|
+
msg += ": #{line_data['msg']}"
|
141
|
+
msg += " [#{line_data['prefix']}]" if line_data["prefix"] && !line_data["prefix"].empty?
|
142
|
+
msg
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -11,6 +11,30 @@ module MuxTf
|
|
11
11
|
run_terraform(tf_prepare_command(["force-unlock", "-force", id], need_auth: true))
|
12
12
|
end
|
13
13
|
|
14
|
+
def tf_stream_helper(cmd, operation:, echo_stdout: false, &block)
|
15
|
+
# stdout = ""
|
16
|
+
|
17
|
+
stderr_handler = StderrLineHandler.new(operation: operation)
|
18
|
+
|
19
|
+
result_struct = stream_terraform(cmd, split_streams: true) { |(stream, raw_line)|
|
20
|
+
case stream
|
21
|
+
when :command
|
22
|
+
log "Running command: #{raw_line.strip} ...", depth: 2
|
23
|
+
when :stdout
|
24
|
+
# stdout += raw_line
|
25
|
+
print raw_line if echo_stdout
|
26
|
+
block&.call(:stdout, raw_line)
|
27
|
+
when :stderr
|
28
|
+
stderr_handler.handle(raw_line)
|
29
|
+
end
|
30
|
+
}
|
31
|
+
|
32
|
+
stderr_handler.flush
|
33
|
+
stderr_handler.do_print_errors
|
34
|
+
|
35
|
+
result_struct
|
36
|
+
end
|
37
|
+
|
14
38
|
def tf_apply(filename: nil, targets: [])
|
15
39
|
args = []
|
16
40
|
args << filename if filename
|
@@ -21,12 +45,22 @@ module MuxTf
|
|
21
45
|
end
|
22
46
|
|
23
47
|
cmd = tf_prepare_command(["apply", *args], need_auth: true)
|
24
|
-
|
48
|
+
|
49
|
+
tf_stream_helper(cmd, operation: :apply, echo_stdout: true)
|
25
50
|
end
|
26
51
|
|
27
52
|
def tf_validate
|
28
53
|
cmd = tf_prepare_command(["validate", "-json"], need_auth: true)
|
29
|
-
|
54
|
+
|
55
|
+
stdout = ""
|
56
|
+
|
57
|
+
tf_stream_helper(cmd, operation: :validate) do |stream, raw_line|
|
58
|
+
stdout += raw_line if stream == :stdout
|
59
|
+
end
|
60
|
+
|
61
|
+
throw :abort, false if stdout.strip.empty?
|
62
|
+
|
63
|
+
JSON.parse(stdout)
|
30
64
|
end
|
31
65
|
|
32
66
|
def tf_init(input: nil, upgrade: nil, reconfigure: nil, color: true, &block)
|
@@ -37,10 +71,11 @@ module MuxTf
|
|
37
71
|
args << "-no-color" unless color
|
38
72
|
|
39
73
|
cmd = tf_prepare_command(["init", *args], need_auth: true)
|
40
|
-
|
74
|
+
stream_terraform(cmd, split_streams: true, &block)
|
41
75
|
end
|
42
76
|
|
43
|
-
def tf_plan(out:, color: true, detailed_exitcode: nil, compact_warnings: false, input: nil, targets: [], json: false,
|
77
|
+
def tf_plan(out:, color: true, detailed_exitcode: nil, compact_warnings: false, input: nil, targets: [], json: false, split_streams: false,
|
78
|
+
&block)
|
44
79
|
args = []
|
45
80
|
args += ["-out", out]
|
46
81
|
args << "-input=#{input.inspect}" unless input.nil?
|
@@ -55,7 +90,7 @@ module MuxTf
|
|
55
90
|
end
|
56
91
|
|
57
92
|
cmd = tf_prepare_command(["plan", *args], need_auth: true)
|
58
|
-
stream_or_run_terraform(cmd, split_streams: json, &block)
|
93
|
+
stream_or_run_terraform(cmd, split_streams: json || split_streams, &block)
|
59
94
|
end
|
60
95
|
|
61
96
|
def tf_show(file, json: false, capture: false)
|
@@ -132,7 +167,10 @@ module MuxTf
|
|
132
167
|
stderr_thread.join
|
133
168
|
output_queue.close
|
134
169
|
end
|
135
|
-
|
170
|
+
until output_queue.closed? && output_queue.empty?
|
171
|
+
value = output_queue.pop
|
172
|
+
yield(value) unless value.nil?
|
173
|
+
end
|
136
174
|
wait_thr.value # Process::Status object returned.
|
137
175
|
end
|
138
176
|
end
|
data/lib/mux_tf/tmux.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "shellwords"
|
4
|
+
require "open3"
|
4
5
|
|
5
6
|
module MuxTf
|
6
7
|
module Tmux
|
@@ -23,6 +24,13 @@ module MuxTf
|
|
23
24
|
panes.find { |pane| pane[:name] == name }
|
24
25
|
end
|
25
26
|
|
27
|
+
def list_panes
|
28
|
+
`tmux list-panes -F "\#{pane_id},\#{pane_index},\#{pane_title}"`.strip.split("\n").map do |row|
|
29
|
+
x = row.split(",")
|
30
|
+
{ id: x[0], index: x[1], name: x[2] }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
26
34
|
def list_windows
|
27
35
|
`tmux list-windows -F "\#{window_id},\#{window_index},\#{window_name}"`.strip.split("\n").map do |row|
|
28
36
|
x = row.split(",")
|
@@ -30,8 +38,15 @@ module MuxTf
|
|
30
38
|
end
|
31
39
|
end
|
32
40
|
|
33
|
-
def new_session(name)
|
34
|
-
|
41
|
+
def new_session(name, cwd: nil, cmd: nil)
|
42
|
+
parts = [
|
43
|
+
"new-session",
|
44
|
+
"-s #{name.inspect}",
|
45
|
+
"-d",
|
46
|
+
cwd ? "-c #{cwd}" : nil,
|
47
|
+
cmd&.inspect
|
48
|
+
].compact
|
49
|
+
tmux parts.join(" ")
|
35
50
|
end
|
36
51
|
|
37
52
|
def select_pane(name)
|
@@ -50,8 +65,23 @@ module MuxTf
|
|
50
65
|
tmux "select-layout tiled"
|
51
66
|
end
|
52
67
|
|
53
|
-
def
|
54
|
-
|
68
|
+
def attach_control(name, on_line:, on_spawn:)
|
69
|
+
parts = [
|
70
|
+
"-C",
|
71
|
+
"attach",
|
72
|
+
"-t #{name.inspect}"
|
73
|
+
].compact
|
74
|
+
tmux parts.join(" "), raise_on_error: false, mode: :popen, on_line: on_line, on_spawn: on_spawn
|
75
|
+
end
|
76
|
+
|
77
|
+
def attach(name, cc: false, control: false)
|
78
|
+
parts = [
|
79
|
+
cc ? "-CC" : nil,
|
80
|
+
control ? "-C" : nil,
|
81
|
+
"attach",
|
82
|
+
"-t #{name.inspect}"
|
83
|
+
].compact
|
84
|
+
tmux parts.join(" "), raise_on_error: false
|
55
85
|
end
|
56
86
|
|
57
87
|
def kill_pane(pane_id)
|
@@ -75,7 +105,7 @@ module MuxTf
|
|
75
105
|
|
76
106
|
parts = [
|
77
107
|
"split-window",
|
78
|
-
cwd
|
108
|
+
cwd ? "-c #{cwd}" : nil,
|
79
109
|
mode_part,
|
80
110
|
"-t #{target_pane.inspect}",
|
81
111
|
cmd&.inspect
|
@@ -89,7 +119,7 @@ module MuxTf
|
|
89
119
|
@tmux_bin ||= `which tmux`.strip
|
90
120
|
end
|
91
121
|
|
92
|
-
def tmux(cmd, raise_on_error: true, mode: :system)
|
122
|
+
def tmux(cmd, raise_on_error: true, mode: :system, on_line: nil, on_spawn: nil)
|
93
123
|
case mode
|
94
124
|
when :system
|
95
125
|
# puts " => tmux #{cmd}"
|
@@ -102,6 +132,25 @@ module MuxTf
|
|
102
132
|
true
|
103
133
|
when :exec
|
104
134
|
exec tmux_bin, *Shellwords.shellwords(cmd)
|
135
|
+
when :popen
|
136
|
+
Open3.popen3(tmux_bin, *Shellwords.shellwords(cmd)) do |stdin, stdout, stderr, wait_thr|
|
137
|
+
on_spawn&.call(stdin, wait_thr)
|
138
|
+
Thread.new do
|
139
|
+
until stdout.closed? || stdout.eof?
|
140
|
+
line = stdout.gets
|
141
|
+
on_line&.call(:stdout, line)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
Thread.new do
|
145
|
+
until stderr.closed? || stderr.eof?
|
146
|
+
line = stderr.gets
|
147
|
+
on_line&.call(:stderr, line)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
# pid = wait_thr.pid
|
151
|
+
exit_status = wait_thr.value
|
152
|
+
fail_with("`tmux #{cmd}' failed with code: #{exit_status}") if raise_on_error && exit_status != 0
|
153
|
+
end
|
105
154
|
end
|
106
155
|
end
|
107
156
|
end
|
data/lib/mux_tf/version.rb
CHANGED
data/lib/mux_tf/version_check.rb
CHANGED
@@ -25,6 +25,6 @@ module MuxTf
|
|
25
25
|
@cache ||= YamlCache.new(File.expand_path("~/.mux_tf.yaml"), default_ttl: 1.hour)
|
26
26
|
end
|
27
27
|
|
28
|
-
module_function :has_updates?, :latest_gem_version, :current_gem_version, :cache
|
28
|
+
module_function :has_updates?, :latest_gem_version, :current_gem_version, :cache # rubocop:disable Style/AccessModifierDeclarations
|
29
29
|
end
|
30
30
|
end
|
data/lib/mux_tf.rb
CHANGED
@@ -26,19 +26,13 @@ require "diff/lcs"
|
|
26
26
|
require "diff/lcs/string"
|
27
27
|
require "diff/lcs/hunk"
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
require_relative "mux_tf/cli"
|
34
|
-
require_relative "mux_tf/tmux"
|
35
|
-
require_relative "mux_tf/terraform_helpers"
|
36
|
-
require_relative "mux_tf/plan_formatter"
|
37
|
-
require_relative "mux_tf/version_check"
|
38
|
-
require_relative "mux_tf/yaml_cache"
|
39
|
-
require_relative "mux_tf/plan_summary_handler"
|
40
|
-
require_relative "mux_tf/plan_utils"
|
29
|
+
require "zeitwerk"
|
30
|
+
loader = Zeitwerk::Loader.for_gem
|
31
|
+
loader.ignore("#{__dir__}/deps.rb")
|
32
|
+
loader.setup
|
41
33
|
|
42
34
|
module MuxTf
|
43
35
|
ROOT = File.expand_path(File.join(__dir__, ".."))
|
44
36
|
end
|
37
|
+
|
38
|
+
loader.eager_load
|
data/mux_tf.gemspec
CHANGED
@@ -29,14 +29,20 @@ Gem::Specification.new do |spec|
|
|
29
29
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
30
30
|
spec.require_paths = ["lib"]
|
31
31
|
|
32
|
+
# below activesupport are deps which cause warnings in ruby 3.3
|
32
33
|
spec.add_dependency "activesupport", "< 7.0.0"
|
34
|
+
spec.add_dependency "base64"
|
35
|
+
spec.add_dependency "bigdecimal"
|
36
|
+
spec.add_dependency "mutex_m"
|
37
|
+
|
33
38
|
spec.add_dependency "awesome_print"
|
34
39
|
spec.add_dependency "diff-lcs"
|
35
40
|
spec.add_dependency "dotenv"
|
36
41
|
spec.add_dependency "hashdiff"
|
37
42
|
spec.add_dependency "pastel"
|
38
|
-
spec.add_dependency "piotrb_cli_utils", "~> 0.1.
|
43
|
+
spec.add_dependency "piotrb_cli_utils", "~> 0.1.1"
|
39
44
|
spec.add_dependency "stateful_parser", "~> 0.1.1"
|
40
45
|
spec.add_dependency "tty-prompt"
|
41
46
|
spec.add_dependency "tty-table"
|
47
|
+
spec.add_dependency "zeitwerk", "~> 2.6"
|
42
48
|
end
|