mux_tf 0.16.0 → 0.17.1
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 +18 -10
- data/lib/mux_tf/cli/current.rb +94 -49
- data/lib/mux_tf/formatter_common.rb +257 -0
- data/lib/mux_tf/init_formatter.rb +306 -0
- data/lib/mux_tf/plan_formatter.rb +174 -519
- data/lib/mux_tf/plan_summary_handler.rb +16 -2
- data/lib/mux_tf/plan_utils.rb +200 -55
- data/lib/mux_tf/stderr_line_handler.rb +1 -1
- data/lib/mux_tf/terraform_helpers.rb +2 -1
- data/lib/mux_tf/version.rb +1 -1
- data/lib/mux_tf/version_check.rb +1 -1
- data/lib/mux_tf/yaml_cache.rb +51 -34
- data/mux_tf.gemspec +6 -1
- metadata +49 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4daae54b1c3dec534cbea1d885966275435c29d6a528afac0a406c574fe5d559
|
4
|
+
data.tar.gz: 5c92881a86eac6d57e955a4194fa78119f5e86934d0b52cbacff473a3d8ddb1a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e1ff3f7eb9936e8eeb0c1f56463405cba9f2b996ac3ff05aee914698ee447befec00ae845ec14328ab7b2e6e80201a63ae7d99dcd5ac424584843ec2091c724e
|
7
|
+
data.tar.gz: bb01ac4e5fbfe60f3ea9aadab25c42543c53c55757c86da14e2ab1eef1d707b35a5b76f9bacb4d075eca00fa58c16eb278cffe1497612ac6cca91b5bb0934caa
|
@@ -8,6 +8,8 @@ module MuxTf
|
|
8
8
|
include PiotrbCliUtils::CriCommandSupport
|
9
9
|
extend PiotrbCliUtils::Util
|
10
10
|
|
11
|
+
attr_reader :last_lock_info
|
12
|
+
|
11
13
|
def plan_cmd
|
12
14
|
define_cmd("plan", summary: "Re-run plan") do |_opts, _args, _cmd|
|
13
15
|
run_validate && run_plan
|
@@ -52,12 +54,25 @@ module MuxTf
|
|
52
54
|
when :error
|
53
55
|
log "something went wrong", depth: 1
|
54
56
|
when :changes
|
55
|
-
|
57
|
+
if ENV["JSON_PLAN"]
|
58
|
+
plan_filename = PlanFilenameGenerator.for_path
|
59
|
+
plan = PlanSummaryHandler.from_file(plan_filename)
|
60
|
+
|
61
|
+
log plan.plan_text_output
|
62
|
+
|
63
|
+
log ""
|
64
|
+
|
65
|
+
plan.simple_summary do |line|
|
66
|
+
log line, depth: 2
|
67
|
+
end
|
68
|
+
else
|
56
69
|
log "Printing Plan Summary ...", depth: 1
|
57
70
|
plan_filename = PlanFilenameGenerator.for_path
|
58
|
-
|
71
|
+
plan = PlanSummaryHandler.from_file(plan_filename)
|
72
|
+
plan.simple_summary do |line|
|
73
|
+
log line, depth: 2
|
74
|
+
end
|
59
75
|
end
|
60
|
-
puts plan_summary_text if ENV["JSON_PLAN"]
|
61
76
|
when :unknown
|
62
77
|
# nothing
|
63
78
|
end
|
@@ -110,13 +125,6 @@ module MuxTf
|
|
110
125
|
created: meta["Created"]
|
111
126
|
}
|
112
127
|
end
|
113
|
-
|
114
|
-
def pretty_plan_summary(filename)
|
115
|
-
plan = PlanSummaryHandler.from_file(filename)
|
116
|
-
plan.simple_summary do |line|
|
117
|
-
log line, depth: 2
|
118
|
-
end
|
119
|
-
end
|
120
128
|
end
|
121
129
|
end
|
122
130
|
end
|
data/lib/mux_tf/cli/current.rb
CHANGED
@@ -14,15 +14,60 @@ module MuxTf
|
|
14
14
|
class << self
|
15
15
|
attr_accessor :plan_command
|
16
16
|
|
17
|
+
def detect_tool_type(tool_cmd: nil)
|
18
|
+
tool_cmd ||= ENV.fetch("MUX_TF_BASE_CMD", "terraform")
|
19
|
+
output = `#{tool_cmd} --version`
|
20
|
+
case output
|
21
|
+
when /^Terraform v(.+)$/
|
22
|
+
{
|
23
|
+
tool_name: "terraform",
|
24
|
+
tool_version: ::Regexp.last_match(1)
|
25
|
+
}
|
26
|
+
when /^OpenTofu v(.+)$/
|
27
|
+
{
|
28
|
+
tool_name: "opentofu",
|
29
|
+
tool_version: ::Regexp.last_match(1)
|
30
|
+
}
|
31
|
+
when /^terragrunt version v(.+)$/
|
32
|
+
result = {
|
33
|
+
tool_wrapper: "terragrunt",
|
34
|
+
tool_wrapper_version: ::Regexp.last_match(1)
|
35
|
+
}
|
36
|
+
tg_tool = ENV.fetch("TG_TF_PATH", "tofu")
|
37
|
+
result.merge! detect_tool_type(tool_cmd: tg_tool)
|
38
|
+
result
|
39
|
+
else
|
40
|
+
raise "can't parse tool version from: #{output.inspect}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def init_tool_env
|
45
|
+
info = detect_tool_type
|
46
|
+
|
47
|
+
if %w[terraform opentofu].include?(info[:tool_name])
|
48
|
+
ENV["TF_IN_AUTOMATION"] = "1"
|
49
|
+
ENV["TF_INPUT"] = "0"
|
50
|
+
end
|
51
|
+
|
52
|
+
return unless info[:tool_wrapper] == "terragrunt"
|
53
|
+
|
54
|
+
if Gem::Version.new(info[:tool_wrapper_version]) >= Gem::Version.new("0.70.0")
|
55
|
+
# new syntax
|
56
|
+
ENV["TG_LOG_FORMAT"] = "json"
|
57
|
+
ENV["TG_TF_FORWARD_STDOUT"] = "true"
|
58
|
+
else
|
59
|
+
# old syntax
|
60
|
+
ENV["TERRAGRUNT_JSON_LOG"] = "1"
|
61
|
+
ENV["TERRAGRUNT_FORWARD_TF_STDOUT"] = "1"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
17
65
|
def run(args)
|
18
66
|
version_check
|
19
67
|
|
20
68
|
self.plan_command = PlanCommand.new
|
21
69
|
|
22
|
-
|
23
|
-
ENV["TF_INPUT"] = "0"
|
24
|
-
ENV["TERRAGRUNT_JSON_LOG"] = "1"
|
25
|
-
ENV["TERRAGRUNT_FORWARD_TF_STDOUT"] = "1"
|
70
|
+
init_tool_env
|
26
71
|
|
27
72
|
if args[0] == "cli"
|
28
73
|
cmd_loop
|
@@ -36,6 +81,7 @@ module MuxTf
|
|
36
81
|
if args[0] && valid_commands.include?(args[0])
|
37
82
|
stop_reason = catch(:stop) {
|
38
83
|
root_cmd.run(args, {}, hard_exit: true)
|
84
|
+
nil
|
39
85
|
}
|
40
86
|
log pastel.red("Stopped: #{stop_reason}") if stop_reason
|
41
87
|
return
|
@@ -94,6 +140,8 @@ module MuxTf
|
|
94
140
|
end
|
95
141
|
|
96
142
|
meta[:warnings]&.each do |warning|
|
143
|
+
next if warning[:printed]
|
144
|
+
|
97
145
|
log "-" * 20
|
98
146
|
log pastel.yellow("Warning: #{warning[:message]}")
|
99
147
|
warning[:body]&.each do |line|
|
@@ -103,6 +151,8 @@ module MuxTf
|
|
103
151
|
end
|
104
152
|
|
105
153
|
meta[:errors]&.each do |error|
|
154
|
+
next if error[:printed]
|
155
|
+
|
106
156
|
log "-" * 20
|
107
157
|
log pastel.red("Error: #{error[:message]}")
|
108
158
|
error[:body]&.each do |line|
|
@@ -149,12 +199,21 @@ module MuxTf
|
|
149
199
|
log wrap_log["unprocessed remedies: #{remedies.to_a}", color: :red], depth: 1
|
150
200
|
return [false, results]
|
151
201
|
end
|
202
|
+
if remedies.delete? :user_error
|
203
|
+
remedy = :user_error
|
204
|
+
log wrap_log["user error encountered!", color: :red]
|
205
|
+
log wrap_log["-" * 40, color: :red]
|
206
|
+
log wrap_log["!! User Error, Please fix the issue and try again", color: :red]
|
207
|
+
log wrap_log["-" * 40, color: :red]
|
208
|
+
results[:user_error] = true
|
209
|
+
return [false, results]
|
210
|
+
end
|
152
211
|
if remedies.delete? :init
|
153
212
|
remedy = :init
|
154
213
|
log wrap_log["Running terraform init ..."], depth: 2
|
155
|
-
exit_code, meta =
|
214
|
+
exit_code, meta = InitFormatter.run_tf_init
|
156
215
|
print_errors_and_warnings(meta)
|
157
|
-
remedies =
|
216
|
+
remedies = InitFormatter.init_status_to_remedies(exit_code, meta)
|
158
217
|
status, r_results = process_remedies(remedies, from: from, level: level + 1)
|
159
218
|
results.merge!(r_results)
|
160
219
|
return [true, r_results] if status
|
@@ -170,9 +229,9 @@ module MuxTf
|
|
170
229
|
remedy = :reconfigure
|
171
230
|
log wrap_log["Running terraform init ..."], depth: 2
|
172
231
|
result = remedy_retry_helper(from: :reconfigure, level: level + 1, attempt: retry_count) {
|
173
|
-
exit_code, meta =
|
232
|
+
exit_code, meta = InitFormatter.run_tf_init(reconfigure: true)
|
174
233
|
print_errors_and_warnings(meta)
|
175
|
-
remedies =
|
234
|
+
remedies = InitFormatter.init_status_to_remedies(exit_code, meta)
|
176
235
|
[remedies, exit_code, meta]
|
177
236
|
}
|
178
237
|
unless result
|
@@ -180,15 +239,6 @@ module MuxTf
|
|
180
239
|
return [false, result]
|
181
240
|
end
|
182
241
|
end
|
183
|
-
if remedies.delete? :user_error
|
184
|
-
remedy = :user_error
|
185
|
-
log wrap_log["user error encountered!", color: :red]
|
186
|
-
log wrap_log["-" * 40, color: :red]
|
187
|
-
log wrap_log["!! User Error, Please fix the issue and try again", color: :red]
|
188
|
-
log wrap_log["-" * 40, color: :red]
|
189
|
-
results[:user_error] = true
|
190
|
-
return [false, results]
|
191
|
-
end
|
192
242
|
if remedies.delete? :auth
|
193
243
|
remedy = :auth
|
194
244
|
log wrap_log["auth error encountered!", color: :red]
|
@@ -221,13 +271,26 @@ module MuxTf
|
|
221
271
|
cmd_loop(status)
|
222
272
|
end
|
223
273
|
|
224
|
-
def cmd_loop(
|
274
|
+
def cmd_loop(initial_status = nil)
|
225
275
|
root_cmd = build_root_cmd
|
226
276
|
|
227
277
|
folder_name = File.basename(Dir.getwd)
|
228
278
|
|
229
279
|
puts root_cmd.help
|
230
280
|
|
281
|
+
status = initial_status
|
282
|
+
|
283
|
+
prompt = proc { format_prompt(folder_name, status) }
|
284
|
+
|
285
|
+
run_cmd_loop(prompt) do |cmd|
|
286
|
+
status = nil
|
287
|
+
throw(:stop, :no_input) if cmd == ""
|
288
|
+
args = Shellwords.split(cmd)
|
289
|
+
root_cmd.run(args, {}, hard_exit: false)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
def format_prompt(folder_name, status)
|
231
294
|
prompt = "#{folder_name} => "
|
232
295
|
case status
|
233
296
|
when :error, :unknown
|
@@ -235,12 +298,7 @@ module MuxTf
|
|
235
298
|
when :changes
|
236
299
|
prompt = "[#{pastel.yellow(status.to_s)}] #{prompt}"
|
237
300
|
end
|
238
|
-
|
239
|
-
run_cmd_loop(prompt) do |cmd|
|
240
|
-
throw(:stop, :no_input) if cmd == ""
|
241
|
-
args = Shellwords.split(cmd)
|
242
|
-
root_cmd.run(args, {}, hard_exit: false)
|
243
|
-
end
|
301
|
+
prompt
|
244
302
|
end
|
245
303
|
|
246
304
|
def build_root_cmd
|
@@ -261,32 +319,19 @@ module MuxTf
|
|
261
319
|
root_cmd
|
262
320
|
end
|
263
321
|
|
264
|
-
def plan_summary_text
|
265
|
-
plan_filename = PlanFilenameGenerator.for_path
|
266
|
-
if File.exist?("#{plan_filename}.txt") && File.mtime("#{plan_filename}.txt").to_f >= File.mtime(plan_filename).to_f
|
267
|
-
File.read("#{plan_filename}.txt")
|
268
|
-
else
|
269
|
-
puts "Inspecting Changes ... #{plan_filename}"
|
270
|
-
data = PlanUtils.text_version_of_plan_show(plan_filename)
|
271
|
-
File.write("#{plan_filename}.txt", data)
|
272
|
-
data
|
273
|
-
end
|
274
|
-
end
|
275
|
-
|
276
322
|
def plan_details_cmd
|
277
323
|
define_cmd("details", summary: "Show Plan Details") do |_opts, _args, _cmd|
|
278
|
-
|
324
|
+
plan_filename = PlanFilenameGenerator.for_path
|
325
|
+
plan = PlanSummaryHandler.from_file(plan_filename)
|
279
326
|
|
280
|
-
|
281
|
-
log "Printing Plan Summary ...", depth: 1
|
327
|
+
log plan.plan_text_output
|
282
328
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
329
|
+
log ""
|
330
|
+
|
331
|
+
log "Resource Summary:"
|
332
|
+
plan.simple_summary do |line|
|
333
|
+
log line
|
288
334
|
end
|
289
|
-
# puts plan_summary_text if ENV["JSON_PLAN"]
|
290
335
|
end
|
291
336
|
end
|
292
337
|
|
@@ -315,7 +360,7 @@ module MuxTf
|
|
315
360
|
define_cmd("force-unlock", summary: "Force unlock state after encountering a lock error!") do # rubocop:disable Metrics/BlockLength
|
316
361
|
prompt = TTY::Prompt.new(interrupt: :noop)
|
317
362
|
|
318
|
-
lock_info = @last_lock_info
|
363
|
+
lock_info = @plan_command.last_lock_info
|
319
364
|
|
320
365
|
if lock_info
|
321
366
|
table = TTY::Table.new(header: %w[Field Value])
|
@@ -358,7 +403,7 @@ module MuxTf
|
|
358
403
|
|
359
404
|
def init_cmd
|
360
405
|
define_cmd("init", summary: "Re-run init") do |_opts, _args, _cmd|
|
361
|
-
exit_code, meta =
|
406
|
+
exit_code, meta = InitFormatter.run_tf_init
|
362
407
|
print_errors_and_warnings(meta)
|
363
408
|
if exit_code != 0
|
364
409
|
log meta.inspect unless meta.empty?
|
@@ -379,7 +424,7 @@ module MuxTf
|
|
379
424
|
|
380
425
|
def reconfigure_cmd
|
381
426
|
define_cmd("reconfigure", summary: "Reconfigure modules/plguins") do |_opts, _args, _cmd|
|
382
|
-
exit_code, meta =
|
427
|
+
exit_code, meta = InitFormatter.run_tf_init(reconfigure: true)
|
383
428
|
print_errors_and_warnings(meta)
|
384
429
|
if exit_code != 0
|
385
430
|
log meta.inspect unless meta.empty?
|
@@ -411,7 +456,7 @@ module MuxTf
|
|
411
456
|
end
|
412
457
|
|
413
458
|
def run_upgrade
|
414
|
-
exit_code, meta =
|
459
|
+
exit_code, meta = InitFormatter.run_tf_init(upgrade: true)
|
415
460
|
print_errors_and_warnings(meta)
|
416
461
|
case exit_code
|
417
462
|
when 0
|
@@ -0,0 +1,257 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module MuxTf
|
4
|
+
module FormatterCommon
|
5
|
+
def tf_cmd_json(cmd_call_proc, &block)
|
6
|
+
last_stderr_line = nil
|
7
|
+
handler = proc { |(stream, raw_line)| # rubocop:disable Metrics/BlockLength
|
8
|
+
case stream
|
9
|
+
when :command
|
10
|
+
log "Running command: #{raw_line.strip} ...", depth: 2
|
11
|
+
when :stdout
|
12
|
+
begin
|
13
|
+
parsed_line = JSON.parse(raw_line)
|
14
|
+
parsed_line.keys.each do |key|
|
15
|
+
if key[0] == "@"
|
16
|
+
parsed_line[key[1..]] = parsed_line[key]
|
17
|
+
parsed_line.delete(key)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
parsed_line.symbolize_keys!
|
21
|
+
parsed_line[:stream] = stream
|
22
|
+
if last_stderr_line
|
23
|
+
emit_line_helper(last_stderr_line, &block)
|
24
|
+
last_stderr_line = nil
|
25
|
+
end
|
26
|
+
emit_line_helper(parsed_line, &block)
|
27
|
+
rescue JSON::ParserError
|
28
|
+
# eg: "[WARN] Provider spacelift-io/spacelift (registry.opentofu.org) gpg key expired, this will fail in future versions of OpenTofu\n"
|
29
|
+
# treat the line as a normal log line
|
30
|
+
parsed_line = {
|
31
|
+
message: raw_line.strip,
|
32
|
+
module: "non-json-log"
|
33
|
+
}
|
34
|
+
parsed_line[:level] = raw_line[0..-2].strip if raw_line.match?(/^[A-Z]+/)
|
35
|
+
parsed_line[:stream] = stream
|
36
|
+
emit_line_helper(parsed_line, &block)
|
37
|
+
end
|
38
|
+
when :stderr
|
39
|
+
parsed_line = parse_non_json_plan_line(raw_line)
|
40
|
+
parsed_line[:stream] = stream
|
41
|
+
|
42
|
+
if parsed_line[:blank]
|
43
|
+
if last_stderr_line
|
44
|
+
emit_line_helper(last_stderr_line, &block)
|
45
|
+
last_stderr_line = nil
|
46
|
+
end
|
47
|
+
elsif parsed_line[:merge_up]
|
48
|
+
if last_stderr_line
|
49
|
+
last_stderr_line[:message] += "\n#{parsed_line[:message]}"
|
50
|
+
else
|
51
|
+
# this is just a standalone message then
|
52
|
+
parsed_line.delete(:merge_up)
|
53
|
+
last_stderr_line = parsed_line
|
54
|
+
end
|
55
|
+
elsif last_stderr_line
|
56
|
+
emit_line_helper(last_stderr_line, &block)
|
57
|
+
last_stderr_line = parsed_line
|
58
|
+
else
|
59
|
+
last_stderr_line = parsed_line
|
60
|
+
end
|
61
|
+
end
|
62
|
+
}
|
63
|
+
|
64
|
+
status = cmd_call_proc.call(handler)
|
65
|
+
|
66
|
+
emit_line_helper(last_stderr_line, &block) if last_stderr_line
|
67
|
+
status
|
68
|
+
end
|
69
|
+
|
70
|
+
def parse_non_json_plan_line(raw_line)
|
71
|
+
result = {}
|
72
|
+
|
73
|
+
if raw_line.match(/^time=(?<timestamp>[^ ]+) level=(?<level>[^ ]+) msg=(?<message>.+?)(?: prefix=\[(?<prefix>.+?)\])?\s*$/)
|
74
|
+
result.merge!($LAST_MATCH_INFO.named_captures.symbolize_keys)
|
75
|
+
result[:module] = "terragrunt"
|
76
|
+
result.delete(:prefix) unless result[:prefix]
|
77
|
+
result[:prefix] = Pathname.new(result[:prefix]).relative_path_from(Dir.getwd).to_s if result[:prefix]
|
78
|
+
|
79
|
+
result[:merge_up] = true if result[:message].match(/^\d+ errors? occurred:$/)
|
80
|
+
elsif raw_line.strip == ""
|
81
|
+
result[:blank] = true
|
82
|
+
else
|
83
|
+
result[:message] = raw_line
|
84
|
+
result[:merge_up] = true
|
85
|
+
end
|
86
|
+
|
87
|
+
# time=2023-08-25T11:44:41-07:00 level=error msg=Terraform invocation failed in /Users/piotr/Work/janepods/.terragrunt-cache/BM86IAj5tW4bZga2lXeYT8tdOKI/V0IEypKSfyl-kHfCnRNAqyX02V8/modules/event-bus prefix=[/Users/piotr/Work/janepods/accounts/eks-dev/admin/apps/kube-system-event-bus]
|
88
|
+
# time=2023-08-25T11:44:41-07:00 level=error msg=1 error occurred:
|
89
|
+
# * [/Users/piotr/Work/janepods/.terragrunt-cache/BM86IAj5tW4bZga2lXeYT8tdOKI/V0IEypKSfyl-kHfCnRNAqyX02V8/modules/event-bus] exit status 2
|
90
|
+
#
|
91
|
+
#
|
92
|
+
result
|
93
|
+
end
|
94
|
+
|
95
|
+
def emit_line_helper(result, &block)
|
96
|
+
result[:level] ||= result[:stream] == :stderr ? "error" : "info"
|
97
|
+
result[:module] ||= result[:stream]
|
98
|
+
result[:type] ||= "unknown"
|
99
|
+
|
100
|
+
result[:message].lstrip! if result[:message] =~ /^\n/
|
101
|
+
|
102
|
+
if result[:message].match(/^Terraform invocation failed in (.+)/)
|
103
|
+
result[:type] = "tf_failed"
|
104
|
+
|
105
|
+
lines = result[:message].split("\n")
|
106
|
+
result[:diagnostic] = {
|
107
|
+
"summary" => "Terraform invocation failed",
|
108
|
+
"detail" => result[:message],
|
109
|
+
roots: [],
|
110
|
+
extra: []
|
111
|
+
}
|
112
|
+
|
113
|
+
lines.each do |line|
|
114
|
+
if line.match(/^\s+\* \[(.+)\] exit status (\d+)$/)
|
115
|
+
result[:diagnostic][:roots] << {
|
116
|
+
path: $LAST_MATCH_INFO[1],
|
117
|
+
status: $LAST_MATCH_INFO[2].to_i
|
118
|
+
}
|
119
|
+
elsif line.match(/^\d+ errors? occurred$/)
|
120
|
+
# noop
|
121
|
+
else
|
122
|
+
result[:diagnostic][:extra] << line
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
result[:message] = "Terraform invocation failed"
|
127
|
+
end
|
128
|
+
|
129
|
+
block.call(result)
|
130
|
+
end
|
131
|
+
|
132
|
+
def print_unhandled_error_line(parsed_line)
|
133
|
+
if parsed_line[:diagnostic]
|
134
|
+
color = :red
|
135
|
+
dinfo = parsed_line[:diagnostic]
|
136
|
+
|
137
|
+
log "#{pastel.decorate(dinfo['severity'].capitalize, color)}: #{dinfo['summary']}", depth: 3
|
138
|
+
log dinfo["detail"].split("\n"), depth: 4 if dinfo["detail"]
|
139
|
+
log format_validation_range(dinfo, color), depth: 4 if dinfo["range"]
|
140
|
+
elsif parsed_line[:message] =~ /^\[reset\]/
|
141
|
+
log pastel.red(parsed_line[:message].gsub(/^\[reset\]/, "")), depth: 3
|
142
|
+
else
|
143
|
+
p parsed_line
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def format_validation_range(dinfo, color)
|
148
|
+
range = dinfo["range"]
|
149
|
+
|
150
|
+
# filename: "../../../modules/pods/jane_pod/main.tf"
|
151
|
+
# start:
|
152
|
+
# line: 151
|
153
|
+
# column: 27
|
154
|
+
# byte: 6632
|
155
|
+
# end:
|
156
|
+
# line: 151
|
157
|
+
# column: 53
|
158
|
+
# byte: 6658
|
159
|
+
|
160
|
+
context_lines = 3
|
161
|
+
|
162
|
+
lines = range["start"]["line"]..range["end"]["line"]
|
163
|
+
columns = range["start"]["column"]..range["end"]["column"]
|
164
|
+
|
165
|
+
# on ../../../modules/pods/jane_pod/main.tf line 151, in module "jane":
|
166
|
+
# 151: jane_resources_preset = var.jane_resources_presetx
|
167
|
+
output = []
|
168
|
+
lines_info = if lines.size == 1
|
169
|
+
"#{lines.first}:#{columns.first}"
|
170
|
+
else
|
171
|
+
"#{lines.first}:#{columns.first} to #{lines.last}:#{columns.last}"
|
172
|
+
end
|
173
|
+
output << "on: #{range['filename']} line#{lines.size > 1 ? 's' : ''}: #{lines_info}"
|
174
|
+
|
175
|
+
# TODO: in terragrunt mode, we need to somehow figure out the path to the cache root, all the paths will end up being relative to that
|
176
|
+
if File.exist?(range["filename"])
|
177
|
+
file_lines = File.read(range["filename"]).split("\n")
|
178
|
+
extract_range = (([lines.first - context_lines,
|
179
|
+
0].max)..([lines.last + context_lines, file_lines.length - 1].min))
|
180
|
+
file_lines.each_with_index do |line, index|
|
181
|
+
if extract_range.cover?(index + 1)
|
182
|
+
if lines.cover?(index + 1)
|
183
|
+
start_col = 1
|
184
|
+
end_col = :max
|
185
|
+
if index + 1 == lines.first
|
186
|
+
start_col = columns.first
|
187
|
+
elsif index + 1 == lines.last
|
188
|
+
start_col = columns.last
|
189
|
+
end
|
190
|
+
painted_line = paint_line(line, color, start_col: start_col, end_col: end_col)
|
191
|
+
output << "#{pastel.decorate('>', color)} #{index + 1}: #{painted_line}"
|
192
|
+
else
|
193
|
+
output << " #{index + 1}: #{line}"
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
elsif dinfo["snippet"]
|
198
|
+
snippet = dinfo["snippet"]
|
199
|
+
# {
|
200
|
+
# "context"=>"locals",
|
201
|
+
# "code"=>" aws_iam_policy.crossplane_aws_ecr.arn",
|
202
|
+
# "start_line"=>72,
|
203
|
+
# "highlight_start_offset"=>8,
|
204
|
+
# "highlight_end_offset"=>41,
|
205
|
+
# "values"=>[]
|
206
|
+
# }
|
207
|
+
output << "Code:"
|
208
|
+
output << "in #{snippet['context']}" if snippet["context"]
|
209
|
+
line = snippet["start_line"]
|
210
|
+
snippet["code"].split("\n").each do |l|
|
211
|
+
output << "#{line}: #{l}"
|
212
|
+
line += 1
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
if dinfo["snippet"] && dinfo["snippet"]["values"]&.any?
|
217
|
+
snippet = dinfo["snippet"]
|
218
|
+
output << ""
|
219
|
+
output << "Values:"
|
220
|
+
snippet["values"].each do |value|
|
221
|
+
output << " #{pastel.bold(value['traversal'])} #{value['statement']}"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
output
|
226
|
+
end
|
227
|
+
|
228
|
+
def paint_line(line, *paint_options, start_col: 1, end_col: :max)
|
229
|
+
end_col = line.length if end_col == :max
|
230
|
+
prefix = line[0, start_col - 1]
|
231
|
+
suffix = line[end_col..]
|
232
|
+
middle = line[start_col - 1..end_col - 1]
|
233
|
+
"#{prefix}#{pastel.decorate(middle, *paint_options)}#{suffix}"
|
234
|
+
end
|
235
|
+
|
236
|
+
def print_tg_error_line(parsed_line)
|
237
|
+
parsed_line[:message].split("\n").each do |line|
|
238
|
+
next if line.strip == ""
|
239
|
+
|
240
|
+
parsed_message = JSON.parse(line)
|
241
|
+
raw_prefix = "[TG ERROR] "
|
242
|
+
prefix = pastel.red(raw_prefix)
|
243
|
+
index = 0
|
244
|
+
lines = parsed_message["msg"].split("\n").map { |l|
|
245
|
+
l = if index.zero?
|
246
|
+
prefix + l
|
247
|
+
else
|
248
|
+
(" " * raw_prefix.length) + l
|
249
|
+
end
|
250
|
+
index += 1
|
251
|
+
l
|
252
|
+
}
|
253
|
+
log lines, depth: 1
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|