makit 0.0.35 → 0.0.36

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