makit 0.0.158 → 0.0.162

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 (180) 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/azure/blob_storage.rb +257 -257
  7. data/lib/makit/azure/cli.rb +284 -284
  8. data/lib/makit/cli/base.rb +17 -17
  9. data/lib/makit/cli/build_commands.rb +500 -500
  10. data/lib/makit/cli/generators/base_generator.rb +74 -74
  11. data/lib/makit/cli/generators/dotnet_generator.rb +50 -50
  12. data/lib/makit/cli/generators/generator_factory.rb +49 -49
  13. data/lib/makit/cli/generators/node_generator.rb +50 -50
  14. data/lib/makit/cli/generators/ruby_generator.rb +77 -77
  15. data/lib/makit/cli/generators/rust_generator.rb +50 -50
  16. data/lib/makit/cli/generators/templates/dotnet_templates.rb +167 -167
  17. data/lib/makit/cli/generators/templates/node_templates.rb +161 -161
  18. data/lib/makit/cli/generators/templates/ruby/gemfile.rb +26 -26
  19. data/lib/makit/cli/generators/templates/ruby/gemspec.rb +41 -41
  20. data/lib/makit/cli/generators/templates/ruby/main_lib.rb +33 -33
  21. data/lib/makit/cli/generators/templates/ruby/rakefile.rb +35 -35
  22. data/lib/makit/cli/generators/templates/ruby/readme.rb +63 -63
  23. data/lib/makit/cli/generators/templates/ruby/test.rb +39 -39
  24. data/lib/makit/cli/generators/templates/ruby/test_helper.rb +29 -29
  25. data/lib/makit/cli/generators/templates/ruby/version.rb +29 -29
  26. data/lib/makit/cli/generators/templates/rust_templates.rb +128 -128
  27. data/lib/makit/cli/main.rb +78 -78
  28. data/lib/makit/cli/pipeline_commands.rb +311 -311
  29. data/lib/makit/cli/project_commands.rb +868 -868
  30. data/lib/makit/cli/repository_commands.rb +661 -661
  31. data/lib/makit/cli/strategy_commands.rb +207 -207
  32. data/lib/makit/cli/utility_commands.rb +521 -521
  33. data/lib/makit/commands/factory.rb +359 -359
  34. data/lib/makit/commands/middleware/base.rb +73 -73
  35. data/lib/makit/commands/middleware/cache.rb +248 -248
  36. data/lib/makit/commands/middleware/command_logger.rb +312 -312
  37. data/lib/makit/commands/middleware/validator.rb +269 -269
  38. data/lib/makit/commands/request.rb +316 -316
  39. data/lib/makit/commands/result.rb +323 -323
  40. data/lib/makit/commands/runner.rb +386 -386
  41. data/lib/makit/commands/strategies/base.rb +171 -171
  42. data/lib/makit/commands/strategies/child_process.rb +162 -162
  43. data/lib/makit/commands/strategies/factory.rb +136 -136
  44. data/lib/makit/commands/strategies/synchronous.rb +139 -139
  45. data/lib/makit/commands.rb +50 -50
  46. data/lib/makit/configuration/dotnet_project.rb +48 -48
  47. data/lib/makit/configuration/gitlab_helper.rb +61 -61
  48. data/lib/makit/configuration/project.rb +292 -292
  49. data/lib/makit/configuration/rakefile_helper.rb +43 -43
  50. data/lib/makit/configuration/step.rb +34 -34
  51. data/lib/makit/configuration/timeout.rb +74 -74
  52. data/lib/makit/configuration.rb +21 -21
  53. data/lib/makit/content/default_gitignore.rb +7 -7
  54. data/lib/makit/content/default_gitignore.txt +225 -225
  55. data/lib/makit/content/default_rakefile.rb +13 -13
  56. data/lib/makit/content/gem_rakefile.rb +16 -16
  57. data/lib/makit/context.rb +1 -1
  58. data/lib/makit/data.rb +49 -49
  59. data/lib/makit/directories.rb +170 -170
  60. data/lib/makit/directory.rb +262 -262
  61. data/lib/makit/docs/files.rb +89 -89
  62. data/lib/makit/docs/rake.rb +102 -102
  63. data/lib/makit/dotnet/cli.rb +69 -69
  64. data/lib/makit/dotnet/project.rb +217 -217
  65. data/lib/makit/dotnet/solution.rb +38 -38
  66. data/lib/makit/dotnet/solution_classlib.rb +239 -239
  67. data/lib/makit/dotnet/solution_console.rb +264 -264
  68. data/lib/makit/dotnet/solution_maui.rb +354 -354
  69. data/lib/makit/dotnet/solution_wasm.rb +275 -275
  70. data/lib/makit/dotnet/solution_wpf.rb +304 -304
  71. data/lib/makit/dotnet.rb +102 -102
  72. data/lib/makit/email.rb +90 -90
  73. data/lib/makit/environment.rb +142 -142
  74. data/lib/makit/examples/runner.rb +370 -370
  75. data/lib/makit/exceptions.rb +45 -45
  76. data/lib/makit/fileinfo.rb +32 -32
  77. data/lib/makit/files.rb +43 -43
  78. data/lib/makit/gems.rb +49 -40
  79. data/lib/makit/git/cli.rb +103 -78
  80. data/lib/makit/git/repository.rb +100 -100
  81. data/lib/makit/git.rb +104 -104
  82. data/lib/makit/gitlab/pipeline.rb +857 -857
  83. data/lib/makit/gitlab/pipeline_service_impl.rb +1535 -1535
  84. data/lib/makit/gitlab_runner.rb +59 -59
  85. data/lib/makit/humanize.rb +218 -218
  86. data/lib/makit/indexer.rb +47 -47
  87. data/lib/makit/io/filesystem.rb +111 -111
  88. data/lib/makit/io/filesystem_service_impl.rb +337 -337
  89. data/lib/makit/lint.rb +212 -212
  90. data/lib/makit/logging/configuration.rb +309 -309
  91. data/lib/makit/logging/format_registry.rb +84 -84
  92. data/lib/makit/logging/formatters/base.rb +39 -39
  93. data/lib/makit/logging/formatters/console_formatter.rb +140 -140
  94. data/lib/makit/logging/formatters/json_formatter.rb +65 -65
  95. data/lib/makit/logging/formatters/plain_text_formatter.rb +71 -71
  96. data/lib/makit/logging/formatters/text_formatter.rb +64 -64
  97. data/lib/makit/logging/log_request.rb +119 -119
  98. data/lib/makit/logging/logger.rb +199 -199
  99. data/lib/makit/logging/sinks/base.rb +91 -91
  100. data/lib/makit/logging/sinks/console.rb +72 -72
  101. data/lib/makit/logging/sinks/file_sink.rb +92 -92
  102. data/lib/makit/logging/sinks/structured.rb +123 -123
  103. data/lib/makit/logging/sinks/unified_file_sink.rb +296 -296
  104. data/lib/makit/logging.rb +578 -578
  105. data/lib/makit/markdown.rb +75 -75
  106. data/lib/makit/mp/basic_object_mp.rb +17 -17
  107. data/lib/makit/mp/command_mp.rb +13 -13
  108. data/lib/makit/mp/command_request.mp.rb +17 -17
  109. data/lib/makit/mp/project_mp.rb +199 -199
  110. data/lib/makit/mp/string_mp.rb +205 -205
  111. data/lib/makit/nuget.rb +243 -74
  112. data/lib/makit/nuget_cache.rb +123 -0
  113. data/lib/makit/podman/podman.rb +458 -458
  114. data/lib/makit/podman/podman_service_impl.rb +1081 -1081
  115. data/lib/makit/port.rb +32 -32
  116. data/lib/makit/port_utility.rb +128 -0
  117. data/lib/makit/process.rb +377 -377
  118. data/lib/makit/protoc.rb +112 -112
  119. data/lib/makit/rake/cli.rb +196 -196
  120. data/lib/makit/rake/trace_controller.rb +174 -174
  121. data/lib/makit/rake.rb +81 -81
  122. data/lib/makit/ruby/cli.rb +185 -185
  123. data/lib/makit/ruby.rb +25 -25
  124. data/lib/makit/rubygems.rb +137 -137
  125. data/lib/makit/secrets/azure_key_vault.rb +322 -322
  126. data/lib/makit/secrets/azure_secrets.rb +183 -183
  127. data/lib/makit/secrets/local_secrets.rb +72 -72
  128. data/lib/makit/secrets/secrets_manager.rb +105 -105
  129. data/lib/makit/secrets.rb +16 -16
  130. data/lib/makit/serializer.rb +130 -130
  131. data/lib/makit/services/builder.rb +186 -186
  132. data/lib/makit/services/error_handler.rb +226 -226
  133. data/lib/makit/services/repository_manager.rb +367 -367
  134. data/lib/makit/services/validator.rb +112 -112
  135. data/lib/makit/setup/classlib.rb +101 -101
  136. data/lib/makit/setup/gem.rb +268 -268
  137. data/lib/makit/setup/pages.rb +11 -11
  138. data/lib/makit/setup/razorclasslib.rb +101 -101
  139. data/lib/makit/setup/runner.rb +54 -54
  140. data/lib/makit/setup.rb +5 -5
  141. data/lib/makit/show.rb +110 -110
  142. data/lib/makit/storage.rb +126 -126
  143. data/lib/makit/symbols.rb +175 -175
  144. data/lib/makit/task_info.rb +130 -130
  145. data/lib/makit/tasks/at_exit.rb +15 -15
  146. data/lib/makit/tasks/build.rb +22 -22
  147. data/lib/makit/tasks/bump.rb +7 -7
  148. data/lib/makit/tasks/clean.rb +13 -13
  149. data/lib/makit/tasks/configure.rb +10 -10
  150. data/lib/makit/tasks/format.rb +10 -10
  151. data/lib/makit/tasks/hook_manager.rb +443 -443
  152. data/lib/makit/tasks/info.rb +368 -368
  153. data/lib/makit/tasks/init.rb +49 -49
  154. data/lib/makit/tasks/integrate.rb +60 -60
  155. data/lib/makit/tasks/pull_incoming.rb +13 -13
  156. data/lib/makit/tasks/secrets.rb +7 -7
  157. data/lib/makit/tasks/setup.rb +16 -16
  158. data/lib/makit/tasks/sync.rb +14 -14
  159. data/lib/makit/tasks/tag.rb +27 -27
  160. data/lib/makit/tasks/task_monkey_patch.rb +81 -81
  161. data/lib/makit/tasks/test.rb +22 -22
  162. data/lib/makit/tasks/update.rb +21 -18
  163. data/lib/makit/tasks/version.rb +6 -6
  164. data/lib/makit/tasks.rb +24 -24
  165. data/lib/makit/test_cache.rb +239 -239
  166. data/lib/makit/tree.rb +37 -37
  167. data/lib/makit/v1/configuration/project_service_impl.rb +370 -370
  168. data/lib/makit/v1/git/git_repository_service_impl.rb +295 -295
  169. data/lib/makit/v1/makit.v1_pb.rb +35 -35
  170. data/lib/makit/v1/makit.v1_services_pb.rb +27 -27
  171. data/lib/makit/v1/services/repository_manager_service_impl.rb +572 -572
  172. data/lib/makit/version.rb +661 -661
  173. data/lib/makit/version_util.rb +21 -21
  174. data/lib/makit/wix.rb +95 -95
  175. data/lib/makit/yaml.rb +29 -29
  176. data/lib/makit/zip.rb +17 -17
  177. data/lib/makit/zip_utility.rb +97 -0
  178. data/lib/makit copy.rb +44 -44
  179. data/lib/makit.rb +119 -115
  180. metadata +5 -2
@@ -1,295 +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
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