belt 0.3.23 → 0.3.24

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: 1e5abc1ab2820331aaad684f79c2b43cd5f5d99d0c38abbbd38e4dcd1efe0aad
4
- data.tar.gz: 315d3cf0a8209683598ba624e5a5017b26d764b0e1e88ab654cdaa7eedfe7ab9
3
+ metadata.gz: 9e5f26e4b71c7d784ea8e6a2596952a21d9357f65a9284b9358c5c79a34e50a8
4
+ data.tar.gz: f98d7e78c1620e38700eb53151cbf21daaace261f0b2802dc36633665bc50d1d
5
5
  SHA512:
6
- metadata.gz: 2a0254ea0e2b6de26b6cf3e65caee76db2df44a714a492edfeef0490e2644fa6bdf2a38c6150e09c71d94d389f699d60fcfd0235be8a44f4da25bc28a0cfba17
7
- data.tar.gz: 7310bc92ea3749017da52f6be928288b3970c5e52a828d948796f44e75a2ff628652db767227622f4e371889c37a7387d2aca02a844fae32c108b95448a4a603
6
+ metadata.gz: d6d165f29d693cbc4cd19b693a74867ad578c70bcc255ffc25672ae616694a7604870e090800df16a73d6534e772d95fd417bdad888c07674f15998f838cdaa7
7
+ data.tar.gz: 835dfd43f148de3966d7cba26784922b2c6f952ab24d1c9fb61abf4f51f596d29107526c91a6cdba5be191f061f91acd6504298bd6067ccc756ac7b556b09a88
@@ -5,6 +5,7 @@ require 'erb'
5
5
  require 'json'
6
6
  require_relative 'app_detection'
7
7
  require_relative 'environment_config'
8
+ require_relative 'setup_command'
8
9
 
9
10
  module Belt
10
11
  module CLI
@@ -100,6 +101,9 @@ module Belt
100
101
  # Resolve state bucket using the specified profile (or current credentials)
101
102
  @state_bucket = resolve_state_bucket_for_profile(@aws_profile)
102
103
 
104
+ # Ensure the state bucket exists (convention over configuration)
105
+ ensure_state_bucket_exists(@state_bucket)
106
+
103
107
  puts 'Creating dns infrastructure...' unless @quiet
104
108
  if @aws_profile && !@quiet
105
109
  puts " Using AWS profile: #{@aws_profile}"
@@ -346,6 +350,26 @@ module Belt
346
350
 
347
351
  "belt-terraform-state-#{data['Account']}"
348
352
  end
353
+
354
+ # Ensure the state bucket exists, creating it if necessary.
355
+ # Uses belt setup state --aws-profile for convention over configuration.
356
+ def ensure_state_bucket_exists(bucket)
357
+ return if bucket == 'belt-terraform-state' # Placeholder means no credentials
358
+ return if bucket_exists?(bucket)
359
+
360
+ puts "State bucket #{bucket} not found. Creating it..." unless @quiet
361
+ args = []
362
+ args += ['--aws-profile', @aws_profile] if @aws_profile
363
+ SetupCommand.new(args, quiet: @quiet, aws_profile: @aws_profile).run_state_setup
364
+ end
365
+
366
+ def bucket_exists?(bucket)
367
+ require 'open3'
368
+ cmd = ['aws', 's3api', 'head-bucket', '--bucket', bucket]
369
+ cmd += ['--profile', @aws_profile] if @aws_profile
370
+ _, status = Open3.capture2e(*cmd)
371
+ status.success?
372
+ end
349
373
  end
350
374
  end
351
375
  end
@@ -29,26 +29,48 @@ module Belt
29
29
  when 'frontend'
30
30
  Belt::CLI::FrontendSetupCommand.run(args)
31
31
  else
32
- puts 'Usage: belt setup <state|tables|frontend> [options]'
33
- puts "\nSubcommands:"
34
- puts ' state Set up S3 bucket for Terraform state'
35
- puts ' tables Generate DynamoDB table definitions from contracts.rb'
36
- puts ' frontend Generate S3 + CloudFront infrastructure for frontend hosting'
32
+ print_usage
37
33
  exit 1
38
34
  end
39
35
  end
40
36
 
41
- def initialize(args = [], quiet: false)
37
+ def self.print_usage
38
+ puts <<~USAGE
39
+ Usage: belt setup <state|tables|frontend> [options]
40
+
41
+ Subcommands:
42
+ state Set up S3 bucket for Terraform state
43
+ tables Generate DynamoDB table definitions from contracts.rb
44
+ frontend Generate S3 + CloudFront infrastructure for frontend hosting
45
+
46
+ Options for state:
47
+ --aws-profile NAME Use a specific AWS profile (for multi-account setups)
48
+ --bucket NAME Use a custom bucket name instead of belt-terraform-state-<account>
49
+ --select Interactively select from existing buckets
50
+
51
+ Examples:
52
+ belt setup state # Create/verify bucket using current credentials
53
+ belt setup state --aws-profile fpshared # Create bucket in a specific AWS account
54
+ belt setup state --select # Choose from existing buckets
55
+ belt setup state --bucket my-bucket # Use a custom bucket name
56
+
57
+ The state bucket stores Terraform state for all Belt applications in an AWS account.
58
+ By default, Belt uses `belt-terraform-state-<account_id>` as the bucket name.
59
+ USAGE
60
+ end
61
+
62
+ def initialize(args = [], quiet: false, aws_profile: nil)
42
63
  @app_name = detect_app_name
43
64
  @env_name = nil
44
65
  @custom_bucket = nil
45
66
  @select_mode = false
46
67
  @quiet = quiet
68
+ @aws_profile = aws_profile
47
69
 
48
70
  parse_args(args)
49
71
 
50
72
  @region = detect_region
51
- @bucket_name = resolve_bucket_name
73
+ @bucket_name = nil # Resolved after credentials check
52
74
  end
53
75
 
54
76
  def run_state_setup
@@ -62,8 +84,11 @@ module Belt
62
84
  exit 1
63
85
  end
64
86
 
87
+ # Show which profile we're using
88
+ puts "Using AWS profile: #{@aws_profile}" if @aws_profile && !@quiet
89
+
65
90
  # Resolve final bucket name now that we have credentials
66
- @bucket_name = resolve_bucket_name unless @custom_bucket
91
+ @bucket_name = @custom_bucket || resolve_bucket_name
67
92
 
68
93
  @bucket_name = interactive_bucket_selection if @select_mode
69
94
  setup_or_verify_bucket
@@ -140,10 +165,14 @@ module Belt
140
165
  when '--bucket'
141
166
  @custom_bucket = args.shift
142
167
  abort '✗ --bucket requires a value' unless @custom_bucket
168
+ when '--aws-profile'
169
+ @aws_profile = args.shift
170
+ abort '✗ --aws-profile requires a value' unless @aws_profile
143
171
  when '--select'
144
172
  @select_mode = true
145
173
  when '--help', '-h'
146
- self.class.run([])
174
+ self.class.print_usage
175
+ exit 0
147
176
  else
148
177
  @env_name = arg unless arg.start_with?('-')
149
178
  end
@@ -194,7 +223,9 @@ module Belt
194
223
  end
195
224
 
196
225
  def list_buckets
197
- output = safe_capture('aws', 's3api', 'list-buckets', '--query', 'Buckets[].Name', '--output', 'json')
226
+ cmd = ['aws', 's3api', 'list-buckets', '--query', 'Buckets[].Name', '--output', 'json']
227
+ cmd += ['--profile', @aws_profile] if @aws_profile
228
+ output = safe_capture(*cmd)
198
229
  return [] unless output
199
230
 
200
231
  JSON.parse(output)
@@ -205,7 +236,9 @@ module Belt
205
236
  # --- AWS operations ---
206
237
 
207
238
  def aws_configured?
208
- output, status = Open3.capture2e('aws', 'sts', 'get-caller-identity')
239
+ cmd = %w[aws sts get-caller-identity]
240
+ cmd += ['--profile', @aws_profile] if @aws_profile
241
+ output, status = Open3.capture2e(*cmd)
209
242
  if status.success?
210
243
  data = begin
211
244
  JSON.parse(output)
@@ -223,7 +256,9 @@ module Belt
223
256
  attr_reader :aws_account_id
224
257
 
225
258
  def bucket_exists?(bucket)
226
- output, status = Open3.capture2e('aws', 's3api', 'head-bucket', '--bucket', bucket)
259
+ cmd = ['aws', 's3api', 'head-bucket', '--bucket', bucket]
260
+ cmd += ['--profile', @aws_profile] if @aws_profile
261
+ output, status = Open3.capture2e(*cmd)
227
262
  return true if status.success?
228
263
 
229
264
  # 403 = bucket exists but owned by someone else; 404 = doesn't exist
@@ -239,6 +274,7 @@ module Belt
239
274
  def create_bucket(bucket)
240
275
  args = ['aws', 's3api', 'create-bucket', '--bucket', bucket, '--region', @region]
241
276
  args.push('--create-bucket-configuration', "LocationConstraint=#{@region}") unless @region == 'us-east-1'
277
+ args += ['--profile', @aws_profile] if @aws_profile
242
278
  run!(*args)
243
279
  end
244
280
 
@@ -252,11 +288,14 @@ module Belt
252
288
  AbortIncompleteMultipartUpload: { DaysAfterInitiation: 7 }
253
289
  }]
254
290
  }
255
- run!('aws', 's3api', 'put-bucket-lifecycle-configuration', '--bucket', bucket,
256
- '--lifecycle-configuration', JSON.generate(lifecycle))
291
+ cmd = ['aws', 's3api', 'put-bucket-lifecycle-configuration', '--bucket', bucket,
292
+ '--lifecycle-configuration', JSON.generate(lifecycle)]
293
+ cmd += ['--profile', @aws_profile] if @aws_profile
294
+ run!(*cmd)
257
295
  end
258
296
 
259
297
  def run!(*args)
298
+ args = inject_profile(args)
260
299
  output, status = Open3.capture2e(*args)
261
300
  return if status.success?
262
301
 
@@ -265,11 +304,23 @@ module Belt
265
304
  exit 1
266
305
  end
267
306
 
268
- def safe_capture(*)
269
- output, status = Open3.capture2(*, err: File::NULL)
307
+ def safe_capture(*args)
308
+ args = inject_profile(args)
309
+ output, status = Open3.capture2(*args, err: File::NULL)
270
310
  status.success? ? output : nil
271
311
  end
272
312
 
313
+ # Inject --profile flag for AWS CLI commands if @aws_profile is set
314
+ def inject_profile(args)
315
+ return args unless @aws_profile
316
+ return args unless args.first == 'aws'
317
+
318
+ # Already has a profile flag
319
+ return args if args.include?('--profile')
320
+
321
+ args + ['--profile', @aws_profile]
322
+ end
323
+
273
324
  def say(message)
274
325
  puts message unless @quiet
275
326
  end
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.3.23'
4
+ VERSION = '0.3.24'
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.3.23
4
+ version: 0.3.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stowzilla