belt 0.2.6 → 0.2.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 17b4d2d7497d0961b96e4628ee718fae22f5da62e321652cd3ff252351883836
4
- data.tar.gz: 155caff17e7ca310097baf72376ed8ad8a1515c2ce397769b2db2d6cb5653a26
3
+ metadata.gz: 5ad9e95cd921b5bf91ffd28544086a895e49ce04a59e4a6b62d64cb9b1ec7bd1
4
+ data.tar.gz: 7e8795bcfc4e7941f5bd6b426a509ada6dd8d931c39b2af777e0725e8a78e021
5
5
  SHA512:
6
- metadata.gz: 491589da65264857a3c6964ec522344b2dd222a3cab655235039bb39c48ad84c23a3ed83bc20aeaa31de2da197ba51d71c37dfed9395eeaf66464cebcbce28cb
7
- data.tar.gz: 9d407911c6bec8492f26ece6bda2f592e67056a44a85ae6c09c99cd877992ffa356893d0a955fea0436684278911164916a1d8066490cb0be80e30368a134837
6
+ metadata.gz: d228ac8ae6a440aa17c56b5ab16e48ebda349666b5b22d08456d5c99df4a66f47461031d48d049f81cd8c9d6253d9aa410e8ffa25a45998296ec5c3af524a651
7
+ data.tar.gz: 943b4993668f4c3be7ede7ff7dda63f7e4682906af122632fba1fe3ef66b257ee3970d93383dfe7fbd8df1d88a4852f1cbdaca37f2beac8db26ad0c995d75230
@@ -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 ||= 'infrastructure'
12
- config_file = File.join(infra_dir, env, 'belt.rb')
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
@@ -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
- BackupConfig.load(@env, infra_dir: @infra_dir)
287
+ @env_config.backups? ? @env_config.backup_config : nil
268
288
  end
269
289
 
270
290
  def run_backup_phase(backup_config)
@@ -443,6 +463,9 @@ module Belt
443
463
  def build_gems(build_dir)
444
464
  puts ' 🐳 Building gems in Docker (Lambda-compatible)...'
445
465
 
466
+ uid = Process.uid
467
+ gid = Process.gid
468
+
446
469
  docker_cmd = [
447
470
  'docker', 'run', '--rm',
448
471
  '--platform', 'linux/amd64',
@@ -454,7 +477,8 @@ module Belt
454
477
  "bundle config set --local without 'development test' && " \
455
478
  'bundle config set silence_root_warning 1 && ' \
456
479
  'bundle install --jobs 4 && ' \
457
- 'bundle clean --force'
480
+ 'bundle clean --force && ' \
481
+ "chown -R #{uid}:#{gid} vendor/"
458
482
  ]
459
483
 
460
484
  output, status = Open3.capture2e(*docker_cmd)
@@ -484,7 +508,7 @@ module Belt
484
508
  .travis.yml .rubocop.yml Rakefile]
485
509
  exts.each do |pattern|
486
510
  Dir.glob(File.join(vendor_dir, '**', pattern)).each do |f|
487
- File.delete(f) if File.file?(f)
511
+ FileUtils.rm_f(f) if File.file?(f)
488
512
  end
489
513
  end
490
514
 
@@ -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
- .reject { |f| File.basename(f) == 'application_record.rb' }
65
- .reject { |f| File.basename(f).start_with?('concerns') }
66
- .sort
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."
@@ -6,9 +6,24 @@ module Belt
6
6
  def self.resolve_origin(request_origin)
7
7
  allowed = allowed_origins
8
8
  return nil if allowed.empty?
9
- return request_origin if request_origin && allowed.include?(request_origin)
9
+ return request_origin if request_origin && matches_allowed?(request_origin, allowed)
10
10
 
11
- allowed.first
11
+ # Don't return a wildcard pattern as a literal origin — use '*' instead
12
+ first = allowed.first
13
+ first.include?('*') ? '*' : first
14
+ end
15
+
16
+ def self.matches_allowed?(origin, allowed)
17
+ return false unless origin
18
+
19
+ allowed.any? do |pattern|
20
+ if pattern.include?('*')
21
+ regex = Regexp.new("\\A#{Regexp.escape(pattern).gsub('\*', '[^.]+')}\\z")
22
+ regex.match?(origin)
23
+ else
24
+ pattern == origin
25
+ end
26
+ end
12
27
  end
13
28
 
14
29
  def self.origin_from_event(event)
data/lib/belt/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Belt
4
- VERSION = '0.2.6'
4
+ VERSION = '0.2.8'
5
5
  end
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.6
4
+ version: 0.2.8
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