makit 0.0.46 → 0.0.48

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