makit 0.0.40 → 0.0.41

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/generated/makit/v1/makit.v1_pb.rb +34 -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 +400 -391
  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 +140 -140
  23. data/lib/makit/directory.rb +200 -200
  24. data/lib/makit/dotnet.rb +155 -156
  25. data/lib/makit/environment.rb +127 -127
  26. data/lib/makit/fileinfo.rb +16 -16
  27. data/lib/makit/files.rb +47 -47
  28. data/lib/makit/git.rb +87 -87
  29. data/lib/makit/gitlab_runner.rb +60 -60
  30. data/lib/makit/humanize.rb +129 -129
  31. data/lib/makit/indexer.rb +56 -56
  32. data/lib/makit/logging.rb +96 -96
  33. data/lib/makit/markdown.rb +75 -75
  34. data/lib/makit/mp/basic_object_mp.rb +16 -16
  35. data/lib/makit/mp/command_request.mp.rb +16 -16
  36. data/lib/makit/mp/project_mp.rb +210 -210
  37. data/lib/makit/mp/string_mp.rb +140 -122
  38. data/lib/makit/nuget.rb +57 -57
  39. data/lib/makit/protoc.rb +104 -104
  40. data/lib/makit/serializer.rb +115 -115
  41. data/lib/makit/show.rb +108 -108
  42. data/lib/makit/storage.rb +131 -131
  43. data/lib/makit/symbols.rb +149 -149
  44. data/lib/makit/tasks.rb +60 -60
  45. data/lib/makit/tree.rb +37 -37
  46. data/lib/makit/v1/makit.v1_services_pb.rb +25 -25
  47. data/lib/makit/version.rb +52 -52
  48. data/lib/makit/wix.rb +95 -95
  49. data/lib/makit/zip.rb +17 -17
  50. data/lib/makit.rb +267 -267
  51. metadata +5 -3
@@ -1,391 +1,400 @@
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
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