makit 0.0.157 → 0.0.158

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 (177) 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 +40 -40
  79. data/lib/makit/git/cli.rb +78 -54
  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 +74 -74
  112. data/lib/makit/podman/podman.rb +458 -458
  113. data/lib/makit/podman/podman_service_impl.rb +1081 -1081
  114. data/lib/makit/port.rb +32 -32
  115. data/lib/makit/process.rb +377 -377
  116. data/lib/makit/protoc.rb +112 -112
  117. data/lib/makit/rake/cli.rb +196 -196
  118. data/lib/makit/rake/trace_controller.rb +174 -174
  119. data/lib/makit/rake.rb +81 -81
  120. data/lib/makit/ruby/cli.rb +185 -185
  121. data/lib/makit/ruby.rb +25 -25
  122. data/lib/makit/rubygems.rb +137 -0
  123. data/lib/makit/secrets/azure_key_vault.rb +322 -322
  124. data/lib/makit/secrets/azure_secrets.rb +183 -183
  125. data/lib/makit/secrets/local_secrets.rb +72 -72
  126. data/lib/makit/secrets/secrets_manager.rb +105 -105
  127. data/lib/makit/secrets.rb +16 -16
  128. data/lib/makit/serializer.rb +130 -130
  129. data/lib/makit/services/builder.rb +186 -186
  130. data/lib/makit/services/error_handler.rb +226 -226
  131. data/lib/makit/services/repository_manager.rb +367 -367
  132. data/lib/makit/services/validator.rb +112 -112
  133. data/lib/makit/setup/classlib.rb +101 -101
  134. data/lib/makit/setup/gem.rb +268 -268
  135. data/lib/makit/setup/pages.rb +11 -11
  136. data/lib/makit/setup/razorclasslib.rb +101 -101
  137. data/lib/makit/setup/runner.rb +54 -54
  138. data/lib/makit/setup.rb +5 -5
  139. data/lib/makit/show.rb +110 -110
  140. data/lib/makit/storage.rb +126 -126
  141. data/lib/makit/symbols.rb +175 -175
  142. data/lib/makit/task_info.rb +130 -130
  143. data/lib/makit/tasks/at_exit.rb +15 -15
  144. data/lib/makit/tasks/build.rb +22 -22
  145. data/lib/makit/tasks/bump.rb +7 -7
  146. data/lib/makit/tasks/clean.rb +13 -13
  147. data/lib/makit/tasks/configure.rb +10 -10
  148. data/lib/makit/tasks/format.rb +10 -10
  149. data/lib/makit/tasks/hook_manager.rb +443 -443
  150. data/lib/makit/tasks/info.rb +368 -368
  151. data/lib/makit/tasks/init.rb +49 -49
  152. data/lib/makit/tasks/integrate.rb +60 -56
  153. data/lib/makit/tasks/pull_incoming.rb +13 -13
  154. data/lib/makit/tasks/secrets.rb +7 -7
  155. data/lib/makit/tasks/setup.rb +16 -16
  156. data/lib/makit/tasks/sync.rb +14 -17
  157. data/lib/makit/tasks/tag.rb +27 -27
  158. data/lib/makit/tasks/task_monkey_patch.rb +81 -81
  159. data/lib/makit/tasks/test.rb +22 -22
  160. data/lib/makit/tasks/update.rb +18 -18
  161. data/lib/makit/tasks/version.rb +6 -6
  162. data/lib/makit/tasks.rb +24 -24
  163. data/lib/makit/test_cache.rb +239 -239
  164. data/lib/makit/tree.rb +37 -37
  165. data/lib/makit/v1/configuration/project_service_impl.rb +370 -370
  166. data/lib/makit/v1/git/git_repository_service_impl.rb +295 -295
  167. data/lib/makit/v1/makit.v1_pb.rb +35 -35
  168. data/lib/makit/v1/makit.v1_services_pb.rb +27 -27
  169. data/lib/makit/v1/services/repository_manager_service_impl.rb +572 -572
  170. data/lib/makit/version.rb +661 -503
  171. data/lib/makit/version_util.rb +21 -21
  172. data/lib/makit/wix.rb +95 -95
  173. data/lib/makit/yaml.rb +29 -29
  174. data/lib/makit/zip.rb +17 -17
  175. data/lib/makit copy.rb +44 -44
  176. data/lib/makit.rb +115 -114
  177. metadata +3 -2
@@ -1,323 +1,323 @@
1
-
2
-
3
- module Makit
4
- module Secrets
5
- # Azure Key Vault operations
6
- class AzureKeyVault
7
- # Required environment variables
8
- REQUIRED_VARS = [
9
- "AZURE_STORAGE_ACCOUNT",
10
- "AZURE_STORAGE_ACCOUNT_KEY",
11
- ].freeze
12
-
13
- # Optional environment variables
14
- OPTIONAL_VARS = [
15
- "AZURE_CDN_RESOURCE_GROUP",
16
- "AZURE_CDN_PROFILE_NAME",
17
- "AZURE_CDN_ENDPOINT_NAME",
18
- "GITLAB_NUGET_TOKEN",
19
- "GITLAB_USERNAME",
20
- ].freeze
21
-
22
- # All environment variablesazure_key_vault.r
23
- ALL_VARS = (REQUIRED_VARS + OPTIONAL_VARS).freeze
24
-
25
- # Check if kvenv is available
26
- def self.kvenv_available?
27
- system("which kvenv > /dev/null 2>&1")
28
- end
29
-
30
- # Check if Azure CLI is authenticated
31
- def self.azure_cli_authenticated?
32
- return false unless system("which az > /dev/null 2>&1")
33
- system("az account show > /dev/null 2>&1")
34
- end
35
-
36
- # Get Azure Key Vault name from environment or use default
37
- def self.keyvault_name
38
- ENV["AZURE_KEYVAULT_NAME"] || "louparslow-secrets"
39
- end
40
-
41
- # Get secret name from environment or auto-generate from GIT_REMOTE_URL
42
- def self.secret_name
43
- return ENV["AZURE_SECRET_NAME"] if ENV["AZURE_SECRET_NAME"] && !ENV["AZURE_SECRET_NAME"].empty?
44
-
45
- # Auto-generate from GIT_REMOTE_URL
46
- git_remote_url = get_git_remote_url
47
- if git_remote_url && !git_remote_url.empty?
48
- generate_secret_name_from_url(git_remote_url)
49
- else
50
- "portal-secrets" # Fallback default
51
- end
52
- end
53
-
54
- # Get GIT_REMOTE_URL from various sources
55
- def self.get_git_remote_url
56
- # Try constant first
57
- return GIT_REMOTE_URL if defined?(GIT_REMOTE_URL) && !GIT_REMOTE_URL.nil? && !GIT_REMOTE_URL.empty?
58
-
59
- # Try Git module
60
- if defined?(Makit::Git) && Makit::Git.git_repo?
61
- url = Makit::Git.get_remote_url
62
- return url if url && !url.empty?
63
- end
64
-
65
- # Try project configuration
66
- if defined?(Makit::Configuration::Project)
67
- project = Makit::Configuration::Project.default
68
- url = project.git_remote_url
69
- return url if url && !url.nil? && !url.empty?
70
- end
71
-
72
- nil
73
- end
74
-
75
- # Generate a valid Azure Key Vault secret name from a Git remote URL
76
- # Azure Key Vault secret names must be 1-127 characters, alphanumeric and hyphens only,
77
- # cannot start or end with hyphen, and cannot have consecutive hyphens
78
- def self.generate_secret_name_from_url(url)
79
- # Remove protocol (http://, https://, git@)
80
- name = url.gsub(/^https?:\/\//, "").gsub(/^git@/, "")
81
-
82
- # Remove .git suffix if present
83
- name = name.gsub(/\.git$/, "")
84
-
85
- # Replace invalid characters with hyphens
86
- name = name.gsub(/[^a-zA-Z0-9\-]/, "-")
87
-
88
- # Remove consecutive hyphens
89
- name = name.gsub(/-+/, "-")
90
-
91
- # Remove leading/trailing hyphens
92
- name = name.gsub(/^-+|-+$/, "")
93
-
94
- # Ensure it starts with a letter or number (Azure requirement)
95
- name = "secret-#{name}" if name.empty? || name.match(/^[^a-zA-Z0-9]/)
96
-
97
- # Truncate to 127 characters (Azure Key Vault limit)
98
- name = name[0, 127]
99
-
100
- # Remove trailing hyphen if truncation created one
101
- name = name.gsub(/-+$/, "")
102
-
103
- # Ensure it's not empty
104
- name = "git-secrets" if name.empty?
105
-
106
- name
107
- end
108
-
109
- # Get secret prefix from environment
110
- def self.secret_prefix
111
- ENV["AZURE_SECRET_PREFIX"]
112
- end
113
-
114
- # Setup method: Attempt to initialize environment variables
115
- def self.setup
116
- puts "Setting up secrets...".colorize(:blue)
117
-
118
- # Check if required variables are already set
119
- missing_required = REQUIRED_VARS.select { |var| ENV[var].nil? || ENV[var].empty? }
120
-
121
- if missing_required.empty?
122
- puts " All required environment variables are already set".colorize(:green)
123
- return true
124
- end
125
-
126
- puts " Missing required variables: #{missing_required.join(", ")}".colorize(:yellow)
127
- puts " Attempting to load from Azure Key Vault...".colorize(:yellow)
128
-
129
- # Try to load from Azure Key Vault
130
- if load(keyvault_name: nil, secret_name: nil)
131
- # Verify again after loading
132
- still_missing = REQUIRED_VARS.select { |var| ENV[var].nil? || ENV[var].empty? }
133
- if still_missing.empty?
134
- puts " Successfully loaded all required secrets".colorize(:green)
135
- return true
136
- else
137
- puts " Warning: Still missing required variables: #{still_missing.join(", ")}".colorize(:yellow)
138
- return false
139
- end
140
- else
141
- puts " Could not load secrets from Azure Key Vault".colorize(:yellow)
142
- puts " Please set environment variables manually or configure Azure Key Vault access".colorize(:yellow)
143
- return false
144
- end
145
- end
146
-
147
- # Verify that expected environment variables are set
148
- def self.verify(warn_only: true)
149
- issues = []
150
- warnings = []
151
-
152
- # Check required variables
153
- REQUIRED_VARS.each do |var|
154
- if ENV[var].nil? || ENV[var].empty?
155
- issues << "Required variable '#{var}' is not set"
156
- end
157
- end
158
-
159
- # Check optional variables (only warn)
160
- OPTIONAL_VARS.each do |var|
161
- if ENV[var].nil? || ENV[var].empty?
162
- warnings << "Optional variable '#{var}' is not set"
163
- end
164
- end
165
-
166
- # Report issues
167
- if issues.any?
168
- if warn_only
169
- issues.each { |issue| puts " ⚠️ #{issue}".colorize(:yellow) }
170
- else
171
- issues.each { |issue| puts " ❌ #{issue}".colorize(:red) }
172
- end
173
- end
174
-
175
- if warnings.any?
176
- warnings.each { |warning| puts " ℹ️ #{warning}".colorize(:cyan) }
177
- end
178
-
179
- if issues.empty? && warnings.empty?
180
- puts " ✅ All environment variables are set".colorize(:green)
181
- return true
182
- elsif issues.empty?
183
- puts " ✅ All required environment variables are set".colorize(:green)
184
- return true
185
- else
186
- return false
187
- end
188
- end
189
-
190
- # Get status of all environment variables
191
- def self.status
192
- puts "Environment Variables Status:".colorize(:blue)
193
- puts ""
194
-
195
- puts "Required Variables:".colorize(:cyan)
196
- REQUIRED_VARS.each do |var|
197
- status = ENV[var].nil? || ENV[var].empty? ? "❌ Not set" : "✅ Set"
198
- color = ENV[var].nil? || ENV[var].empty? ? :red : :green
199
- puts " #{var}: #{status}".colorize(color)
200
- end
201
-
202
- puts ""
203
- puts "Optional Variables:".colorize(:cyan)
204
- OPTIONAL_VARS.each do |var|
205
- status = ENV[var].nil? || ENV[var].empty? ? "⚪ Not set" : "✅ Set"
206
- color = ENV[var].nil? || ENV[var].empty? ? :yellow : :green
207
- puts " #{var}: #{status}".colorize(color)
208
- end
209
- end
210
-
211
- # Load secrets from Azure Key Vault using kvenv
212
- # @param keyvault_name [String, nil] The Azure Key Vault name (defaults to ENV["AZURE_KEYVAULT_NAME"] or "louparslow-secrets")
213
- # @param secret_name [String, nil] The secret name in Key Vault (defaults to ENV["AZURE_SECRET_NAME"] or "portal-secrets")
214
- # @return [Boolean] true if secrets were loaded successfully, false otherwise
215
- def self.load(keyvault_name: nil, secret_name: nil)
216
- # Use provided parameters or fall back to defaults
217
- kv_name = keyvault_name || self.keyvault_name
218
- sec_name = secret_name || self.secret_name
219
-
220
- unless kvenv_available?
221
- puts " Warning: kvenv not installed, secrets will not be loaded".colorize(:yellow)
222
- return false
223
- end
224
-
225
- # Check if Azure CLI is authenticated (kvenv can use this)
226
- unless azure_cli_authenticated?
227
- puts " Warning: Azure CLI not authenticated, run 'az login' first".colorize(:yellow)
228
- return false
229
- end
230
-
231
- cache_file = "/tmp/kvenv-#{sec_name}.json"
232
-
233
- # Build kvenv command
234
- cmd_parts = [
235
- "kvenv cache",
236
- "--azure",
237
- "--azure-keyvault-name #{kv_name}",
238
- ]
239
-
240
- if secret_prefix
241
- cmd_parts << "--secret-prefix #{secret_prefix}"
242
- else
243
- cmd_parts << "--secret-name #{sec_name}"
244
- end
245
-
246
- cmd_parts << "--output #{cache_file}"
247
-
248
- # Run kvenv to cache secrets
249
- puts " Loading secrets from Key Vault: #{kv_name}, Secret: #{sec_name}".colorize(:cyan)
250
- success = system(cmd_parts.join(" "))
251
- return false unless success
252
- return false unless File.exist?(cache_file)
253
-
254
- # Load secrets from cache file
255
- begin
256
- require "json"
257
- secrets = JSON.parse(File.read(cache_file))
258
-
259
- # Set environment variables (don't override existing ones)
260
- loaded_count = 0
261
- secrets.each do |key, value|
262
- unless ENV[key]
263
- ENV[key] = value
264
- loaded_count += 1
265
- end
266
- end
267
-
268
- puts " Loaded #{loaded_count} secrets from Azure Key Vault (#{secrets.keys.count} total in secret)".colorize(:green)
269
- true
270
- rescue => e
271
- puts " Warning: Could not parse secrets from cache: #{e.message}".colorize(:yellow)
272
- false
273
- end
274
- end
275
-
276
- # Set a secret in Azure Key Vault
277
- # @param secret_name [String] The name of the secret to set
278
- # @param secret_value [String] The value of the secret
279
- # @param keyvault_name [String, nil] The Azure Key Vault name (defaults to ENV["AZURE_KEYVAULT_NAME"] or "louparslow-secrets")
280
- # @return [Boolean] true if secret was set successfully, false otherwise
281
- def self.set(secret_name, secret_value, keyvault_name: nil)
282
- # Use provided parameter or fall back to default
283
- kv_name = keyvault_name || self.keyvault_name
284
-
285
- if secret_name.nil? || secret_name.empty?
286
- raise "Secret name is required"
287
- end
288
-
289
- if secret_value.nil? || secret_value.empty?
290
- raise "Secret value is required"
291
- end
292
-
293
- unless azure_cli_authenticated?
294
- raise "Azure CLI is not authenticated. Run 'az login' first"
295
- end
296
-
297
- puts " Setting secret '#{secret_name}' in Key Vault: #{kv_name}".colorize(:cyan)
298
-
299
- # Build Azure CLI command
300
- cmd = [
301
- "az keyvault secret set",
302
- "--vault-name '#{kv_name}'",
303
- "--name '#{secret_name}'",
304
- "--value '#{secret_value}'",
305
- "2>&1",
306
- ].join(" ")
307
-
308
- output = `#{cmd}`
309
- exit_code = $?.exitstatus
310
-
311
- if exit_code != 0
312
- puts " Error: Failed to set secret".colorize(:red)
313
- puts " #{output}".colorize(:red)
314
- return false
315
- end
316
-
317
- puts " Successfully set secret '#{secret_name}' in Key Vault".colorize(:green)
318
- true
319
- end
320
- end
321
- end
322
- end
1
+
2
+
3
+ module Makit
4
+ module Secrets
5
+ # Azure Key Vault operations
6
+ class AzureKeyVault
7
+ # Required environment variables
8
+ REQUIRED_VARS = [
9
+ "AZURE_STORAGE_ACCOUNT",
10
+ "AZURE_STORAGE_ACCOUNT_KEY",
11
+ ].freeze
12
+
13
+ # Optional environment variables
14
+ OPTIONAL_VARS = [
15
+ "AZURE_CDN_RESOURCE_GROUP",
16
+ "AZURE_CDN_PROFILE_NAME",
17
+ "AZURE_CDN_ENDPOINT_NAME",
18
+ "GITLAB_NUGET_TOKEN",
19
+ "GITLAB_USERNAME",
20
+ ].freeze
21
+
22
+ # All environment variablesazure_key_vault.r
23
+ ALL_VARS = (REQUIRED_VARS + OPTIONAL_VARS).freeze
24
+
25
+ # Check if kvenv is available
26
+ def self.kvenv_available?
27
+ system("which kvenv > /dev/null 2>&1")
28
+ end
29
+
30
+ # Check if Azure CLI is authenticated
31
+ def self.azure_cli_authenticated?
32
+ return false unless system("which az > /dev/null 2>&1")
33
+ system("az account show > /dev/null 2>&1")
34
+ end
35
+
36
+ # Get Azure Key Vault name from environment or use default
37
+ def self.keyvault_name
38
+ ENV["AZURE_KEYVAULT_NAME"] || "louparslow-secrets"
39
+ end
40
+
41
+ # Get secret name from environment or auto-generate from GIT_REMOTE_URL
42
+ def self.secret_name
43
+ return ENV["AZURE_SECRET_NAME"] if ENV["AZURE_SECRET_NAME"] && !ENV["AZURE_SECRET_NAME"].empty?
44
+
45
+ # Auto-generate from GIT_REMOTE_URL
46
+ git_remote_url = get_git_remote_url
47
+ if git_remote_url && !git_remote_url.empty?
48
+ generate_secret_name_from_url(git_remote_url)
49
+ else
50
+ "portal-secrets" # Fallback default
51
+ end
52
+ end
53
+
54
+ # Get GIT_REMOTE_URL from various sources
55
+ def self.get_git_remote_url
56
+ # Try constant first
57
+ return GIT_REMOTE_URL if defined?(GIT_REMOTE_URL) && !GIT_REMOTE_URL.nil? && !GIT_REMOTE_URL.empty?
58
+
59
+ # Try Git module
60
+ if defined?(Makit::Git) && Makit::Git.git_repo?
61
+ url = Makit::Git.get_remote_url
62
+ return url if url && !url.empty?
63
+ end
64
+
65
+ # Try project configuration
66
+ if defined?(Makit::Configuration::Project)
67
+ project = Makit::Configuration::Project.default
68
+ url = project.git_remote_url
69
+ return url if url && !url.nil? && !url.empty?
70
+ end
71
+
72
+ nil
73
+ end
74
+
75
+ # Generate a valid Azure Key Vault secret name from a Git remote URL
76
+ # Azure Key Vault secret names must be 1-127 characters, alphanumeric and hyphens only,
77
+ # cannot start or end with hyphen, and cannot have consecutive hyphens
78
+ def self.generate_secret_name_from_url(url)
79
+ # Remove protocol (http://, https://, git@)
80
+ name = url.gsub(/^https?:\/\//, "").gsub(/^git@/, "")
81
+
82
+ # Remove .git suffix if present
83
+ name = name.gsub(/\.git$/, "")
84
+
85
+ # Replace invalid characters with hyphens
86
+ name = name.gsub(/[^a-zA-Z0-9\-]/, "-")
87
+
88
+ # Remove consecutive hyphens
89
+ name = name.gsub(/-+/, "-")
90
+
91
+ # Remove leading/trailing hyphens
92
+ name = name.gsub(/^-+|-+$/, "")
93
+
94
+ # Ensure it starts with a letter or number (Azure requirement)
95
+ name = "secret-#{name}" if name.empty? || name.match(/^[^a-zA-Z0-9]/)
96
+
97
+ # Truncate to 127 characters (Azure Key Vault limit)
98
+ name = name[0, 127]
99
+
100
+ # Remove trailing hyphen if truncation created one
101
+ name = name.gsub(/-+$/, "")
102
+
103
+ # Ensure it's not empty
104
+ name = "git-secrets" if name.empty?
105
+
106
+ name
107
+ end
108
+
109
+ # Get secret prefix from environment
110
+ def self.secret_prefix
111
+ ENV["AZURE_SECRET_PREFIX"]
112
+ end
113
+
114
+ # Setup method: Attempt to initialize environment variables
115
+ def self.setup
116
+ puts "Setting up secrets...".colorize(:blue)
117
+
118
+ # Check if required variables are already set
119
+ missing_required = REQUIRED_VARS.select { |var| ENV[var].nil? || ENV[var].empty? }
120
+
121
+ if missing_required.empty?
122
+ puts " All required environment variables are already set".colorize(:green)
123
+ return true
124
+ end
125
+
126
+ puts " Missing required variables: #{missing_required.join(", ")}".colorize(:yellow)
127
+ puts " Attempting to load from Azure Key Vault...".colorize(:yellow)
128
+
129
+ # Try to load from Azure Key Vault
130
+ if load(keyvault_name: nil, secret_name: nil)
131
+ # Verify again after loading
132
+ still_missing = REQUIRED_VARS.select { |var| ENV[var].nil? || ENV[var].empty? }
133
+ if still_missing.empty?
134
+ puts " Successfully loaded all required secrets".colorize(:green)
135
+ return true
136
+ else
137
+ puts " Warning: Still missing required variables: #{still_missing.join(", ")}".colorize(:yellow)
138
+ return false
139
+ end
140
+ else
141
+ puts " Could not load secrets from Azure Key Vault".colorize(:yellow)
142
+ puts " Please set environment variables manually or configure Azure Key Vault access".colorize(:yellow)
143
+ return false
144
+ end
145
+ end
146
+
147
+ # Verify that expected environment variables are set
148
+ def self.verify(warn_only: true)
149
+ issues = []
150
+ warnings = []
151
+
152
+ # Check required variables
153
+ REQUIRED_VARS.each do |var|
154
+ if ENV[var].nil? || ENV[var].empty?
155
+ issues << "Required variable '#{var}' is not set"
156
+ end
157
+ end
158
+
159
+ # Check optional variables (only warn)
160
+ OPTIONAL_VARS.each do |var|
161
+ if ENV[var].nil? || ENV[var].empty?
162
+ warnings << "Optional variable '#{var}' is not set"
163
+ end
164
+ end
165
+
166
+ # Report issues
167
+ if issues.any?
168
+ if warn_only
169
+ issues.each { |issue| puts " ⚠️ #{issue}".colorize(:yellow) }
170
+ else
171
+ issues.each { |issue| puts " ❌ #{issue}".colorize(:red) }
172
+ end
173
+ end
174
+
175
+ if warnings.any?
176
+ warnings.each { |warning| puts " ℹ️ #{warning}".colorize(:cyan) }
177
+ end
178
+
179
+ if issues.empty? && warnings.empty?
180
+ puts " ✅ All environment variables are set".colorize(:green)
181
+ return true
182
+ elsif issues.empty?
183
+ puts " ✅ All required environment variables are set".colorize(:green)
184
+ return true
185
+ else
186
+ return false
187
+ end
188
+ end
189
+
190
+ # Get status of all environment variables
191
+ def self.status
192
+ puts "Environment Variables Status:".colorize(:blue)
193
+ puts ""
194
+
195
+ puts "Required Variables:".colorize(:cyan)
196
+ REQUIRED_VARS.each do |var|
197
+ status = ENV[var].nil? || ENV[var].empty? ? "❌ Not set" : "✅ Set"
198
+ color = ENV[var].nil? || ENV[var].empty? ? :red : :green
199
+ puts " #{var}: #{status}".colorize(color)
200
+ end
201
+
202
+ puts ""
203
+ puts "Optional Variables:".colorize(:cyan)
204
+ OPTIONAL_VARS.each do |var|
205
+ status = ENV[var].nil? || ENV[var].empty? ? "⚪ Not set" : "✅ Set"
206
+ color = ENV[var].nil? || ENV[var].empty? ? :yellow : :green
207
+ puts " #{var}: #{status}".colorize(color)
208
+ end
209
+ end
210
+
211
+ # Load secrets from Azure Key Vault using kvenv
212
+ # @param keyvault_name [String, nil] The Azure Key Vault name (defaults to ENV["AZURE_KEYVAULT_NAME"] or "louparslow-secrets")
213
+ # @param secret_name [String, nil] The secret name in Key Vault (defaults to ENV["AZURE_SECRET_NAME"] or "portal-secrets")
214
+ # @return [Boolean] true if secrets were loaded successfully, false otherwise
215
+ def self.load(keyvault_name: nil, secret_name: nil)
216
+ # Use provided parameters or fall back to defaults
217
+ kv_name = keyvault_name || self.keyvault_name
218
+ sec_name = secret_name || self.secret_name
219
+
220
+ unless kvenv_available?
221
+ puts " Warning: kvenv not installed, secrets will not be loaded".colorize(:yellow)
222
+ return false
223
+ end
224
+
225
+ # Check if Azure CLI is authenticated (kvenv can use this)
226
+ unless azure_cli_authenticated?
227
+ puts " Warning: Azure CLI not authenticated, run 'az login' first".colorize(:yellow)
228
+ return false
229
+ end
230
+
231
+ cache_file = "/tmp/kvenv-#{sec_name}.json"
232
+
233
+ # Build kvenv command
234
+ cmd_parts = [
235
+ "kvenv cache",
236
+ "--azure",
237
+ "--azure-keyvault-name #{kv_name}",
238
+ ]
239
+
240
+ if secret_prefix
241
+ cmd_parts << "--secret-prefix #{secret_prefix}"
242
+ else
243
+ cmd_parts << "--secret-name #{sec_name}"
244
+ end
245
+
246
+ cmd_parts << "--output #{cache_file}"
247
+
248
+ # Run kvenv to cache secrets
249
+ puts " Loading secrets from Key Vault: #{kv_name}, Secret: #{sec_name}".colorize(:cyan)
250
+ success = system(cmd_parts.join(" "))
251
+ return false unless success
252
+ return false unless File.exist?(cache_file)
253
+
254
+ # Load secrets from cache file
255
+ begin
256
+ require "json"
257
+ secrets = JSON.parse(File.read(cache_file))
258
+
259
+ # Set environment variables (don't override existing ones)
260
+ loaded_count = 0
261
+ secrets.each do |key, value|
262
+ unless ENV[key]
263
+ ENV[key] = value
264
+ loaded_count += 1
265
+ end
266
+ end
267
+
268
+ puts " Loaded #{loaded_count} secrets from Azure Key Vault (#{secrets.keys.count} total in secret)".colorize(:green)
269
+ true
270
+ rescue => e
271
+ puts " Warning: Could not parse secrets from cache: #{e.message}".colorize(:yellow)
272
+ false
273
+ end
274
+ end
275
+
276
+ # Set a secret in Azure Key Vault
277
+ # @param secret_name [String] The name of the secret to set
278
+ # @param secret_value [String] The value of the secret
279
+ # @param keyvault_name [String, nil] The Azure Key Vault name (defaults to ENV["AZURE_KEYVAULT_NAME"] or "louparslow-secrets")
280
+ # @return [Boolean] true if secret was set successfully, false otherwise
281
+ def self.set(secret_name, secret_value, keyvault_name: nil)
282
+ # Use provided parameter or fall back to default
283
+ kv_name = keyvault_name || self.keyvault_name
284
+
285
+ if secret_name.nil? || secret_name.empty?
286
+ raise "Secret name is required"
287
+ end
288
+
289
+ if secret_value.nil? || secret_value.empty?
290
+ raise "Secret value is required"
291
+ end
292
+
293
+ unless azure_cli_authenticated?
294
+ raise "Azure CLI is not authenticated. Run 'az login' first"
295
+ end
296
+
297
+ puts " Setting secret '#{secret_name}' in Key Vault: #{kv_name}".colorize(:cyan)
298
+
299
+ # Build Azure CLI command
300
+ cmd = [
301
+ "az keyvault secret set",
302
+ "--vault-name '#{kv_name}'",
303
+ "--name '#{secret_name}'",
304
+ "--value '#{secret_value}'",
305
+ "2>&1",
306
+ ].join(" ")
307
+
308
+ output = `#{cmd}`
309
+ exit_code = $?.exitstatus
310
+
311
+ if exit_code != 0
312
+ puts " Error: Failed to set secret".colorize(:red)
313
+ puts " #{output}".colorize(:red)
314
+ return false
315
+ end
316
+
317
+ puts " Successfully set secret '#{secret_name}' in Key Vault".colorize(:green)
318
+ true
319
+ end
320
+ end
321
+ end
322
+ end
323
323