belt 0.2.7 → 0.2.9
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/lib/belt/cli/backup_config.rb +4 -89
- data/lib/belt/cli/backup_runner.rb +37 -54
- data/lib/belt/cli/console_command.rb +9 -0
- data/lib/belt/cli/deploy_command.rb +21 -1
- data/lib/belt/cli/environment_config.rb +131 -0
- data/lib/belt/cli/tables_command.rb +3 -3
- data/lib/belt/cli/terraform_command.rb +8 -0
- data/lib/belt/helpers/cors_origin.rb +1 -1
- data/lib/belt/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 37cf283dc30b43f875f8600eb4b00e46b5782f374d3be306d0c5adaec7e9024a
|
|
4
|
+
data.tar.gz: 1153f552cef87426f91afcf28647065017b77a09bdd9347241366ed9cc455b76
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4cafc0f29e0148e74950e9fe0898e74632f2563980f60ffece7bfbb58ec7187ec30f928a0958dff9d9832fe79b21e8a87e512f26c4cddfa58c1f243776cea5eb
|
|
7
|
+
data.tar.gz: 7850fc8e4c5921646c25f77f67fdfcae663e148ec597b514b095b637733b0e55f34182e8c64ea52f2294ed687b3a24f7b6a56462b30df07811a01ffae23da952
|
|
@@ -7,17 +7,11 @@ module Belt
|
|
|
7
7
|
|
|
8
8
|
# Load backup config from infrastructure/<env>/belt.rb
|
|
9
9
|
# Returns nil if no config file exists or backups aren't configured
|
|
10
|
+
# NOTE: Prefer EnvironmentConfig.load which loads the full config including backups.
|
|
11
|
+
# This method is kept for backward compatibility.
|
|
10
12
|
def self.load(env, infra_dir: nil)
|
|
11
|
-
infra_dir
|
|
12
|
-
|
|
13
|
-
return nil unless File.exist?(config_file)
|
|
14
|
-
|
|
15
|
-
config = new
|
|
16
|
-
evaluator = ConfigEvaluator.new(config)
|
|
17
|
-
evaluator.evaluate(File.read(config_file), config_file)
|
|
18
|
-
|
|
19
|
-
# Return nil if no backups were configured
|
|
20
|
-
config.any? ? config : nil
|
|
13
|
+
env_config = EnvironmentConfig.load(env, infra_dir: infra_dir)
|
|
14
|
+
env_config.backups? ? env_config.backup_config : nil
|
|
21
15
|
end
|
|
22
16
|
|
|
23
17
|
def initialize
|
|
@@ -38,85 +32,6 @@ module Belt
|
|
|
38
32
|
def any?
|
|
39
33
|
dynamodb? || cognito? || @s3_buckets.any?
|
|
40
34
|
end
|
|
41
|
-
|
|
42
|
-
# Evaluates the belt.rb config file in an isolated context
|
|
43
|
-
# that intercepts Belt.configure calls
|
|
44
|
-
class ConfigEvaluator
|
|
45
|
-
def initialize(config)
|
|
46
|
-
@config = config
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def evaluate(content, filename)
|
|
50
|
-
# The config file calls Belt.configure { |config| ... }
|
|
51
|
-
# We wrap the content to intercept the Belt constant lookup
|
|
52
|
-
# by evaluating in a module where Belt is redefined
|
|
53
|
-
config = @config
|
|
54
|
-
sandbox = Module.new
|
|
55
|
-
# Define a Belt module inside the sandbox that has .configure
|
|
56
|
-
belt_proxy = Module.new do
|
|
57
|
-
define_singleton_method(:configure) do |&block|
|
|
58
|
-
block.call(ConfigDSL.new(config))
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
sandbox.const_set(:Belt, belt_proxy)
|
|
62
|
-
|
|
63
|
-
# Evaluate the file content within the sandbox module
|
|
64
|
-
sandbox.module_eval(content, filename)
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# DSL yielded to the block inside Belt.configure { |config| ... }
|
|
69
|
-
class ConfigDSL
|
|
70
|
-
def initialize(config)
|
|
71
|
-
@config = config
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def backups(enabled = nil, &block)
|
|
75
|
-
if enabled == true
|
|
76
|
-
# Simple mode: config.backups = true → just DynamoDB for all tables
|
|
77
|
-
@config.instance_variable_set(:@dynamodb_tables, :all)
|
|
78
|
-
elsif block
|
|
79
|
-
backup_dsl = BackupDSL.new(@config)
|
|
80
|
-
backup_dsl.instance_eval(&block)
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
# DSL for the backups block
|
|
86
|
-
class BackupDSL
|
|
87
|
-
def initialize(config)
|
|
88
|
-
@config = config
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def dynamodb(scope = :all)
|
|
92
|
-
if scope == :all
|
|
93
|
-
@config.instance_variable_set(:@dynamodb_tables, :all)
|
|
94
|
-
else
|
|
95
|
-
tables = Array(scope)
|
|
96
|
-
@config.instance_variable_set(:@dynamodb_tables, tables.map(&:to_s))
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
def cognito(*exports)
|
|
101
|
-
@config.instance_variable_set(:@cognito_exports, exports.flatten.map(&:to_sym))
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
def s3(*buckets)
|
|
105
|
-
@config.instance_variable_set(:@s3_buckets, buckets.flatten.map(&:to_sym))
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def retention(opts = {})
|
|
109
|
-
current = @config.instance_variable_get(:@retention)
|
|
110
|
-
merged = current.merge(opts.transform_keys(&:to_sym))
|
|
111
|
-
|
|
112
|
-
# Convert duration objects (90.days) to integer days
|
|
113
|
-
merged.transform_values! do |v|
|
|
114
|
-
v.respond_to?(:to_i) ? v.to_i : v
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
@config.instance_variable_set(:@retention, merged)
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
35
|
end
|
|
121
36
|
end
|
|
122
37
|
end
|
|
@@ -14,7 +14,7 @@ module Belt
|
|
|
14
14
|
@infra_dir = infra_dir
|
|
15
15
|
@app_name = app_name
|
|
16
16
|
@timestamp = Time.now.strftime('%Y%m%d-%H%M%S')
|
|
17
|
-
@backup_bucket = "#{@app_name}-backups-#{@env}"
|
|
17
|
+
@backup_bucket = sanitize_bucket_name("#{@app_name}-backups-#{@env}")
|
|
18
18
|
@errors = []
|
|
19
19
|
@summary = []
|
|
20
20
|
end
|
|
@@ -41,6 +41,8 @@ module Belt
|
|
|
41
41
|
|
|
42
42
|
puts " 📦 Creating backup bucket: #{@backup_bucket}"
|
|
43
43
|
|
|
44
|
+
errors_before = @errors.size
|
|
45
|
+
|
|
44
46
|
run_aws('s3', 'mb', "s3://#{@backup_bucket}", '--region', detect_region)
|
|
45
47
|
|
|
46
48
|
# Enable versioning
|
|
@@ -54,7 +56,11 @@ module Belt
|
|
|
54
56
|
'--public-access-block-configuration',
|
|
55
57
|
'BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true')
|
|
56
58
|
|
|
57
|
-
|
|
59
|
+
if @errors.size > errors_before
|
|
60
|
+
puts ' ⚠️ Backup bucket creation failed (see warnings below)'
|
|
61
|
+
else
|
|
62
|
+
puts ' ✅ Backup bucket created and secured'
|
|
63
|
+
end
|
|
58
64
|
end
|
|
59
65
|
|
|
60
66
|
# ─── DynamoDB ────────────────────────────────────────────────────
|
|
@@ -153,11 +159,11 @@ module Belt
|
|
|
153
159
|
|
|
154
160
|
def export_cognito_users(pool_id)
|
|
155
161
|
all_users = []
|
|
156
|
-
|
|
162
|
+
next_token = nil
|
|
157
163
|
|
|
158
164
|
loop do
|
|
159
|
-
args = ['aws', 'cognito-idp', 'list-users', '--user-pool-id', pool_id, '--max-
|
|
160
|
-
args += ['--
|
|
165
|
+
args = ['aws', 'cognito-idp', 'list-users', '--user-pool-id', pool_id, '--max-items', '60']
|
|
166
|
+
args += ['--starting-token', next_token] if next_token
|
|
161
167
|
|
|
162
168
|
output, status = Open3.capture2(*args)
|
|
163
169
|
break unless status.success?
|
|
@@ -165,8 +171,8 @@ module Belt
|
|
|
165
171
|
data = JSON.parse(output)
|
|
166
172
|
all_users.concat(data['Users'] || [])
|
|
167
173
|
|
|
168
|
-
|
|
169
|
-
break if
|
|
174
|
+
next_token = data['NextToken']
|
|
175
|
+
break if next_token.nil? || next_token.empty?
|
|
170
176
|
|
|
171
177
|
puts ' Fetching next page of users...'
|
|
172
178
|
end
|
|
@@ -327,69 +333,41 @@ module Belt
|
|
|
327
333
|
end
|
|
328
334
|
end
|
|
329
335
|
|
|
330
|
-
# ───
|
|
336
|
+
# ─── Table Discovery ──────────────────────────────────────────────
|
|
331
337
|
|
|
332
338
|
def resolve_table_names
|
|
333
|
-
@resolve_table_names ||=
|
|
339
|
+
@resolve_table_names ||= discover_tables_from_aws
|
|
334
340
|
end
|
|
335
341
|
|
|
336
|
-
def
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
names = try_terraform_table_output
|
|
342
|
-
return names if names
|
|
342
|
+
def discover_tables_from_aws
|
|
343
|
+
# List all DynamoDB tables matching the app-env prefix directly from AWS.
|
|
344
|
+
# This is the source of truth — no Terraform output maintenance required.
|
|
345
|
+
prefix = "#{@app_name}-#{@env}-"
|
|
346
|
+
sanitized_prefix = sanitize_bucket_name(prefix)
|
|
343
347
|
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
def try_terraform_table_output
|
|
349
|
-
output, status = Open3.capture2('terraform', 'output', '-json', 'dynamodb_table_names')
|
|
350
|
-
return nil unless status.success?
|
|
351
|
-
|
|
352
|
-
names = begin
|
|
353
|
-
JSON.parse(output)
|
|
354
|
-
rescue StandardError
|
|
355
|
-
nil
|
|
348
|
+
output, status = Open3.capture2('aws', 'dynamodb', 'list-tables', '--output', 'json')
|
|
349
|
+
unless status.success?
|
|
350
|
+
@errors << 'Failed to list DynamoDB tables from AWS'
|
|
351
|
+
return []
|
|
356
352
|
end
|
|
357
|
-
names.is_a?(Array) && names.any? ? Array(names) : nil
|
|
358
|
-
end
|
|
359
|
-
|
|
360
|
-
def fetch_table_names_from_all_outputs
|
|
361
|
-
output, status = Open3.capture2('terraform', 'output', '-json')
|
|
362
|
-
return [] unless status.success?
|
|
363
353
|
|
|
364
|
-
|
|
365
|
-
JSON.parse(output)
|
|
354
|
+
all_tables = begin
|
|
355
|
+
JSON.parse(output)['TableNames'] || []
|
|
366
356
|
rescue StandardError
|
|
367
|
-
|
|
368
|
-
end
|
|
369
|
-
|
|
370
|
-
# Look for dynamodb_table_names in output
|
|
371
|
-
if all_outputs['dynamodb_table_names']
|
|
372
|
-
val = all_outputs['dynamodb_table_names']['value']
|
|
373
|
-
return Array(val) if val
|
|
357
|
+
[]
|
|
374
358
|
end
|
|
375
359
|
|
|
376
|
-
|
|
377
|
-
all_outputs.each do |key, data|
|
|
378
|
-
next unless key.include?('table')
|
|
379
|
-
|
|
380
|
-
val = data['value']
|
|
381
|
-
return Array(val) if val.is_a?(Array) && val.any?
|
|
382
|
-
end
|
|
383
|
-
|
|
384
|
-
[]
|
|
360
|
+
all_tables.select { |t| t.start_with?(prefix) || t.start_with?(sanitized_prefix) }
|
|
385
361
|
end
|
|
386
362
|
|
|
363
|
+
# ─── Terraform Output Resolution ─────────────────────────────────
|
|
364
|
+
|
|
387
365
|
def resolve_cognito_pool_id
|
|
388
366
|
env_dir = File.join(@infra_dir, @env)
|
|
389
367
|
return nil unless Dir.exist?(env_dir)
|
|
390
368
|
|
|
391
369
|
Dir.chdir(env_dir) do
|
|
392
|
-
output, status = Open3.capture2('terraform', 'output', '-json', 'user_pool_id')
|
|
370
|
+
output, status = Open3.capture2('terraform', 'output', '-json', 'user_pool_id', err: File::NULL)
|
|
393
371
|
if status.success?
|
|
394
372
|
val = begin
|
|
395
373
|
JSON.parse(output)
|
|
@@ -400,7 +378,7 @@ module Belt
|
|
|
400
378
|
end
|
|
401
379
|
|
|
402
380
|
# Fallback: search all outputs
|
|
403
|
-
output, status = Open3.capture2('terraform', 'output', '-json')
|
|
381
|
+
output, status = Open3.capture2('terraform', 'output', '-json', err: File::NULL)
|
|
404
382
|
if status.success?
|
|
405
383
|
all_outputs = begin
|
|
406
384
|
JSON.parse(output)
|
|
@@ -477,6 +455,11 @@ module Belt
|
|
|
477
455
|
|
|
478
456
|
# ─── Helpers ─────────────────────────────────────────────────────
|
|
479
457
|
|
|
458
|
+
def sanitize_bucket_name(name)
|
|
459
|
+
# S3 bucket names must be DNS-compliant: lowercase, hyphens, no underscores
|
|
460
|
+
name.tr('_', '-').downcase.gsub(/[^a-z0-9\-.]/, '-').gsub(/-{2,}/, '-')
|
|
461
|
+
end
|
|
462
|
+
|
|
480
463
|
def short_table_name(table_name)
|
|
481
464
|
table_name.sub(/\A#{Regexp.escape(@app_name)}-#{Regexp.escape(@env)}-/, '')
|
|
482
465
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'optparse'
|
|
4
|
+
require_relative 'environment_config'
|
|
4
5
|
|
|
5
6
|
module Belt
|
|
6
7
|
module CLI
|
|
@@ -25,6 +26,8 @@ module Belt
|
|
|
25
26
|
@environment = @args.first || ENV['BELT_ENV'] || 'dev'
|
|
26
27
|
ENV['ENVIRONMENT'] = @environment
|
|
27
28
|
|
|
29
|
+
apply_env_config!
|
|
30
|
+
|
|
28
31
|
if @options[:run]
|
|
29
32
|
exec_runner(@options[:run])
|
|
30
33
|
else
|
|
@@ -45,6 +48,12 @@ module Belt
|
|
|
45
48
|
end.parse!(@args)
|
|
46
49
|
end
|
|
47
50
|
|
|
51
|
+
def apply_env_config!
|
|
52
|
+
env_config = EnvironmentConfig.load(@environment)
|
|
53
|
+
env_config.apply!
|
|
54
|
+
puts " 🔑 Using AWS profile: #{env_config.aws_profile}" if env_config.aws_profile?
|
|
55
|
+
end
|
|
56
|
+
|
|
48
57
|
def exec_console
|
|
49
58
|
production_guard!
|
|
50
59
|
boot_app
|
|
@@ -8,6 +8,7 @@ require_relative 'env_resolver'
|
|
|
8
8
|
require_relative 'terraform_command'
|
|
9
9
|
require_relative 'backup_config'
|
|
10
10
|
require_relative 'backup_runner'
|
|
11
|
+
require_relative 'environment_config'
|
|
11
12
|
|
|
12
13
|
module Belt
|
|
13
14
|
module CLI
|
|
@@ -144,6 +145,8 @@ module Belt
|
|
|
144
145
|
validate!
|
|
145
146
|
env_dir = File.join(@infra_dir, @env)
|
|
146
147
|
|
|
148
|
+
load_and_apply_env_config!
|
|
149
|
+
|
|
147
150
|
puts "belt → deploying #{@env} (in #{env_dir}/)\n\n"
|
|
148
151
|
|
|
149
152
|
ensure_lockfile_consistent!
|
|
@@ -168,6 +171,7 @@ module Belt
|
|
|
168
171
|
|
|
169
172
|
def run_backup_only
|
|
170
173
|
validate!
|
|
174
|
+
load_and_apply_env_config!
|
|
171
175
|
|
|
172
176
|
puts "belt → running backups for #{@env}\n\n"
|
|
173
177
|
|
|
@@ -190,6 +194,7 @@ module Belt
|
|
|
190
194
|
|
|
191
195
|
def run_rebuild
|
|
192
196
|
validate!
|
|
197
|
+
load_and_apply_env_config!
|
|
193
198
|
validate_aws!
|
|
194
199
|
|
|
195
200
|
puts "belt → rebuilding Lambda for #{@env}\n\n"
|
|
@@ -207,6 +212,21 @@ module Belt
|
|
|
207
212
|
|
|
208
213
|
private
|
|
209
214
|
|
|
215
|
+
def load_and_apply_env_config!
|
|
216
|
+
@env_config = EnvironmentConfig.load(@env, infra_dir: @infra_dir)
|
|
217
|
+
@env_config.apply!
|
|
218
|
+
print_env_config_info
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def print_env_config_info
|
|
222
|
+
puts " 🔑 Using AWS profile: #{@env_config.aws_profile}" if @env_config.aws_profile?
|
|
223
|
+
return unless @env_config.env_vars?
|
|
224
|
+
|
|
225
|
+
@env_config.env_vars.each_key do |key|
|
|
226
|
+
puts " 📌 Setting #{key}"
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
210
230
|
def find_project_root
|
|
211
231
|
# Walk up from infra dir to find project root (where Gemfile lives)
|
|
212
232
|
dir = @infra_dir ? File.dirname(@infra_dir) : Dir.pwd
|
|
@@ -264,7 +284,7 @@ module Belt
|
|
|
264
284
|
end
|
|
265
285
|
|
|
266
286
|
def load_backup_config
|
|
267
|
-
|
|
287
|
+
@env_config.backups? ? @env_config.backup_config : nil
|
|
268
288
|
end
|
|
269
289
|
|
|
270
290
|
def run_backup_phase(backup_config)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Belt
|
|
4
|
+
module CLI
|
|
5
|
+
class EnvironmentConfig
|
|
6
|
+
attr_reader :aws_profile, :env_vars, :backup_config
|
|
7
|
+
|
|
8
|
+
# Load environment config from infrastructure/<env>/belt.rb
|
|
9
|
+
# Returns an EnvironmentConfig instance (never nil — unconfigured is valid)
|
|
10
|
+
def self.load(env, infra_dir: nil)
|
|
11
|
+
infra_dir ||= 'infrastructure'
|
|
12
|
+
config_file = File.join(infra_dir, env, 'belt.rb')
|
|
13
|
+
|
|
14
|
+
config = new
|
|
15
|
+
return config unless File.exist?(config_file)
|
|
16
|
+
|
|
17
|
+
evaluator = ConfigEvaluator.new(config)
|
|
18
|
+
evaluator.evaluate(File.read(config_file), config_file)
|
|
19
|
+
config
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def initialize
|
|
23
|
+
@aws_profile = nil
|
|
24
|
+
@env_vars = {}
|
|
25
|
+
@backup_config = BackupConfig.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def aws_profile?
|
|
29
|
+
!@aws_profile.nil? && !@aws_profile.empty?
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def env_vars?
|
|
33
|
+
@env_vars.any?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def backups?
|
|
37
|
+
@backup_config.any?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Apply the configured aws_profile and env vars to the current process.
|
|
41
|
+
# Call this before running terraform, aws cli, etc.
|
|
42
|
+
def apply!
|
|
43
|
+
ENV['AWS_PROFILE'] = @aws_profile if aws_profile?
|
|
44
|
+
@env_vars.each { |key, value| ENV[key] = value }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class ConfigEvaluator
|
|
48
|
+
def initialize(config)
|
|
49
|
+
@config = config
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def evaluate(content, filename)
|
|
53
|
+
config = @config
|
|
54
|
+
sandbox = Module.new
|
|
55
|
+
belt_proxy = Module.new do
|
|
56
|
+
define_singleton_method(:configure) do |&block|
|
|
57
|
+
block.call(ConfigDSL.new(config))
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
sandbox.const_set(:Belt, belt_proxy)
|
|
61
|
+
sandbox.module_eval(content, filename)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
class ConfigDSL
|
|
66
|
+
def initialize(config)
|
|
67
|
+
@config = config
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def aws_profile=(profile)
|
|
71
|
+
@config.instance_variable_set(:@aws_profile, profile.to_s)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def env(&)
|
|
75
|
+
env_dsl = EnvDSL.new(@config)
|
|
76
|
+
env_dsl.instance_eval(&)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def backups(enabled = nil, &block)
|
|
80
|
+
if enabled == true
|
|
81
|
+
@config.backup_config.instance_variable_set(:@dynamodb_tables, :all)
|
|
82
|
+
elsif block
|
|
83
|
+
backup_dsl = BackupDSL.new(@config.backup_config)
|
|
84
|
+
backup_dsl.instance_eval(&block)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
class EnvDSL
|
|
90
|
+
def initialize(config)
|
|
91
|
+
@config = config
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def set(key, value)
|
|
95
|
+
@config.instance_variable_get(:@env_vars)[key.to_s] = value.to_s
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# DSL for the backups block
|
|
100
|
+
class BackupDSL
|
|
101
|
+
def initialize(backup_config)
|
|
102
|
+
@backup_config = backup_config
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def dynamodb(scope = :all)
|
|
106
|
+
if scope == :all
|
|
107
|
+
@backup_config.instance_variable_set(:@dynamodb_tables, :all)
|
|
108
|
+
else
|
|
109
|
+
tables = Array(scope)
|
|
110
|
+
@backup_config.instance_variable_set(:@dynamodb_tables, tables.map(&:to_s))
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def cognito(*exports)
|
|
115
|
+
@backup_config.instance_variable_set(:@cognito_exports, exports.flatten.map(&:to_sym))
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def s3(*buckets)
|
|
119
|
+
@backup_config.instance_variable_set(:@s3_buckets, buckets.flatten.map(&:to_sym))
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def retention(opts = {})
|
|
123
|
+
current = @backup_config.instance_variable_get(:@retention)
|
|
124
|
+
merged = current.merge(opts.transform_keys(&:to_sym))
|
|
125
|
+
merged.transform_values! { |v| v.respond_to?(:to_i) ? v.to_i : v }
|
|
126
|
+
@backup_config.instance_variable_set(:@retention, merged)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -61,9 +61,9 @@ module Belt
|
|
|
61
61
|
|
|
62
62
|
def parse_models
|
|
63
63
|
model_files = Dir.glob(File.join(MODELS_DIR, '*.rb'))
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
.reject { |f| File.basename(f) == 'application_record.rb' }
|
|
65
|
+
.reject { |f| File.basename(f).start_with?('concerns') }
|
|
66
|
+
.sort
|
|
67
67
|
|
|
68
68
|
model_files.filter_map { |f| parse_model_file(f) }
|
|
69
69
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative 'env_resolver'
|
|
4
|
+
require_relative 'environment_config'
|
|
4
5
|
|
|
5
6
|
module Belt
|
|
6
7
|
module CLI
|
|
@@ -53,6 +54,7 @@ module Belt
|
|
|
53
54
|
|
|
54
55
|
def run
|
|
55
56
|
validate!
|
|
57
|
+
apply_env_config!
|
|
56
58
|
env_dir = File.join(@infra_dir, @env)
|
|
57
59
|
args = ['terraform', @action, *@extra_args]
|
|
58
60
|
puts "belt → #{args.join(' ')} (in #{env_dir}/)"
|
|
@@ -61,6 +63,12 @@ module Belt
|
|
|
61
63
|
|
|
62
64
|
private
|
|
63
65
|
|
|
66
|
+
def apply_env_config!
|
|
67
|
+
env_config = EnvironmentConfig.load(@env, infra_dir: @infra_dir)
|
|
68
|
+
env_config.apply!
|
|
69
|
+
puts " 🔑 Using AWS profile: #{env_config.aws_profile}" if env_config.aws_profile?
|
|
70
|
+
end
|
|
71
|
+
|
|
64
72
|
def validate!
|
|
65
73
|
unless @infra_dir
|
|
66
74
|
abort "Error: No infrastructure/ directory found. Run `belt generate environment #{@env}` first."
|
|
@@ -18,7 +18,7 @@ module Belt
|
|
|
18
18
|
|
|
19
19
|
allowed.any? do |pattern|
|
|
20
20
|
if pattern.include?('*')
|
|
21
|
-
regex = Regexp.new(
|
|
21
|
+
regex = Regexp.new("\\A#{Regexp.escape(pattern).gsub('\*', '[^.]+')}\\z")
|
|
22
22
|
regex.match?(origin)
|
|
23
23
|
else
|
|
24
24
|
pattern == origin
|
data/lib/belt/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: belt
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
4
|
+
version: 0.2.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stowzilla
|
|
@@ -78,6 +78,7 @@ files:
|
|
|
78
78
|
- lib/belt/cli/destroy_command.rb
|
|
79
79
|
- lib/belt/cli/env_resolver.rb
|
|
80
80
|
- lib/belt/cli/environment_command.rb
|
|
81
|
+
- lib/belt/cli/environment_config.rb
|
|
81
82
|
- lib/belt/cli/frontend_command.rb
|
|
82
83
|
- lib/belt/cli/frontend_deploy_command.rb
|
|
83
84
|
- lib/belt/cli/frontend_env_command.rb
|