makit 0.0.57 → 0.0.58

Sign up to get free protection for your applications and to get access to all the features.
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 +404 -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_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 +182 -182
  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 +96 -96
  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 +137 -137
  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 +111 -111
  39. data/lib/makit/storage.rb +131 -131
  40. data/lib/makit/symbols.rb +149 -149
  41. data/lib/makit/tasks.rb +61 -61
  42. data/lib/makit/tree.rb +37 -37
  43. data/lib/makit/v1/makit.v1_services_pb.rb +25 -25
  44. data/lib/makit/version.rb +68 -68
  45. data/lib/makit/wix.rb +95 -95
  46. data/lib/makit/yaml.rb +19 -19
  47. data/lib/makit/zip.rb +17 -17
  48. data/lib/makit.rb +267 -267
  49. metadata +3 -5
  50. data/lib/generated/makit/v1/makit.v1_pb.rb +0 -35
  51. data/lib/generated/makit/v1/web/link_pb.rb +0 -20
@@ -1,399 +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
- @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
+ 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