belt 0.1.3 → 0.1.5
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 +9 -0
- data/README.md +5 -4
- data/lib/belt/action_router.rb +7 -0
- data/lib/belt/assets/belt-default.png +0 -0
- data/lib/belt/assets/welcome.css +112 -0
- data/lib/belt/cli/app_detection.rb +6 -0
- data/lib/belt/cli/deploy_command.rb +182 -0
- data/lib/belt/cli/env_resolver.rb +10 -0
- data/lib/belt/cli/environment_command.rb +12 -7
- data/lib/belt/cli/generate_command.rb +164 -9
- data/lib/belt/cli/new_command.rb +164 -21
- data/lib/belt/cli/server_command.rb +302 -0
- data/lib/belt/cli/setup_command.rb +40 -6
- data/lib/belt/cli/terraform_command.rb +2 -1
- data/lib/belt/cli.rb +17 -16
- data/lib/belt/controllers/welcome_controller.rb +102 -0
- data/lib/belt/version.rb +1 -1
- data/lib/belt.rb +1 -0
- data/lib/templates/environment/backend.tf.erb +1 -1
- data/lib/templates/environment/main.tf.erb +6 -5
- data/lib/templates/environment/outputs.tf.erb +9 -0
- data/lib/templates/frontend_infra/frontend.tf.erb +2 -2
- data/lib/templates/new_app/AGENTS.md.erb +4 -3
- data/lib/templates/new_app/infrastructure/routes.tf.rb.erb +1 -0
- data/lib/templates/new_app/lambda/lib/routes/routes.rb.erb +1 -0
- metadata +7 -1
data/lib/belt/cli.rb
CHANGED
|
@@ -11,6 +11,8 @@ require_relative 'cli/frontend_deploy_command'
|
|
|
11
11
|
require_relative 'cli/views_command'
|
|
12
12
|
require_relative 'cli/setup_command'
|
|
13
13
|
require_relative 'cli/terraform_command'
|
|
14
|
+
require_relative 'cli/deploy_command'
|
|
15
|
+
require_relative 'cli/server_command'
|
|
14
16
|
require_relative 'cli/routes_command'
|
|
15
17
|
require_relative 'cli/tasks_command'
|
|
16
18
|
require_relative 'cli/console_command'
|
|
@@ -24,15 +26,8 @@ module Belt
|
|
|
24
26
|
%w[console c] => Belt::CLI::ConsoleCommand,
|
|
25
27
|
%w[tasks --tasks -T] => Belt::CLI::TasksCommand,
|
|
26
28
|
'setup' => Belt::CLI::SetupCommand,
|
|
27
|
-
'deploy' =>
|
|
28
|
-
|
|
29
|
-
if subcommand == 'frontend'
|
|
30
|
-
Belt::CLI::FrontendDeployCommand.run(args)
|
|
31
|
-
else
|
|
32
|
-
puts 'Usage: belt deploy frontend <environment>'
|
|
33
|
-
exit 1
|
|
34
|
-
end
|
|
35
|
-
},
|
|
29
|
+
'deploy' => Belt::CLI::DeployCommand,
|
|
30
|
+
%w[server s] => Belt::CLI::ServerCommand,
|
|
36
31
|
%w[version --version -v] => ->(_args) { puts "Belt #{Belt::VERSION}" }
|
|
37
32
|
}.freeze
|
|
38
33
|
|
|
@@ -80,6 +75,10 @@ module Belt
|
|
|
80
75
|
generate frontend <react|vue|svelte> Scaffold a frontend app
|
|
81
76
|
generate views <resource> [fields...] Generate React pages for REST actions
|
|
82
77
|
generate environment <name> Create a new environment
|
|
78
|
+
server Start local dev server (frontend)
|
|
79
|
+
s Alias for server
|
|
80
|
+
deploy [environment] Deploy to AWS (init → plan → apply)
|
|
81
|
+
deploy frontend <env> Build and deploy frontend to AWS
|
|
83
82
|
routes [-g PATTERN] [-f json] Show route definitions
|
|
84
83
|
console Start an interactive console (IRB)
|
|
85
84
|
c Alias for console
|
|
@@ -88,12 +87,11 @@ module Belt
|
|
|
88
87
|
setup state Create/select S3 state bucket
|
|
89
88
|
setup tables <env> Generate DynamoDB tables from schema
|
|
90
89
|
setup frontend <env> Generate S3 + CloudFront infrastructure
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
output <env> terraform output for environment
|
|
90
|
+
init [environment] <env> terraform init for environment
|
|
91
|
+
plan [environment] <env> terraform plan for environment
|
|
92
|
+
apply [environment] <env> terraform apply for environment
|
|
93
|
+
destroy [environment] <env> terraform destroy for environment
|
|
94
|
+
output [environment] <env> terraform output for environment
|
|
97
95
|
--version Show Belt version
|
|
98
96
|
|
|
99
97
|
Rake Tasks:
|
|
@@ -110,8 +108,11 @@ module Belt
|
|
|
110
108
|
belt new blog --frontend react
|
|
111
109
|
belt generate resource post title:string content:text status:string
|
|
112
110
|
belt generate frontend react
|
|
113
|
-
belt
|
|
111
|
+
belt server # Start local frontend server
|
|
112
|
+
belt deploy # Deploy dev to AWS
|
|
113
|
+
belt deploy prod --auto # Deploy prod without confirmation
|
|
114
114
|
belt deploy frontend wups
|
|
115
|
+
belt setup frontend wups
|
|
115
116
|
belt apply wups
|
|
116
117
|
belt tasks # list all rake tasks
|
|
117
118
|
belt lambda:build_layer # run a rake task directly
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'base64'
|
|
4
|
+
|
|
5
|
+
module Belt
|
|
6
|
+
# Gem-embedded welcome controller that serves the default "it works!" page.
|
|
7
|
+
# Resolved via the ActionRouter's Belt:: namespace fallback when no app-defined
|
|
8
|
+
# WelcomeController exists. Gets replaced once the user defines their own root route.
|
|
9
|
+
class WelcomeController < BeltController::Base
|
|
10
|
+
skip_before_action :authenticate!
|
|
11
|
+
|
|
12
|
+
ASSETS_DIR = File.expand_path('../assets', __dir__)
|
|
13
|
+
|
|
14
|
+
def show
|
|
15
|
+
title = ENV.fetch('WELCOME_TITLE', 'Welcome to Belt')
|
|
16
|
+
subtitle = ENV.fetch('WELCOME_SUBTITLE', 'API Gateway → Lambda → DynamoDB — all connected.')
|
|
17
|
+
app_name = ENV.fetch('APP_NAME', 'my-app')
|
|
18
|
+
|
|
19
|
+
html_response(render_welcome_page(title: title, subtitle: subtitle, app_name: app_name))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
def render_welcome_page(title:, subtitle:, app_name:)
|
|
25
|
+
<<~HTML
|
|
26
|
+
<!DOCTYPE html>
|
|
27
|
+
<html lang="en">
|
|
28
|
+
<head>
|
|
29
|
+
<meta charset="UTF-8">
|
|
30
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
31
|
+
<title>#{escape_html(title)}</title>
|
|
32
|
+
<style>#{welcome_css}</style>
|
|
33
|
+
</head>
|
|
34
|
+
<body>
|
|
35
|
+
<div class="hero">
|
|
36
|
+
<img src="data:image/png;base64,#{background_image_base64}" alt="Belt" class="hero-bg" />
|
|
37
|
+
<div class="overlay">
|
|
38
|
+
<h1>#{escape_html(title)}</h1>
|
|
39
|
+
<p class="subtitle">#{escape_html(subtitle)}</p>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
<div class="container">
|
|
43
|
+
<div class="stack-check">
|
|
44
|
+
<div class="check-item check-pass">
|
|
45
|
+
<span class="icon">✓</span>
|
|
46
|
+
<span>API Gateway</span>
|
|
47
|
+
</div>
|
|
48
|
+
<div class="arrow">→</div>
|
|
49
|
+
<div class="check-item check-pass">
|
|
50
|
+
<span class="icon">✓</span>
|
|
51
|
+
<span>Lambda</span>
|
|
52
|
+
</div>
|
|
53
|
+
<div class="arrow">→</div>
|
|
54
|
+
#{dynamodb_check_html}
|
|
55
|
+
</div>
|
|
56
|
+
<div class="next-steps">
|
|
57
|
+
<h2>Next Steps</h2>
|
|
58
|
+
<ol>
|
|
59
|
+
<li>Generate a resource: <code>belt g resource post title:string body:text</code></li>
|
|
60
|
+
<li>Deploy: <code>belt deploy</code></li>
|
|
61
|
+
<li>This page will be replaced once you define your own root route.</li>
|
|
62
|
+
</ol>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</body>
|
|
66
|
+
</html>
|
|
67
|
+
HTML
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def dynamodb_check_html
|
|
71
|
+
if dynamodb_connected?
|
|
72
|
+
'<div class="check-item check-pass"><span class="icon">✓</span><span>DynamoDB</span></div>'
|
|
73
|
+
else
|
|
74
|
+
'<div class="check-item check-warn"><span class="icon">⚠</span><span>DynamoDB</span></div>'
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def dynamodb_connected?
|
|
79
|
+
return false unless defined?(Aws::DynamoDB::Client)
|
|
80
|
+
|
|
81
|
+
client = Aws::DynamoDB::Client.new
|
|
82
|
+
client.list_tables(limit: 1)
|
|
83
|
+
true
|
|
84
|
+
rescue StandardError
|
|
85
|
+
false
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def escape_html(text)
|
|
89
|
+
text.to_s.gsub('&', '&').gsub('<', '<').gsub('>', '>').gsub('"', '"')
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def welcome_css
|
|
93
|
+
@welcome_css ||= File.read(File.join(ASSETS_DIR, 'welcome.css'))
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def background_image_base64
|
|
97
|
+
@background_image_base64 ||= Base64.strict_encode64(
|
|
98
|
+
File.binread(File.join(ASSETS_DIR, 'belt-default.png'))
|
|
99
|
+
)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
data/lib/belt/version.rb
CHANGED
data/lib/belt.rb
CHANGED
|
@@ -8,9 +8,9 @@ terraform {
|
|
|
8
8
|
source = "hashicorp/random"
|
|
9
9
|
version = "~> 3.0"
|
|
10
10
|
}
|
|
11
|
-
|
|
12
|
-
source = "
|
|
13
|
-
version = "
|
|
11
|
+
conveyor-belt = {
|
|
12
|
+
source = "stowzilla/conveyor-belt"
|
|
13
|
+
version = "~> 0.0.1"
|
|
14
14
|
}
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -27,12 +27,13 @@ provider "aws" {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
provider "
|
|
30
|
+
provider "conveyor-belt" {
|
|
31
31
|
environment = var.environment
|
|
32
32
|
aws_region = var.aws_region
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
resource "
|
|
35
|
+
resource "conveyor_belt" "main" {
|
|
36
|
+
provider = conveyor-belt
|
|
36
37
|
source = "${path.module}/../routes.tf.rb"
|
|
37
38
|
app_name = var.app_name
|
|
38
39
|
lambda_source_dir = "${path.module}/../../lambda"
|
|
@@ -9,7 +9,7 @@ resource "random_string" "frontend_suffix" {
|
|
|
9
9
|
|
|
10
10
|
# S3 bucket for frontend static assets
|
|
11
11
|
resource "aws_s3_bucket" "frontend" {
|
|
12
|
-
bucket = "<%= @app_name %>-frontend-${var.environment}-${random_string.frontend_suffix.result}"
|
|
12
|
+
bucket = "<%= s3_safe_name(@app_name) %>-frontend-${var.environment}-${random_string.frontend_suffix.result}"
|
|
13
13
|
|
|
14
14
|
lifecycle {
|
|
15
15
|
ignore_changes = [bucket]
|
|
@@ -155,5 +155,5 @@ output "frontend_url" {
|
|
|
155
155
|
}
|
|
156
156
|
|
|
157
157
|
output "api_url" {
|
|
158
|
-
value = values(
|
|
158
|
+
value = values(conveyor_belt.main.api_gateway_urls)[0]
|
|
159
159
|
}
|
|
@@ -6,7 +6,7 @@ This file explains the project structure, tooling, and conventions for AI agents
|
|
|
6
6
|
|
|
7
7
|
- **Belt** — CLI and runtime framework (like Rails for serverless). Provides Lambda handler, action router, controller base class, and CLI tooling.
|
|
8
8
|
- **ActiveItem** — ActiveRecord-like ORM for DynamoDB. Models inherit from `ActiveItem::Base`.
|
|
9
|
-
- **
|
|
9
|
+
- **Conveyor Belt** — Terraform provider that reads a Ruby DSL (`routes.tf.rb`) and creates API Gateway + Lambda + IAM infrastructure.
|
|
10
10
|
- **Lambda Loadout** — Lambda cold-start optimizer (auto-required by Belt).
|
|
11
11
|
|
|
12
12
|
## Project Structure
|
|
@@ -23,7 +23,7 @@ This file explains the project structure, tooling, and conventions for AI agents
|
|
|
23
23
|
│ ├── lib/routes/ # Route manifests (auto-generated)
|
|
24
24
|
│ └── Gemfile # Lambda-specific dependencies
|
|
25
25
|
├── infrastructure/
|
|
26
|
-
│ ├── routes.tf.rb # API routes (
|
|
26
|
+
│ ├── routes.tf.rb # API routes (Conveyor Belt DSL)
|
|
27
27
|
│ ├── schema.tf.rb # Model schema definitions
|
|
28
28
|
│ └── <env>/ # Per-environment Terraform configs
|
|
29
29
|
├── Gemfile # Project-level dependencies
|
|
@@ -44,6 +44,7 @@ belt init <env> # terraform init
|
|
|
44
44
|
belt plan <env> # terraform plan
|
|
45
45
|
belt apply <env> # terraform apply
|
|
46
46
|
belt destroy <env> # terraform destroy
|
|
47
|
+
belt destroy environment <env> # same (mirrors generate environment)
|
|
47
48
|
belt output <env> # terraform output
|
|
48
49
|
```
|
|
49
50
|
|
|
@@ -58,7 +59,7 @@ belt output <env> # terraform output
|
|
|
58
59
|
end
|
|
59
60
|
```
|
|
60
61
|
|
|
61
|
-
2.
|
|
62
|
+
2. Conveyor Belt creates an API Gateway where the namespace becomes a base path mapping. URLs look like:
|
|
62
63
|
```
|
|
63
64
|
https://api.<env>.example.com/<%= @app_name %>/things
|
|
64
65
|
```
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Routes
|
|
4
4
|
<%= @module_name.upcase %> = [
|
|
5
|
+
{ verb: 'GET', path: '/', controller: 'welcome', action: 'show' },
|
|
5
6
|
# { verb: 'GET', path: '/posts', controller: 'posts', action: 'index' },
|
|
6
7
|
# { verb: 'POST', path: '/posts', controller: 'posts', action: 'create' },
|
|
7
8
|
# { verb: 'GET', path: '/posts/{id}', controller: 'posts', action: 'show' },
|
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.1.
|
|
4
|
+
version: 0.1.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stowzilla
|
|
@@ -52,10 +52,13 @@ files:
|
|
|
52
52
|
- exe/belt
|
|
53
53
|
- lib/belt.rb
|
|
54
54
|
- lib/belt/action_router.rb
|
|
55
|
+
- lib/belt/assets/belt-default.png
|
|
56
|
+
- lib/belt/assets/welcome.css
|
|
55
57
|
- lib/belt/cli.rb
|
|
56
58
|
- lib/belt/cli/app_detection.rb
|
|
57
59
|
- lib/belt/cli/bucket_security.rb
|
|
58
60
|
- lib/belt/cli/console_command.rb
|
|
61
|
+
- lib/belt/cli/deploy_command.rb
|
|
59
62
|
- lib/belt/cli/env_resolver.rb
|
|
60
63
|
- lib/belt/cli/environment_command.rb
|
|
61
64
|
- lib/belt/cli/frontend_command.rb
|
|
@@ -66,11 +69,13 @@ files:
|
|
|
66
69
|
- lib/belt/cli/routes_command.rb
|
|
67
70
|
- lib/belt/cli/routes_command/route_inference.rb
|
|
68
71
|
- lib/belt/cli/routes_command/schema_loader.rb
|
|
72
|
+
- lib/belt/cli/server_command.rb
|
|
69
73
|
- lib/belt/cli/setup_command.rb
|
|
70
74
|
- lib/belt/cli/tables_command.rb
|
|
71
75
|
- lib/belt/cli/tasks_command.rb
|
|
72
76
|
- lib/belt/cli/terraform_command.rb
|
|
73
77
|
- lib/belt/cli/views_command.rb
|
|
78
|
+
- lib/belt/controllers/welcome_controller.rb
|
|
74
79
|
- lib/belt/helpers/cors_origin.rb
|
|
75
80
|
- lib/belt/helpers/error_logging.rb
|
|
76
81
|
- lib/belt/helpers/response.rb
|
|
@@ -84,6 +89,7 @@ files:
|
|
|
84
89
|
- lib/belt_controller/base.rb
|
|
85
90
|
- lib/templates/environment/backend.tf.erb
|
|
86
91
|
- lib/templates/environment/main.tf.erb
|
|
92
|
+
- lib/templates/environment/outputs.tf.erb
|
|
87
93
|
- lib/templates/environment/terraform.tfvars.erb
|
|
88
94
|
- lib/templates/environment/variables.tf.erb
|
|
89
95
|
- lib/templates/frontend/react/index.html.erb
|