makit 0.0.144 → 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 (165) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +41 -41
  3. data/exe/makit +5 -5
  4. data/lib/makit/apache.rb +28 -28
  5. data/lib/makit/auto.rb +48 -48
  6. data/lib/makit/cli/base.rb +17 -0
  7. data/lib/makit/cli/build_commands.rb +500 -500
  8. data/lib/makit/cli/generators/base_generator.rb +74 -74
  9. data/lib/makit/cli/generators/dotnet_generator.rb +50 -50
  10. data/lib/makit/cli/generators/generator_factory.rb +49 -49
  11. data/lib/makit/cli/generators/node_generator.rb +50 -50
  12. data/lib/makit/cli/generators/ruby_generator.rb +77 -77
  13. data/lib/makit/cli/generators/rust_generator.rb +50 -50
  14. data/lib/makit/cli/generators/templates/dotnet_templates.rb +167 -167
  15. data/lib/makit/cli/generators/templates/node_templates.rb +161 -161
  16. data/lib/makit/cli/generators/templates/ruby/gemfile.rb +26 -26
  17. data/lib/makit/cli/generators/templates/ruby/gemspec.rb +41 -40
  18. data/lib/makit/cli/generators/templates/ruby/main_lib.rb +33 -33
  19. data/lib/makit/cli/generators/templates/ruby/rakefile.rb +35 -35
  20. data/lib/makit/cli/generators/templates/ruby/readme.rb +63 -63
  21. data/lib/makit/cli/generators/templates/ruby/test.rb +39 -39
  22. data/lib/makit/cli/generators/templates/ruby/test_helper.rb +29 -29
  23. data/lib/makit/cli/generators/templates/ruby/version.rb +29 -29
  24. data/lib/makit/cli/generators/templates/rust_templates.rb +128 -128
  25. data/lib/makit/cli/main.rb +78 -69
  26. data/lib/makit/cli/pipeline_commands.rb +311 -0
  27. data/lib/makit/cli/project_commands.rb +868 -868
  28. data/lib/makit/cli/repository_commands.rb +661 -661
  29. data/lib/makit/cli/strategy_commands.rb +207 -212
  30. data/lib/makit/cli/utility_commands.rb +521 -521
  31. data/lib/makit/commands/factory.rb +359 -359
  32. data/lib/makit/commands/middleware/base.rb +73 -73
  33. data/lib/makit/commands/middleware/cache.rb +248 -248
  34. data/lib/makit/commands/middleware/command_logger.rb +312 -312
  35. data/lib/makit/commands/middleware/validator.rb +269 -269
  36. data/lib/makit/commands/request.rb +316 -316
  37. data/lib/makit/commands/result.rb +323 -323
  38. data/lib/makit/commands/runner.rb +386 -386
  39. data/lib/makit/commands/strategies/base.rb +171 -171
  40. data/lib/makit/commands/strategies/child_process.rb +162 -162
  41. data/lib/makit/commands/strategies/factory.rb +136 -136
  42. data/lib/makit/commands/strategies/synchronous.rb +139 -139
  43. data/lib/makit/commands.rb +50 -50
  44. data/lib/makit/configuration/dotnet_project.rb +48 -48
  45. data/lib/makit/configuration/gitlab_helper.rb +61 -58
  46. data/lib/makit/configuration/project.rb +446 -168
  47. data/lib/makit/configuration/rakefile_helper.rb +43 -43
  48. data/lib/makit/configuration/step.rb +34 -34
  49. data/lib/makit/configuration/timeout.rb +74 -74
  50. data/lib/makit/configuration.rb +21 -16
  51. data/lib/makit/content/default_gitignore.rb +7 -7
  52. data/lib/makit/content/default_gitignore.txt +225 -225
  53. data/lib/makit/content/default_rakefile.rb +13 -13
  54. data/lib/makit/content/gem_rakefile.rb +16 -16
  55. data/lib/makit/context.rb +1 -1
  56. data/lib/makit/data.rb +49 -49
  57. data/lib/makit/directories.rb +140 -140
  58. data/lib/makit/directory.rb +262 -262
  59. data/lib/makit/docs/files.rb +89 -89
  60. data/lib/makit/docs/rake.rb +102 -102
  61. data/lib/makit/dotnet/cli.rb +69 -69
  62. data/lib/makit/dotnet/project.rb +217 -217
  63. data/lib/makit/dotnet/solution.rb +38 -38
  64. data/lib/makit/dotnet/solution_classlib.rb +239 -239
  65. data/lib/makit/dotnet/solution_console.rb +264 -264
  66. data/lib/makit/dotnet/solution_maui.rb +354 -354
  67. data/lib/makit/dotnet/solution_wasm.rb +275 -275
  68. data/lib/makit/dotnet/solution_wpf.rb +304 -304
  69. data/lib/makit/dotnet.rb +102 -102
  70. data/lib/makit/email.rb +90 -90
  71. data/lib/makit/environment.rb +142 -142
  72. data/lib/makit/examples/runner.rb +370 -370
  73. data/lib/makit/exceptions.rb +45 -45
  74. data/lib/makit/fileinfo.rb +32 -24
  75. data/lib/makit/files.rb +43 -43
  76. data/lib/makit/gems.rb +40 -40
  77. data/lib/makit/git/cli.rb +54 -54
  78. data/lib/makit/git/repository.rb +266 -90
  79. data/lib/makit/git.rb +104 -98
  80. data/lib/makit/gitlab/pipeline.rb +857 -0
  81. data/lib/makit/gitlab/pipeline_service_impl.rb +1536 -0
  82. data/lib/makit/gitlab_runner.rb +59 -59
  83. data/lib/makit/humanize.rb +218 -137
  84. data/lib/makit/indexer.rb +47 -47
  85. data/lib/makit/io/filesystem.rb +111 -0
  86. data/lib/makit/io/filesystem_service_impl.rb +337 -0
  87. data/lib/makit/logging/configuration.rb +308 -308
  88. data/lib/makit/logging/format_registry.rb +84 -84
  89. data/lib/makit/logging/formatters/base.rb +39 -39
  90. data/lib/makit/logging/formatters/console_formatter.rb +140 -140
  91. data/lib/makit/logging/formatters/json_formatter.rb +65 -65
  92. data/lib/makit/logging/formatters/plain_text_formatter.rb +71 -71
  93. data/lib/makit/logging/formatters/text_formatter.rb +64 -64
  94. data/lib/makit/logging/log_request.rb +119 -119
  95. data/lib/makit/logging/logger.rb +199 -199
  96. data/lib/makit/logging/sinks/base.rb +91 -91
  97. data/lib/makit/logging/sinks/console.rb +72 -72
  98. data/lib/makit/logging/sinks/file_sink.rb +92 -92
  99. data/lib/makit/logging/sinks/structured.rb +123 -123
  100. data/lib/makit/logging/sinks/unified_file_sink.rb +296 -296
  101. data/lib/makit/logging.rb +565 -565
  102. data/lib/makit/markdown.rb +75 -75
  103. data/lib/makit/mp/basic_object_mp.rb +17 -17
  104. data/lib/makit/mp/command_mp.rb +13 -13
  105. data/lib/makit/mp/command_request.mp.rb +17 -17
  106. data/lib/makit/mp/project_mp.rb +199 -199
  107. data/lib/makit/mp/string_mp.rb +205 -199
  108. data/lib/makit/nuget.rb +74 -74
  109. data/lib/makit/podman/podman.rb +458 -0
  110. data/lib/makit/podman/podman_service_impl.rb +1081 -0
  111. data/lib/makit/port.rb +32 -32
  112. data/lib/makit/process.rb +377 -377
  113. data/lib/makit/protoc.rb +112 -107
  114. data/lib/makit/rake/cli.rb +196 -196
  115. data/lib/makit/rake/trace_controller.rb +174 -174
  116. data/lib/makit/rake.rb +81 -81
  117. data/lib/makit/ruby/cli.rb +185 -185
  118. data/lib/makit/ruby.rb +25 -25
  119. data/lib/makit/secrets.rb +51 -51
  120. data/lib/makit/serializer.rb +130 -130
  121. data/lib/makit/services/builder.rb +186 -186
  122. data/lib/makit/services/error_handler.rb +226 -226
  123. data/lib/makit/services/repository_manager.rb +367 -231
  124. data/lib/makit/services/validator.rb +112 -112
  125. data/lib/makit/setup/classlib.rb +101 -101
  126. data/lib/makit/setup/gem.rb +268 -268
  127. data/lib/makit/setup/pages.rb +11 -11
  128. data/lib/makit/setup/razorclasslib.rb +101 -101
  129. data/lib/makit/setup/runner.rb +54 -54
  130. data/lib/makit/setup.rb +5 -5
  131. data/lib/makit/show.rb +110 -110
  132. data/lib/makit/storage.rb +126 -126
  133. data/lib/makit/symbols.rb +175 -170
  134. data/lib/makit/task_info.rb +130 -130
  135. data/lib/makit/tasks/at_exit.rb +15 -15
  136. data/lib/makit/tasks/build.rb +22 -22
  137. data/lib/makit/tasks/clean.rb +13 -13
  138. data/lib/makit/tasks/configure.rb +10 -10
  139. data/lib/makit/tasks/format.rb +10 -10
  140. data/lib/makit/tasks/hook_manager.rb +443 -443
  141. data/lib/makit/tasks/init.rb +49 -49
  142. data/lib/makit/tasks/integrate.rb +29 -29
  143. data/lib/makit/tasks/pull_incoming.rb +13 -13
  144. data/lib/makit/tasks/setup.rb +16 -16
  145. data/lib/makit/tasks/sync.rb +17 -17
  146. data/lib/makit/tasks/tag.rb +16 -16
  147. data/lib/makit/tasks/task_monkey_patch.rb +81 -81
  148. data/lib/makit/tasks/test.rb +22 -22
  149. data/lib/makit/tasks/update.rb +18 -18
  150. data/lib/makit/tasks.rb +20 -20
  151. data/lib/makit/test_cache.rb +239 -239
  152. data/lib/makit/tree.rb +37 -37
  153. data/lib/makit/v1/configuration/project_service_impl.rb +371 -0
  154. data/lib/makit/v1/git/git_repository_service_impl.rb +295 -0
  155. data/lib/makit/v1/makit.v1_pb.rb +35 -35
  156. data/lib/makit/v1/makit.v1_services_pb.rb +27 -27
  157. data/lib/makit/v1/services/repository_manager_service_impl.rb +572 -0
  158. data/lib/makit/version.rb +100 -100
  159. data/lib/makit/version_util.rb +21 -21
  160. data/lib/makit/wix.rb +95 -95
  161. data/lib/makit/yaml.rb +29 -29
  162. data/lib/makit/zip.rb +17 -17
  163. data/lib/makit copy.rb +44 -44
  164. data/lib/makit.rb +111 -43
  165. metadata +61 -36
@@ -0,0 +1,371 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../../generated/makit/v1/configuration/project_service_services_pb"
4
+ require_relative "../../configuration/gitlab_helper"
5
+ require_relative "../../configuration/rakefile_helper"
6
+ require_relative "../../configuration/step"
7
+
8
+ module Makit
9
+ module V1
10
+ module Configuration
11
+ # gRPC service implementation for ProjectService
12
+ # Implements all the RPC methods defined in the protobuf service definition
13
+ class ProjectServiceImpl < ProjectService::Service
14
+ # Load a project from a JSON file
15
+ def load_from_file(request, _unused_call)
16
+ raise ArgumentError, "File does not exist: #{request.path}" unless File.exist?(request.path)
17
+
18
+ content = File.read(request.path)
19
+ data = JSON.parse(content, symbolize_names: true)
20
+
21
+ project = convert_from_json_data(data)
22
+ project
23
+ rescue ArgumentError => e
24
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INVALID_ARGUMENT, e.message)
25
+ rescue StandardError => e
26
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INTERNAL, e.message)
27
+ end
28
+
29
+ # Load a project from JSON string
30
+ def load_from_json(request, _unused_call)
31
+ raise ArgumentError, "JSON string cannot be empty" if request.json_string.nil? || request.json_string.strip.empty?
32
+
33
+ data = JSON.parse(request.json_string, symbolize_names: true)
34
+ project = convert_from_json_data(data)
35
+ project
36
+ rescue ArgumentError => e
37
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INVALID_ARGUMENT, e.message)
38
+ rescue StandardError => e
39
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INTERNAL, e.message)
40
+ end
41
+
42
+ # Save a project to a specific path as pretty JSON
43
+ def save_to_file(request, _unused_call)
44
+ validate_project_model(request.project)
45
+ raise ArgumentError, "Path cannot be empty" if request.path.nil? || request.path.strip.empty?
46
+
47
+ File.write(request.path, convert_to_json_pretty(request.project))
48
+ Google::Protobuf::Empty.new
49
+ rescue ArgumentError => e
50
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INVALID_ARGUMENT, e.message)
51
+ rescue StandardError => e
52
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INTERNAL, e.message)
53
+ end
54
+
55
+ # Save a project to the default .makit.json file
56
+ def save_to_default(request, _unused_call)
57
+ validate_project_model(request.project)
58
+ File.write(".makit.json", convert_to_json_pretty(request.project))
59
+ Google::Protobuf::Empty.new
60
+ rescue ArgumentError => e
61
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INVALID_ARGUMENT, e.message)
62
+ rescue StandardError => e
63
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INTERNAL, e.message)
64
+ end
65
+
66
+ # Load the default project from .makit.json or create a new one
67
+ def load_default(request, _unused_call)
68
+ if File.exist?(".makit.json")
69
+ load_from_file(LoadFromFileRequest.new(path: ".makit.json"), nil)
70
+ else
71
+ project = Project.new(
72
+ name: "",
73
+ version: "0.0.0",
74
+ project_type: "",
75
+ authors: "authors",
76
+ description: "description",
77
+ license_expression: "MIT",
78
+ steps: []
79
+ )
80
+ Makit::Logging.default_logger.warn("Project not configured")
81
+ project
82
+ end
83
+ rescue StandardError => e
84
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INTERNAL, e.message)
85
+ end
86
+
87
+ # Generate GitLab CI YAML from project
88
+ def generate_gitlab_ci(request, _unused_call)
89
+ validate_project_model(request.project)
90
+ raise ArgumentError, "Path cannot be empty" if request.path.nil? || request.path.strip.empty?
91
+
92
+ # Convert protobuf model to Ruby model for GitLab helper
93
+ ruby_model = convert_to_ruby_model(request.project)
94
+ Makit::Configuration::GitLabHelper.to_yaml(ruby_model, request.path)
95
+ Google::Protobuf::Empty.new
96
+ rescue ArgumentError => e
97
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INVALID_ARGUMENT, e.message)
98
+ rescue StandardError => e
99
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INTERNAL, e.message)
100
+ end
101
+
102
+ # Generate Rakefile from project
103
+ def generate_rakefile(request, _unused_call)
104
+ validate_project_model(request.project)
105
+
106
+ # Convert protobuf model to Ruby model for Rakefile helper
107
+ ruby_model = convert_to_ruby_model(request.project)
108
+ content = Makit::Configuration::RakefileHelper.generate(ruby_model)
109
+ GenerateRakefileResponse.new(content: content)
110
+ rescue ArgumentError => e
111
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INVALID_ARGUMENT, e.message)
112
+ rescue StandardError => e
113
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INTERNAL, e.message)
114
+ end
115
+
116
+ # Write Rakefile to file
117
+ def write_rakefile(request, _unused_call)
118
+ validate_project_model(request.project)
119
+ raise ArgumentError, "Path cannot be empty" if request.path.nil? || request.path.strip.empty?
120
+
121
+ # Convert protobuf model to Ruby model for Rakefile helper
122
+ ruby_model = convert_to_ruby_model(request.project)
123
+ Makit::Configuration::RakefileHelper.write(ruby_model, request.path)
124
+ Google::Protobuf::Empty.new
125
+ rescue ArgumentError => e
126
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INVALID_ARGUMENT, e.message)
127
+ rescue StandardError => e
128
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INTERNAL, e.message)
129
+ end
130
+
131
+ # Validate project configuration
132
+ def validate_project(request, _unused_call)
133
+ errors = validate_project_model_errors(request.project)
134
+ ValidateProjectResponse.new(errors: errors)
135
+ rescue StandardError => e
136
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INTERNAL, e.message)
137
+ end
138
+
139
+ # Merge two project configurations
140
+ def merge_projects(request, _unused_call)
141
+ merged = Project.new(
142
+ git_remote_url: request.override_project.git_remote_url || request.base_project.git_remote_url,
143
+ name: request.override_project.name || request.base_project.name,
144
+ version: request.override_project.version || request.base_project.version,
145
+ project_type: request.override_project.project_type || request.base_project.project_type,
146
+ authors: request.override_project.authors || request.base_project.authors,
147
+ description: request.override_project.description || request.base_project.description,
148
+ license_expression: request.override_project.license_expression || request.base_project.license_expression,
149
+ dotnet_projects: request.override_project.dotnet_projects.any? ? request.override_project.dotnet_projects.to_a : request.base_project.dotnet_projects.to_a,
150
+ steps: request.override_project.steps.any? ? request.override_project.steps.to_a : request.base_project.steps.to_a
151
+ )
152
+ merged
153
+ rescue StandardError => e
154
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INTERNAL, e.message)
155
+ end
156
+
157
+ # Create a new project with default values
158
+ def create_project(request, _unused_call)
159
+ raise ArgumentError, "Project name cannot be empty" if request.name.nil? || request.name.strip.empty?
160
+
161
+ project = Project.new(
162
+ name: request.name,
163
+ version: request.version || "0.0.0",
164
+ project_type: request.project_type || "gem",
165
+ authors: "authors",
166
+ description: "description",
167
+ license_expression: "MIT",
168
+ steps: []
169
+ )
170
+ project
171
+ rescue ArgumentError => e
172
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INVALID_ARGUMENT, e.message)
173
+ rescue StandardError => e
174
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INTERNAL, e.message)
175
+ end
176
+
177
+ # Update project with new values
178
+ def update_project(request, _unused_call)
179
+ updated_project = Project.new(
180
+ git_remote_url: request.project.git_remote_url,
181
+ name: request.project.name,
182
+ version: request.project.version,
183
+ project_type: request.project.project_type,
184
+ authors: request.project.authors,
185
+ description: request.project.description,
186
+ license_expression: request.project.license_expression,
187
+ dotnet_projects: request.project.dotnet_projects.to_a,
188
+ steps: request.project.steps.to_a
189
+ )
190
+
191
+ # Apply updates
192
+ request.updates.each do |key, value|
193
+ case key.to_sym
194
+ when :name
195
+ updated_project.name = value
196
+ when :version
197
+ updated_project.version = value
198
+ when :project_type
199
+ updated_project.project_type = value
200
+ when :authors
201
+ updated_project.authors = value
202
+ when :description
203
+ updated_project.description = value
204
+ when :license_expression
205
+ updated_project.license_expression = value
206
+ when :git_remote_url
207
+ updated_project.git_remote_url = value
208
+ end
209
+ end
210
+
211
+ updated_project
212
+ rescue StandardError => e
213
+ raise GRPC::BadStatus.new(GRPC::Core::StatusCodes::INTERNAL, e.message)
214
+ end
215
+
216
+ private
217
+
218
+ # Validate project model
219
+ def validate_project_model(project)
220
+ errors = validate_project_model_errors(project)
221
+ raise ArgumentError, errors.join(", ") if errors.any?
222
+ errors
223
+ end
224
+
225
+ # Validate project model and return errors without raising
226
+ def validate_project_model_errors(project)
227
+ errors = []
228
+
229
+ errors << "Project name cannot be empty" if project.name.nil? || project.name.strip.empty?
230
+ errors << "Project version cannot be empty" if project.version.nil? || project.version.strip.empty?
231
+ errors << "Project type cannot be empty" if project.project_type.nil? || project.project_type.strip.empty?
232
+
233
+ # Validate steps
234
+ project.steps.each_with_index do |step, index|
235
+ errors << "Step #{index} name cannot be empty" if step.name.nil? || step.name.strip.empty?
236
+ errors << "Step #{index} description cannot be empty" if step.description.nil? || step.description.strip.empty?
237
+ errors << "Step #{index} commands cannot be empty" if step.commands.nil? || step.commands.empty?
238
+ end
239
+
240
+ errors
241
+ end
242
+
243
+ # Convert JSON data to Project
244
+ def convert_from_json_data(data)
245
+ project = Project.new(
246
+ git_remote_url: data[:git_remote_url] || "",
247
+ name: data[:name] || "",
248
+ version: data[:version] || "",
249
+ project_type: data[:project_type] || "",
250
+ authors: data[:authors] || "authors",
251
+ description: data[:description] || "description",
252
+ license_expression: data[:license_expression] || "MIT",
253
+ dotnet_projects: data[:dotnet_projects] || []
254
+ )
255
+
256
+ # Convert steps
257
+ (data[:steps] || []).each do |step_data|
258
+ step = Step.new(
259
+ name: step_data[:name] || "",
260
+ description: step_data[:description] || "",
261
+ commands: step_data[:commands] || []
262
+ )
263
+ project.steps << step
264
+ end
265
+
266
+ project
267
+ end
268
+
269
+ # Convert Project to pretty JSON
270
+ def convert_to_json_pretty(project)
271
+ project_data = {
272
+ name: project.name,
273
+ version: project.version,
274
+ project_type: project.project_type,
275
+ authors: project.authors,
276
+ description: project.description,
277
+ license_expression: project.license_expression,
278
+ steps: project.steps.map do |step|
279
+ {
280
+ name: step.name,
281
+ description: step.description,
282
+ commands: step.commands.to_a
283
+ }
284
+ end
285
+ }
286
+
287
+ JSON.pretty_generate(project_data)
288
+ end
289
+
290
+ # Convert Protobuf Project to Ruby model for helper classes
291
+ def convert_to_ruby_model(proto_model)
292
+ # Create a simple Ruby object that mimics the Project interface
293
+ ruby_model = Object.new
294
+
295
+ def ruby_model.name
296
+ @name
297
+ end
298
+
299
+ def ruby_model.name=(value)
300
+ @name = value
301
+ end
302
+
303
+ def ruby_model.version
304
+ @version
305
+ end
306
+
307
+ def ruby_model.version=(value)
308
+ @version = value
309
+ end
310
+
311
+ def ruby_model.project_type
312
+ @project_type
313
+ end
314
+
315
+ def ruby_model.project_type=(value)
316
+ @project_type = value
317
+ end
318
+
319
+ def ruby_model.steps
320
+ @steps ||= []
321
+ end
322
+
323
+ def ruby_model.steps=(value)
324
+ @steps = value
325
+ end
326
+
327
+ ruby_model.name = proto_model.name
328
+ ruby_model.version = proto_model.version
329
+ ruby_model.project_type = proto_model.project_type
330
+
331
+ # Convert steps
332
+ ruby_steps = proto_model.steps.map do |proto_step|
333
+ ruby_step = Object.new
334
+
335
+ def ruby_step.name
336
+ @name
337
+ end
338
+
339
+ def ruby_step.name=(value)
340
+ @name = value
341
+ end
342
+
343
+ def ruby_step.description
344
+ @description
345
+ end
346
+
347
+ def ruby_step.description=(value)
348
+ @description = value
349
+ end
350
+
351
+ def ruby_step.commands
352
+ @commands ||= []
353
+ end
354
+
355
+ def ruby_step.commands=(value)
356
+ @commands = value
357
+ end
358
+
359
+ ruby_step.name = proto_step.name
360
+ ruby_step.description = proto_step.description
361
+ ruby_step.commands = proto_step.commands.to_a
362
+ ruby_step
363
+ end
364
+
365
+ ruby_model.steps = ruby_steps
366
+ ruby_model
367
+ end
368
+ end
369
+ end
370
+ end
371
+ end
@@ -0,0 +1,295 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module Makit
6
+ module V1
7
+ module Git
8
+ # Implementation of the GitRepositoryService gRPC service
9
+ class GitRepositoryServiceImpl < GitRepositoryService::Service
10
+ # Get current repository state
11
+ def get_repository_state(request, _unused_call)
12
+ # Get the current repository state using direct git commands
13
+ model = GitRepositoryModel.new
14
+
15
+ # Basic repository checks
16
+ model.is_git_repo = Dir.exist?(".git")
17
+ model.is_ci = ENV["CI"] == "true"
18
+ model.is_detached = `git status`.include?("detached")
19
+ model.is_read_only = !model.is_git_repo || model.is_detached
20
+ model.is_clean = `git status --porcelain`.empty?
21
+
22
+ # File information
23
+ model.unstaged_files.concat(`git status --porcelain`.split("\n"))
24
+ model.untracked_files.concat(`git ls-files --others --exclude-standard`.split("\n"))
25
+ model.tracked_file_infos.concat(get_file_infos_proto)
26
+ model.untracked_file_infos.concat(get_untracked_file_infos_proto)
27
+
28
+ # Commit information
29
+ model.branch = `git branch --show-current`.strip
30
+ model.commit_sha = `git rev-parse HEAD`.strip
31
+ model.commit_message = `git log -1 --pretty=%B`.strip
32
+ model.commit_date = `git log -1 --pretty=%cd`.strip
33
+ model.commit_author = `git log -1 --pretty=%an`.strip
34
+ model.commit_email = `git log -1 --pretty=%ae`.strip
35
+ model.remote_url = `git remote get-url origin`.strip
36
+
37
+ model
38
+ end
39
+
40
+ # Save repository state to file
41
+ def save_repository_state(request, _unused_call)
42
+ raise ArgumentError, "Model cannot be nil" if request.repository.nil?
43
+ raise ArgumentError, "Path cannot be empty" if request.path.nil? || request.path.strip.empty?
44
+
45
+ # Convert protobuf model to JSON and save
46
+ json_data = convert_proto_to_json(request.repository)
47
+ File.write(request.path, JSON.pretty_generate(json_data))
48
+
49
+ Google::Protobuf::Empty.new
50
+ end
51
+
52
+ # Load repository state from file
53
+ def load_repository_state(request, _unused_call)
54
+ raise ArgumentError, "File does not exist: #{request.path}" unless File.exist?(request.path)
55
+
56
+ content = File.read(request.path)
57
+ json_data = JSON.parse(content, symbolize_names: true)
58
+ convert_json_to_proto(json_data)
59
+ end
60
+
61
+ # Validate repository state
62
+ def validate_repository_state(request, _unused_call)
63
+ errors = []
64
+
65
+ if request.repository.nil?
66
+ errors << "Model cannot be nil"
67
+ else
68
+ # Basic validation
69
+ if request.repository.is_git_repo
70
+ errors << "Branch cannot be empty for git repository" if request.repository.branch.nil? || request.repository.branch.strip.empty?
71
+ errors << "Commit SHA cannot be empty for git repository" if request.repository.commit_sha.nil? || request.repository.commit_sha.strip.empty?
72
+ end
73
+ end
74
+
75
+ ValidateRepositoryStateResponse.new(errors: errors)
76
+ end
77
+
78
+ # Merge two repository states
79
+ def merge_repository_states(request, _unused_call)
80
+ base = request.base_repository
81
+ override = request.override_repository
82
+
83
+ merged = GitRepositoryModel.new
84
+
85
+ # Use override values if present and not empty, otherwise use base values
86
+ merged.is_git_repo = override.is_git_repo || base.is_git_repo
87
+ merged.is_ci = override.is_ci || base.is_ci
88
+ merged.is_detached = override.is_detached || base.is_detached
89
+ merged.is_read_only = override.is_read_only || base.is_read_only
90
+ merged.is_clean = override.is_clean || base.is_clean
91
+ merged.unstaged_files.concat(override.unstaged_files.any? ? override.unstaged_files.to_a : base.unstaged_files.to_a)
92
+ merged.untracked_files.concat(override.untracked_files.any? ? override.untracked_files.to_a : base.untracked_files.to_a)
93
+ merged.tracked_file_infos.concat(override.tracked_file_infos.any? ? override.tracked_file_infos.to_a : base.tracked_file_infos.to_a)
94
+ merged.untracked_file_infos.concat(override.untracked_file_infos.any? ? override.untracked_file_infos.to_a : base.untracked_file_infos.to_a)
95
+ merged.branch = (override.branch && !override.branch.empty?) ? override.branch : base.branch
96
+ merged.commit_sha = (override.commit_sha && !override.commit_sha.empty?) ? override.commit_sha : base.commit_sha
97
+ merged.commit_message = (override.commit_message && !override.commit_message.empty?) ? override.commit_message : base.commit_message
98
+ merged.commit_date = (override.commit_date && !override.commit_date.empty?) ? override.commit_date : base.commit_date
99
+ merged.commit_author = (override.commit_author && !override.commit_author.empty?) ? override.commit_author : base.commit_author
100
+ merged.commit_email = (override.commit_email && !override.commit_email.empty?) ? override.commit_email : base.commit_email
101
+ merged.remote_url = (override.remote_url && !override.remote_url.empty?) ? override.remote_url : base.remote_url
102
+
103
+ merged
104
+ end
105
+
106
+ # Create a new repository state
107
+ def create_repository_state(request, _unused_call)
108
+ model = GitRepositoryModel.new
109
+
110
+ model.is_git_repo = request.is_git_repo
111
+ model.is_ci = request.is_ci
112
+ model.is_detached = request.is_detached
113
+ model.is_read_only = request.is_read_only
114
+ model.is_clean = request.is_clean
115
+ model.branch = request.branch
116
+ model.commit_sha = request.commit_sha
117
+ model.commit_message = request.commit_message
118
+ model.commit_date = request.commit_date
119
+ model.commit_author = request.commit_author
120
+ model.commit_email = request.commit_email
121
+ model.remote_url = request.remote_url
122
+
123
+ model
124
+ end
125
+
126
+ # Update repository state
127
+ def update_repository_state(request, _unused_call)
128
+ raise ArgumentError, "Model cannot be nil" if request.repository.nil?
129
+ raise ArgumentError, "Updates cannot be nil" if request.updates.nil?
130
+
131
+ model = GitRepositoryModel.new
132
+ model.is_git_repo = request.repository.is_git_repo
133
+ model.is_ci = request.repository.is_ci
134
+ model.is_detached = request.repository.is_detached
135
+ model.is_read_only = request.repository.is_read_only
136
+ model.is_clean = request.repository.is_clean
137
+ model.unstaged_files.concat(request.repository.unstaged_files.to_a)
138
+ model.untracked_files.concat(request.repository.untracked_files.to_a)
139
+ model.tracked_file_infos.concat(request.repository.tracked_file_infos.to_a)
140
+ model.untracked_file_infos.concat(request.repository.untracked_file_infos.to_a)
141
+ model.branch = request.repository.branch
142
+ model.commit_sha = request.repository.commit_sha
143
+ model.commit_message = request.repository.commit_message
144
+ model.commit_date = request.repository.commit_date
145
+ model.commit_author = request.repository.commit_author
146
+ model.commit_email = request.repository.commit_email
147
+ model.remote_url = request.repository.remote_url
148
+
149
+ # Apply updates
150
+ request.updates.each do |key, value|
151
+ case key.to_sym
152
+ when :is_git_repo
153
+ model.is_git_repo = value == "true"
154
+ when :is_ci
155
+ model.is_ci = value == "true"
156
+ when :is_detached
157
+ model.is_detached = value == "true"
158
+ when :is_read_only
159
+ model.is_read_only = value == "true"
160
+ when :is_clean
161
+ model.is_clean = value == "true"
162
+ when :branch
163
+ model.branch = value
164
+ when :commit_sha
165
+ model.commit_sha = value
166
+ when :commit_message
167
+ model.commit_message = value
168
+ when :commit_date
169
+ model.commit_date = value
170
+ when :commit_author
171
+ model.commit_author = value
172
+ when :commit_email
173
+ model.commit_email = value
174
+ when :remote_url
175
+ model.remote_url = value
176
+ end
177
+ end
178
+
179
+ model
180
+ end
181
+
182
+ private
183
+
184
+ # Get file information for tracked files
185
+ def get_file_infos_proto
186
+ file_infos = []
187
+ file_list = `git ls-files`.split("\n")
188
+ file_list.each do |file|
189
+ file_infos << convert_file_info_to_proto(file)
190
+ rescue StandardError
191
+ next
192
+ end
193
+ file_infos.sort_by { |info| info.mtime.seconds }.reverse!
194
+ file_infos
195
+ end
196
+
197
+ # Get file information for untracked files
198
+ def get_untracked_file_infos_proto
199
+ file_infos = []
200
+ file_list = `git ls-files --others --exclude-standard`.split("\n")
201
+ file_list.each do |file|
202
+ file_infos << convert_file_info_to_proto(file)
203
+ rescue StandardError
204
+ next
205
+ end
206
+ file_infos.sort_by { |info| info.mtime.seconds }.reverse!
207
+ file_infos
208
+ end
209
+
210
+ # Convert file path to protobuf FileInfo
211
+ def convert_file_info_to_proto(file_path)
212
+ timestamp = Google::Protobuf::Timestamp.new
213
+ mtime = File.mtime(file_path)
214
+ timestamp.seconds = mtime.to_i
215
+ timestamp.nanos = mtime.nsec
216
+
217
+ FileInfo.new(
218
+ name: file_path,
219
+ mtime: timestamp,
220
+ size: File.size(file_path)
221
+ )
222
+ end
223
+
224
+ # Convert protobuf model to JSON hash
225
+ def convert_proto_to_json(proto_model)
226
+ {
227
+ is_git_repo: proto_model.is_git_repo,
228
+ is_ci: proto_model.is_ci,
229
+ is_detached: proto_model.is_detached,
230
+ is_read_only: proto_model.is_read_only,
231
+ is_clean: proto_model.is_clean,
232
+ unstaged_files: proto_model.unstaged_files.to_a,
233
+ untracked_files: proto_model.untracked_files.to_a,
234
+ tracked_file_infos: proto_model.tracked_file_infos.map { |info| convert_file_info_to_json(info) },
235
+ untracked_file_infos: proto_model.untracked_file_infos.map { |info| convert_file_info_to_json(info) },
236
+ branch: proto_model.branch,
237
+ commit_sha: proto_model.commit_sha,
238
+ commit_message: proto_model.commit_message,
239
+ commit_date: proto_model.commit_date,
240
+ commit_author: proto_model.commit_author,
241
+ commit_email: proto_model.commit_email,
242
+ remote_url: proto_model.remote_url
243
+ }
244
+ end
245
+
246
+ # Convert JSON hash to protobuf model
247
+ def convert_json_to_proto(json_data)
248
+ model = GitRepositoryModel.new
249
+
250
+ model.is_git_repo = json_data[:is_git_repo] || false
251
+ model.is_ci = json_data[:is_ci] || false
252
+ model.is_detached = json_data[:is_detached] || false
253
+ model.is_read_only = json_data[:is_read_only] || false
254
+ model.is_clean = json_data[:is_clean] || false
255
+ model.unstaged_files.concat(json_data[:unstaged_files] || [])
256
+ model.untracked_files.concat(json_data[:untracked_files] || [])
257
+ model.tracked_file_infos.concat((json_data[:tracked_file_infos] || []).map { |info| convert_file_info_from_json(info) })
258
+ model.untracked_file_infos.concat((json_data[:untracked_file_infos] || []).map { |info| convert_file_info_from_json(info) })
259
+ model.branch = json_data[:branch] || ""
260
+ model.commit_sha = json_data[:commit_sha] || ""
261
+ model.commit_message = json_data[:commit_message] || ""
262
+ model.commit_date = json_data[:commit_date] || ""
263
+ model.commit_author = json_data[:commit_author] || ""
264
+ model.commit_email = json_data[:commit_email] || ""
265
+ model.remote_url = json_data[:remote_url] || ""
266
+
267
+ model
268
+ end
269
+
270
+ # Convert protobuf FileInfo to JSON hash
271
+ def convert_file_info_to_json(proto_file_info)
272
+ {
273
+ name: proto_file_info.name,
274
+ mtime: Time.at(proto_file_info.mtime.seconds, proto_file_info.mtime.nanos, :nsec).to_s,
275
+ size: proto_file_info.size
276
+ }
277
+ end
278
+
279
+ # Convert JSON hash to protobuf FileInfo
280
+ def convert_file_info_from_json(json_file_info)
281
+ timestamp = Google::Protobuf::Timestamp.new
282
+ mtime = Time.parse(json_file_info[:mtime])
283
+ timestamp.seconds = mtime.to_i
284
+ timestamp.nanos = mtime.nsec
285
+
286
+ FileInfo.new(
287
+ name: json_file_info[:name],
288
+ mtime: timestamp,
289
+ size: json_file_info[:size]
290
+ )
291
+ end
292
+ end
293
+ end
294
+ end
295
+ end