brut 0.18.2 → 0.19.0
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/brut/cli/apps/build_assets.rb +63 -32
- data/lib/brut/cli/apps/db.rb +198 -78
- data/lib/brut/cli/apps/deploy.rb +215 -140
- data/lib/brut/cli/apps/new/app.rb +127 -41
- data/lib/brut/cli/apps/scaffold.rb +108 -105
- data/lib/brut/cli/apps/test.rb +45 -26
- data/lib/brut/cli/commands/base_command.rb +58 -22
- data/lib/brut/cli/commands/compound_command.rb +2 -4
- data/lib/brut/cli/commands/execution_context.rb +17 -10
- data/lib/brut/cli/commands/help.rb +112 -6
- data/lib/brut/cli/commands/output_error.rb +1 -1
- data/lib/brut/cli/execute_result.rb +4 -0
- data/lib/brut/cli/executor.rb +7 -7
- data/lib/brut/cli/logger.rb +122 -0
- data/lib/brut/cli/output.rb +9 -45
- data/lib/brut/cli/parsed_command_line.rb +33 -10
- data/lib/brut/cli/runner.rb +37 -8
- data/lib/brut/cli/terminal.rb +74 -0
- data/lib/brut/cli/terminal_theme.rb +131 -0
- data/lib/brut/cli.rb +7 -3
- data/lib/brut/framework/mcp.rb +4 -3
- data/lib/brut/front_end/asset_metadata.rb +9 -5
- data/lib/brut/spec_support/cli_command_support.rb +9 -3
- data/lib/brut/spec_support/e2e_test_server.rb +3 -3
- data/lib/brut/tui/script.rb +1 -1
- data/lib/brut/version.rb +1 -1
- metadata +18 -4
- data/lib/brut/cli/apps/deploy_base.rb +0 -86
- data/lib/brut/cli/apps/heroku_container_based_deploy.rb +0 -226
- data/templates/segments/Heroku/bin/deploy +0 -11
|
@@ -1,12 +1,42 @@
|
|
|
1
1
|
require "fileutils"
|
|
2
2
|
require "erb"
|
|
3
3
|
require "ostruct"
|
|
4
|
+
require "lipgloss"
|
|
4
5
|
|
|
5
6
|
class Brut::CLI::Apps::New::App < Brut::CLI::Commands::BaseCommand
|
|
6
7
|
def name = "new"
|
|
7
8
|
def description = "Create a Brut App or modify an existing one with new segments"
|
|
8
9
|
def args_description = "app_name"
|
|
9
10
|
|
|
11
|
+
LOGO_WIDE = %{
|
|
12
|
+
################# #### ################# ################
|
|
13
|
+
################## #### ################## #################
|
|
14
|
+
##### ##### #### ##### ###### ##### #####
|
|
15
|
+
##### ##### #### #### #### #### ########## ##### ##### ##### #####
|
|
16
|
+
##### ###### ########## #### #### ########## ##### ##### ##### ######
|
|
17
|
+
################ ###### #### #### #### ################# ################
|
|
18
|
+
##### ###### ##### #### #### #### ################# ##### ######
|
|
19
|
+
##### ##### #### #### #### #### ##### ##### ##### #####
|
|
20
|
+
##### ##### #### #### #### #### ##### ##### ##### #####
|
|
21
|
+
##### ###### #### ##### ##### #### ##### #### ##### ######
|
|
22
|
+
################## #### ############## ####### ##### #### #################
|
|
23
|
+
############### #### ####### #### ###### ##### ##### ##############
|
|
24
|
+
}.strip
|
|
25
|
+
LOGO_NARROW = %{
|
|
26
|
+
################# ####
|
|
27
|
+
################## ####
|
|
28
|
+
##### ##### ####
|
|
29
|
+
##### ##### #### #### #### #### ##########
|
|
30
|
+
##### ###### ########## #### #### ##########
|
|
31
|
+
################ ###### #### #### ####
|
|
32
|
+
##### ###### ##### #### #### ####
|
|
33
|
+
##### ##### #### #### #### ####
|
|
34
|
+
##### ##### #### #### #### ####
|
|
35
|
+
##### ###### #### ##### ##### ####
|
|
36
|
+
################## #### ############## #######
|
|
37
|
+
############### #### ####### #### ######
|
|
38
|
+
}.strip
|
|
39
|
+
|
|
10
40
|
def accepts = [
|
|
11
41
|
Brut::CLI::Apps::New::Prefix,
|
|
12
42
|
Brut::CLI::Apps::New::AppId,
|
|
@@ -25,16 +55,15 @@ class Brut::CLI::Apps::New::App < Brut::CLI::Commands::BaseCommand
|
|
|
25
55
|
Brut::CLI::Apps::New::AppId,
|
|
26
56
|
"App identifier, which must be able to be used as a hostname or other Internet identifier. Derived from your app name, if omitted"
|
|
27
57
|
],
|
|
28
|
-
[
|
|
29
|
-
"--[no-]interactive",
|
|
30
|
-
"Don't ask for user input, just assume default answers",
|
|
31
|
-
],
|
|
32
58
|
[
|
|
33
59
|
"--organization=ORG",
|
|
34
60
|
Brut::CLI::Apps::New::Organization,
|
|
35
61
|
"Organization name, e.g. what you'd use for GitHub. Defaults to the app-id value"
|
|
36
62
|
],
|
|
37
|
-
|
|
63
|
+
[
|
|
64
|
+
"--[no-]interactive",
|
|
65
|
+
"Set if you want to be prompted before the app is actually created",
|
|
66
|
+
],
|
|
38
67
|
[
|
|
39
68
|
"--prefix=PREFIX",
|
|
40
69
|
Brut::CLI::Apps::New::Prefix,
|
|
@@ -60,16 +89,28 @@ class Brut::CLI::Apps::New::App < Brut::CLI::Commands::BaseCommand
|
|
|
60
89
|
app_name = argv[0]
|
|
61
90
|
|
|
62
91
|
if !app_name
|
|
63
|
-
|
|
92
|
+
error "app_name is required"
|
|
64
93
|
return 1
|
|
65
94
|
end
|
|
66
95
|
|
|
96
|
+
if terminal.cols >= LOGO_WIDE.lines[0].length
|
|
97
|
+
puts theme.title.render("WELCOME TO")
|
|
98
|
+
puts
|
|
99
|
+
puts theme.title.render(LOGO_WIDE)
|
|
100
|
+
elsif terminal.cols >= LOGO_NARROW.lines[0].length
|
|
101
|
+
puts theme.title.render("WELCOME TO")
|
|
102
|
+
puts
|
|
103
|
+
puts theme.title.render(LOGO_NARROW)
|
|
104
|
+
else
|
|
105
|
+
puts theme.title.render("WELCOME TO BRUT")
|
|
106
|
+
end
|
|
107
|
+
|
|
67
108
|
options.set_default(:app_id, Brut::CLI::Apps::New::AppId.from_app_name(app_name))
|
|
68
109
|
options.set_default(:prefix, Brut::CLI::Apps::New::Prefix.from_app_id(options.app_id))
|
|
69
110
|
options.set_default(:organization, Brut::CLI::Apps::New::Prefix.from_app_id(options.app_id))
|
|
70
111
|
options.set_default(:demo, true)
|
|
71
|
-
options.set_default(:interactive, true)
|
|
72
112
|
options.set_default(:dir,Pathname.pwd)
|
|
113
|
+
options.set_default(:interactive, true)
|
|
73
114
|
|
|
74
115
|
segment_names = Set.new(options.segments)
|
|
75
116
|
if options.demo?
|
|
@@ -128,45 +169,90 @@ class Brut::CLI::Apps::New::App < Brut::CLI::Commands::BaseCommand
|
|
|
128
169
|
end
|
|
129
170
|
segments.sort!
|
|
130
171
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
Brut::CLI::Apps::New::Ops::BaseOp.dry_run = true
|
|
172
|
+
info "Creating a new Brut app with these options:"
|
|
173
|
+
rows = [
|
|
174
|
+
["App Name", app_name],
|
|
175
|
+
["Path to New App", current_dir / app_name],
|
|
176
|
+
["App Id", options.app_id ],
|
|
177
|
+
["Prefix", options.prefix ],
|
|
178
|
+
["Organization", options.organization ],
|
|
179
|
+
["Segments", segments.map(&:class).map(&:segment_name).join(", ") ],
|
|
180
|
+
]
|
|
181
|
+
rows.each do |(attr,val)|
|
|
182
|
+
info " #{attr}: #{val}"
|
|
143
183
|
end
|
|
144
|
-
|
|
145
184
|
if options.interactive?
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
185
|
+
puts
|
|
186
|
+
puts theme.subheader.render("Options for your new app:")
|
|
187
|
+
table = Lipgloss::Table.new.headers(["Attribute", "Value"]).
|
|
188
|
+
rows(rows).
|
|
189
|
+
border(:rounded).
|
|
190
|
+
style_func(rows: rows.size, columns: 2) do |row,column|
|
|
191
|
+
if row == Lipgloss::Table::HEADER_ROW
|
|
192
|
+
if column == 0
|
|
193
|
+
Lipgloss::Style.new.inherit(theme.header).align_horizontal(:right).padding_right(1).padding_left(1)
|
|
194
|
+
else
|
|
195
|
+
Lipgloss::Style.new.inherit(theme.header).align_horizontal(:left).padding_right(1).padding_left(1)
|
|
196
|
+
end
|
|
197
|
+
elsif column == 0
|
|
198
|
+
Lipgloss::Style.new.inherit(theme.subheader).align_horizontal(:right).padding_right(1).padding_left(1)
|
|
199
|
+
else
|
|
200
|
+
Lipgloss::Style.new.inherit(theme.none).align_horizontal(:left).padding_right(1).padding_left(1)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
puts table.render
|
|
205
|
+
|
|
206
|
+
puts "Proceed? (y/n): "
|
|
207
|
+
answer = stdin.gets.strip.downcase
|
|
208
|
+
if answer != "y"
|
|
209
|
+
puts theme.warning.render("Aborting app creation")
|
|
210
|
+
return 0
|
|
151
211
|
end
|
|
152
212
|
end
|
|
153
213
|
|
|
154
|
-
|
|
214
|
+
if options.dry_run?
|
|
215
|
+
info "Dry Run only"
|
|
216
|
+
Brut::CLI::Apps::New::Ops::BaseOp.dry_run = true
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
info "Creating Base app"
|
|
155
220
|
base.create!
|
|
156
221
|
segments.each do |segment|
|
|
157
|
-
|
|
222
|
+
info "Creating segment: #{segment.class.friendly_name}"
|
|
158
223
|
segment.add!
|
|
159
224
|
end
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
225
|
+
|
|
226
|
+
puts
|
|
227
|
+
print theme.header.render("Your app ")
|
|
228
|
+
print theme.subheader.render(app_name)
|
|
229
|
+
print theme.header.render(" was created - time to get building!")
|
|
230
|
+
puts
|
|
231
|
+
puts
|
|
232
|
+
|
|
233
|
+
in_computer = Lipgloss::List.new.items(
|
|
234
|
+
[
|
|
235
|
+
theme.code.render("cd #{current_dir / app_name}"),
|
|
236
|
+
theme.code.render("dx/build"),
|
|
237
|
+
theme.code.render("dx/start"),
|
|
238
|
+
"#{theme.weak.render("(in another terminal)")} #{theme.code.render('dx/exec bash')}",
|
|
239
|
+
]
|
|
240
|
+
).enumerator(:arabic).enumerator_style(theme.bullet(1))
|
|
241
|
+
in_docker = Lipgloss::List.new.items(
|
|
242
|
+
[
|
|
243
|
+
theme.code.render("bin/setup"),
|
|
244
|
+
theme.code.render("bin/dev"),
|
|
245
|
+
]
|
|
246
|
+
).enumerator(:arabic).enumerator_style(theme.bullet(1))
|
|
247
|
+
list = Lipgloss::List.new.items([
|
|
248
|
+
"On your computer:\n#{in_computer.render}",
|
|
249
|
+
"Inside the Docker container:\n#{in_docker.render}",
|
|
250
|
+
"Navigate to #{theme.url.render('http://localhost:6502')} to see your app",
|
|
251
|
+
"Inside the Docker container, try #{theme.code.render('bin/setup')} help to find more commands",
|
|
252
|
+
]).enumerator(:arabic).
|
|
253
|
+
enumerator_style(theme.bullet(0))
|
|
254
|
+
puts list.render
|
|
255
|
+
puts
|
|
170
256
|
end
|
|
171
257
|
|
|
172
258
|
class Segment < Brut::CLI::Commands::BaseCommand
|
|
@@ -195,7 +281,7 @@ class Brut::CLI::Apps::New::App < Brut::CLI::Commands::BaseCommand
|
|
|
195
281
|
|
|
196
282
|
segment_name = argv[0]
|
|
197
283
|
if !segment_name
|
|
198
|
-
|
|
284
|
+
error "segment_name is required"
|
|
199
285
|
return 1
|
|
200
286
|
end
|
|
201
287
|
if options.demo?
|
|
@@ -207,7 +293,7 @@ class Brut::CLI::Apps::New::App < Brut::CLI::Commands::BaseCommand
|
|
|
207
293
|
|
|
208
294
|
|
|
209
295
|
if options.dry_run?
|
|
210
|
-
|
|
296
|
+
puts "Dry Run"
|
|
211
297
|
Brut::CLI::Apps::New::Ops::BaseOp.dry_run = true
|
|
212
298
|
end
|
|
213
299
|
|
|
@@ -227,11 +313,11 @@ class Brut::CLI::Apps::New::App < Brut::CLI::Commands::BaseCommand
|
|
|
227
313
|
)
|
|
228
314
|
end
|
|
229
315
|
if !segment
|
|
230
|
-
|
|
316
|
+
error "'#{segment_name}' is not a segment. Allowed values: sidekiq, heroku"
|
|
231
317
|
return 1
|
|
232
318
|
end
|
|
233
319
|
|
|
234
|
-
|
|
320
|
+
puts "Adding #{segment_name} to this app"
|
|
235
321
|
segment.add!
|
|
236
322
|
segment.output_post_add_messaging(stdout:)
|
|
237
323
|
0
|