belt 0.5.3 → 0.6.1
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/CHANGELOG.md +38 -0
- data/lib/belt/authentication/controller.rb +1 -3
- data/lib/belt/cli/deploy_command.rb +48 -25
- data/lib/belt/cli/environment_command.rb +1 -0
- data/lib/belt/cli/new_command.rb +1 -0
- data/lib/belt/docs/authentication.md +2 -2
- data/lib/belt/docs/controllers.md +13 -2
- data/lib/belt/docs/deployment.md +14 -1
- data/lib/belt/docs/lambda_handler.md +0 -1
- data/lib/belt/version.rb +5 -1
- data/lib/belt_controller/base.rb +12 -2
- data/lib/belt_controller/callback_halting.rb +50 -0
- data/lib/templates/environment/main.tf.erb +5 -1
- data/lib/templates/module/main.tf.erb +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: 66c35b71a47f74ba7805bce24b37eb02fa6ded7246872df30a951a35be22d21f
|
|
4
|
+
data.tar.gz: 5ddc3bb756fdf782da5074abb72bd0c7275c6a90f4ba98c81963b92cb9cd6bb9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 44320ad96e29e29a6bb9b88d2de94822f982dad01e92bc639204c38d2bb7a306463b7b570ac8c35cbf13cc00a223a55442b52171ac64f3357de98a9662a5e3e9
|
|
7
|
+
data.tar.gz: 8e176982bec58892623685d2b3bfd02cde1ab22d9fcac2c6eb1bfe58f5ae27f02d28b708cced560b803379b36bcd2bea5463fce749bde4ebd9b169cbfff878ba
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.1
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **`before_action` now halts the request when it produces a response.** The docs said
|
|
8
|
+
a callback could halt by calling `error_response`, but `dispatch` ignored callback
|
|
9
|
+
output and ran the action anyway, so the action's response replaced the error. A
|
|
10
|
+
guard like `error_response('Forbidden', 403) unless admin?` let everyone through.
|
|
11
|
+
Now, once a callback calls a response helper (`error_response`, `success_response`,
|
|
12
|
+
`html_response`, `head`, `render`) or returns a response, the remaining callbacks, the
|
|
13
|
+
action and the `after_action`s are skipped and that response is returned
|
|
14
|
+
(Rails-style). Apps that worked around this with inline guards in each action keep
|
|
15
|
+
working; the inline checks are now redundant.
|
|
16
|
+
|
|
17
|
+
## 0.6.0
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **Ruby 4.0.** New apps and environments now run on the `ruby4.0` Lambda runtime.
|
|
22
|
+
The generated `infrastructure/<env>/main.tf` sets `ruby_version = "4.0"` on the
|
|
23
|
+
conveyor-belt provider (one setting for runtime + gem build image), and both
|
|
24
|
+
generated `main.tf` files now pin conveyor-belt `~> 0.0.16`, the first release that
|
|
25
|
+
migrates already-deployed functions when `ruby_version` changes. Belt, activeitem,
|
|
26
|
+
lambda_loadout, belt-pay and belt-messaging all pass their suites on Ruby 4.0. CI now
|
|
27
|
+
tests 3.3, 3.4 and 4.0. Existing apps: see [UPGRADING.md](UPGRADING.md#ruby-34--40).
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **`belt deploy <env> --rebuild` builds gems for the runtime the function actually
|
|
32
|
+
runs.** It used to look for `ruby_version` in `terraform state show conveyor_belt.main`,
|
|
33
|
+
but that's a provider setting and never appears in resource state, so `--rebuild`
|
|
34
|
+
always compiled in `build-ruby3.4`. On a `ruby4.0` function that ships a
|
|
35
|
+
`vendor/bundle/ruby/3.4.0` bundle Ruby 4 can't load. Image selection is now: resource
|
|
36
|
+
`docker_build_image` → the live function's runtime → `ruby_version` in the env's
|
|
37
|
+
`main.tf` → `build-ruby3.4`.
|
|
38
|
+
- Removed `runtime: ruby3.3` from the `config/lambda/*.yml` examples in the docs.
|
|
39
|
+
conveyor-belt ignores that key; the runtime comes from the provider's `ruby_version`.
|
|
40
|
+
|
|
3
41
|
## 0.5.3
|
|
4
42
|
|
|
5
43
|
### Fixed
|
|
@@ -88,9 +88,7 @@ module Belt
|
|
|
88
88
|
false
|
|
89
89
|
end
|
|
90
90
|
|
|
91
|
-
# before_action guard. Raises,
|
|
92
|
-
# (see BeltController::Base#run_before_actions) — returning a response hash would
|
|
93
|
-
# let the action run anyway. Belt maps the error to 401.
|
|
91
|
+
# before_action guard. Raises NotAuthenticated, which Belt maps to 401.
|
|
94
92
|
def authenticate_user!
|
|
95
93
|
raise NotAuthenticated, 'Authentication required' unless user_signed_in?
|
|
96
94
|
end
|
|
@@ -490,7 +490,7 @@ module Belt
|
|
|
490
490
|
begin
|
|
491
491
|
puts ' 📦 Building Lambda package...'
|
|
492
492
|
assemble_package(lambda_dir, build_dir)
|
|
493
|
-
build_gems(build_dir)
|
|
493
|
+
build_gems(build_dir, function_name)
|
|
494
494
|
zip_file = create_zip(build_dir)
|
|
495
495
|
|
|
496
496
|
puts ' 🚀 Deploying to AWS...'
|
|
@@ -572,13 +572,13 @@ module Belt
|
|
|
572
572
|
puts ''
|
|
573
573
|
end
|
|
574
574
|
|
|
575
|
-
def build_gems(build_dir)
|
|
575
|
+
def build_gems(build_dir, function_name)
|
|
576
576
|
puts ' 🐳 Building gems in Docker (Lambda-compatible)...'
|
|
577
577
|
|
|
578
578
|
uid = Process.uid
|
|
579
579
|
gid = Process.gid
|
|
580
580
|
|
|
581
|
-
image = detect_docker_build_image
|
|
581
|
+
image = detect_docker_build_image(function_name)
|
|
582
582
|
|
|
583
583
|
docker_cmd = [
|
|
584
584
|
'docker', 'run', '--rm',
|
|
@@ -606,35 +606,58 @@ module Belt
|
|
|
606
606
|
puts ' ✅ Gems installed'
|
|
607
607
|
end
|
|
608
608
|
|
|
609
|
-
#
|
|
610
|
-
#
|
|
611
|
-
|
|
609
|
+
# Picks the image gems are compiled in. Native extensions and the
|
|
610
|
+
# vendor/bundle/ruby/<abi> path must match the function's runtime, so:
|
|
611
|
+
# 1. docker_build_image on the conveyor_belt resource (fully custom)
|
|
612
|
+
# 2. the live function's runtime (ruby4.0 → build-ruby4.0)
|
|
613
|
+
# 3. ruby_version in the env's conveyor-belt provider block
|
|
614
|
+
# 4. DEFAULT_DOCKER_BUILD_IMAGE
|
|
615
|
+
def detect_docker_build_image(function_name)
|
|
612
616
|
env_dir = File.join(@infra_dir, @env)
|
|
613
|
-
|
|
617
|
+
|
|
618
|
+
image = docker_build_image_from_state(env_dir)
|
|
619
|
+
if image
|
|
620
|
+
puts " 📦 Using custom build image: #{image}"
|
|
621
|
+
return image
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
version = ruby_version_from_lambda(function_name) || ruby_version_from_provider(env_dir)
|
|
625
|
+
return DEFAULT_DOCKER_BUILD_IMAGE unless version
|
|
626
|
+
|
|
627
|
+
image = "public.ecr.aws/sam/build-ruby#{version}:latest-x86_64"
|
|
628
|
+
puts " 📦 Using build image for Ruby #{version}: #{image}"
|
|
629
|
+
image
|
|
630
|
+
end
|
|
631
|
+
|
|
632
|
+
def docker_build_image_from_state(env_dir)
|
|
633
|
+
return nil unless Dir.exist?(File.join(env_dir, '.terraform'))
|
|
614
634
|
|
|
615
635
|
Dir.chdir(env_dir) do
|
|
616
636
|
output, status = Open3.capture2('terraform', 'state', 'show', 'conveyor_belt.main')
|
|
617
|
-
|
|
618
|
-
# Prefer explicit docker_build_image if set
|
|
619
|
-
image_match = output.match(/docker_build_image\s*=\s*"([^"]+)"/)
|
|
620
|
-
if image_match
|
|
621
|
-
image = image_match[1]
|
|
622
|
-
puts " 📦 Using custom build image: #{image}"
|
|
623
|
-
return image
|
|
624
|
-
end
|
|
637
|
+
return nil unless status.success?
|
|
625
638
|
|
|
626
|
-
|
|
627
|
-
version_match = output.match(/ruby_version\s*=\s*"([^"]+)"/)
|
|
628
|
-
if version_match
|
|
629
|
-
version = version_match[1]
|
|
630
|
-
image = "public.ecr.aws/sam/build-ruby#{version}:latest-x86_64"
|
|
631
|
-
puts " 📦 Using build image for Ruby #{version}: #{image}"
|
|
632
|
-
return image
|
|
633
|
-
end
|
|
634
|
-
end
|
|
639
|
+
output[/docker_build_image\s*=\s*"([^"]+)"/, 1]
|
|
635
640
|
end
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
# "ruby4.0" → "4.0"
|
|
644
|
+
def ruby_version_from_lambda(function_name)
|
|
645
|
+
output, status = Open3.capture2(
|
|
646
|
+
'aws', 'lambda', 'get-function-configuration',
|
|
647
|
+
'--function-name', function_name,
|
|
648
|
+
'--query', 'Runtime',
|
|
649
|
+
'--output', 'text'
|
|
650
|
+
)
|
|
651
|
+
return nil unless status.success?
|
|
652
|
+
|
|
653
|
+
output.strip[/\Aruby(\d+\.\d+)\z/, 1]
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
def ruby_version_from_provider(env_dir)
|
|
657
|
+
main_tf = File.join(env_dir, 'main.tf')
|
|
658
|
+
return nil unless File.exist?(main_tf)
|
|
636
659
|
|
|
637
|
-
|
|
660
|
+
File.read(main_tf)[/^\s*ruby_version\s*=\s*"([^"]+)"/, 1]
|
|
638
661
|
end
|
|
639
662
|
|
|
640
663
|
def strip_vendor_fat(build_dir)
|
data/lib/belt/cli/new_command.rb
CHANGED
|
@@ -71,8 +71,8 @@ end
|
|
|
71
71
|
```
|
|
72
72
|
|
|
73
73
|
`authenticate_user!` raises `Belt::Authentication::NotAuthenticated`, which Belt already
|
|
74
|
-
maps to **401**.
|
|
75
|
-
|
|
74
|
+
maps to **401**. (Your own guards can also halt by calling a response helper, e.g.
|
|
75
|
+
`error_response('Forbidden', 403) unless admin?`.)
|
|
76
76
|
|
|
77
77
|
| Method | |
|
|
78
78
|
|---|---|
|
|
@@ -96,8 +96,19 @@ class ApplicationController < BeltController::Base
|
|
|
96
96
|
end
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
Callbacks run in definition order. `before_action`
|
|
100
|
-
calling a response helper (
|
|
99
|
+
Callbacks run in definition order. A `before_action` halts the request by
|
|
100
|
+
calling a response helper (`error_response`, `success_response`, `head`,
|
|
101
|
+
`render`, …). The remaining callbacks, the action and the `after_action`s are
|
|
102
|
+
skipped, and that response is returned:
|
|
103
|
+
|
|
104
|
+
```ruby
|
|
105
|
+
def require_admin!
|
|
106
|
+
error_response('Forbidden', 403) unless admin?
|
|
107
|
+
end
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Raising works too: the exception goes through `rescue_from` as usual (e.g.
|
|
111
|
+
`Belt::AuthenticationError` → 401).
|
|
101
112
|
|
|
102
113
|
## Strong Parameters
|
|
103
114
|
|
data/lib/belt/docs/deployment.md
CHANGED
|
@@ -67,13 +67,26 @@ The Lambda entry point is specified in `config/lambda/<name>.yml`:
|
|
|
67
67
|
|
|
68
68
|
```yaml
|
|
69
69
|
handler: lambda/<name>.lambda_handler
|
|
70
|
-
runtime: ruby3.3
|
|
71
70
|
timeout: 30
|
|
72
71
|
memory: 256
|
|
73
72
|
environment:
|
|
74
73
|
ENVIRONMENT: ${var.environment}
|
|
75
74
|
```
|
|
76
75
|
|
|
76
|
+
The Ruby version is set once per environment, on the conveyor-belt provider in
|
|
77
|
+
`infrastructure/<env>/main.tf` — it drives both the Lambda runtime and the image
|
|
78
|
+
gems are compiled in:
|
|
79
|
+
|
|
80
|
+
```hcl
|
|
81
|
+
provider "conveyor-belt" {
|
|
82
|
+
environment = var.environment
|
|
83
|
+
aws_region = var.aws_region
|
|
84
|
+
ruby_version = "4.0" # → runtime ruby4.0, build image build-ruby4.0
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
See [UPGRADING.md](../../../UPGRADING.md#ruby-34--40) to move an existing app.
|
|
89
|
+
|
|
77
90
|
## Route Manifests
|
|
78
91
|
|
|
79
92
|
Before deploying, generate the route manifest used at runtime:
|
data/lib/belt/version.rb
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Belt
|
|
4
|
-
VERSION = '0.
|
|
4
|
+
VERSION = '0.6.1'
|
|
5
|
+
|
|
6
|
+
# Ruby version new apps run on in Lambda. Rendered into the conveyor-belt
|
|
7
|
+
# provider block (`ruby_version`) by `belt new` / `belt generate environment`.
|
|
8
|
+
LAMBDA_RUBY_VERSION = '4.0'
|
|
5
9
|
end
|
data/lib/belt_controller/base.rb
CHANGED
|
@@ -9,12 +9,15 @@ require_relative '../belt/helpers/error_logging'
|
|
|
9
9
|
require_relative '../belt/helpers/cors_origin'
|
|
10
10
|
require_relative '../belt/rendering'
|
|
11
11
|
require_relative 'implicit_response'
|
|
12
|
+
require_relative 'callback_halting'
|
|
12
13
|
|
|
13
14
|
module BeltController
|
|
14
15
|
class Base
|
|
15
16
|
include Belt::Helpers::Response
|
|
16
17
|
include Belt::Rendering
|
|
17
18
|
include ImplicitResponse
|
|
19
|
+
# Must come after Belt::Helpers::Response / Belt::Rendering (wraps their helpers).
|
|
20
|
+
include CallbackHalting
|
|
18
21
|
# Cognito identity: current_user, authenticate_user!, cognito_admin?.
|
|
19
22
|
# Inert unless the app declares `cognito_authenticatable` on a model.
|
|
20
23
|
include Belt::Authentication::Controller
|
|
@@ -139,7 +142,11 @@ module BeltController
|
|
|
139
142
|
raise Belt::ActionNotFound, "The action '#{action_sym}' could not be found for #{self.class.name}"
|
|
140
143
|
end
|
|
141
144
|
|
|
142
|
-
|
|
145
|
+
# A before_action that produced a response halts the request (Rails-style):
|
|
146
|
+
# the action and after_actions don't run.
|
|
147
|
+
halted = run_before_actions(action_sym)
|
|
148
|
+
return halted if halted
|
|
149
|
+
|
|
143
150
|
# Snapshot ivars so we only auto-serialize assigns set by the action (Rails-style)
|
|
144
151
|
@__assigns_before = instance_variables
|
|
145
152
|
result = send(action_sym)
|
|
@@ -178,14 +185,17 @@ module BeltController
|
|
|
178
185
|
|
|
179
186
|
private
|
|
180
187
|
|
|
188
|
+
# Returns the halting response if a callback produced one, else nil.
|
|
181
189
|
def run_before_actions(action_sym)
|
|
182
190
|
self.class.all_before_actions.each do |callback|
|
|
183
191
|
next if callback[:only] && !callback[:only].include?(action_sym)
|
|
184
192
|
next if callback[:except]&.include?(action_sym)
|
|
185
193
|
next if should_skip_callback?(callback[:method], action_sym)
|
|
186
194
|
|
|
187
|
-
|
|
195
|
+
response = run_halting_callback(callback[:method])
|
|
196
|
+
return response if response
|
|
188
197
|
end
|
|
198
|
+
nil
|
|
189
199
|
end
|
|
190
200
|
|
|
191
201
|
def run_after_actions(action_sym)
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BeltController
|
|
4
|
+
# Rails-style halting for before_action callbacks.
|
|
5
|
+
#
|
|
6
|
+
# A before_action halts the request by producing a response. Either form works:
|
|
7
|
+
#
|
|
8
|
+
# def require_admin!
|
|
9
|
+
# error_response('Forbidden', 403) unless admin?
|
|
10
|
+
# end
|
|
11
|
+
#
|
|
12
|
+
# def require_admin!
|
|
13
|
+
# return if admin?
|
|
14
|
+
#
|
|
15
|
+
# error_response('Forbidden', 403)
|
|
16
|
+
# Belt::Observability::Logger.warn('non-admin hit admin route')
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# Once a callback halts, the remaining callbacks, the action and the
|
|
20
|
+
# after_actions are skipped, and that response is returned.
|
|
21
|
+
#
|
|
22
|
+
# Belt's response helpers are pure (they build and return a hash), so we
|
|
23
|
+
# record any response built while a callback is running. That catches the
|
|
24
|
+
# second form, where the response isn't the callback's return value.
|
|
25
|
+
# Included by BeltController::Base after the response helpers so these
|
|
26
|
+
# wrappers sit in front of them in the ancestor chain.
|
|
27
|
+
module CallbackHalting
|
|
28
|
+
# render builds its response via html_response, so it's covered too.
|
|
29
|
+
%i[success_response error_response html_response head].each do |helper|
|
|
30
|
+
define_method(helper) do |*args|
|
|
31
|
+
response = super(*args)
|
|
32
|
+
@__callback_response = response if @__running_callback
|
|
33
|
+
response
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
# Run one before_action. Returns the response it produced (halt), or nil.
|
|
40
|
+
# A returned response wins over one that was only built along the way.
|
|
41
|
+
def run_halting_callback(method_name)
|
|
42
|
+
@__callback_response = nil
|
|
43
|
+
@__running_callback = true
|
|
44
|
+
result = send(method_name)
|
|
45
|
+
lambda_response?(result) ? result : @__callback_response
|
|
46
|
+
ensure
|
|
47
|
+
@__running_callback = false
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -10,7 +10,7 @@ terraform {
|
|
|
10
10
|
}
|
|
11
11
|
conveyor-belt = {
|
|
12
12
|
source = "stowzilla/conveyor-belt"
|
|
13
|
-
version = "~> 0.0.
|
|
13
|
+
version = "~> 0.0.16"
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -30,6 +30,10 @@ provider "aws" {
|
|
|
30
30
|
provider "conveyor-belt" {
|
|
31
31
|
environment = var.environment
|
|
32
32
|
aws_region = var.aws_region
|
|
33
|
+
|
|
34
|
+
# Lambda runtime (ruby<version>) and the Docker image gems are compiled in.
|
|
35
|
+
# Changing it on a deployed environment migrates every function on the next apply.
|
|
36
|
+
ruby_version = "<%= Belt::LAMBDA_RUBY_VERSION %>"
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
module "app" {
|
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.
|
|
4
|
+
version: 0.6.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stowzilla
|
|
@@ -192,6 +192,7 @@ files:
|
|
|
192
192
|
- lib/belt/version.rb
|
|
193
193
|
- lib/belt/views/welcome/show.html.erb
|
|
194
194
|
- lib/belt_controller/base.rb
|
|
195
|
+
- lib/belt_controller/callback_halting.rb
|
|
195
196
|
- lib/belt_controller/implicit_response.rb
|
|
196
197
|
- lib/templates/dns/backend.tf.erb
|
|
197
198
|
- lib/templates/dns/belt.rb.erb
|