makit 0.0.82 → 0.0.84

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