makit 0.0.143 → 0.0.145

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/lib/makit/cli/base.rb +17 -0
  3. data/lib/makit/cli/generators/templates/ruby/gemspec.rb +1 -0
  4. data/lib/makit/cli/main.rb +9 -0
  5. data/lib/makit/cli/pipeline_commands.rb +311 -0
  6. data/lib/makit/cli/strategy_commands.rb +2 -7
  7. data/lib/makit/commands/result.rb +1 -1
  8. data/lib/makit/configuration/dotnet_project.rb +9 -0
  9. data/lib/makit/configuration/gitlab_helper.rb +4 -1
  10. data/lib/makit/configuration/project.rb +362 -84
  11. data/lib/makit/configuration/timeout.rb +1 -1
  12. data/lib/makit/configuration.rb +5 -0
  13. data/lib/makit/fileinfo.rb +8 -0
  14. data/lib/makit/git/repository.rb +207 -31
  15. data/lib/makit/git.rb +6 -0
  16. data/lib/makit/gitlab/pipeline.rb +857 -0
  17. data/lib/makit/gitlab/pipeline_service_impl.rb +1536 -0
  18. data/lib/makit/humanize.rb +81 -0
  19. data/lib/makit/io/filesystem.rb +111 -0
  20. data/lib/makit/io/filesystem_service_impl.rb +337 -0
  21. data/lib/makit/mp/string_mp.rb +15 -9
  22. data/lib/makit/podman/podman.rb +458 -0
  23. data/lib/makit/podman/podman_service_impl.rb +1081 -0
  24. data/lib/makit/process.rb +214 -0
  25. data/lib/makit/protoc.rb +6 -1
  26. data/lib/makit/services/repository_manager.rb +268 -132
  27. data/lib/makit/symbols.rb +5 -0
  28. data/lib/makit/v1/configuration/project_service_impl.rb +371 -0
  29. data/lib/makit/v1/git/git_repository_service_impl.rb +295 -0
  30. data/lib/makit/v1/makit.v1_pb.rb +1 -1
  31. data/lib/makit/v1/makit.v1_services_pb.rb +1 -1
  32. data/lib/makit/v1/services/repository_manager_service_impl.rb +572 -0
  33. data/lib/makit/version.rb +1 -1
  34. data/lib/makit.rb +68 -0
  35. metadata +61 -36
@@ -133,5 +133,86 @@ module Makit
133
133
  parts << "0 seconds" if parts.empty?
134
134
  parts.join(", ")
135
135
  end
136
+
137
+ def self.get_pipeline_execution_summary(execution_result)
138
+ summary = "Pipeline Execution Summary\n"
139
+ summary += " Execution ID: #{execution_result.execution_id}\n"
140
+ summary += " Status: #{execution_result.status}\n"
141
+ summary += " Started: #{execution_result.started_at}\n"
142
+ summary += " Finished: #{execution_result.finished_at}\n" if execution_result.finished_at
143
+
144
+ if execution_result.respond_to?(:total_duration_seconds) && execution_result.total_duration_seconds
145
+ summary += " Duration: #{get_humanized_duration(execution_result.total_duration_seconds)}\n"
146
+ end
147
+
148
+ if execution_result.respond_to?(:podman_version) && execution_result.podman_version
149
+ summary += " Podman Version: #{execution_result.podman_version}\n"
150
+ end
151
+
152
+ if execution_result.respond_to?(:execution_host) && execution_result.execution_host
153
+ summary += " Host: #{execution_result.execution_host}\n"
154
+ end
155
+
156
+ summary += " Jobs: (#{execution_result.job_results.length})\n"
157
+ execution_result.job_results.each do |job_result|
158
+ summary += "\n"
159
+ summary += indent_string(get_job_execution_details(job_result), 4)
160
+ summary += "\n"
161
+ end
162
+
163
+ if execution_result.errors && execution_result.errors.any?
164
+ summary += "\n Errors:\n"
165
+ execution_result.errors.each do |error|
166
+ summary += " - #{error}\n"
167
+ end
168
+ end
169
+
170
+ if execution_result.warnings && execution_result.warnings.any?
171
+ summary += "\n Warnings:\n"
172
+ execution_result.warnings.each do |warning|
173
+ summary += " - #{warning}\n"
174
+ end
175
+ end
176
+
177
+ summary
178
+ end
179
+
180
+ def self.get_job_execution_details(job_result)
181
+ details = "Job: #{job_result.job_name}\n"
182
+ details += " Status: #{job_result.status}\n"
183
+ details += " Started: #{job_result.started_at}\n"
184
+ details += " Finished: #{job_result.finished_at}\n" if job_result.finished_at
185
+ details += " Exit Code: #{job_result.exit_code}\n" if job_result.exit_code
186
+
187
+ if job_result.errors && job_result.errors.any?
188
+ details += " Errors:\n"
189
+ job_result.errors.each do |error|
190
+ details += " - #{error}\n"
191
+ end
192
+ end
193
+
194
+ if job_result.logs && job_result.logs.any?
195
+ details += " Logs:\n"
196
+ job_result.logs.each do |log|
197
+ details += " #{log}\n"
198
+ end
199
+ end
200
+
201
+ if job_result.respond_to?(:stdout) && job_result.stdout && !job_result.stdout.empty?
202
+ details += " Stdout:\n"
203
+ job_result.stdout.split("\n").each do |line|
204
+ details += " #{line}\n"
205
+ end
206
+ end
207
+
208
+ if job_result.respond_to?(:stderr) && job_result.stderr && !job_result.stderr.empty?
209
+ details += " Stderr:\n"
210
+ job_result.stderr.split("\n").each do |line|
211
+ details += " #{line}\n"
212
+ end
213
+ end
214
+
215
+ details
216
+ end
136
217
  end
137
218
  end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "filesystem_service_impl"
4
+
5
+ module Makit
6
+ module IO
7
+ class FileSystem
8
+ def initialize
9
+ @service = FileSystemServiceImpl.new
10
+ end
11
+
12
+ # Get a single file's content and/or metadata
13
+ def get_file(path, include_metadata: true, include_content: true)
14
+ request = {
15
+ path: path,
16
+ include_metadata: include_metadata,
17
+ include_content: include_content
18
+ }
19
+
20
+ result = @service.get_file(request)
21
+
22
+ if result.respond_to?(:file)
23
+ # gRPC mode
24
+ result.file
25
+ else
26
+ # Fallback mode
27
+ result[:file]
28
+ end
29
+ end
30
+
31
+ # Create or update a file
32
+ def save_file(path, binary_data: nil, text_lines: nil)
33
+ request = {
34
+ path: path,
35
+ binary_data: binary_data,
36
+ text_lines: text_lines
37
+ }
38
+
39
+ result = @service.save_file(request)
40
+
41
+ if result.respond_to?(:file)
42
+ # gRPC mode
43
+ result.file
44
+ else
45
+ # Fallback mode
46
+ result[:file]
47
+ end
48
+ end
49
+
50
+ # Delete a file by path
51
+ def delete_file(path)
52
+ request = { path: path }
53
+ @service.delete_file(request)
54
+ end
55
+
56
+ # List files in a directory according to query flags
57
+ def list_files(directory, recursive: false, pattern: nil, respect_gitignore: false, include_metadata: true, include_content: false)
58
+ request = {
59
+ directory: directory,
60
+ recursive: recursive,
61
+ pattern: pattern,
62
+ respect_gitignore: respect_gitignore,
63
+ include_metadata: include_metadata,
64
+ include_content: include_content
65
+ }
66
+
67
+ result = @service.list_files(request)
68
+
69
+ if result.respond_to?(:files)
70
+ # gRPC mode
71
+ result.files
72
+ else
73
+ # Fallback mode
74
+ result[:files]
75
+ end
76
+ end
77
+
78
+ # Create a directory
79
+ def create_directory(path)
80
+ request = { path: path }
81
+ @service.create_directory(request)
82
+ end
83
+
84
+ # Delete a directory
85
+ def delete_directory(path)
86
+ request = { path: path }
87
+ @service.delete_directory(request)
88
+ end
89
+
90
+ # Upload large files in chunks
91
+ def upload(chunks)
92
+ request = { chunks: chunks }
93
+ result = @service.upload(request)
94
+
95
+ if result.respond_to?(:file)
96
+ # gRPC mode
97
+ result.file
98
+ else
99
+ # Fallback mode
100
+ result[:file]
101
+ end
102
+ end
103
+
104
+ # Download a file in chunks
105
+ def download(path)
106
+ request = { path: path }
107
+ @service.download(request)
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,337 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "fileutils"
4
+ require "mime/types"
5
+ require "digest"
6
+ require "time"
7
+
8
+ module Makit
9
+ module IO
10
+ class FileSystemServiceImpl
11
+ def initialize
12
+ # Initialize any required state
13
+ end
14
+
15
+ # Check if gRPC service is available
16
+ def self.grpc_available?
17
+ defined?(Makit::Io::FileSystemService)
18
+ end
19
+
20
+ # Get a single file's content and/or metadata
21
+ def get_file(request, _unused_call = nil)
22
+ path = request.respond_to?(:path) ? request.path : request[:path]
23
+ include_metadata = request.respond_to?(:include_metadata) ? request.include_metadata : request[:include_metadata]
24
+ include_content = request.respond_to?(:include_content) ? request.include_content : request[:include_content]
25
+
26
+ # Default to metadata only if not specified
27
+ include_metadata = true if include_metadata.nil?
28
+ include_content = false if include_content.nil?
29
+
30
+ unless File.exist?(path)
31
+ raise Errno::ENOENT, "No such file or directory: #{path}"
32
+ end
33
+
34
+ file_data = {
35
+ path: path
36
+ }
37
+
38
+ if include_content
39
+ if binary_file?(path)
40
+ file_data[:binary_data] = File.binread(path)
41
+ else
42
+ file_data[:text_lines] = File.readlines(path).map(&:chomp)
43
+ end
44
+ end
45
+
46
+ if include_metadata
47
+ file_data[:metadata] = get_file_metadata(path)
48
+ end
49
+
50
+ if self.class.grpc_available?
51
+ Makit::Io::FileResponse.new(file: Makit::Io::File.new(file_data))
52
+ else
53
+ { file: file_data }
54
+ end
55
+ end
56
+
57
+ # Create or update a file
58
+ def save_file(request, _unused_call = nil)
59
+ path = request.respond_to?(:path) ? request.path : request[:path]
60
+ binary_data = request.respond_to?(:binary_data) ? request.binary_data : request[:binary_data]
61
+ text_lines = request.respond_to?(:text_lines) ? request.text_lines : request[:text_lines]
62
+
63
+ # Ensure directory exists
64
+ FileUtils.mkdir_p(File.dirname(path))
65
+
66
+ if binary_data && !binary_data.empty?
67
+ File.binwrite(path, binary_data)
68
+ elsif text_lines && !text_lines.empty?
69
+ File.write(path, text_lines.join("\n") + "\n")
70
+ else
71
+ raise ArgumentError, "Either binary_data or text_lines must be provided"
72
+ end
73
+
74
+ file_data = {
75
+ path: path,
76
+ metadata: get_file_metadata(path)
77
+ }
78
+
79
+ if binary_data && !binary_data.empty?
80
+ file_data[:binary_data] = binary_data
81
+ elsif text_lines && !text_lines.empty?
82
+ file_data[:text_lines] = text_lines
83
+ end
84
+
85
+ if self.class.grpc_available?
86
+ Makit::Io::FileResponse.new(file: Makit::Io::File.new(file_data))
87
+ else
88
+ { file: file_data }
89
+ end
90
+ end
91
+
92
+ # Delete a file by path
93
+ def delete_file(request, _unused_call = nil)
94
+ path = request.respond_to?(:path) ? request.path : request[:path]
95
+
96
+ unless File.exist?(path)
97
+ raise Errno::ENOENT, "No such file or directory: #{path}"
98
+ end
99
+
100
+ File.delete(path)
101
+
102
+ if self.class.grpc_available?
103
+ Google::Protobuf::Empty.new
104
+ else
105
+ {}
106
+ end
107
+ end
108
+
109
+ # List files in a directory according to query flags
110
+ def list_files(request, _unused_call = nil)
111
+ directory = request.respond_to?(:directory) ? request.directory : request[:directory]
112
+ recursive = request.respond_to?(:recursive) ? request.recursive : request[:recursive]
113
+ pattern = request.respond_to?(:pattern) ? request.pattern : request[:pattern]
114
+ respect_gitignore = request.respond_to?(:respect_gitignore) ? request.respect_gitignore : request[:respect_gitignore]
115
+ include_metadata = request.respond_to?(:include_metadata) ? request.include_metadata : request[:include_metadata]
116
+ include_content = request.respond_to?(:include_content) ? request.include_content : request[:include_content]
117
+
118
+ # Default values
119
+ recursive = false if recursive.nil?
120
+ respect_gitignore = false if respect_gitignore.nil?
121
+ include_metadata = true if include_metadata.nil?
122
+ include_content = false if include_content.nil?
123
+
124
+ unless Dir.exist?(directory)
125
+ raise Errno::ENOENT, "No such file or directory: #{directory}"
126
+ end
127
+
128
+ files = []
129
+
130
+ # Get gitignore patterns if requested
131
+ gitignore_patterns = []
132
+ if respect_gitignore
133
+ gitignore_file = File.join(directory, ".gitignore")
134
+ if File.exist?(gitignore_file)
135
+ gitignore_patterns = File.readlines(gitignore_file).map(&:strip).reject(&:empty?)
136
+ end
137
+ end
138
+
139
+ # Find files
140
+ if recursive
141
+ Dir.glob(File.join(directory, "**", "*")).each do |file_path|
142
+ next if File.directory?(file_path)
143
+ next if should_ignore_file?(file_path, gitignore_patterns)
144
+ next if pattern && !File.fnmatch(pattern, File.basename(file_path))
145
+
146
+ files << build_file_data(file_path, include_metadata, include_content)
147
+ end
148
+ else
149
+ Dir.entries(directory).each do |entry|
150
+ next if entry == "." || entry == ".."
151
+
152
+ file_path = File.join(directory, entry)
153
+ next if File.directory?(file_path)
154
+ next if should_ignore_file?(file_path, gitignore_patterns)
155
+ next if pattern && !File.fnmatch(pattern, entry)
156
+
157
+ files << build_file_data(file_path, include_metadata, include_content)
158
+ end
159
+ end
160
+
161
+ if self.class.grpc_available?
162
+ Makit::Io::FileList.new(files: files.map { |f| Makit::Io::File.new(f) })
163
+ else
164
+ { files: files }
165
+ end
166
+ end
167
+
168
+ # Create a directory
169
+ def create_directory(request, _unused_call = nil)
170
+ path = request.respond_to?(:path) ? request.path : request[:path]
171
+
172
+ FileUtils.mkdir_p(path)
173
+
174
+ if self.class.grpc_available?
175
+ Google::Protobuf::Empty.new
176
+ else
177
+ {}
178
+ end
179
+ end
180
+
181
+ # Delete a directory
182
+ def delete_directory(request, _unused_call = nil)
183
+ path = request.respond_to?(:path) ? request.path : request[:path]
184
+
185
+ unless Dir.exist?(path)
186
+ raise Errno::ENOENT, "No such file or directory: #{path}"
187
+ end
188
+
189
+ Dir.rmdir(path)
190
+
191
+ if self.class.grpc_available?
192
+ Google::Protobuf::Empty.new
193
+ else
194
+ {}
195
+ end
196
+ end
197
+
198
+ # Upload large files in chunks
199
+ def upload(request, _unused_call = nil)
200
+ chunks = request.respond_to?(:chunks) ? request.chunks : request[:chunks]
201
+
202
+ raise ArgumentError, "No chunks provided" if chunks.nil? || chunks.empty?
203
+
204
+ # Group chunks by path
205
+ chunks_by_path = {}
206
+ chunks.each do |chunk|
207
+ path = chunk.respond_to?(:path) ? chunk.path : chunk[:path]
208
+ data = chunk.respond_to?(:data) ? chunk.data : chunk[:data]
209
+ metadata = chunk.respond_to?(:metadata) ? chunk.metadata : chunk[:metadata]
210
+ is_final = chunk.respond_to?(:is_final) ? chunk.is_final : chunk[:is_final]
211
+
212
+ chunks_by_path[path] ||= { data: "", metadata: metadata }
213
+ chunks_by_path[path][:data] += data
214
+ end
215
+
216
+ # Save each file
217
+ results = []
218
+ chunks_by_path.each do |path, file_info|
219
+ File.binwrite(path, file_info[:data])
220
+
221
+ file_data = {
222
+ path: path,
223
+ binary_data: file_info[:data],
224
+ metadata: get_file_metadata(path)
225
+ }
226
+
227
+ results << file_data
228
+ end
229
+
230
+ # Return the first (or only) file result
231
+ result = results.first
232
+
233
+ if self.class.grpc_available?
234
+ Makit::Io::FileResponse.new(file: Makit::Io::File.new(result))
235
+ else
236
+ { file: result }
237
+ end
238
+ end
239
+
240
+ # Download a file in chunks
241
+ def download(request, _unused_call = nil)
242
+ path = request.respond_to?(:path) ? request.path : request[:path]
243
+
244
+ unless File.exist?(path)
245
+ raise Errno::ENOENT, "No such file or directory: #{path}"
246
+ end
247
+
248
+ content = File.binread(path)
249
+ chunk_size = 1024 # 1KB chunks
250
+ chunks = []
251
+
252
+ (0...content.length).step(chunk_size) do |i|
253
+ chunk_data = content[i, chunk_size]
254
+ is_final = (i + chunk_size >= content.length)
255
+
256
+ chunk = {
257
+ path: path,
258
+ data: chunk_data,
259
+ metadata: is_final ? get_file_metadata(path) : nil,
260
+ is_final: is_final
261
+ }
262
+
263
+ chunks << chunk
264
+ end
265
+
266
+ if self.class.grpc_available?
267
+ chunks.map { |c| Makit::Io::FileChunk.new(c) }
268
+ else
269
+ chunks
270
+ end
271
+ end
272
+
273
+ private
274
+
275
+ def get_file_metadata(path)
276
+ stat = File.stat(path)
277
+
278
+ {
279
+ mime_type: get_mime_type(path),
280
+ size_bytes: stat.size,
281
+ created_at: stat.birthtime.iso8601,
282
+ modified_at: stat.mtime.iso8601,
283
+ owner: stat.uid.to_s,
284
+ is_directory: stat.directory?,
285
+ checksum: calculate_checksum(path)
286
+ }
287
+ end
288
+
289
+ def get_mime_type(path)
290
+ mime_type = MIME::Types.type_for(path).first
291
+ mime_type ? mime_type.content_type : "application/octet-stream"
292
+ end
293
+
294
+ def calculate_checksum(path)
295
+ Digest::SHA256.file(path).hexdigest
296
+ end
297
+
298
+ def binary_file?(path)
299
+ # Simple heuristic: check if file contains null bytes or high bytes
300
+ File.open(path, "rb") do |file|
301
+ chunk = file.read(1024)
302
+ return true if chunk.nil? || chunk.include?("\x00") || chunk.bytes.any? { |b| b > 127 }
303
+ end
304
+ false
305
+ rescue
306
+ true # If we can't read it, assume it's binary
307
+ end
308
+
309
+ def should_ignore_file?(file_path, gitignore_patterns)
310
+ return false if gitignore_patterns.empty?
311
+
312
+ relative_path = file_path
313
+ gitignore_patterns.any? do |pattern|
314
+ File.fnmatch(pattern, relative_path) || File.fnmatch(pattern, File.basename(relative_path))
315
+ end
316
+ end
317
+
318
+ def build_file_data(file_path, include_metadata, include_content)
319
+ file_data = { path: file_path }
320
+
321
+ if include_content
322
+ if binary_file?(file_path)
323
+ file_data[:binary_data] = File.binread(file_path)
324
+ else
325
+ file_data[:text_lines] = File.readlines(file_path).map(&:chomp)
326
+ end
327
+ end
328
+
329
+ if include_metadata
330
+ file_data[:metadata] = get_file_metadata(file_path)
331
+ end
332
+
333
+ file_data
334
+ end
335
+ end
336
+ end
337
+ end
@@ -61,24 +61,30 @@ class String
61
61
  Makit::Commands::Runner.default.execute(request)
62
62
  end
63
63
 
64
- def cache_run(timestamp = nil)
64
+ def cache_run(timestamp = nil, timeout: nil)
65
65
  #puts "cache_run: #{self}"
66
66
  command = self
67
67
  request = Makit::Commands::Request.from_string(command)
68
+
69
+ # Build request options
70
+ request_options = {
71
+ command: request.command,
72
+ arguments: request.arguments,
73
+ directory: request.directory,
74
+ timeout: timeout || request.timeout,
75
+ environment: request.environment || {},
76
+ metadata: request.metadata || {},
77
+ }
78
+
68
79
  if timestamp
69
80
  # Add timestamp to metadata for cache key generation
70
- request = Makit::Commands::Request.new(
71
- command: request.command,
72
- arguments: request.arguments,
73
- directory: request.directory,
74
- timeout: request.timeout,
75
- environment: request.environment || {},
76
- metadata: (request.metadata || {}).merge(timestamp: timestamp),
77
- )
81
+ request_options[:metadata] = request_options[:metadata].merge(timestamp: timestamp)
78
82
  #puts "timestamp: #{timestamp}"
79
83
  else
80
84
  #puts "no timestamp"
81
85
  end
86
+
87
+ request = Makit::Commands::Request.new(**request_options)
82
88
  Makit::Commands::Runner.default.execute(request)
83
89
  end
84
90