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.
- checksums.yaml +4 -4
- data/README.md +41 -41
- data/exe/makit +5 -5
- data/lib/makit/apache.rb +28 -28
- data/lib/makit/auto.rb +48 -48
- data/lib/makit/azure/blob_storage.rb +257 -257
- data/lib/makit/azure/cli.rb +284 -284
- data/lib/makit/cli/base.rb +17 -17
- data/lib/makit/cli/build_commands.rb +500 -500
- data/lib/makit/cli/generators/base_generator.rb +74 -74
- data/lib/makit/cli/generators/dotnet_generator.rb +50 -50
- data/lib/makit/cli/generators/generator_factory.rb +49 -49
- data/lib/makit/cli/generators/node_generator.rb +50 -50
- data/lib/makit/cli/generators/ruby_generator.rb +77 -77
- data/lib/makit/cli/generators/rust_generator.rb +50 -50
- data/lib/makit/cli/generators/templates/dotnet_templates.rb +167 -167
- data/lib/makit/cli/generators/templates/node_templates.rb +161 -161
- data/lib/makit/cli/generators/templates/ruby/gemfile.rb +26 -26
- data/lib/makit/cli/generators/templates/ruby/gemspec.rb +41 -41
- data/lib/makit/cli/generators/templates/ruby/main_lib.rb +33 -33
- data/lib/makit/cli/generators/templates/ruby/rakefile.rb +35 -35
- data/lib/makit/cli/generators/templates/ruby/readme.rb +63 -63
- data/lib/makit/cli/generators/templates/ruby/test.rb +39 -39
- data/lib/makit/cli/generators/templates/ruby/test_helper.rb +29 -29
- data/lib/makit/cli/generators/templates/ruby/version.rb +29 -29
- data/lib/makit/cli/generators/templates/rust_templates.rb +128 -128
- data/lib/makit/cli/main.rb +78 -78
- data/lib/makit/cli/pipeline_commands.rb +311 -311
- data/lib/makit/cli/project_commands.rb +868 -868
- data/lib/makit/cli/repository_commands.rb +661 -661
- data/lib/makit/cli/strategy_commands.rb +207 -207
- data/lib/makit/cli/utility_commands.rb +521 -521
- data/lib/makit/commands/factory.rb +359 -359
- data/lib/makit/commands/middleware/base.rb +73 -73
- data/lib/makit/commands/middleware/cache.rb +248 -248
- data/lib/makit/commands/middleware/command_logger.rb +312 -312
- data/lib/makit/commands/middleware/validator.rb +269 -269
- data/lib/makit/commands/request.rb +316 -316
- data/lib/makit/commands/result.rb +323 -323
- data/lib/makit/commands/runner.rb +386 -386
- data/lib/makit/commands/strategies/base.rb +171 -171
- data/lib/makit/commands/strategies/child_process.rb +162 -162
- data/lib/makit/commands/strategies/factory.rb +136 -136
- data/lib/makit/commands/strategies/synchronous.rb +139 -139
- data/lib/makit/commands.rb +50 -50
- data/lib/makit/configuration/dotnet_project.rb +48 -48
- data/lib/makit/configuration/gitlab_helper.rb +61 -61
- data/lib/makit/configuration/project.rb +292 -292
- data/lib/makit/configuration/rakefile_helper.rb +43 -43
- data/lib/makit/configuration/step.rb +34 -34
- data/lib/makit/configuration/timeout.rb +74 -74
- data/lib/makit/configuration.rb +21 -21
- data/lib/makit/content/default_gitignore.rb +7 -7
- data/lib/makit/content/default_gitignore.txt +225 -225
- data/lib/makit/content/default_rakefile.rb +13 -13
- data/lib/makit/content/gem_rakefile.rb +16 -16
- data/lib/makit/context.rb +1 -1
- data/lib/makit/data.rb +49 -49
- data/lib/makit/directories.rb +170 -170
- data/lib/makit/directory.rb +262 -262
- data/lib/makit/docs/files.rb +89 -89
- data/lib/makit/docs/rake.rb +102 -102
- data/lib/makit/dotnet/cli.rb +69 -69
- data/lib/makit/dotnet/project.rb +217 -217
- data/lib/makit/dotnet/solution.rb +38 -38
- data/lib/makit/dotnet/solution_classlib.rb +239 -239
- data/lib/makit/dotnet/solution_console.rb +264 -264
- data/lib/makit/dotnet/solution_maui.rb +354 -354
- data/lib/makit/dotnet/solution_wasm.rb +275 -275
- data/lib/makit/dotnet/solution_wpf.rb +304 -304
- data/lib/makit/dotnet.rb +102 -102
- data/lib/makit/email.rb +90 -90
- data/lib/makit/environment.rb +142 -142
- data/lib/makit/examples/runner.rb +370 -370
- data/lib/makit/exceptions.rb +45 -45
- data/lib/makit/fileinfo.rb +32 -32
- data/lib/makit/files.rb +43 -43
- data/lib/makit/gems.rb +40 -40
- data/lib/makit/git/cli.rb +78 -54
- data/lib/makit/git/repository.rb +100 -100
- data/lib/makit/git.rb +104 -104
- data/lib/makit/gitlab/pipeline.rb +857 -857
- data/lib/makit/gitlab/pipeline_service_impl.rb +1535 -1535
- data/lib/makit/gitlab_runner.rb +59 -59
- data/lib/makit/humanize.rb +218 -218
- data/lib/makit/indexer.rb +47 -47
- data/lib/makit/io/filesystem.rb +111 -111
- data/lib/makit/io/filesystem_service_impl.rb +337 -337
- data/lib/makit/lint.rb +212 -212
- data/lib/makit/logging/configuration.rb +309 -309
- data/lib/makit/logging/format_registry.rb +84 -84
- data/lib/makit/logging/formatters/base.rb +39 -39
- data/lib/makit/logging/formatters/console_formatter.rb +140 -140
- data/lib/makit/logging/formatters/json_formatter.rb +65 -65
- data/lib/makit/logging/formatters/plain_text_formatter.rb +71 -71
- data/lib/makit/logging/formatters/text_formatter.rb +64 -64
- data/lib/makit/logging/log_request.rb +119 -119
- data/lib/makit/logging/logger.rb +199 -199
- data/lib/makit/logging/sinks/base.rb +91 -91
- data/lib/makit/logging/sinks/console.rb +72 -72
- data/lib/makit/logging/sinks/file_sink.rb +92 -92
- data/lib/makit/logging/sinks/structured.rb +123 -123
- data/lib/makit/logging/sinks/unified_file_sink.rb +296 -296
- data/lib/makit/logging.rb +578 -578
- data/lib/makit/markdown.rb +75 -75
- data/lib/makit/mp/basic_object_mp.rb +17 -17
- data/lib/makit/mp/command_mp.rb +13 -13
- data/lib/makit/mp/command_request.mp.rb +17 -17
- data/lib/makit/mp/project_mp.rb +199 -199
- data/lib/makit/mp/string_mp.rb +205 -205
- data/lib/makit/nuget.rb +74 -74
- data/lib/makit/podman/podman.rb +458 -458
- data/lib/makit/podman/podman_service_impl.rb +1081 -1081
- data/lib/makit/port.rb +32 -32
- data/lib/makit/process.rb +377 -377
- data/lib/makit/protoc.rb +112 -112
- data/lib/makit/rake/cli.rb +196 -196
- data/lib/makit/rake/trace_controller.rb +174 -174
- data/lib/makit/rake.rb +81 -81
- data/lib/makit/ruby/cli.rb +185 -185
- data/lib/makit/ruby.rb +25 -25
- data/lib/makit/rubygems.rb +137 -0
- data/lib/makit/secrets/azure_key_vault.rb +322 -322
- data/lib/makit/secrets/azure_secrets.rb +183 -183
- data/lib/makit/secrets/local_secrets.rb +72 -72
- data/lib/makit/secrets/secrets_manager.rb +105 -105
- data/lib/makit/secrets.rb +16 -16
- data/lib/makit/serializer.rb +130 -130
- data/lib/makit/services/builder.rb +186 -186
- data/lib/makit/services/error_handler.rb +226 -226
- data/lib/makit/services/repository_manager.rb +367 -367
- data/lib/makit/services/validator.rb +112 -112
- data/lib/makit/setup/classlib.rb +101 -101
- data/lib/makit/setup/gem.rb +268 -268
- data/lib/makit/setup/pages.rb +11 -11
- data/lib/makit/setup/razorclasslib.rb +101 -101
- data/lib/makit/setup/runner.rb +54 -54
- data/lib/makit/setup.rb +5 -5
- data/lib/makit/show.rb +110 -110
- data/lib/makit/storage.rb +126 -126
- data/lib/makit/symbols.rb +175 -175
- data/lib/makit/task_info.rb +130 -130
- data/lib/makit/tasks/at_exit.rb +15 -15
- data/lib/makit/tasks/build.rb +22 -22
- data/lib/makit/tasks/bump.rb +7 -7
- data/lib/makit/tasks/clean.rb +13 -13
- data/lib/makit/tasks/configure.rb +10 -10
- data/lib/makit/tasks/format.rb +10 -10
- data/lib/makit/tasks/hook_manager.rb +443 -443
- data/lib/makit/tasks/info.rb +368 -368
- data/lib/makit/tasks/init.rb +49 -49
- data/lib/makit/tasks/integrate.rb +60 -56
- data/lib/makit/tasks/pull_incoming.rb +13 -13
- data/lib/makit/tasks/secrets.rb +7 -7
- data/lib/makit/tasks/setup.rb +16 -16
- data/lib/makit/tasks/sync.rb +14 -17
- data/lib/makit/tasks/tag.rb +27 -27
- data/lib/makit/tasks/task_monkey_patch.rb +81 -81
- data/lib/makit/tasks/test.rb +22 -22
- data/lib/makit/tasks/update.rb +18 -18
- data/lib/makit/tasks/version.rb +6 -6
- data/lib/makit/tasks.rb +24 -24
- data/lib/makit/test_cache.rb +239 -239
- data/lib/makit/tree.rb +37 -37
- data/lib/makit/v1/configuration/project_service_impl.rb +370 -370
- data/lib/makit/v1/git/git_repository_service_impl.rb +295 -295
- data/lib/makit/v1/makit.v1_pb.rb +35 -35
- data/lib/makit/v1/makit.v1_services_pb.rb +27 -27
- data/lib/makit/v1/services/repository_manager_service_impl.rb +572 -572
- data/lib/makit/version.rb +661 -503
- data/lib/makit/version_util.rb +21 -21
- data/lib/makit/wix.rb +95 -95
- data/lib/makit/yaml.rb +29 -29
- data/lib/makit/zip.rb +17 -17
- data/lib/makit copy.rb +44 -44
- data/lib/makit.rb +115 -114
- metadata +3 -2
data/lib/makit/azure/cli.rb
CHANGED
|
@@ -1,285 +1,285 @@
|
|
|
1
|
-
# Azure helper methods for CLI operations
|
|
2
|
-
module Makit
|
|
3
|
-
module Azure
|
|
4
|
-
class Cli
|
|
5
|
-
# Check if Azure CLI is installed
|
|
6
|
-
def self.available?
|
|
7
|
-
system("which az > /dev/null 2>&1")
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
# Check if Azure CLI is authenticated
|
|
11
|
-
def self.authenticated?
|
|
12
|
-
return false unless available?
|
|
13
|
-
system("az account show > /dev/null 2>&1")
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
# Get Azure Storage account from environment variable
|
|
17
|
-
def self.storage_account
|
|
18
|
-
ENV["AZURE_STORAGE_ACCOUNT"]
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Get Azure Storage account key from environment variable
|
|
22
|
-
def self.storage_account_key
|
|
23
|
-
ENV["AZURE_STORAGE_ACCOUNT_KEY"]
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Validate Azure Storage credentials are set
|
|
27
|
-
def self.validate_storage_credentials!
|
|
28
|
-
if storage_account.nil? || storage_account.empty?
|
|
29
|
-
raise "AZURE_STORAGE_ACCOUNT environment variable is not set"
|
|
30
|
-
end
|
|
31
|
-
if storage_account_key.nil? || storage_account_key.empty?
|
|
32
|
-
raise "AZURE_STORAGE_ACCOUNT_KEY environment variable is not set"
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Delete all blobs in a storage path
|
|
37
|
-
# @param path [String] The storage path (e.g., '$web/apps/portal/v0.1.0')
|
|
38
|
-
def self.delete_blobs(path)
|
|
39
|
-
validate_storage_credentials!
|
|
40
|
-
puts " Deleting existing files in #{path}/".colorize(:yellow)
|
|
41
|
-
|
|
42
|
-
cmd = [
|
|
43
|
-
"az storage blob delete-batch",
|
|
44
|
-
"--source '#{path}'",
|
|
45
|
-
"--account-name #{storage_account}",
|
|
46
|
-
"--account-key '#{storage_account_key}'",
|
|
47
|
-
"--pattern '*'",
|
|
48
|
-
"2>/dev/null",
|
|
49
|
-
].join(" ")
|
|
50
|
-
|
|
51
|
-
system(cmd)
|
|
52
|
-
# Don't fail if path doesn't exist (first deployment)
|
|
53
|
-
true
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# Upload files to Azure Storage
|
|
57
|
-
# @param source [String] Local directory to upload from
|
|
58
|
-
# @param destination [String] Storage path destination (e.g., '$web/apps/portal/v0.1.0')
|
|
59
|
-
def self.upload_blobs(source, destination)
|
|
60
|
-
validate_storage_credentials!
|
|
61
|
-
puts " Uploading files from #{source} to #{destination}/".colorize(:green)
|
|
62
|
-
|
|
63
|
-
unless Dir.exist?(source)
|
|
64
|
-
raise "Source directory does not exist: #{source}"
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
cmd = [
|
|
68
|
-
"az storage blob upload-batch",
|
|
69
|
-
"--destination '#{destination}'",
|
|
70
|
-
"--source #{source}",
|
|
71
|
-
"--account-name #{storage_account}",
|
|
72
|
-
"--account-key '#{storage_account_key}'",
|
|
73
|
-
"--overwrite",
|
|
74
|
-
].join(" ")
|
|
75
|
-
|
|
76
|
-
unless system(cmd)
|
|
77
|
-
raise "Failed to upload blobs to Azure Storage"
|
|
78
|
-
end
|
|
79
|
-
true
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# Get CDN resource group from environment variable
|
|
83
|
-
def self.cdn_resource_group
|
|
84
|
-
ENV["AZURE_CDN_RESOURCE_GROUP"]
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# Get CDN profile name from environment variable
|
|
88
|
-
def self.cdn_profile_name
|
|
89
|
-
ENV["AZURE_CDN_PROFILE_NAME"]
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# Get CDN endpoint name from environment variable
|
|
93
|
-
def self.cdn_endpoint_name
|
|
94
|
-
ENV["AZURE_CDN_ENDPOINT_NAME"]
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Check if CDN variables are configured
|
|
98
|
-
def self.cdn_configured?
|
|
99
|
-
!cdn_resource_group.nil? && !cdn_resource_group.empty? &&
|
|
100
|
-
!cdn_profile_name.nil? && !cdn_profile_name.empty? &&
|
|
101
|
-
!cdn_endpoint_name.nil? && !cdn_endpoint_name.empty?
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
# Purge CDN cache for a specific path
|
|
105
|
-
# @param path [String] The path to purge (e.g., '/apps/portal/v0.1.0/*')
|
|
106
|
-
def self.purge_cdn_cache(path)
|
|
107
|
-
unless cdn_configured?
|
|
108
|
-
puts " CDN variables not configured, skipping cache purge".colorize(:yellow)
|
|
109
|
-
return false
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
puts " Purging Azure CDN cache for #{path}".colorize(:green)
|
|
113
|
-
|
|
114
|
-
cmd = [
|
|
115
|
-
"az cdn endpoint purge",
|
|
116
|
-
"--resource-group '#{cdn_resource_group}'",
|
|
117
|
-
"--profile-name '#{cdn_profile_name}'",
|
|
118
|
-
"--name '#{cdn_endpoint_name}'",
|
|
119
|
-
"--content-paths '#{path}'",
|
|
120
|
-
].join(" ")
|
|
121
|
-
|
|
122
|
-
success = system(cmd)
|
|
123
|
-
unless success
|
|
124
|
-
puts " Warning: CDN purge failed or CDN not configured".colorize(:yellow)
|
|
125
|
-
end
|
|
126
|
-
success
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
# Get static website endpoint URL for the storage account
|
|
130
|
-
# @return [String] The static website endpoint URL (e.g., 'https://louparslow.z22.web.core.windows.net')
|
|
131
|
-
def self.static_website_endpoint
|
|
132
|
-
validate_storage_credentials!
|
|
133
|
-
|
|
134
|
-
unless available?
|
|
135
|
-
# Fallback if Azure CLI not available
|
|
136
|
-
return "https://#{storage_account}.z22.web.core.windows.net"
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
# Try to get the static website endpoint from Azure
|
|
140
|
-
cmd = "az storage account show --name #{storage_account} --query 'primaryEndpoints.web' --output tsv 2>/dev/null"
|
|
141
|
-
endpoint = `#{cmd}`.strip
|
|
142
|
-
|
|
143
|
-
if endpoint.nil? || endpoint.empty?
|
|
144
|
-
# Fallback: construct URL from storage account name
|
|
145
|
-
# This assumes the standard format, but may not work for all storage accounts
|
|
146
|
-
endpoint = "https://#{storage_account}.z22.web.core.windows.net"
|
|
147
|
-
puts " Warning: Could not retrieve static website endpoint, using default format".colorize(:yellow)
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
# Remove trailing slash if present
|
|
151
|
-
endpoint.chomp("/")
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
# List Key Vaults in a resource group
|
|
155
|
-
# @param resource_group [String] The Azure resource group name
|
|
156
|
-
# @return [Array<Hash>] Array of key vault information hashes, or empty array if none found
|
|
157
|
-
def self.list_key_vaults(resource_group)
|
|
158
|
-
unless available?
|
|
159
|
-
raise "Azure CLI is not installed. Install it from: https://aka.ms/InstallAzureCLI"
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
unless authenticated?
|
|
163
|
-
raise "Azure CLI is not authenticated. Run 'az login' first"
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
if resource_group.nil? || resource_group.empty?
|
|
167
|
-
raise "Resource group name is required"
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
puts " Listing Key Vaults in resource group: #{resource_group}".colorize(:cyan)
|
|
171
|
-
|
|
172
|
-
# Query for key vaults in the resource group
|
|
173
|
-
cmd = [
|
|
174
|
-
"az keyvault list",
|
|
175
|
-
"--resource-group '#{resource_group}'",
|
|
176
|
-
"--query '[].{Name:name, Location:location, ResourceGroup:resourceGroup, VaultUri:properties.vaultUri}'",
|
|
177
|
-
"--output json",
|
|
178
|
-
"2>/dev/null",
|
|
179
|
-
].join(" ")
|
|
180
|
-
|
|
181
|
-
output = `#{cmd}`.strip
|
|
182
|
-
|
|
183
|
-
if output.nil? || output.empty?
|
|
184
|
-
puts " No Key Vaults found in resource group: #{resource_group}".colorize(:yellow)
|
|
185
|
-
return []
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
begin
|
|
189
|
-
require "json"
|
|
190
|
-
key_vaults = JSON.parse(output)
|
|
191
|
-
key_vaults
|
|
192
|
-
rescue JSON::ParserError => e
|
|
193
|
-
puts " Error parsing Key Vault list: #{e.message}".colorize(:red)
|
|
194
|
-
puts " Raw output: #{output}".colorize(:yellow)
|
|
195
|
-
[]
|
|
196
|
-
end
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
# Grant Key Vault access to a service principal
|
|
200
|
-
# @param app_id [String] The service principal app ID (client ID)
|
|
201
|
-
# @param resource_group [String] The Azure resource group name (default: "LouParslow")
|
|
202
|
-
# @param keyvault_name [String] The Key Vault name (default: "louparslow-dev-secrets")
|
|
203
|
-
# @return [Boolean] true if successful, raises error on failure
|
|
204
|
-
def self.grant_keyvault_access(app_id, resource_group: "LouParslow", keyvault_name: "louparslow-dev-secrets")
|
|
205
|
-
unless available?
|
|
206
|
-
raise "Azure CLI is not installed. Install it from: https://aka.ms/InstallAzureCLI"
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
unless authenticated?
|
|
210
|
-
raise "Azure CLI is not authenticated. Run 'az login' first"
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
if app_id.nil? || app_id.empty?
|
|
214
|
-
raise "Service principal app ID (appId) is required"
|
|
215
|
-
end
|
|
216
|
-
|
|
217
|
-
if resource_group.nil? || resource_group.empty?
|
|
218
|
-
raise "Resource group name is required"
|
|
219
|
-
end
|
|
220
|
-
|
|
221
|
-
if keyvault_name.nil? || keyvault_name.empty?
|
|
222
|
-
raise "Key Vault name is required"
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
puts " Granting Key Vault access to service principal...".colorize(:cyan)
|
|
226
|
-
puts " App ID: #{app_id}".colorize(:white)
|
|
227
|
-
puts " Resource Group: #{resource_group}".colorize(:white)
|
|
228
|
-
puts " Key Vault: #{keyvault_name}".colorize(:white)
|
|
229
|
-
|
|
230
|
-
# Get subscription ID
|
|
231
|
-
puts " Getting subscription ID...".colorize(:cyan)
|
|
232
|
-
subscription_cmd = "az account show --query id -o tsv 2>/dev/null"
|
|
233
|
-
subscription_id = `#{subscription_cmd}`.strip
|
|
234
|
-
|
|
235
|
-
if subscription_id.nil? || subscription_id.empty?
|
|
236
|
-
raise "Failed to get subscription ID. Ensure you're logged in with 'az login'"
|
|
237
|
-
end
|
|
238
|
-
|
|
239
|
-
puts " Subscription ID: #{subscription_id}".colorize(:white)
|
|
240
|
-
|
|
241
|
-
# Get service principal object ID
|
|
242
|
-
puts " Getting service principal object ID...".colorize(:cyan)
|
|
243
|
-
sp_object_id_cmd = "az ad sp show --id '#{app_id}' --query id -o tsv 2>/dev/null"
|
|
244
|
-
sp_object_id = `#{sp_object_id_cmd}`.strip
|
|
245
|
-
|
|
246
|
-
if sp_object_id.nil? || sp_object_id.empty?
|
|
247
|
-
raise "Failed to get service principal object ID for app ID: #{app_id}. Ensure the service principal exists."
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
puts " Service Principal Object ID: #{sp_object_id}".colorize(:white)
|
|
251
|
-
|
|
252
|
-
# Grant "Key Vault Secrets User" role
|
|
253
|
-
scope = "/subscriptions/#{subscription_id}/resourceGroups/#{resource_group}/providers/Microsoft.KeyVault/vaults/#{keyvault_name}"
|
|
254
|
-
puts " Granting 'Key Vault Secrets User' role...".colorize(:cyan)
|
|
255
|
-
puts " Scope: #{scope}".colorize(:white)
|
|
256
|
-
|
|
257
|
-
role_assignment_cmd = [
|
|
258
|
-
"az role assignment create",
|
|
259
|
-
"--assignee '#{sp_object_id}'",
|
|
260
|
-
"--role 'Key Vault Secrets User'",
|
|
261
|
-
"--scope '#{scope}'",
|
|
262
|
-
"2>&1",
|
|
263
|
-
].join(" ")
|
|
264
|
-
|
|
265
|
-
output = `#{role_assignment_cmd}`
|
|
266
|
-
exit_code = $?.exitstatus
|
|
267
|
-
|
|
268
|
-
if exit_code != 0
|
|
269
|
-
# Check if role assignment already exists
|
|
270
|
-
if output.include?("already exists") || output.include?("RoleAssignmentExists")
|
|
271
|
-
puts " Role assignment already exists (this is OK)".colorize(:yellow)
|
|
272
|
-
return true
|
|
273
|
-
else
|
|
274
|
-
puts " Error output: #{output}".colorize(:red)
|
|
275
|
-
raise "Failed to grant Key Vault access: #{output}"
|
|
276
|
-
end
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
puts " Successfully granted Key Vault access".colorize(:green)
|
|
280
|
-
true
|
|
281
|
-
end
|
|
282
|
-
end
|
|
283
|
-
end
|
|
284
|
-
end
|
|
1
|
+
# Azure helper methods for CLI operations
|
|
2
|
+
module Makit
|
|
3
|
+
module Azure
|
|
4
|
+
class Cli
|
|
5
|
+
# Check if Azure CLI is installed
|
|
6
|
+
def self.available?
|
|
7
|
+
system("which az > /dev/null 2>&1")
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Check if Azure CLI is authenticated
|
|
11
|
+
def self.authenticated?
|
|
12
|
+
return false unless available?
|
|
13
|
+
system("az account show > /dev/null 2>&1")
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Get Azure Storage account from environment variable
|
|
17
|
+
def self.storage_account
|
|
18
|
+
ENV["AZURE_STORAGE_ACCOUNT"]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Get Azure Storage account key from environment variable
|
|
22
|
+
def self.storage_account_key
|
|
23
|
+
ENV["AZURE_STORAGE_ACCOUNT_KEY"]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Validate Azure Storage credentials are set
|
|
27
|
+
def self.validate_storage_credentials!
|
|
28
|
+
if storage_account.nil? || storage_account.empty?
|
|
29
|
+
raise "AZURE_STORAGE_ACCOUNT environment variable is not set"
|
|
30
|
+
end
|
|
31
|
+
if storage_account_key.nil? || storage_account_key.empty?
|
|
32
|
+
raise "AZURE_STORAGE_ACCOUNT_KEY environment variable is not set"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Delete all blobs in a storage path
|
|
37
|
+
# @param path [String] The storage path (e.g., '$web/apps/portal/v0.1.0')
|
|
38
|
+
def self.delete_blobs(path)
|
|
39
|
+
validate_storage_credentials!
|
|
40
|
+
puts " Deleting existing files in #{path}/".colorize(:yellow)
|
|
41
|
+
|
|
42
|
+
cmd = [
|
|
43
|
+
"az storage blob delete-batch",
|
|
44
|
+
"--source '#{path}'",
|
|
45
|
+
"--account-name #{storage_account}",
|
|
46
|
+
"--account-key '#{storage_account_key}'",
|
|
47
|
+
"--pattern '*'",
|
|
48
|
+
"2>/dev/null",
|
|
49
|
+
].join(" ")
|
|
50
|
+
|
|
51
|
+
system(cmd)
|
|
52
|
+
# Don't fail if path doesn't exist (first deployment)
|
|
53
|
+
true
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Upload files to Azure Storage
|
|
57
|
+
# @param source [String] Local directory to upload from
|
|
58
|
+
# @param destination [String] Storage path destination (e.g., '$web/apps/portal/v0.1.0')
|
|
59
|
+
def self.upload_blobs(source, destination)
|
|
60
|
+
validate_storage_credentials!
|
|
61
|
+
puts " Uploading files from #{source} to #{destination}/".colorize(:green)
|
|
62
|
+
|
|
63
|
+
unless Dir.exist?(source)
|
|
64
|
+
raise "Source directory does not exist: #{source}"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
cmd = [
|
|
68
|
+
"az storage blob upload-batch",
|
|
69
|
+
"--destination '#{destination}'",
|
|
70
|
+
"--source #{source}",
|
|
71
|
+
"--account-name #{storage_account}",
|
|
72
|
+
"--account-key '#{storage_account_key}'",
|
|
73
|
+
"--overwrite",
|
|
74
|
+
].join(" ")
|
|
75
|
+
|
|
76
|
+
unless system(cmd)
|
|
77
|
+
raise "Failed to upload blobs to Azure Storage"
|
|
78
|
+
end
|
|
79
|
+
true
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Get CDN resource group from environment variable
|
|
83
|
+
def self.cdn_resource_group
|
|
84
|
+
ENV["AZURE_CDN_RESOURCE_GROUP"]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Get CDN profile name from environment variable
|
|
88
|
+
def self.cdn_profile_name
|
|
89
|
+
ENV["AZURE_CDN_PROFILE_NAME"]
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Get CDN endpoint name from environment variable
|
|
93
|
+
def self.cdn_endpoint_name
|
|
94
|
+
ENV["AZURE_CDN_ENDPOINT_NAME"]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Check if CDN variables are configured
|
|
98
|
+
def self.cdn_configured?
|
|
99
|
+
!cdn_resource_group.nil? && !cdn_resource_group.empty? &&
|
|
100
|
+
!cdn_profile_name.nil? && !cdn_profile_name.empty? &&
|
|
101
|
+
!cdn_endpoint_name.nil? && !cdn_endpoint_name.empty?
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Purge CDN cache for a specific path
|
|
105
|
+
# @param path [String] The path to purge (e.g., '/apps/portal/v0.1.0/*')
|
|
106
|
+
def self.purge_cdn_cache(path)
|
|
107
|
+
unless cdn_configured?
|
|
108
|
+
puts " CDN variables not configured, skipping cache purge".colorize(:yellow)
|
|
109
|
+
return false
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
puts " Purging Azure CDN cache for #{path}".colorize(:green)
|
|
113
|
+
|
|
114
|
+
cmd = [
|
|
115
|
+
"az cdn endpoint purge",
|
|
116
|
+
"--resource-group '#{cdn_resource_group}'",
|
|
117
|
+
"--profile-name '#{cdn_profile_name}'",
|
|
118
|
+
"--name '#{cdn_endpoint_name}'",
|
|
119
|
+
"--content-paths '#{path}'",
|
|
120
|
+
].join(" ")
|
|
121
|
+
|
|
122
|
+
success = system(cmd)
|
|
123
|
+
unless success
|
|
124
|
+
puts " Warning: CDN purge failed or CDN not configured".colorize(:yellow)
|
|
125
|
+
end
|
|
126
|
+
success
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Get static website endpoint URL for the storage account
|
|
130
|
+
# @return [String] The static website endpoint URL (e.g., 'https://louparslow.z22.web.core.windows.net')
|
|
131
|
+
def self.static_website_endpoint
|
|
132
|
+
validate_storage_credentials!
|
|
133
|
+
|
|
134
|
+
unless available?
|
|
135
|
+
# Fallback if Azure CLI not available
|
|
136
|
+
return "https://#{storage_account}.z22.web.core.windows.net"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Try to get the static website endpoint from Azure
|
|
140
|
+
cmd = "az storage account show --name #{storage_account} --query 'primaryEndpoints.web' --output tsv 2>/dev/null"
|
|
141
|
+
endpoint = `#{cmd}`.strip
|
|
142
|
+
|
|
143
|
+
if endpoint.nil? || endpoint.empty?
|
|
144
|
+
# Fallback: construct URL from storage account name
|
|
145
|
+
# This assumes the standard format, but may not work for all storage accounts
|
|
146
|
+
endpoint = "https://#{storage_account}.z22.web.core.windows.net"
|
|
147
|
+
puts " Warning: Could not retrieve static website endpoint, using default format".colorize(:yellow)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Remove trailing slash if present
|
|
151
|
+
endpoint.chomp("/")
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# List Key Vaults in a resource group
|
|
155
|
+
# @param resource_group [String] The Azure resource group name
|
|
156
|
+
# @return [Array<Hash>] Array of key vault information hashes, or empty array if none found
|
|
157
|
+
def self.list_key_vaults(resource_group)
|
|
158
|
+
unless available?
|
|
159
|
+
raise "Azure CLI is not installed. Install it from: https://aka.ms/InstallAzureCLI"
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
unless authenticated?
|
|
163
|
+
raise "Azure CLI is not authenticated. Run 'az login' first"
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
if resource_group.nil? || resource_group.empty?
|
|
167
|
+
raise "Resource group name is required"
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
puts " Listing Key Vaults in resource group: #{resource_group}".colorize(:cyan)
|
|
171
|
+
|
|
172
|
+
# Query for key vaults in the resource group
|
|
173
|
+
cmd = [
|
|
174
|
+
"az keyvault list",
|
|
175
|
+
"--resource-group '#{resource_group}'",
|
|
176
|
+
"--query '[].{Name:name, Location:location, ResourceGroup:resourceGroup, VaultUri:properties.vaultUri}'",
|
|
177
|
+
"--output json",
|
|
178
|
+
"2>/dev/null",
|
|
179
|
+
].join(" ")
|
|
180
|
+
|
|
181
|
+
output = `#{cmd}`.strip
|
|
182
|
+
|
|
183
|
+
if output.nil? || output.empty?
|
|
184
|
+
puts " No Key Vaults found in resource group: #{resource_group}".colorize(:yellow)
|
|
185
|
+
return []
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
begin
|
|
189
|
+
require "json"
|
|
190
|
+
key_vaults = JSON.parse(output)
|
|
191
|
+
key_vaults
|
|
192
|
+
rescue JSON::ParserError => e
|
|
193
|
+
puts " Error parsing Key Vault list: #{e.message}".colorize(:red)
|
|
194
|
+
puts " Raw output: #{output}".colorize(:yellow)
|
|
195
|
+
[]
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Grant Key Vault access to a service principal
|
|
200
|
+
# @param app_id [String] The service principal app ID (client ID)
|
|
201
|
+
# @param resource_group [String] The Azure resource group name (default: "LouParslow")
|
|
202
|
+
# @param keyvault_name [String] The Key Vault name (default: "louparslow-dev-secrets")
|
|
203
|
+
# @return [Boolean] true if successful, raises error on failure
|
|
204
|
+
def self.grant_keyvault_access(app_id, resource_group: "LouParslow", keyvault_name: "louparslow-dev-secrets")
|
|
205
|
+
unless available?
|
|
206
|
+
raise "Azure CLI is not installed. Install it from: https://aka.ms/InstallAzureCLI"
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
unless authenticated?
|
|
210
|
+
raise "Azure CLI is not authenticated. Run 'az login' first"
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
if app_id.nil? || app_id.empty?
|
|
214
|
+
raise "Service principal app ID (appId) is required"
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
if resource_group.nil? || resource_group.empty?
|
|
218
|
+
raise "Resource group name is required"
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
if keyvault_name.nil? || keyvault_name.empty?
|
|
222
|
+
raise "Key Vault name is required"
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
puts " Granting Key Vault access to service principal...".colorize(:cyan)
|
|
226
|
+
puts " App ID: #{app_id}".colorize(:white)
|
|
227
|
+
puts " Resource Group: #{resource_group}".colorize(:white)
|
|
228
|
+
puts " Key Vault: #{keyvault_name}".colorize(:white)
|
|
229
|
+
|
|
230
|
+
# Get subscription ID
|
|
231
|
+
puts " Getting subscription ID...".colorize(:cyan)
|
|
232
|
+
subscription_cmd = "az account show --query id -o tsv 2>/dev/null"
|
|
233
|
+
subscription_id = `#{subscription_cmd}`.strip
|
|
234
|
+
|
|
235
|
+
if subscription_id.nil? || subscription_id.empty?
|
|
236
|
+
raise "Failed to get subscription ID. Ensure you're logged in with 'az login'"
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
puts " Subscription ID: #{subscription_id}".colorize(:white)
|
|
240
|
+
|
|
241
|
+
# Get service principal object ID
|
|
242
|
+
puts " Getting service principal object ID...".colorize(:cyan)
|
|
243
|
+
sp_object_id_cmd = "az ad sp show --id '#{app_id}' --query id -o tsv 2>/dev/null"
|
|
244
|
+
sp_object_id = `#{sp_object_id_cmd}`.strip
|
|
245
|
+
|
|
246
|
+
if sp_object_id.nil? || sp_object_id.empty?
|
|
247
|
+
raise "Failed to get service principal object ID for app ID: #{app_id}. Ensure the service principal exists."
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
puts " Service Principal Object ID: #{sp_object_id}".colorize(:white)
|
|
251
|
+
|
|
252
|
+
# Grant "Key Vault Secrets User" role
|
|
253
|
+
scope = "/subscriptions/#{subscription_id}/resourceGroups/#{resource_group}/providers/Microsoft.KeyVault/vaults/#{keyvault_name}"
|
|
254
|
+
puts " Granting 'Key Vault Secrets User' role...".colorize(:cyan)
|
|
255
|
+
puts " Scope: #{scope}".colorize(:white)
|
|
256
|
+
|
|
257
|
+
role_assignment_cmd = [
|
|
258
|
+
"az role assignment create",
|
|
259
|
+
"--assignee '#{sp_object_id}'",
|
|
260
|
+
"--role 'Key Vault Secrets User'",
|
|
261
|
+
"--scope '#{scope}'",
|
|
262
|
+
"2>&1",
|
|
263
|
+
].join(" ")
|
|
264
|
+
|
|
265
|
+
output = `#{role_assignment_cmd}`
|
|
266
|
+
exit_code = $?.exitstatus
|
|
267
|
+
|
|
268
|
+
if exit_code != 0
|
|
269
|
+
# Check if role assignment already exists
|
|
270
|
+
if output.include?("already exists") || output.include?("RoleAssignmentExists")
|
|
271
|
+
puts " Role assignment already exists (this is OK)".colorize(:yellow)
|
|
272
|
+
return true
|
|
273
|
+
else
|
|
274
|
+
puts " Error output: #{output}".colorize(:red)
|
|
275
|
+
raise "Failed to grant Key Vault access: #{output}"
|
|
276
|
+
end
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
puts " Successfully granted Key Vault access".colorize(:green)
|
|
280
|
+
true
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
285
|
|
data/lib/makit/cli/base.rb
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "clamp"
|
|
4
|
-
|
|
5
|
-
module Makit
|
|
6
|
-
module Cli
|
|
7
|
-
# Base class for CLI commands
|
|
8
|
-
class Base < Clamp::Command
|
|
9
|
-
# Common functionality for CLI commands can be added here
|
|
10
|
-
|
|
11
|
-
# Helper method to define command descriptions
|
|
12
|
-
def self.desc(command_name, description)
|
|
13
|
-
self.description = "#{command_name} - #{description}"
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "clamp"
|
|
4
|
+
|
|
5
|
+
module Makit
|
|
6
|
+
module Cli
|
|
7
|
+
# Base class for CLI commands
|
|
8
|
+
class Base < Clamp::Command
|
|
9
|
+
# Common functionality for CLI commands can be added here
|
|
10
|
+
|
|
11
|
+
# Helper method to define command descriptions
|
|
12
|
+
def self.desc(command_name, description)
|
|
13
|
+
self.description = "#{command_name} - #{description}"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|