makit 0.0.53 → 0.0.54

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 (53) 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 +399 -399
  14. data/lib/makit/commands.rb +21 -21
  15. data/lib/makit/content/default_gitignore.rb +5 -5
  16. data/lib/makit/content/default_gitignore.txt +222 -222
  17. data/lib/makit/content/default_rakefile.rb +11 -11
  18. data/lib/makit/content/gem_rakefile.rb +14 -14
  19. data/lib/makit/data.rb +50 -50
  20. data/lib/makit/directories.rb +140 -140
  21. data/lib/makit/directory.rb +200 -200
  22. data/lib/makit/dotnet.rb +170 -170
  23. data/lib/makit/environment.rb +127 -127
  24. data/lib/makit/fileinfo.rb +16 -16
  25. data/lib/makit/files.rb +47 -47
  26. data/lib/makit/git.rb +87 -87
  27. data/lib/makit/gitlab_runner.rb +60 -60
  28. data/lib/makit/humanize.rb +129 -129
  29. data/lib/makit/indexer.rb +56 -56
  30. data/lib/makit/logging.rb +96 -96
  31. data/lib/makit/markdown.rb +75 -75
  32. data/lib/makit/mp/basic_object_mp.rb +16 -16
  33. data/lib/makit/mp/command_request.mp.rb +16 -16
  34. data/lib/makit/mp/project_mp.rb +210 -210
  35. data/lib/makit/mp/string_mp.rb +137 -137
  36. data/lib/makit/nuget.rb +57 -57
  37. data/lib/makit/protoc.rb +104 -104
  38. data/lib/makit/serializer.rb +115 -115
  39. data/lib/makit/show.rb +101 -94
  40. data/lib/makit/storage.rb +131 -131
  41. data/lib/makit/symbols.rb +149 -149
  42. data/lib/makit/tasks.rb +61 -61
  43. data/lib/makit/tree.rb +37 -37
  44. data/lib/makit/v1/makit.v1_pb.rb +6 -4
  45. data/lib/makit/v1/makit.v1_services_pb.rb +25 -25
  46. data/lib/makit/version.rb +68 -68
  47. data/lib/makit/wix.rb +95 -95
  48. data/lib/makit/yaml.rb +19 -19
  49. data/lib/makit/zip.rb +17 -17
  50. data/lib/makit.rb +267 -267
  51. metadata +1 -3
  52. data/lib/generated/makit/v1/makit.v1_pb.rb +0 -35
  53. data/lib/generated/makit/v1/web/link_pb.rb +0 -20
@@ -1,399 +1,399 @@
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
- end
182
-
183
- def cache_try(command_request)
184
- cache_try(command_request, @default_modified)
185
- end
186
-
187
- def cache_try(command_request, timestamp)
188
- command_request.exit_on_error = false
189
- cache_run(command_request, timestamp)
190
- end
191
-
192
- # Show the output of a command and return a Makit::V1::Command.
193
- def show(args)
194
- request = parse_args(args)
195
- command = execute(request)
196
-
197
- show_output = true
198
- exit_on_error = true
199
- Makit::LOGGER.info(Makit::CommandRunner.get_command_summary(command))
200
- show_output = true if command.exit_code != 0
201
- Makit::LOGGER.info(indent_string("\n#{command.output}\n#{command.error}\n".strip, 2)) if show_output
202
- exit(command.exit_code) if exit_on_error && command.exit_code != 0 # unless process_status.success?
203
- command
204
- end
205
-
206
- # Parse and return a Makit::V1::CommandRequest.
207
- def parse_command_request(source)
208
- return Makit::V1::CommandRequest.new(source) if source.is_a? Hash
209
- return source if source.is_a? Makit::V1::CommandRequest
210
- if source.is_a? String
211
- return parse_args(source)
212
- end
213
-
214
- raise "Invalid source" unless source.is_a? Makit::V1::CommandRequest
215
- end
216
-
217
- def parse_command_request_from_hash(hash)
218
- raise "Invalid hash" unless hash.is_a? Hash
219
- Makit::V1::CommandRequest.new(hash)
220
- end
221
-
222
- def parse_command_request_from_string(source)
223
- raise "Invalid source" unless source.is_a? String
224
- words = source.split(" ")
225
- hash = {
226
- name: words.shift,
227
- arguments: words,
228
- exit_on_error: true,
229
- }
230
- end
231
-
232
- # Parse the command line arguments into a Makit::V1::CommandRequest.
233
- def parse_args(args)
234
- #raise "No command specified" if args.empty?
235
- if args.is_a? Makit::V1::CommandRequest
236
- args
237
- else
238
- if args.is_a? String
239
- args = args.split(" ")
240
- if (args.length == 1)
241
- hash = {
242
- name: args[0],
243
- arguments: [],
244
- exit_on_error: true,
245
- }
246
- Makit::V1::CommandRequest.new(hash)
247
- else
248
- hash = {
249
- name: args.shift,
250
- arguments: args,
251
- exit_on_error: true,
252
- }
253
-
254
- Makit::V1::CommandRequest.new(hash)
255
- end
256
- else
257
- Makit::V1::CommandRequest.new(args)
258
- end
259
- end
260
- end
261
-
262
- def get_path_name(name)
263
- # replace all characters that a not valid in a filename with an underscore
264
- name.gsub(/[^0-9a-z]/i, "_")
265
- end
266
-
267
- # Given a Makit::V1::CommandRequest, execute the command and return a Makit::V1::Command.
268
- def execute(args)
269
- command_request = parse_args(args)
270
- command_request.directory = Dir.pwd if command_request.directory.nil?
271
- command_request.directory = Dir.pwd if command_request.directory.length == 0
272
- result = Makit::V1::Command.new(name: command_request.name)
273
- command_request.arguments.each do |arg|
274
- result.arguments.push(arg)
275
- end
276
- command = "#{command_request.name} #{command_request.arguments.join(" ")}"
277
- result.directory = command_request.directory
278
- start = Time.now
279
- filename_friendly_timestamp = Time.now.strftime("%Y.%m.%d_%H%M%S")
280
- log_filename = File.join(Makit::Directories::LOG, "#{filename_friendly_timestamp}.log")
281
-
282
- # assign a directory variable to the current working directory, if not specified,
283
- # otherwise assign the specified directory
284
- command_request.directory = Dir.pwd if command_request.directory.nil?
285
- command_request.directory = Dir.pwd if command_request.directory.length == 0
286
- raise "Invalid directory" unless Dir.exist?(command_request.directory)
287
-
288
- result.started_at = Google::Protobuf::Timestamp.new(seconds: start.to_i, nanos: start.nsec.to_i)
289
- ############# execute the command
290
- (output, error, exit_code) = execute_command(command, command_request.directory, command_request.timeout)
291
- result.output = output.force_encoding("ASCII-8BIT")
292
- result.error = error.force_encoding("ASCII-8BIT")
293
- result.exit_code = exit_code.nil? ? 0 : exit_code
294
-
295
- elapsed_time = Time.now - start
296
- seconds = elapsed_time.to_i
297
- nanos = ((elapsed_time - seconds) * 1_000_000_000).to_i
298
-
299
- result.duration = Google::Protobuf::Duration.new(seconds: seconds, nanos: nanos)
300
-
301
- result
302
- end
303
-
304
- # pure function to execute a command
305
- # returns (stdout, stderr, exit_code) or raise an exception
306
- def execute_command(command, directory, timeout)
307
- original_directory = Dir.pwd
308
- begin
309
- output = nil
310
- error = nil
311
- process_status = nil
312
- Dir.chdir(directory) do
313
- output, error, process_status = Open3.capture3(command)
314
- end
315
- return [output, error, process_status.exitstatus]
316
- rescue => e
317
- # restore the original working directory
318
- Dir.chdir(original_directory)
319
- message_parts = []
320
- message_parts << "failed to execute #{command}"
321
- message_parts << "directory: #{directory}"
322
- message_parts << "timeout: #{timeout}" unless timeout.nil?
323
- message_parts << "error: #{e.message}"
324
- message = message_parts.join("\n") + "\n"
325
- return ["", message, 1]
326
- #raise Makit::Error, message
327
- end
328
- end
329
-
330
- def execute_command_request(command_request)
331
- # if the command_request is not a Makit::V1::CommandRequest, raise an error
332
- raise "Invalid command_request" unless command_request.is_a? Makit::V1::CommandRequest
333
-
334
- args = Array.new
335
- command_request.arguments.each do |arg|
336
- args.push(arg)
337
- end
338
- result = Makit::V1::Command.new({
339
- name: command_request.name,
340
- arguments: args,
341
- started_at: Google::Protobuf::Timestamp.new({ seconds: Time.now.to_i, nanos: Time.now.nsec }),
342
- })
343
-
344
- begin
345
- rescue => e
346
- end
347
- end
348
-
349
- def indent_string(input_string, indent_spaces)
350
- indentation = " " * indent_spaces
351
- input_string.lines.map { |line| indentation + line }.join
352
- end
353
-
354
- def self.get_command_summary(command)
355
- symbol = Makit::Symbols.warning
356
- symbol = Makit::Symbols.checkmark if !command.exit_code.nil? && command.exit_code.zero?
357
- symbol = Makit::Symbols.error if command.exit_code != 0
358
- summary = "#{symbol} #{command.name.colorize(:yellow)} #{command.arguments.join(" ")}"
359
-
360
- if summary.length > 80
361
- summary = summary.to_lines(80, command.name.length + 3)
362
- end
363
-
364
- summary
365
- end
366
-
367
- def log_rake_commands
368
- dir = File.join(Makit::Directories::PROJECT_ARTIFACTS, "commands")
369
- FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
370
-
371
- # open a text file to write to
372
- File.open(File.join(dir, "rake.commands.txt"), "w") do |file|
373
- #rake_commands = commands.select { |command| command.name == "rake" }
374
- commands.each do |command|
375
- file.puts " " + Makit::CommandRunner.get_command_summary(command).strip_color_codes
376
- file.puts " start time: #{command.started_at}"
377
- file.puts command.output.strip_color_codes unless command.output.strip_color_codes.strip.length == 0
378
- file.puts command.error.strip_color_codes unless command.error.strip_color_codes.strip.length == 0
379
- file.puts " "
380
- end
381
- end
382
- end
383
-
384
- def log_slowest_commands
385
- dir = File.join(Makit::Directories::PROJECT_ARTIFACTS, "commands")
386
- FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
387
-
388
- # open a text file to write to
389
- File.open(File.join(dir, "slow.commands.txt"), "w") do |file|
390
- Makit::RUNNER.commands.sort_by { |command| (command.duration.seconds + (command.duration.nanos / 1_000_000_000.0)) }.reverse.first(5).each do |command|
391
- # Convert to float representation
392
- duration_in_float = command.duration.seconds + (command.duration.nanos / 1_000_000_000.0)
393
- #puts " #{command.name} took #{duration_in_float} seconds"
394
- file.puts " " + Makit::CommandRunner.get_command_summary(command).strip_color_codes + " (#{command.duration.seconds} seconds)"
395
- end
396
- end
397
- end
398
- end # class CommandRunner
399
- 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
+ end
182
+
183
+ def cache_try(command_request)
184
+ cache_try(command_request, @default_modified)
185
+ end
186
+
187
+ def cache_try(command_request, timestamp)
188
+ command_request.exit_on_error = false
189
+ cache_run(command_request, timestamp)
190
+ end
191
+
192
+ # Show the output of a command and return a Makit::V1::Command.
193
+ def show(args)
194
+ request = parse_args(args)
195
+ command = execute(request)
196
+
197
+ show_output = true
198
+ exit_on_error = true
199
+ Makit::LOGGER.info(Makit::CommandRunner.get_command_summary(command))
200
+ show_output = true if command.exit_code != 0
201
+ Makit::LOGGER.info(indent_string("\n#{command.output}\n#{command.error}\n".strip, 2)) if show_output
202
+ exit(command.exit_code) if exit_on_error && command.exit_code != 0 # unless process_status.success?
203
+ command
204
+ end
205
+
206
+ # Parse and return a Makit::V1::CommandRequest.
207
+ def parse_command_request(source)
208
+ return Makit::V1::CommandRequest.new(source) if source.is_a? Hash
209
+ return source if source.is_a? Makit::V1::CommandRequest
210
+ if source.is_a? String
211
+ return parse_args(source)
212
+ end
213
+
214
+ raise "Invalid source" unless source.is_a? Makit::V1::CommandRequest
215
+ end
216
+
217
+ def parse_command_request_from_hash(hash)
218
+ raise "Invalid hash" unless hash.is_a? Hash
219
+ Makit::V1::CommandRequest.new(hash)
220
+ end
221
+
222
+ def parse_command_request_from_string(source)
223
+ raise "Invalid source" unless source.is_a? String
224
+ words = source.split(" ")
225
+ hash = {
226
+ name: words.shift,
227
+ arguments: words,
228
+ exit_on_error: true,
229
+ }
230
+ end
231
+
232
+ # Parse the command line arguments into a Makit::V1::CommandRequest.
233
+ def parse_args(args)
234
+ #raise "No command specified" if args.empty?
235
+ if args.is_a? Makit::V1::CommandRequest
236
+ args
237
+ else
238
+ if args.is_a? String
239
+ args = args.split(" ")
240
+ if (args.length == 1)
241
+ hash = {
242
+ name: args[0],
243
+ arguments: [],
244
+ exit_on_error: true,
245
+ }
246
+ Makit::V1::CommandRequest.new(hash)
247
+ else
248
+ hash = {
249
+ name: args.shift,
250
+ arguments: args,
251
+ exit_on_error: true,
252
+ }
253
+
254
+ Makit::V1::CommandRequest.new(hash)
255
+ end
256
+ else
257
+ Makit::V1::CommandRequest.new(args)
258
+ end
259
+ end
260
+ end
261
+
262
+ def get_path_name(name)
263
+ # replace all characters that a not valid in a filename with an underscore
264
+ name.gsub(/[^0-9a-z]/i, "_")
265
+ end
266
+
267
+ # Given a Makit::V1::CommandRequest, execute the command and return a Makit::V1::Command.
268
+ def execute(args)
269
+ command_request = parse_args(args)
270
+ command_request.directory = Dir.pwd if command_request.directory.nil?
271
+ command_request.directory = Dir.pwd if command_request.directory.length == 0
272
+ result = Makit::V1::Command.new(name: command_request.name)
273
+ command_request.arguments.each do |arg|
274
+ result.arguments.push(arg)
275
+ end
276
+ command = "#{command_request.name} #{command_request.arguments.join(" ")}"
277
+ result.directory = command_request.directory
278
+ start = Time.now
279
+ filename_friendly_timestamp = Time.now.strftime("%Y.%m.%d_%H%M%S")
280
+ log_filename = File.join(Makit::Directories::LOG, "#{filename_friendly_timestamp}.log")
281
+
282
+ # assign a directory variable to the current working directory, if not specified,
283
+ # otherwise assign the specified directory
284
+ command_request.directory = Dir.pwd if command_request.directory.nil?
285
+ command_request.directory = Dir.pwd if command_request.directory.length == 0
286
+ raise "Invalid directory" unless Dir.exist?(command_request.directory)
287
+
288
+ result.started_at = Google::Protobuf::Timestamp.new(seconds: start.to_i, nanos: start.nsec.to_i)
289
+ ############# execute the command
290
+ (output, error, exit_code) = execute_command(command, command_request.directory, command_request.timeout)
291
+ result.output = output.force_encoding("ASCII-8BIT")
292
+ result.error = error.force_encoding("ASCII-8BIT")
293
+ result.exit_code = exit_code.nil? ? 0 : exit_code
294
+
295
+ elapsed_time = Time.now - start
296
+ seconds = elapsed_time.to_i
297
+ nanos = ((elapsed_time - seconds) * 1_000_000_000).to_i
298
+
299
+ result.duration = Google::Protobuf::Duration.new(seconds: seconds, nanos: nanos)
300
+
301
+ result
302
+ end
303
+
304
+ # pure function to execute a command
305
+ # returns (stdout, stderr, exit_code) or raise an exception
306
+ def execute_command(command, directory, timeout)
307
+ original_directory = Dir.pwd
308
+ begin
309
+ output = nil
310
+ error = nil
311
+ process_status = nil
312
+ Dir.chdir(directory) do
313
+ output, error, process_status = Open3.capture3(command)
314
+ end
315
+ return [output, error, process_status.exitstatus]
316
+ rescue => e
317
+ # restore the original working directory
318
+ Dir.chdir(original_directory)
319
+ message_parts = []
320
+ message_parts << "failed to execute #{command}"
321
+ message_parts << "directory: #{directory}"
322
+ message_parts << "timeout: #{timeout}" unless timeout.nil?
323
+ message_parts << "error: #{e.message}"
324
+ message = message_parts.join("\n") + "\n"
325
+ return ["", message, 1]
326
+ #raise Makit::Error, message
327
+ end
328
+ end
329
+
330
+ def execute_command_request(command_request)
331
+ # if the command_request is not a Makit::V1::CommandRequest, raise an error
332
+ raise "Invalid command_request" unless command_request.is_a? Makit::V1::CommandRequest
333
+
334
+ args = Array.new
335
+ command_request.arguments.each do |arg|
336
+ args.push(arg)
337
+ end
338
+ result = Makit::V1::Command.new({
339
+ name: command_request.name,
340
+ arguments: args,
341
+ started_at: Google::Protobuf::Timestamp.new({ seconds: Time.now.to_i, nanos: Time.now.nsec }),
342
+ })
343
+
344
+ begin
345
+ rescue => e
346
+ end
347
+ end
348
+
349
+ def indent_string(input_string, indent_spaces)
350
+ indentation = " " * indent_spaces
351
+ input_string.lines.map { |line| indentation + line }.join
352
+ end
353
+
354
+ def self.get_command_summary(command)
355
+ symbol = Makit::Symbols.warning
356
+ symbol = Makit::Symbols.checkmark if !command.exit_code.nil? && command.exit_code.zero?
357
+ symbol = Makit::Symbols.error if command.exit_code != 0
358
+ summary = "#{symbol} #{command.name.colorize(:yellow)} #{command.arguments.join(" ")}"
359
+
360
+ if summary.length > 80
361
+ summary = summary.to_lines(80, command.name.length + 3)
362
+ end
363
+
364
+ summary
365
+ end
366
+
367
+ def log_rake_commands
368
+ dir = File.join(Makit::Directories::PROJECT_ARTIFACTS, "commands")
369
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
370
+
371
+ # open a text file to write to
372
+ File.open(File.join(dir, "rake.commands.txt"), "w") do |file|
373
+ #rake_commands = commands.select { |command| command.name == "rake" }
374
+ commands.each do |command|
375
+ file.puts " " + Makit::CommandRunner.get_command_summary(command).strip_color_codes
376
+ file.puts " start time: #{command.started_at}"
377
+ file.puts command.output.strip_color_codes unless command.output.strip_color_codes.strip.length == 0
378
+ file.puts command.error.strip_color_codes unless command.error.strip_color_codes.strip.length == 0
379
+ file.puts " "
380
+ end
381
+ end
382
+ end
383
+
384
+ def log_slowest_commands
385
+ dir = File.join(Makit::Directories::PROJECT_ARTIFACTS, "commands")
386
+ FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
387
+
388
+ # open a text file to write to
389
+ File.open(File.join(dir, "slow.commands.txt"), "w") do |file|
390
+ Makit::RUNNER.commands.sort_by { |command| (command.duration.seconds + (command.duration.nanos / 1_000_000_000.0)) }.reverse.first(5).each do |command|
391
+ # Convert to float representation
392
+ duration_in_float = command.duration.seconds + (command.duration.nanos / 1_000_000_000.0)
393
+ #puts " #{command.name} took #{duration_in_float} seconds"
394
+ file.puts " " + Makit::CommandRunner.get_command_summary(command).strip_color_codes + " (#{command.duration.seconds} seconds)"
395
+ end
396
+ end
397
+ end
398
+ end # class CommandRunner
399
+ end # module Makit