makit 0.0.47 → 0.0.48

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/lib/makit/apache.rb +32 -32
  3. data/lib/makit/cli/clean.rb +14 -14
  4. data/lib/makit/cli/clone.rb +59 -59
  5. data/lib/makit/cli/init.rb +38 -38
  6. data/lib/makit/cli/main.rb +33 -33
  7. data/lib/makit/cli/make.rb +54 -54
  8. data/lib/makit/cli/new.rb +37 -37
  9. data/lib/makit/cli/nuget_cache.rb +38 -38
  10. data/lib/makit/cli/pull.rb +31 -31
  11. data/lib/makit/cli/setup.rb +71 -71
  12. data/lib/makit/cli/work.rb +21 -21
  13. data/lib/makit/command_runner.rb +391 -400
  14. data/lib/makit/commands.rb +21 -21
  15. data/lib/makit/content/default_gitignore.rb +5 -5
  16. data/lib/makit/content/default_rakefile.rb +11 -11
  17. data/lib/makit/content/gem_rakefile.rb +14 -14
  18. data/lib/makit/data.rb +50 -50
  19. data/lib/makit/directories.rb +140 -140
  20. data/lib/makit/directory.rb +200 -200
  21. data/lib/makit/dotnet.rb +162 -163
  22. data/lib/makit/environment.rb +127 -127
  23. data/lib/makit/fileinfo.rb +16 -16
  24. data/lib/makit/files.rb +47 -47
  25. data/lib/makit/git.rb +87 -87
  26. data/lib/makit/gitlab_runner.rb +60 -60
  27. data/lib/makit/humanize.rb +129 -129
  28. data/lib/makit/indexer.rb +56 -56
  29. data/lib/makit/logging.rb +96 -96
  30. data/lib/makit/markdown.rb +75 -75
  31. data/lib/makit/mp/basic_object_mp.rb +16 -16
  32. data/lib/makit/mp/command_request.mp.rb +16 -16
  33. data/lib/makit/mp/project_mp.rb +210 -210
  34. data/lib/makit/mp/string_mp.rb +122 -140
  35. data/lib/makit/nuget.rb +57 -57
  36. data/lib/makit/protoc.rb +104 -104
  37. data/lib/makit/serializer.rb +115 -115
  38. data/lib/makit/show.rb +108 -108
  39. data/lib/makit/storage.rb +131 -131
  40. data/lib/makit/symbols.rb +149 -149
  41. data/lib/makit/tasks.rb +60 -61
  42. data/lib/makit/tree.rb +37 -37
  43. data/lib/makit/v1/makit.v1_pb.rb +34 -34
  44. data/lib/makit/v1/makit.v1_services_pb.rb +25 -25
  45. data/lib/makit/version.rb +52 -52
  46. data/lib/makit/wix.rb +95 -95
  47. data/lib/makit/zip.rb +17 -17
  48. data/lib/makit.rb +267 -267
  49. metadata +2 -4
  50. data/lib/generated/makit/v1/makit.v1_pb.rb +0 -34
  51. data/lib/generated/makit/v1/web/link_pb.rb +0 -20
@@ -1,400 +1,391 @@
1
- require "English"
2
- require "open3"
3
- require "socket"
4
- require "etc"
5
- require "logger"
6
- require_relative "mp/command_request.mp"
7
-
8
- # This module provides classes for the Makit gem.
9
- module Makit
10
- # This class provide methods running commands.
11
- #
12
- class CommandRunner
13
- attr_accessor :show_output_on_success, :log_to_artifacts, :commands
14
- attr_accessor :debug, :default_modified
15
-
16
- def initialize
17
- @show_output_on_success = false
18
- @log_to_artifacts = false
19
- @commands = []
20
- @debug = false
21
- @default_modified = Makit::Git::get_file_infos.first.mtime # Makit::GIT_FILE_INFOS.first.mtime
22
- end
23
-
24
- # search command history for matching commands
25
- def search(query)
26
- results = []
27
- commands.each do |command|
28
- keywords = []
29
- keywords << command.name.downcase
30
- command.arguments.each { |argument|
31
- keywords << argument.downcase unless keywords.include?(argument.downcase)
32
- }
33
- # output is bytes, so convert to UTF 8 string
34
- #output_bytes = command.output.clone
35
- #output = command.output.clone.force_encoding('UTF-8')
36
- #error_bytes = command.error.clone
37
- #error = error_bytes.force_encoding('UTF-8')
38
- #output.split(" ").each {|word|
39
- # if(word.length > 3)
40
- # keywords << word.downcase unless keywords.include?(word.downcase)
41
- # end
42
- #}
43
- #error.split(" ").each { |word|
44
- # if(word.length > 3)
45
- # keywords << word.downcase unless keywords.include?(word.downcase)
46
- # end
47
- #}
48
- matchCount = 0
49
- terms = query.downcase.split(" ")
50
- terms.each { |term|
51
- if (keywords.include?(term))
52
- matchCount += 1
53
- end
54
- }
55
- if (matchCount == terms.length)
56
- results << command
57
- end
58
- end
59
- results
60
- end
61
-
62
- def get_cache_filename(command)
63
- int_hash = Digest::SHA256.hexdigest("#{command.name}.#{command.arguments.join(" ")}")
64
- hash_string = "#{int_hash}"
65
- cache_filename = Makit::Directories::PROJECT_ARTIFACTS +
66
- "/commands/cache/#{hash_string}.pb"
67
- # create the directory if it does not already exist
68
- FileUtils.mkdir_p(File.dirname(cache_filename))
69
- cache_filename
70
- end
71
-
72
- def cache_run(command_request)
73
- cache_run(command_request, @default_modified)
74
- end
75
-
76
- # if there is a matching cached command result, that then the specified timestamp,
77
- # then return the cached result
78
- # otherwise run the command and save the result to a cache file
79
- # then return the result
80
- def cache_run(command_request, timestamp)
81
- raise "Invalid command_request" unless command_request.is_a? Makit::V1::CommandRequest
82
- cache_filename = get_cache_filename(command_request)
83
-
84
- # Check if timestamp is a valid Time object
85
- unless timestamp.is_a?(Time)
86
- raise ArgumentError, "timestamp must be a Time object, got #{timestamp.class}"
87
- end
88
-
89
- if debug
90
- puts " timestamp: #{Makit::Humanize.get_humanized_timestamp(timestamp)}"
91
- puts " command.name: #{command_request.name}"
92
- puts " command.arguments: #{command_request.arguments.join(" ")}"
93
- puts " cache_filename: #{cache_filename}"
94
- end
95
- #cache_filename = Makit::Directories::PROJECT_ARTIFACTS + "/commands/#{command_request.name}.#{command_request.arguments.join("_")}.#{timestamp.seconds}.pb"
96
- if File.exist?(cache_filename)
97
- cache_timestamp = File.mtime(cache_filename)
98
- if debug
99
- puts " cache timestamp: #{Makit::Humanize.get_humanized_timestamp(cache_timestamp)}"
100
- end
101
- #puts "cache file date: #{File.mtime(cache_filename)}"
102
- if (File.mtime(cache_filename) > timestamp)
103
-
104
- #puts " found cached command (newer than #{timestamp})".colorize(:grey)
105
- #puts " cache_filename: #{cache_filename}"
106
- command = Makit::Serializer.open(cache_filename, Makit::V1::Command)
107
- show_cached_command(command)
108
- if command.exit_code != 0
109
- abort "cached command failed: #{command.name} #{command.arguments.join(" ")}"
110
- end
111
- return command
112
- else
113
- puts " cache_filename exists, but is older than #{timestamp}" if debug
114
- File.delete(cache_filename)
115
- end
116
- end
117
-
118
- command = run(command_request)
119
- # make sure the cache directory exists
120
- FileUtils.mkdir_p(File.dirname(cache_filename))
121
- #puts "saving command to cache_filename"
122
- Makit::Serializer.save_as(cache_filename, command)
123
- commands.push(command)
124
- command
125
- end
126
-
127
-
128
-
129
- def show_command(command)
130
- if command.exit_code != 0
131
- puts Makit::CommandRunner.get_command_summary(command) + " (exit code #{command.exit_code})".colorize(:default)
132
- puts " directory: #{command.directory}\n"
133
- puts " duration: #{command.duration.seconds} seconds\n"
134
- puts Makit::Humanize::indent_string(command.output, 2) if command.output.length > 0
135
- puts Makit::Humanize::indent_string(command.error, 2) if command.error.length > 0
136
- #exit 1 if command.exit_on_error
137
- else
138
- puts Makit::CommandRunner.get_command_summary(command) + " (#{command.duration.seconds} seconds)".colorize(:cyan)
139
- puts Makit::Humanize::indent_string(command.output, 2).colorize(:default) if show_output_on_success
140
- end
141
- end
142
-
143
- def show_cached_command(command)
144
- if command.exit_code != 0
145
- puts Makit::CommandRunner.get_command_summary(command) + " (exit code #{command.exit_code})".colorize(:default)
146
- puts " directory: #{command.directory}\n"
147
- puts " duration: #{command.duration.seconds} seconds\n"
148
- puts Makit::Humanize::indent_string(command.output, 2) if command.output.length > 0
149
- puts Makit::Humanize::indent_string(command.error, 2) if command.error.length > 0
150
- #exit 1 if command.exit_on_error
151
- else
152
- puts Makit::CommandRunner.get_command_summary(command).strip_color_codes.colorize(:grey) + " (#{command.duration.seconds} seconds)".colorize(:grey)
153
- puts Makit::Humanize::indent_string(command.output, 2).colorize(:default) if show_output_on_success
154
- end
155
- end
156
-
157
- # Run a command and return a Makit::V1::Command.
158
- def run(command_request)
159
- raise "Invalid command_request" unless command_request.is_a? Makit::V1::CommandRequest
160
- command = execute(command_request)
161
- show_output = true
162
- exit_on_error = true
163
-
164
- log_to_artifacts(command) if @log_to_artifacts
165
- show_command(command)
166
- if command.exit_code != 0
167
- exit 1 if command_request.exit_on_error
168
- end
169
- commands.push(command)
170
- command
171
- end
172
-
173
- def log_to_artifacts(command)
174
- log_filename = get_cache_filename(command)
175
- Makit::Serializer.save_as(log_filename, command)
176
- end
177
-
178
- # Run a command and return a Makit::V1::Command.
179
- def try(args)
180
- request = parse_command_request(args)
181
- request.exit_on_error = false
182
- run(request)
183
- end
184
-
185
- def cache_try(command_request)
186
- cache_try(command_request, @default_modified)
187
- end
188
- def cache_try(command_request, timestamp)
189
- command_request.exit_on_error = false
190
- cache_run(command_request, timestamp)
191
- end
192
-
193
- # Show the output of a command and return a Makit::V1::Command.
194
- def show(args)
195
- request = parse_args(args)
196
- command = execute(request)
197
-
198
- show_output = true
199
- exit_on_error = true
200
- Makit::LOGGER.info(Makit::CommandRunner.get_command_summary(command))
201
- show_output = true if command.exit_code != 0
202
- Makit::LOGGER.info(indent_string("\n#{command.output}\n#{command.error}\n".strip, 2)) if show_output
203
- exit(command.exit_code) if exit_on_error && command.exit_code != 0 # unless process_status.success?
204
- command
205
- end
206
-
207
- # Parse and return a Makit::V1::CommandRequest.
208
- def parse_command_request(source)
209
- return Makit::V1::CommandRequest.new(source) if source.is_a? Hash
210
- return source if source.is_a? Makit::V1::CommandRequest
211
- if source.is_a? String
212
- return parse_args(source)
213
- end
214
-
215
- raise "Invalid source" unless source.is_a? Makit::V1::CommandRequest
216
- end
217
-
218
- def parse_command_request_from_hash(hash)
219
- raise "Invalid hash" unless hash.is_a? Hash
220
- Makit::V1::CommandRequest.new(hash)
221
- end
222
-
223
- def parse_command_request_from_string(source)
224
- raise "Invalid source" unless source.is_a? String
225
- words = source.split(" ")
226
- hash = {
227
- name: words.shift,
228
- arguments: words,
229
- exit_on_error: true,
230
- }
231
- end
232
-
233
- # Parse the command line arguments into a Makit::V1::CommandRequest.
234
- def parse_args(args)
235
- #raise "No command specified" if args.empty?
236
- if args.is_a? Makit::V1::CommandRequest
237
- args
238
- else
239
- if args.is_a? String
240
- args = args.split(" ")
241
- if (args.length == 1)
242
- hash = {
243
- name: args[0],
244
- arguments: [],
245
- exit_on_error: true,
246
- }
247
- Makit::V1::CommandRequest.new(hash)
248
- else
249
- hash = {
250
- name: args.shift,
251
- arguments: args,
252
- exit_on_error: true,
253
- }
254
-
255
- Makit::V1::CommandRequest.new(hash)
256
- end
257
- else
258
- Makit::V1::CommandRequest.new(args)
259
- end
260
- end
261
- end
262
-
263
- def get_path_name(name)
264
- # replace all characters that a not valid in a filename with an underscore
265
- name.gsub(/[^0-9a-z]/i, "_")
266
- end
267
-
268
- # Given a Makit::V1::CommandRequest, execute the command and return a Makit::V1::Command.
269
- def execute(args)
270
- command_request = parse_args(args)
271
- command_request.directory = Dir.pwd if command_request.directory.nil?
272
- command_request.directory = Dir.pwd if command_request.directory.length == 0
273
- result = Makit::V1::Command.new(name: command_request.name)
274
- command_request.arguments.each do |arg|
275
- result.arguments.push(arg)
276
- end
277
- command = "#{command_request.name} #{command_request.arguments.join(" ")}"
278
- result.directory = command_request.directory
279
- start = Time.now
280
- filename_friendly_timestamp = Time.now.strftime("%Y.%m.%d_%H%M%S")
281
- log_filename = File.join(Makit::Directories::LOG, "#{filename_friendly_timestamp}.log")
282
-
283
- # assign a directory variable to the current working directory, if not specified,
284
- # otherwise assign the specified directory
285
- command_request.directory = Dir.pwd if command_request.directory.nil?
286
- command_request.directory = Dir.pwd if command_request.directory.length == 0
287
- raise "Invalid directory" unless Dir.exist?(command_request.directory)
288
-
289
- result.started_at = Google::Protobuf::Timestamp.new(seconds: start.to_i, nanos: start.nsec.to_i)
290
- ############# execute the command
291
- (output, error, exit_code) = execute_command(command, command_request.directory, command_request.timeout)
292
- result.output = output.force_encoding("ASCII-8BIT")
293
- result.error = error.force_encoding("ASCII-8BIT")
294
- result.exit_code = exit_code.nil? ? 0 : exit_code
295
-
296
- elapsed_time = Time.now - start
297
- seconds = elapsed_time.to_i
298
- nanos = ((elapsed_time - seconds) * 1_000_000_000).to_i
299
-
300
- result.duration = Google::Protobuf::Duration.new(seconds: seconds, nanos: nanos)
301
-
302
- result
303
- end
304
-
305
- # pure function to execute a command
306
- # returns (stdout, stderr, exit_code) or raise an exception
307
- def execute_command(command, directory, timeout)
308
- original_directory = Dir.pwd
309
- begin
310
- output = nil
311
- error = nil
312
- process_status = nil
313
- Dir.chdir(directory) do
314
- output, error, process_status = Open3.capture3(command)
315
- end
316
- return [output, error, process_status.exitstatus]
317
- rescue => e
318
- # restore the original working directory
319
- Dir.chdir(original_directory)
320
- message_parts = []
321
- message_parts << "failed to execute #{command}"
322
- message_parts << "directory: #{directory}"
323
- message_parts << "timeout: #{timeout}" unless timeout.nil?
324
- message_parts << "error: #{e.message}"
325
- message = message_parts.join("\n") + "\n"
326
- return ["", message, 1]
327
- #raise Makit::Error, message
328
- end
329
- end
330
-
331
- def execute_command_request(command_request)
332
- # if the command_request is not a Makit::V1::CommandRequest, raise an error
333
- raise "Invalid command_request" unless command_request.is_a? Makit::V1::CommandRequest
334
-
335
- args = Array.new
336
- command_request.arguments.each do |arg|
337
- args.push(arg)
338
- end
339
- result = Makit::V1::Command.new({
340
- name: command_request.name,
341
- arguments: args,
342
- started_at: Google::Protobuf::Timestamp.new({ seconds: Time.now.to_i, nanos: Time.now.nsec }),
343
- })
344
-
345
- begin
346
- rescue => e
347
- end
348
- end
349
-
350
- def indent_string(input_string, indent_spaces)
351
- indentation = " " * indent_spaces
352
- input_string.lines.map { |line| indentation + line }.join
353
- end
354
-
355
- def self.get_command_summary(command)
356
- symbol = Makit::Symbols.warning
357
- symbol = Makit::Symbols.checkmark if !command.exit_code.nil? && command.exit_code.zero?
358
- symbol = Makit::Symbols.error if command.exit_code != 0
359
- summary = "#{symbol} #{command.name.colorize(:yellow)} #{command.arguments.join(" ")}"
360
-
361
- if summary.length > 80
362
- summary = summary.to_lines(80, command.name.length + 3)
363
- end
364
-
365
- summary
366
- end
367
-
368
- def log_rake_commands
369
- dir = File.join(Makit::Directories::PROJECT_ARTIFACTS, "commands")
370
- FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
371
-
372
- # open a text file to write to
373
- File.open(File.join(dir, "rake.commands.txt"), "w") do |file|
374
- #rake_commands = commands.select { |command| command.name == "rake" }
375
- commands.each do |command|
376
- file.puts " " + Makit::CommandRunner.get_command_summary(command).strip_color_codes
377
- file.puts " start time: #{command.started_at}"
378
- file.puts command.output.strip_color_codes unless command.output.strip_color_codes.strip.length == 0
379
- file.puts command.error.strip_color_codes unless command.error.strip_color_codes.strip.length == 0
380
- file.puts " "
381
- end
382
- end
383
- end
384
-
385
- def log_slowest_commands
386
- dir = File.join(Makit::Directories::PROJECT_ARTIFACTS, "commands")
387
- FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
388
-
389
- # open a text file to write to
390
- File.open(File.join(dir, "slow.commands.txt"), "w") do |file|
391
- Makit::RUNNER.commands.sort_by { |command| (command.duration.seconds + (command.duration.nanos / 1_000_000_000.0)) }.reverse.first(5).each do |command|
392
- # Convert to float representation
393
- duration_in_float = command.duration.seconds + (command.duration.nanos / 1_000_000_000.0)
394
- #puts " #{command.name} took #{duration_in_float} seconds"
395
- file.puts " " + Makit::CommandRunner.get_command_summary(command).strip_color_codes + " (#{command.duration.seconds} seconds)"
396
- end
397
- end
398
- end
399
- end # class CommandRunner
400
- end # module Makit
1
+ require "English"
2
+ require "open3"
3
+ require "socket"
4
+ require "etc"
5
+ require "logger"
6
+ require_relative "mp/command_request.mp"
7
+
8
+ # This module provides classes for the Makit gem.
9
+ module Makit
10
+ # This class provide methods running commands.
11
+ #
12
+ class CommandRunner
13
+ attr_accessor :show_output_on_success, :log_to_artifacts, :commands
14
+ attr_accessor :debug, :default_modified
15
+
16
+ def initialize
17
+ @show_output_on_success = false
18
+ @log_to_artifacts = false
19
+ @commands = []
20
+ @debug = false
21
+ @default_modified = Makit::Git::get_file_infos.first.mtime # Makit::GIT_FILE_INFOS.first.mtime
22
+ end
23
+
24
+ # search command history for matching commands
25
+ def search(query)
26
+ results = []
27
+ commands.each do |command|
28
+ keywords = []
29
+ keywords << command.name.downcase
30
+ command.arguments.each { |argument|
31
+ keywords << argument.downcase unless keywords.include?(argument.downcase)
32
+ }
33
+ # output is bytes, so convert to UTF 8 string
34
+ #output_bytes = command.output.clone
35
+ #output = command.output.clone.force_encoding('UTF-8')
36
+ #error_bytes = command.error.clone
37
+ #error = error_bytes.force_encoding('UTF-8')
38
+ #output.split(" ").each {|word|
39
+ # if(word.length > 3)
40
+ # keywords << word.downcase unless keywords.include?(word.downcase)
41
+ # end
42
+ #}
43
+ #error.split(" ").each { |word|
44
+ # if(word.length > 3)
45
+ # keywords << word.downcase unless keywords.include?(word.downcase)
46
+ # end
47
+ #}
48
+ matchCount = 0
49
+ terms = query.downcase.split(" ")
50
+ terms.each { |term|
51
+ if (keywords.include?(term))
52
+ matchCount += 1
53
+ end
54
+ }
55
+ if (matchCount == terms.length)
56
+ results << command
57
+ end
58
+ end
59
+ results
60
+ end
61
+
62
+ def get_cache_filename(command)
63
+ int_hash = Digest::SHA256.hexdigest("#{command.name}.#{command.arguments.join(" ")}")
64
+ hash_string = "#{int_hash}"
65
+ cache_filename = Makit::Directories::PROJECT_ARTIFACTS +
66
+ "/commands/cache/#{hash_string}.pb"
67
+ # create the directory if it does not already exist
68
+ FileUtils.mkdir_p(File.dirname(cache_filename))
69
+ cache_filename
70
+ end
71
+
72
+ def cache_run(command_request)
73
+ cache_run(command_request, @default_modified)
74
+ end
75
+
76
+ # if there is a matching cached command result, that then the specified timestamp,
77
+ # then return the cached result
78
+ # otherwise run the command and save the result to a cache file
79
+ # then return the result
80
+ def cache_run(command_request, timestamp)
81
+ raise "Invalid command_request" unless command_request.is_a? Makit::V1::CommandRequest
82
+ cache_filename = get_cache_filename(command_request)
83
+
84
+ # Check if timestamp is a valid Time object
85
+ unless timestamp.is_a?(Time)
86
+ raise ArgumentError, "timestamp must be a Time object, got #{timestamp.class}"
87
+ end
88
+
89
+ if debug
90
+ puts " timestamp: #{Makit::Humanize.get_humanized_timestamp(timestamp)}"
91
+ puts " command.name: #{command_request.name}"
92
+ puts " command.arguments: #{command_request.arguments.join(" ")}"
93
+ puts " cache_filename: #{cache_filename}"
94
+ end
95
+ #cache_filename = Makit::Directories::PROJECT_ARTIFACTS + "/commands/#{command_request.name}.#{command_request.arguments.join("_")}.#{timestamp.seconds}.pb"
96
+ if File.exist?(cache_filename)
97
+ cache_timestamp = File.mtime(cache_filename)
98
+ if debug
99
+ puts " cache timestamp: #{Makit::Humanize.get_humanized_timestamp(cache_timestamp)}"
100
+ end
101
+ #puts "cache file date: #{File.mtime(cache_filename)}"
102
+ if (File.mtime(cache_filename) > timestamp)
103
+
104
+ #puts " found cached command (newer than #{timestamp})".colorize(:grey)
105
+ #puts " cache_filename: #{cache_filename}"
106
+ command = Makit::Serializer.open(cache_filename, Makit::V1::Command)
107
+ show_cached_command(command)
108
+ if command.exit_code != 0
109
+ abort "cached command failed: #{command.name} #{command.arguments.join(" ")}"
110
+ end
111
+ return command
112
+ else
113
+ puts " cache_filename exists, but is older than #{timestamp}" if debug
114
+ File.delete(cache_filename)
115
+ end
116
+ end
117
+
118
+ command = run(command_request)
119
+ # make sure the cache directory exists
120
+ FileUtils.mkdir_p(File.dirname(cache_filename))
121
+ #puts "saving command to cache_filename"
122
+ Makit::Serializer.save_as(cache_filename, command)
123
+ commands.push(command)
124
+ command
125
+ end
126
+
127
+ def show_command(command)
128
+ if command.exit_code != 0
129
+ puts Makit::CommandRunner.get_command_summary(command) + " (exit code #{command.exit_code})".colorize(:default)
130
+ puts " directory: #{command.directory}\n"
131
+ puts " duration: #{command.duration.seconds} seconds\n"
132
+ puts Makit::Humanize::indent_string(command.output, 2) if command.output.length > 0
133
+ puts Makit::Humanize::indent_string(command.error, 2) if command.error.length > 0
134
+ #exit 1 if command.exit_on_error
135
+ else
136
+ puts Makit::CommandRunner.get_command_summary(command) + " (#{command.duration.seconds} seconds)".colorize(:cyan)
137
+ puts Makit::Humanize::indent_string(command.output, 2).colorize(:default) if show_output_on_success
138
+ end
139
+ end
140
+
141
+ def show_cached_command(command)
142
+ if command.exit_code != 0
143
+ puts Makit::CommandRunner.get_command_summary(command) + " (exit code #{command.exit_code})".colorize(:default)
144
+ puts " directory: #{command.directory}\n"
145
+ puts " duration: #{command.duration.seconds} seconds\n"
146
+ puts Makit::Humanize::indent_string(command.output, 2) if command.output.length > 0
147
+ puts Makit::Humanize::indent_string(command.error, 2) if command.error.length > 0
148
+ #exit 1 if command.exit_on_error
149
+ else
150
+ puts Makit::CommandRunner.get_command_summary(command).strip_color_codes.colorize(:grey) + " (#{command.duration.seconds} seconds)".colorize(:grey)
151
+ puts Makit::Humanize::indent_string(command.output, 2).colorize(:default) if show_output_on_success
152
+ end
153
+ end
154
+
155
+ # Run a command and return a Makit::V1::Command.
156
+ def run(command_request)
157
+ raise "Invalid command_request" unless command_request.is_a? Makit::V1::CommandRequest
158
+ command = execute(command_request)
159
+ show_output = true
160
+ exit_on_error = true
161
+
162
+ log_to_artifacts(command) if @log_to_artifacts
163
+ show_command(command)
164
+ if command.exit_code != 0
165
+ exit 1 if command_request.exit_on_error
166
+ end
167
+ commands.push(command)
168
+ command
169
+ end
170
+
171
+ def log_to_artifacts(command)
172
+ log_filename = get_cache_filename(command)
173
+ Makit::Serializer.save_as(log_filename, command)
174
+ end
175
+
176
+ # Run a command and return a Makit::V1::Command.
177
+ def try(args)
178
+ request = parse_command_request(args)
179
+ request.exit_on_error = false
180
+ run(request)
181
+ #run2(args, false)
182
+ end
183
+
184
+ # Show the output of a command and return a Makit::V1::Command.
185
+ def show(args)
186
+ request = parse_args(args)
187
+ command = execute(request)
188
+
189
+ show_output = true
190
+ exit_on_error = true
191
+ Makit::LOGGER.info(Makit::CommandRunner.get_command_summary(command))
192
+ show_output = true if command.exit_code != 0
193
+ Makit::LOGGER.info(indent_string("\n#{command.output}\n#{command.error}\n".strip, 2)) if show_output
194
+ exit(command.exit_code) if exit_on_error && command.exit_code != 0 # unless process_status.success?
195
+ command
196
+ end
197
+
198
+ # Parse and return a Makit::V1::CommandRequest.
199
+ def parse_command_request(source)
200
+ return Makit::V1::CommandRequest.new(source) if source.is_a? Hash
201
+ return source if source.is_a? Makit::V1::CommandRequest
202
+ if source.is_a? String
203
+ return parse_args(source)
204
+ end
205
+
206
+ raise "Invalid source" unless source.is_a? Makit::V1::CommandRequest
207
+ end
208
+
209
+ def parse_command_request_from_hash(hash)
210
+ raise "Invalid hash" unless hash.is_a? Hash
211
+ Makit::V1::CommandRequest.new(hash)
212
+ end
213
+
214
+ def parse_command_request_from_string(source)
215
+ raise "Invalid source" unless source.is_a? String
216
+ words = source.split(" ")
217
+ hash = {
218
+ name: words.shift,
219
+ arguments: words,
220
+ exit_on_error: true,
221
+ }
222
+ end
223
+
224
+ # Parse the command line arguments into a Makit::V1::CommandRequest.
225
+ def parse_args(args)
226
+ #raise "No command specified" if args.empty?
227
+ if args.is_a? Makit::V1::CommandRequest
228
+ args
229
+ else
230
+ if args.is_a? String
231
+ args = args.split(" ")
232
+ if (args.length == 1)
233
+ hash = {
234
+ name: args[0],
235
+ arguments: [],
236
+ exit_on_error: true,
237
+ }
238
+ Makit::V1::CommandRequest.new(hash)
239
+ else
240
+ hash = {
241
+ name: args.shift,
242
+ arguments: args,
243
+ exit_on_error: true,
244
+ }
245
+
246
+ Makit::V1::CommandRequest.new(hash)
247
+ end
248
+ else
249
+ Makit::V1::CommandRequest.new(args)
250
+ end
251
+ end
252
+ end
253
+
254
+ def get_path_name(name)
255
+ # replace all characters that a not valid in a filename with an underscore
256
+ name.gsub(/[^0-9a-z]/i, "_")
257
+ end
258
+
259
+ # Given a Makit::V1::CommandRequest, execute the command and return a Makit::V1::Command.
260
+ def execute(args)
261
+ command_request = parse_args(args)
262
+ command_request.directory = Dir.pwd if command_request.directory.nil?
263
+ command_request.directory = Dir.pwd if command_request.directory.length == 0
264
+ result = Makit::V1::Command.new(name: command_request.name)
265
+ command_request.arguments.each do |arg|
266
+ result.arguments.push(arg)
267
+ end
268
+ command = "#{command_request.name} #{command_request.arguments.join(" ")}"
269
+ result.directory = command_request.directory
270
+ start = Time.now
271
+ filename_friendly_timestamp = Time.now.strftime("%Y.%m.%d_%H%M%S")
272
+ log_filename = File.join(Makit::Directories::LOG, "#{filename_friendly_timestamp}.log")
273
+
274
+ # assign a directory variable to the current working directory, if not specified,
275
+ # otherwise assign the specified directory
276
+ command_request.directory = Dir.pwd if command_request.directory.nil?
277
+ command_request.directory = Dir.pwd if command_request.directory.length == 0
278
+ raise "Invalid directory" unless Dir.exist?(command_request.directory)
279
+
280
+ result.started_at = Google::Protobuf::Timestamp.new(seconds: start.to_i, nanos: start.nsec.to_i)
281
+ ############# execute the command
282
+ (output, error, exit_code) = execute_command(command, command_request.directory, command_request.timeout)
283
+ result.output = output.force_encoding("ASCII-8BIT")
284
+ result.error = error.force_encoding("ASCII-8BIT")
285
+ result.exit_code = exit_code.nil? ? 0 : exit_code
286
+
287
+ elapsed_time = Time.now - start
288
+ seconds = elapsed_time.to_i
289
+ nanos = ((elapsed_time - seconds) * 1_000_000_000).to_i
290
+
291
+ result.duration = Google::Protobuf::Duration.new(seconds: seconds, nanos: nanos)
292
+
293
+ result
294
+ end
295
+
296
+ # pure function to execute a command
297
+ # returns (stdout, stderr, exit_code) or raise an exception
298
+ def execute_command(command, directory, timeout)
299
+ original_directory = Dir.pwd
300
+ begin
301
+ output = nil
302
+ error = nil
303
+ process_status = nil
304
+ Dir.chdir(directory) do
305
+ output, error, process_status = Open3.capture3(command)
306
+ end
307
+ return [output, error, process_status.exitstatus]
308
+ rescue => e
309
+ # restore the original working directory
310
+ Dir.chdir(original_directory)
311
+ message_parts = []
312
+ message_parts << "failed to execute #{command}"
313
+ message_parts << "directory: #{directory}"
314
+ message_parts << "timeout: #{timeout}" unless timeout.nil?
315
+ message_parts << "error: #{e.message}"
316
+ message = message_parts.join("\n") + "\n"
317
+ return ["", message, 1]
318
+ #raise Makit::Error, message
319
+ end
320
+ end
321
+
322
+ def execute_command_request(command_request)
323
+ # if the command_request is not a Makit::V1::CommandRequest, raise an error
324
+ raise "Invalid command_request" unless command_request.is_a? Makit::V1::CommandRequest
325
+
326
+ args = Array.new
327
+ command_request.arguments.each do |arg|
328
+ args.push(arg)
329
+ end
330
+ result = Makit::V1::Command.new({
331
+ name: command_request.name,
332
+ arguments: args,
333
+ started_at: Google::Protobuf::Timestamp.new({ seconds: Time.now.to_i, nanos: Time.now.nsec }),
334
+ })
335
+
336
+ begin
337
+ rescue => e
338
+ end
339
+ end
340
+
341
+ def indent_string(input_string, indent_spaces)
342
+ indentation = " " * indent_spaces
343
+ input_string.lines.map { |line| indentation + line }.join
344
+ end
345
+
346
+ def self.get_command_summary(command)
347
+ symbol = Makit::Symbols.warning
348
+ symbol = Makit::Symbols.checkmark if !command.exit_code.nil? && command.exit_code.zero?
349
+ symbol = Makit::Symbols.error if command.exit_code != 0
350
+ summary = "#{symbol} #{command.name.colorize(:yellow)} #{command.arguments.join(" ")}"
351
+
352
+ if summary.length > 80
353
+ summary = summary.to_lines(80, command.name.length + 3)
354
+ end
355
+
356
+ summary
357
+ end
358
+
359
+ def log_rake_commands
360
+ dir = File.join(Makit::Directories::PROJECT_ARTIFACTS, "commands")
361
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
362
+
363
+ # open a text file to write to
364
+ File.open(File.join(dir, "rake.commands.txt"), "w") do |file|
365
+ #rake_commands = commands.select { |command| command.name == "rake" }
366
+ commands.each do |command|
367
+ file.puts " " + Makit::CommandRunner.get_command_summary(command).strip_color_codes
368
+ file.puts " start time: #{command.started_at}"
369
+ file.puts command.output.strip_color_codes unless command.output.strip_color_codes.strip.length == 0
370
+ file.puts command.error.strip_color_codes unless command.error.strip_color_codes.strip.length == 0
371
+ file.puts " "
372
+ end
373
+ end
374
+ end
375
+
376
+ def log_slowest_commands
377
+ dir = File.join(Makit::Directories::PROJECT_ARTIFACTS, "commands")
378
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
379
+
380
+ # open a text file to write to
381
+ File.open(File.join(dir, "slow.commands.txt"), "w") do |file|
382
+ Makit::RUNNER.commands.sort_by { |command| (command.duration.seconds + (command.duration.nanos / 1_000_000_000.0)) }.reverse.first(5).each do |command|
383
+ # Convert to float representation
384
+ duration_in_float = command.duration.seconds + (command.duration.nanos / 1_000_000_000.0)
385
+ #puts " #{command.name} took #{duration_in_float} seconds"
386
+ file.puts " " + Makit::CommandRunner.get_command_summary(command).strip_color_codes + " (#{command.duration.seconds} seconds)"
387
+ end
388
+ end
389
+ end
390
+ end # class CommandRunner
391
+ end # module Makit