makit 0.0.147 → 0.0.153

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generated/makit/v1/configuration/project_pb.rb +22 -0
  3. data/lib/generated/makit/v1/configuration/project_service_pb.rb +34 -0
  4. data/lib/generated/makit/v1/configuration/project_service_services_pb.rb +51 -0
  5. data/lib/generated/makit/v1/git/git_repository_model_pb.rb +22 -0
  6. data/lib/generated/makit/v1/git/git_repository_service_pb.rb +29 -0
  7. data/lib/generated/makit/v1/git/git_repository_service_services_pb.rb +39 -0
  8. data/lib/generated/makit/v1/gitlab/pipeline_pb.rb +26 -0
  9. data/lib/generated/makit/v1/gitlab/pipeline_result_pb.rb +29 -0
  10. data/lib/generated/makit/v1/gitlab/pipeline_service_pb.rb +36 -0
  11. data/lib/generated/makit/v1/gitlab/pipeline_service_services_pb.rb +41 -0
  12. data/lib/generated/makit/v1/grpc/service_specification_pb.rb +27 -0
  13. data/lib/generated/makit/v1/grpc/test_specification_pb.rb +29 -0
  14. data/lib/generated/makit/v1/io/filesystem_pb.rb +27 -0
  15. data/lib/generated/makit/v1/io/filesystem_services_pb.rb +47 -0
  16. data/lib/generated/makit/v1/makit.v1_pb.rb +35 -0
  17. data/lib/generated/makit/v1/makit.v1_services_pb.rb +26 -0
  18. data/lib/generated/makit/v1/podman/podman_service_pb.rb +64 -0
  19. data/lib/generated/makit/v1/podman/podman_service_services_pb.rb +52 -0
  20. data/lib/generated/makit/v1/services/repository_manager_model_pb.rb +23 -0
  21. data/lib/generated/makit/v1/services/repository_manager_service_pb.rb +32 -0
  22. data/lib/generated/makit/v1/services/repository_manager_service_services_pb.rb +35 -0
  23. data/lib/generated/makit/v1/spec/message_proto_generator_pb.rb +33 -0
  24. data/lib/generated/makit/v1/spec/message_proto_generator_services_pb.rb +38 -0
  25. data/lib/generated/makit/v1/spec/message_spec_pb.rb +31 -0
  26. data/lib/generated/makit/v1/spec/message_spec_suite_pb.rb +30 -0
  27. data/lib/generated/makit/v1/spec/message_spec_test_pb.rb +34 -0
  28. data/lib/generated/makit/v1/spec/proto_service_pb.rb +53 -0
  29. data/lib/generated/makit/v1/spec/proto_service_services_pb.rb +42 -0
  30. data/lib/generated/makit/v1/spec/spec_manifest_pb.rb +44 -0
  31. data/lib/generated/makit/v1/web/link_pb.rb +20 -0
  32. data/lib/makit/azure/blob_storage.rb +257 -0
  33. data/lib/makit/azure/cli.rb +285 -0
  34. data/lib/makit/configuration/project.rb +137 -291
  35. data/lib/makit/git/repository.rb +24 -190
  36. data/lib/makit/gitlab/pipeline.rb +16 -16
  37. data/lib/makit/gitlab/pipeline_service_impl.rb +43 -43
  38. data/lib/makit/io/filesystem_service_impl.rb +6 -6
  39. data/lib/makit/lint.rb +212 -0
  40. data/lib/makit/logging/configuration.rb +2 -1
  41. data/lib/makit/logging.rb +15 -2
  42. data/lib/makit/podman/podman.rb +20 -20
  43. data/lib/makit/podman/podman_service_impl.rb +41 -41
  44. data/lib/makit/secrets/azure_key_vault.rb +323 -0
  45. data/lib/makit/secrets/azure_secrets.rb +183 -0
  46. data/lib/makit/secrets/local_secrets.rb +72 -0
  47. data/lib/makit/secrets/secrets_manager.rb +105 -0
  48. data/lib/makit/secrets.rb +10 -45
  49. data/lib/makit/tasks/bump.rb +7 -0
  50. data/lib/makit/tasks/info.rb +204 -0
  51. data/lib/makit/tasks/integrate.rb +28 -1
  52. data/lib/makit/tasks/secrets.rb +7 -0
  53. data/lib/makit/tasks/version.rb +6 -0
  54. data/lib/makit/tasks.rb +4 -0
  55. data/lib/makit/v1/configuration/project_service_impl.rb +1 -1
  56. data/lib/makit/version.rb +382 -1
  57. data/lib/makit.rb +21 -18
  58. metadata +46 -5
@@ -2,25 +2,18 @@
2
2
 
3
3
  require "json"
4
4
 
5
- # Only load gRPC service implementation if generated files exist
6
- if File.exist?(File.join(__dir__, "..", "..", "generated", "makit", "v1", "configuration", "project_service_services_pb.rb"))
7
- require_relative "../v1/configuration/project_service_impl"
8
- end
5
+ # Load gRPC service implementation
6
+ require_relative "../v1/configuration/project_service_impl"
9
7
 
10
8
  module Makit
11
9
  module Configuration
12
- # Project configuration management - Phase 3: Routes calls to gRPC service
10
+ # Project configuration management - Refactored to use protobuf messages exclusively
13
11
  class Project
14
12
  attr_accessor :git_remote_url, :name, :version, :project_type, :steps, :authors, :description, :license_expression
15
13
  attr_accessor :dotnet_projects
16
14
 
17
15
  def initialize(name, version = nil, project_type = nil)
18
- # Check if gRPC service is available
19
- @grpc_available = defined?(Makit::V1::Configuration::ProjectServiceImpl)
20
-
21
- if @grpc_available
22
- @grpc_service = Makit::V1::Configuration::ProjectServiceImpl.new
23
- end
16
+ @grpc_service = Makit::V1::Configuration::ProjectServiceImpl.new
24
17
 
25
18
  # Support both keyword arguments and positional arguments for compatibility
26
19
  if name.is_a?(Hash)
@@ -35,236 +28,119 @@ module Makit
35
28
  @project_type = project_type
36
29
  end
37
30
 
38
- if @grpc_available
39
- # Create project via gRPC service (handle empty names)
40
- if @name && !@name.strip.empty?
41
- create_request = Makit::V1::Configuration::CreateProjectRequest.new(
42
- name: @name,
43
- version: @version || "0.0.0",
44
- project_type: @project_type || "gem"
45
- )
46
- @proto_model = @grpc_service.create_project(create_request, nil)
47
- else
48
- # Create empty project directly (bypass gRPC validation for empty names)
49
- @proto_model = Makit::V1::Configuration::Project.new(
50
- name: @name || "",
51
- version: @version || "0.0.0",
52
- project_type: @project_type || "",
53
- authors: "authors",
54
- description: "description",
55
- license_expression: "MIT",
56
- steps: []
57
- )
58
- end
59
-
60
- # Initialize Ruby attributes for backward compatibility
61
- @authors = @proto_model.authors
62
- @description = @proto_model.description
63
- @license_expression = @proto_model.license_expression
64
- @steps = convert_steps_from_proto(@proto_model.steps)
65
- @git_remote_url = @proto_model.git_remote_url
66
- @dotnet_projects = @proto_model.dotnet_projects.to_a
31
+ # Create project via gRPC service (handle empty names)
32
+ if @name && !@name.strip.empty?
33
+ create_request = Makit::V1::Configuration::CreateProjectRequest.new(
34
+ name: @name,
35
+ version: @version || "0.0.0",
36
+ project_type: @project_type || "gem"
37
+ )
38
+ @proto_model = @grpc_service.create_project(create_request, nil)
67
39
  else
68
- # Fallback implementation when gRPC is not available
69
- @name = @name || ""
70
- @version = @version || "0.0.0"
71
- @project_type = @project_type || ""
72
- @authors = "authors"
73
- @description = "description"
74
- @license_expression = "MIT"
75
- @steps = []
76
- @git_remote_url = ""
77
- @dotnet_projects = []
40
+ # Create empty project directly (bypass gRPC validation for empty names)
41
+ @proto_model = Makit::V1::Configuration::Project.new(
42
+ name: @name || "",
43
+ version: @version || "0.0.0",
44
+ project_type: @project_type || "",
45
+ authors: "authors",
46
+ description: "description",
47
+ license_expression: "MIT",
48
+ steps: []
49
+ )
78
50
  end
51
+
52
+ # Initialize Ruby attributes for backward compatibility
53
+ sync_attributes_from_proto
79
54
  end
80
55
 
81
56
  def add_step(step)
82
57
  raise ArgumentError, "Step must be a Makit::Configuration::Step instance" unless step.is_a?(Step)
83
58
 
84
- if @grpc_available
85
- # Add step to Protobuf model
86
- proto_step = Makit::V1::Configuration::Step.new(
87
- name: step.name,
88
- description: step.description,
89
- commands: step.commands
90
- )
91
- @proto_model.steps << proto_step
92
- end
59
+ # Add step to Protobuf model
60
+ proto_step = Makit::V1::Configuration::Step.new(
61
+ name: step.name,
62
+ description: step.description,
63
+ commands: step.commands
64
+ )
65
+ @proto_model.steps << proto_step
93
66
 
94
67
  # Update Ruby attribute for backward compatibility
95
68
  @steps << step
96
69
  end
97
70
 
98
71
  def to_json(*args)
99
- if @grpc_available
100
- # Use gRPC service to convert to JSON
101
- project_data = {
102
- name: @proto_model.name,
103
- version: @proto_model.version,
104
- project_type: @proto_model.project_type,
105
- authors: @proto_model.authors,
106
- description: @proto_model.description,
107
- license_expression: @proto_model.license_expression,
108
- steps: @proto_model.steps.map do |step|
109
- {
110
- name: step.name,
111
- description: step.description,
112
- commands: step.commands.to_a
113
- }
114
- end
115
- }
116
- else
117
- # Fallback implementation
118
- project_data = {
119
- name: @name,
120
- version: @version,
121
- project_type: @project_type,
122
- authors: @authors,
123
- description: @description,
124
- license_expression: @license_expression,
125
- steps: @steps.map do |step|
126
- {
127
- name: step.name,
128
- description: step.description,
129
- commands: step.commands
130
- }
131
- end
132
- }
133
- end
72
+ project_data = {
73
+ name: @proto_model.name,
74
+ version: @proto_model.version,
75
+ project_type: @proto_model.project_type,
76
+ authors: @proto_model.authors,
77
+ description: @proto_model.description,
78
+ license_expression: @proto_model.license_expression,
79
+ steps: @proto_model.steps.map do |step|
80
+ {
81
+ name: step.name,
82
+ description: step.description,
83
+ commands: step.commands.to_a
84
+ }
85
+ end
86
+ }
134
87
  project_data.to_json(*args)
135
88
  end
136
89
 
137
90
  def to_json_pretty
138
- if @grpc_available
139
- # Use gRPC service to convert to pretty JSON
140
- project_data = {
141
- name: @proto_model.name,
142
- version: @proto_model.version,
143
- project_type: @proto_model.project_type,
144
- authors: @proto_model.authors,
145
- description: @proto_model.description,
146
- license_expression: @proto_model.license_expression,
147
- steps: @proto_model.steps.map do |step|
148
- {
149
- name: step.name,
150
- description: step.description,
151
- commands: step.commands.to_a
152
- }
153
- end
154
- }
155
- else
156
- # Fallback implementation
157
- project_data = {
158
- name: @name,
159
- version: @version,
160
- project_type: @project_type,
161
- authors: @authors,
162
- description: @description,
163
- license_expression: @license_expression,
164
- steps: @steps.map do |step|
165
- {
166
- name: step.name,
167
- description: step.description,
168
- commands: step.commands
169
- }
170
- end
171
- }
172
- end
91
+ project_data = {
92
+ name: @proto_model.name,
93
+ version: @proto_model.version,
94
+ project_type: @proto_model.project_type,
95
+ authors: @proto_model.authors,
96
+ description: @proto_model.description,
97
+ license_expression: @proto_model.license_expression,
98
+ steps: @proto_model.steps.map do |step|
99
+ {
100
+ name: step.name,
101
+ description: step.description,
102
+ commands: step.commands.to_a
103
+ }
104
+ end
105
+ }
173
106
  JSON.pretty_generate(project_data)
174
107
  end
175
108
 
176
109
  def self.from_json(path)
177
- if defined?(Makit::V1::Configuration::ProjectServiceImpl)
178
- # Use gRPC service to load from file
179
- grpc_service = Makit::V1::Configuration::ProjectServiceImpl.new
180
- request = Makit::V1::Configuration::LoadFromFileRequest.new(path: path)
181
- proto_model = grpc_service.load_from_file(request, nil)
182
-
183
- # Convert Protobuf model to Ruby object
184
- convert_from_proto_model(proto_model)
185
- else
186
- # Fallback implementation
187
- content = File.read(path)
188
- data = JSON.parse(content, symbolize_names: true)
189
- new(data[:name], data[:version], data[:project_type]).tap do |project|
190
- project.authors = data[:authors] || "authors"
191
- project.description = data[:description] || "description"
192
- project.license_expression = data[:license_expression] || "MIT"
193
- project.git_remote_url = data[:git_remote_url] || ""
194
- project.dotnet_projects = data[:dotnet_projects] || []
195
-
196
- # Load steps
197
- (data[:steps] || []).each do |step_data|
198
- step = Step.new(
199
- name: step_data[:name] || "",
200
- description: step_data[:description] || "",
201
- commands: step_data[:commands] || []
202
- )
203
- project.steps << step
204
- end
205
- end
206
- end
110
+ # Use gRPC service to load from file
111
+ grpc_service = Makit::V1::Configuration::ProjectServiceImpl.new
112
+ request = Makit::V1::Configuration::LoadFromFileRequest.new(path: path)
113
+ proto_model = grpc_service.load_from_file(request, nil)
114
+
115
+ # Convert Protobuf model to Ruby object
116
+ convert_from_proto_model(proto_model)
207
117
  end
208
118
 
209
119
  # Class method for deserializing from JSON string (used by serializer)
210
120
  def self.decode_json(json_string)
211
- if defined?(Makit::V1::Configuration::ProjectServiceImpl)
212
- # Use gRPC service to load from JSON string
213
- grpc_service = Makit::V1::Configuration::ProjectServiceImpl.new
214
- request = Makit::V1::Configuration::LoadFromJsonRequest.new(json_string: json_string)
215
- proto_model = grpc_service.load_from_json(request, nil)
216
-
217
- # Convert Protobuf model to Ruby object
218
- convert_from_proto_model(proto_model)
219
- else
220
- # Fallback implementation
221
- data = JSON.parse(json_string, symbolize_names: true)
222
- new(data[:name], data[:version], data[:project_type]).tap do |project|
223
- project.authors = data[:authors] || "authors"
224
- project.description = data[:description] || "description"
225
- project.license_expression = data[:license_expression] || "MIT"
226
- project.git_remote_url = data[:git_remote_url] || ""
227
- project.dotnet_projects = data[:dotnet_projects] || []
228
-
229
- # Load steps
230
- (data[:steps] || []).each do |step_data|
231
- step = Step.new(
232
- name: step_data[:name] || "",
233
- description: step_data[:description] || "",
234
- commands: step_data[:commands] || []
235
- )
236
- project.steps << step
237
- end
238
- end
239
- end
121
+ # Use gRPC service to load from JSON string
122
+ grpc_service = Makit::V1::Configuration::ProjectServiceImpl.new
123
+ request = Makit::V1::Configuration::LoadFromJsonRequest.new(json_string: json_string)
124
+ proto_model = grpc_service.load_from_json(request, nil)
125
+
126
+ # Convert Protobuf model to Ruby object
127
+ convert_from_proto_model(proto_model)
240
128
  end
241
129
 
242
130
  def to_gitlab_ci(path)
243
- if @grpc_available
244
- # Use gRPC service to generate GitLab CI
245
- request = Makit::V1::Configuration::GenerateGitlabCiRequest.new(
246
- project: @proto_model,
247
- path: path
248
- )
249
- @grpc_service.generate_gitlab_ci(request, nil)
250
- else
251
- # Fallback implementation - just create a basic GitLab CI file
252
- require_relative "gitlab_helper"
253
- Makit::Configuration::GitLabHelper.to_yaml(self, path)
254
- end
131
+ # Use gRPC service to generate GitLab CI
132
+ request = Makit::V1::Configuration::GenerateGitlabCiRequest.new(
133
+ project: @proto_model,
134
+ path: path
135
+ )
136
+ @grpc_service.generate_gitlab_ci(request, nil)
255
137
  end
256
138
 
257
139
  def to_rakefile
258
- if @grpc_available
259
- # Use gRPC service to generate Rakefile
260
- request = Makit::V1::Configuration::GenerateRakefileRequest.new(project: @proto_model)
261
- response = @grpc_service.generate_rakefile(request, nil)
262
- response.content
263
- else
264
- # Fallback implementation
265
- require_relative "rakefile_helper"
266
- Makit::Configuration::RakefileHelper.generate(self)
267
- end
140
+ # Use gRPC service to generate Rakefile
141
+ request = Makit::V1::Configuration::GenerateRakefileRequest.new(project: @proto_model)
142
+ response = @grpc_service.generate_rakefile(request, nil)
143
+ response.content
268
144
  end
269
145
 
270
146
  # Save the project to a specific path as pretty JSON
@@ -274,17 +150,12 @@ module Makit
274
150
  raise ArgumentError, "Project name cannot be empty. Please set a valid project name before saving."
275
151
  end
276
152
 
277
- if @grpc_available
278
- # Use gRPC service to save to file
279
- request = Makit::V1::Configuration::SaveToFileRequest.new(
280
- project: @proto_model,
281
- path: path
282
- )
283
- @grpc_service.save_to_file(request, nil)
284
- else
285
- # Fallback implementation
286
- File.write(path, to_json_pretty)
287
- end
153
+ # Use gRPC service to save to file
154
+ request = Makit::V1::Configuration::SaveToFileRequest.new(
155
+ project: @proto_model,
156
+ path: path
157
+ )
158
+ @grpc_service.save_to_file(request, nil)
288
159
  end
289
160
 
290
161
  def save
@@ -293,135 +164,109 @@ module Makit
293
164
  raise ArgumentError, "Project name cannot be empty. Please set a valid project name before saving."
294
165
  end
295
166
 
296
- if @grpc_available
297
- # Use gRPC service to save to default location
298
- request = Makit::V1::Configuration::SaveToDefaultRequest.new(project: @proto_model)
299
- @grpc_service.save_to_default(request, nil)
300
- else
301
- # Fallback implementation
302
- File.write(".makit.json", to_json_pretty)
303
- end
167
+ # Use gRPC service to save to default location
168
+ request = Makit::V1::Configuration::SaveToDefaultRequest.new(project: @proto_model)
169
+ @grpc_service.save_to_default(request, nil)
304
170
  end
305
171
 
306
172
  # Display the project configuration in YAML format
307
173
  def show
308
174
  require "yaml"
309
175
 
310
- if @grpc_available
311
- project_data = {
312
- name: @proto_model.name,
313
- version: @proto_model.version,
314
- project_type: @proto_model.project_type,
315
- git_remote_url: @proto_model.git_remote_url,
316
- steps: @proto_model.steps.map do |step|
317
- {
318
- name: step.name,
319
- description: step.description,
320
- commands: step.commands.to_a
321
- }
322
- end
323
- }
324
- else
325
- project_data = {
326
- name: @name,
327
- version: @version,
328
- project_type: @project_type,
329
- git_remote_url: @git_remote_url,
330
- steps: @steps.map do |step|
331
- {
332
- name: step.name,
333
- description: step.description,
334
- commands: step.commands
335
- }
336
- end
337
- }
338
- end
176
+ project_data = {
177
+ name: @proto_model.name,
178
+ version: @proto_model.version,
179
+ project_type: @proto_model.project_type,
180
+ git_remote_url: @proto_model.git_remote_url,
181
+ steps: @proto_model.steps.map do |step|
182
+ {
183
+ name: step.name,
184
+ description: step.description,
185
+ commands: step.commands.to_a
186
+ }
187
+ end
188
+ }
339
189
 
340
190
  puts YAML.dump(project_data)
341
191
  end
342
192
 
343
193
  # Load a project from the default .makit.json file
344
194
  def self.default
345
- if defined?(Makit::V1::Configuration::ProjectServiceImpl)
346
- # Use gRPC service to load default project
347
- grpc_service = Makit::V1::Configuration::ProjectServiceImpl.new
348
- request = Google::Protobuf::Empty.new
349
- proto_model = grpc_service.load_default(request, nil)
350
-
351
- # Convert Protobuf model to Ruby object
352
- convert_from_proto_model(proto_model)
353
- else
354
- # Fallback implementation
355
- if File.exist?(".makit.json")
356
- from_json(".makit.json")
357
- else
358
- new("", "0.0.0", "")
359
- end
360
- end
195
+ # Use gRPC service to load default project
196
+ grpc_service = Makit::V1::Configuration::ProjectServiceImpl.new
197
+ request = Google::Protobuf::Empty.new
198
+ proto_model = grpc_service.load_default(request, nil)
199
+
200
+ # Convert Protobuf model to Ruby object
201
+ convert_from_proto_model(proto_model)
361
202
  end
362
203
 
363
204
  # Update project attributes
364
205
  def name=(value)
365
206
  @name = value
366
- update_proto_attribute(:name, value) if @grpc_available
207
+ update_proto_attribute(:name, value)
367
208
  end
368
209
 
369
210
  def version=(value)
370
211
  @version = value
371
- update_proto_attribute(:version, value) if @grpc_available
212
+ update_proto_attribute(:version, value)
372
213
  end
373
214
 
374
215
  def project_type=(value)
375
216
  @project_type = value
376
- update_proto_attribute(:project_type, value) if @grpc_available
217
+ update_proto_attribute(:project_type, value)
377
218
  end
378
219
 
379
220
  def authors=(value)
380
221
  @authors = value
381
- update_proto_attribute(:authors, value) if @grpc_available
222
+ update_proto_attribute(:authors, value)
382
223
  end
383
224
 
384
225
  def description=(value)
385
226
  @description = value
386
- update_proto_attribute(:description, value) if @grpc_available
227
+ update_proto_attribute(:description, value)
387
228
  end
388
229
 
389
230
  def license_expression=(value)
390
231
  @license_expression = value
391
- update_proto_attribute(:license_expression, value) if @grpc_available
232
+ update_proto_attribute(:license_expression, value)
392
233
  end
393
234
 
394
235
  def git_remote_url=(value)
395
236
  @git_remote_url = value
396
- update_proto_attribute(:git_remote_url, value) if @grpc_available
237
+ update_proto_attribute(:git_remote_url, value)
397
238
  end
398
239
 
399
240
  def dotnet_projects=(value)
400
241
  @dotnet_projects = value
401
- @proto_model.dotnet_projects.replace(value || []) if @grpc_available
242
+ @proto_model.dotnet_projects.replace(value || [])
402
243
  end
403
244
 
404
245
  private
405
246
 
247
+ # Synchronize Ruby attributes from protobuf model
248
+ def sync_attributes_from_proto
249
+ @authors = @proto_model.authors
250
+ @description = @proto_model.description
251
+ @license_expression = @proto_model.license_expression
252
+ @steps = convert_steps_from_proto(@proto_model.steps)
253
+ @git_remote_url = @proto_model.git_remote_url
254
+ @dotnet_projects = @proto_model.dotnet_projects.to_a
255
+ end
256
+
406
257
  # Convert Protobuf model to Ruby Project object
407
258
  def self.convert_from_proto_model(proto_model)
408
259
  project = new(proto_model.name, proto_model.version, proto_model.project_type)
409
260
 
410
261
  # Update all attributes from Protobuf model
411
262
  project.instance_variable_set(:@proto_model, proto_model)
412
- project.instance_variable_set(:@authors, proto_model.authors)
413
- project.instance_variable_set(:@description, proto_model.description)
414
- project.instance_variable_set(:@license_expression, proto_model.license_expression)
415
- project.instance_variable_set(:@git_remote_url, proto_model.git_remote_url)
416
- project.instance_variable_set(:@dotnet_projects, proto_model.dotnet_projects.to_a)
417
- project.instance_variable_set(:@steps, project.send(:convert_steps_from_proto, proto_model.steps))
263
+ project.send(:sync_attributes_from_proto)
418
264
 
419
265
  project
420
266
  end
421
267
 
422
268
  # Convert Protobuf steps to Ruby Step objects
423
269
  def convert_steps_from_proto(proto_steps)
424
- return [] unless @grpc_available
425
270
  proto_steps.map do |proto_step|
426
271
  Step.new(
427
272
  name: proto_step.name,
@@ -433,14 +278,15 @@ module Makit
433
278
 
434
279
  # Update a single attribute in the Protobuf model
435
280
  def update_proto_attribute(attribute, value)
436
- return unless @grpc_available
437
281
  updates = { attribute.to_s => value }
438
282
  request = Makit::V1::Configuration::UpdateProjectRequest.new(
439
283
  project: @proto_model,
440
284
  updates: updates
441
285
  )
442
286
  @proto_model = @grpc_service.update_project(request, nil)
287
+ # Sync Ruby attributes after update
288
+ sync_attributes_from_proto
443
289
  end
444
290
  end
445
291
  end
446
- end
292
+ end