belt 0.2.15 → 0.2.17
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/deploy_command.rb +35 -1
- data/lib/belt/cli/tables_command.rb +29 -4
- data/lib/belt/cli.rb +48 -10
- data/lib/belt/root.rb +11 -1
- data/lib/belt/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4e1813b72986b167b9449f438b18331cc2e9be46cffa58bd059c403f3c35703e
|
|
4
|
+
data.tar.gz: 26c3aae3c049bbebff471d6a8a514114a26f8b2d0fdf4671cb154ae9a056798f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 23bbf9977c0cd765c7a1630e7a74ed480db6d62ee07c22e95e6a491a3a9c04d824b8463b0ef63ae4c4b343e13e96ff26c9edb5c86c8005fce7f0a167b6894c64
|
|
7
|
+
data.tar.gz: e7f625fe93dcb125289530e2a3bdb9dee8c37938eeea41bc9154a31aca6b47675a6336e39c986f3a801719f4cf22052bad40968ff6e1b9a82936140f74432f4a
|
|
@@ -14,6 +14,7 @@ require_relative 'path_gem_materializer'
|
|
|
14
14
|
module Belt
|
|
15
15
|
module CLI
|
|
16
16
|
class DeployCommand
|
|
17
|
+
DEFAULT_DOCKER_BUILD_IMAGE = 'public.ecr.aws/sam/build-ruby3.4:latest-x86_64'
|
|
17
18
|
def self.run(args)
|
|
18
19
|
# Handle `belt deploy frontend <env>` as before
|
|
19
20
|
if args.first == 'frontend'
|
|
@@ -514,12 +515,14 @@ module Belt
|
|
|
514
515
|
uid = Process.uid
|
|
515
516
|
gid = Process.gid
|
|
516
517
|
|
|
518
|
+
image = detect_docker_build_image
|
|
519
|
+
|
|
517
520
|
docker_cmd = [
|
|
518
521
|
'docker', 'run', '--rm',
|
|
519
522
|
'--platform', 'linux/amd64',
|
|
520
523
|
'-v', "#{build_dir}:/var/task",
|
|
521
524
|
'-w', '/var/task',
|
|
522
|
-
|
|
525
|
+
image,
|
|
523
526
|
'/bin/bash', '-c',
|
|
524
527
|
"bundle config set --local path 'vendor/bundle' && " \
|
|
525
528
|
"bundle config set --local without 'development test' && " \
|
|
@@ -540,6 +543,37 @@ module Belt
|
|
|
540
543
|
puts ' ✅ Gems installed'
|
|
541
544
|
end
|
|
542
545
|
|
|
546
|
+
# Reads docker_build_image from the conveyor_belt Terraform resource state.
|
|
547
|
+
# Falls back to DEFAULT_DOCKER_BUILD_IMAGE if state is unavailable or attribute not set.
|
|
548
|
+
def detect_docker_build_image
|
|
549
|
+
env_dir = File.join(@infra_dir, @env)
|
|
550
|
+
return DEFAULT_DOCKER_BUILD_IMAGE unless Dir.exist?(File.join(env_dir, '.terraform'))
|
|
551
|
+
|
|
552
|
+
Dir.chdir(env_dir) do
|
|
553
|
+
output, status = Open3.capture2('terraform', 'state', 'show', 'conveyor_belt.main')
|
|
554
|
+
if status.success?
|
|
555
|
+
# Prefer explicit docker_build_image if set
|
|
556
|
+
image_match = output.match(/docker_build_image\s*=\s*"([^"]+)"/)
|
|
557
|
+
if image_match
|
|
558
|
+
image = image_match[1]
|
|
559
|
+
puts " 📦 Using custom build image: #{image}"
|
|
560
|
+
return image
|
|
561
|
+
end
|
|
562
|
+
|
|
563
|
+
# Otherwise derive from ruby_version
|
|
564
|
+
version_match = output.match(/ruby_version\s*=\s*"([^"]+)"/)
|
|
565
|
+
if version_match
|
|
566
|
+
version = version_match[1]
|
|
567
|
+
image = "public.ecr.aws/sam/build-ruby#{version}:latest-x86_64"
|
|
568
|
+
puts " 📦 Using build image for Ruby #{version}: #{image}"
|
|
569
|
+
return image
|
|
570
|
+
end
|
|
571
|
+
end
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
DEFAULT_DOCKER_BUILD_IMAGE
|
|
575
|
+
end
|
|
576
|
+
|
|
543
577
|
def strip_vendor_fat(build_dir)
|
|
544
578
|
vendor_dir = File.join(build_dir, 'vendor')
|
|
545
579
|
return unless Dir.exist?(vendor_dir)
|
|
@@ -31,7 +31,8 @@ module Belt
|
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
def run
|
|
34
|
-
validate!
|
|
34
|
+
return unless validate!
|
|
35
|
+
|
|
35
36
|
models = parse_models
|
|
36
37
|
if models.empty?
|
|
37
38
|
puts "No models found in #{MODELS_DIR}/" unless @quiet
|
|
@@ -49,11 +50,11 @@ module Belt
|
|
|
49
50
|
abort "Error: No models directory found at #{MODELS_DIR}/. " \
|
|
50
51
|
'Run `belt generate model` to create your first model.'
|
|
51
52
|
end
|
|
52
|
-
return
|
|
53
|
+
return false
|
|
53
54
|
end
|
|
54
|
-
return if Dir.exist?(MODULE_DIR)
|
|
55
|
+
return true if Dir.exist?(MODULE_DIR)
|
|
55
56
|
|
|
56
|
-
return if @quiet
|
|
57
|
+
return false if @quiet
|
|
57
58
|
|
|
58
59
|
abort "Error: Module directory not found at #{MODULE_DIR}/.\n" \
|
|
59
60
|
'Run `belt new` to create a project with the correct structure.'
|
|
@@ -83,6 +84,12 @@ module Belt
|
|
|
83
84
|
# Extract indexes() declaration
|
|
84
85
|
indexes = extract_indexes(content)
|
|
85
86
|
|
|
87
|
+
# Extract belongs_to associations and generate convention indexes
|
|
88
|
+
indexes += extract_belongs_to_indexes(content)
|
|
89
|
+
|
|
90
|
+
# Deduplicate by index name
|
|
91
|
+
indexes.uniq! { |idx| idx[:name] }
|
|
92
|
+
|
|
86
93
|
{ name: model_name, indexes: indexes }
|
|
87
94
|
end
|
|
88
95
|
|
|
@@ -108,6 +115,24 @@ module Belt
|
|
|
108
115
|
indexes
|
|
109
116
|
end
|
|
110
117
|
|
|
118
|
+
# Extract belongs_to declarations and generate convention-based GSI indexes.
|
|
119
|
+
# belongs_to :conversation → ConversationIndex with partition_key: 'conversationId'
|
|
120
|
+
def extract_belongs_to_indexes(content)
|
|
121
|
+
indexes = []
|
|
122
|
+
|
|
123
|
+
# Skip commented-out belongs_to lines
|
|
124
|
+
content.lines.reject { |line| line.strip.start_with?('#') }.join
|
|
125
|
+
.scan(/belongs_to\s+:(\w+)/) do |match|
|
|
126
|
+
association_name = match[0]
|
|
127
|
+
index_name = "#{Belt::Inflector.classify(association_name)}Index"
|
|
128
|
+
partition_key = "#{association_name}Id"
|
|
129
|
+
|
|
130
|
+
indexes << { name: index_name, partition_key: partition_key, sort_key: nil }
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
indexes
|
|
134
|
+
end
|
|
135
|
+
|
|
111
136
|
def generate_dynamodb_tf(models)
|
|
112
137
|
dest = File.join(MODULE_DIR, 'dynamodb.tf')
|
|
113
138
|
existing_content = File.exist?(dest) ? File.read(dest) : nil
|
data/lib/belt/cli.rb
CHANGED
|
@@ -53,6 +53,9 @@ module Belt
|
|
|
53
53
|
|
|
54
54
|
TERRAFORM_ACTIONS = Belt::CLI::TerraformCommand::ACTIONS
|
|
55
55
|
|
|
56
|
+
# Commands that can run without being inside a Belt project
|
|
57
|
+
STANDALONE_COMMANDS = %w[new version --version -v doctor].freeze
|
|
58
|
+
|
|
56
59
|
def self.start(args)
|
|
57
60
|
command = args.shift
|
|
58
61
|
|
|
@@ -61,17 +64,11 @@ module Belt
|
|
|
61
64
|
exit 1
|
|
62
65
|
end
|
|
63
66
|
|
|
67
|
+
# For project-level commands, find and chdir to the project root
|
|
68
|
+
ensure_project_root!(command) unless STANDALONE_COMMANDS.include?(command)
|
|
69
|
+
|
|
64
70
|
# `belt destroy` is ambiguous: could be terraform destroy or belt destroy <generator>.
|
|
65
|
-
|
|
66
|
-
if command == 'destroy'
|
|
67
|
-
if args.empty? || args.first =~ /\A-/
|
|
68
|
-
# belt destroy --help → DestroyCommand help
|
|
69
|
-
# belt destroy (no args) → terraform destroy (needs env)
|
|
70
|
-
return DestroyCommand.run(args) if args.include?('--help') || args.include?('-h')
|
|
71
|
-
elsif DestroyCommand::GENERATORS.include?(args.first) || GeneratorRegistry.generator_names.include?(args.first)
|
|
72
|
-
return DestroyCommand.run(args)
|
|
73
|
-
end
|
|
74
|
-
end
|
|
71
|
+
return if command == 'destroy' && route_destroy_command(args)
|
|
75
72
|
|
|
76
73
|
# Terraform shorthand: belt init wups, belt plan wups, belt apply wups, belt destroy wups
|
|
77
74
|
return Belt::CLI::TerraformCommand.run(command, args) if TERRAFORM_ACTIONS.include?(command)
|
|
@@ -161,5 +158,46 @@ module Belt
|
|
|
161
158
|
belt plugin new messaging # scaffold a belt-messaging style plugin gem
|
|
162
159
|
USAGE
|
|
163
160
|
end
|
|
161
|
+
|
|
162
|
+
def self.not_in_app_message(command)
|
|
163
|
+
<<~MSG
|
|
164
|
+
Could not find a Belt application. Run `belt #{command}` from within a Belt project directory, or create a new one:
|
|
165
|
+
|
|
166
|
+
belt new <app_name> Create a new Belt application
|
|
167
|
+
belt new <app_name> --frontend react With a React frontend
|
|
168
|
+
belt new <app_name> --domain myapp.com With a custom domain
|
|
169
|
+
|
|
170
|
+
Examples:
|
|
171
|
+
belt new blog
|
|
172
|
+
belt new my-api --frontend react
|
|
173
|
+
belt new shop --domain shop.example.com
|
|
174
|
+
|
|
175
|
+
See `belt new --help` for all options.
|
|
176
|
+
MSG
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def self.ensure_project_root!(command)
|
|
180
|
+
if Belt.root?
|
|
181
|
+
Dir.chdir(Belt.root)
|
|
182
|
+
else
|
|
183
|
+
puts not_in_app_message(command)
|
|
184
|
+
exit 1
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Routes `belt destroy` to DestroyCommand when args indicate a generator.
|
|
189
|
+
# Returns true if handled, false to fall through to TerraformCommand.
|
|
190
|
+
def self.route_destroy_command(args) # rubocop:disable Naming/PredicateMethod
|
|
191
|
+
if args.empty? || args.first =~ /\A-/
|
|
192
|
+
if args.include?('--help') || args.include?('-h')
|
|
193
|
+
DestroyCommand.run(args)
|
|
194
|
+
return true
|
|
195
|
+
end
|
|
196
|
+
elsif DestroyCommand::GENERATORS.include?(args.first) || GeneratorRegistry.generator_names.include?(args.first)
|
|
197
|
+
DestroyCommand.run(args)
|
|
198
|
+
return true
|
|
199
|
+
end
|
|
200
|
+
false
|
|
201
|
+
end
|
|
164
202
|
end
|
|
165
203
|
end
|
data/lib/belt/root.rb
CHANGED
|
@@ -9,8 +9,14 @@ module Belt
|
|
|
9
9
|
@root = path
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
+
def self.root?
|
|
13
|
+
!root.nil?
|
|
14
|
+
end
|
|
15
|
+
|
|
12
16
|
# Resolves the path to routes.rb, checking config/ first then legacy paths.
|
|
13
17
|
def self.routes_file
|
|
18
|
+
return nil unless root
|
|
19
|
+
|
|
14
20
|
candidates = [
|
|
15
21
|
File.join(root, 'config/routes.rb'),
|
|
16
22
|
File.join(root, 'config/routes.tf.rb'),
|
|
@@ -21,6 +27,8 @@ module Belt
|
|
|
21
27
|
|
|
22
28
|
# Resolves the path to contracts.rb, checking config/ first then legacy paths.
|
|
23
29
|
def self.contracts_file
|
|
30
|
+
return nil unless root
|
|
31
|
+
|
|
24
32
|
candidates = [
|
|
25
33
|
File.join(root, 'config/contracts.rb'),
|
|
26
34
|
File.join(root, 'config/contracts.tf.rb'),
|
|
@@ -37,6 +45,8 @@ module Belt
|
|
|
37
45
|
|
|
38
46
|
# Resolves the lambda config directory.
|
|
39
47
|
def self.lambda_config_dir
|
|
48
|
+
return nil unless root
|
|
49
|
+
|
|
40
50
|
File.join(root, 'config/lambda')
|
|
41
51
|
end
|
|
42
52
|
|
|
@@ -52,7 +62,7 @@ module Belt
|
|
|
52
62
|
|
|
53
63
|
dir = parent
|
|
54
64
|
end
|
|
55
|
-
|
|
65
|
+
nil
|
|
56
66
|
end
|
|
57
67
|
|
|
58
68
|
private_class_method :detect_root
|
data/lib/belt/version.rb
CHANGED