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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 18ebf2d762c75463ec24d10dc71e6f293030840847ad80a93be12e308be402e5
4
- data.tar.gz: 6a03c2aed681fce6aefdbd2e5b57d5d5b99664b27592714e34e15938064d9d7f
3
+ metadata.gz: 85deaeeb3b8c032710ef05c4d61c0190e5c4f12294a5b38e1f154bb8c106dc96
4
+ data.tar.gz: d9530ce8adebb693f5b9334213b3d26f5c82078634279ae448073693afe751f8
5
5
  SHA512:
6
- metadata.gz: 8af92231267472414c6e9a65c70ba86868ab3fcebc287ed50fa5c96c495fee004cb4456a4a33e31db6505a6a967a12b50b77c607a1eb4dafa75f50e74ecb6601
7
- data.tar.gz: 3dc4ac735686f7f7c2ca934ccdb1d127995a074ec4e625d56facf6d0b5654db65e60767a81daa639bb5cec3b97c46f1804c2df1f6127653f3b9dee07e3fb6164
6
+ metadata.gz: 4db91cd807771f84cf32e6f12e9520863ed1bf5718b6ff8efe0e703126b8d3ee62f837bdd78782ae2523f6ca8bac97d3d190952b818d5c65d9234fcc020a76fc
7
+ data.tar.gz: 9b040971b7c3a8a037a77356f60e71f83a8c27b37537b50456c52b141bd2359a47cd93b2148b661ff995b85ad0313ae3440899e4fa35946881aa66d92fee6f76
@@ -6,22 +6,44 @@ require "brut/cli"
6
6
  class Brut::CLI::Apps::BuildAssets < Brut::CLI::Commands::BaseCommand
7
7
  def description = "Build and manage code and assets destined for the browser, such as CSS, JS, or images"
8
8
 
9
- def opts = [
10
- [
11
- "--[no-]clean",
12
- "If set, any old files from previous runs are deleted. If omitted, is false in production and true otherwise",
13
- ],
14
- ]
9
+ class BaseCommand < Brut::CLI::Commands::BaseCommand
10
+ def bootstrap? = false
11
+ def default_rack_env = "development"
12
+ def opts = [
13
+ [
14
+ "--[no-]clean",
15
+ "If set, any old files from previous runs are deleted. Defaults to false in production, true everywhere else.",
16
+ ],
17
+ ]
18
+ def friendly_name(file)
19
+ Pathname(file).relative_path_from(Brut.container.project_root).to_s
20
+ end
21
+ end
15
22
 
16
23
  def name = "build-assets"
17
24
 
18
- def default_command_class = All
25
+ def default_command
26
+ @default_command ||= All.new
27
+ end
19
28
  def bootstrap? = default_command.bootstrap?
29
+ def default_rack_env = default_command.default_rack_env
30
+
31
+ def run
32
+ delegate_to_command(default_command)
33
+ end
34
+
35
+ def commands = [
36
+ All.new,
37
+ Images.new,
38
+ Css.new,
39
+ Js.new,
40
+ ]
41
+
20
42
 
21
43
  class All < Brut::CLI::Commands::CompoundCommand
22
44
  def default_rack_env = "development"
23
45
  def description = "Build all assets"
24
- def bootstrap? = false
46
+ def bootstrap? = true
25
47
 
26
48
  def initialize
27
49
  super([
@@ -41,24 +63,31 @@ class Brut::CLI::Apps::BuildAssets < Brut::CLI::Commands::BaseCommand
41
63
  end
42
64
  end
43
65
 
44
- class Images < Brut::CLI::Commands::BaseCommand
45
- def default_rack_env = "development"
66
+ class Images < BaseCommand
46
67
  def description = "Copy images to the public folder"
47
68
  def detailed_description = %{
48
69
  This is to ensure that any images your code references will end up in the public directory, so they are served properly. This is not for managing images that may be referenced in CSS files. See the `css` command for information on that.
49
70
  }
50
- def bootstrap? = false
51
71
 
52
72
  def run
53
73
  src_dir = Brut.container.images_src_dir
54
74
  dest_dir = Brut.container.images_root_dir
55
75
 
56
- system! "rsync --archive --delete --verbose \"#{src_dir}/\" \"#{dest_dir}\""
76
+ puts "Syncing images from #{theme.code.render(friendly_name(src_dir.to_s))} to #{theme.code.render(friendly_name(dest_dir.to_s))}"
77
+ rsync_args = [
78
+ "--archive",
79
+ "--verbose",
80
+ ]
81
+ if options.clean?(default: options.env != "production")
82
+ puts "Deleting old images from #{theme.code.render(friendly_name(dest_dir.to_s))}"
83
+ rsync_args << "--delete"
84
+ end
85
+ system! "rsync #{rsync_args.join(' ')} \"#{src_dir}/\" \"#{dest_dir}\""
57
86
  end
58
87
  end
59
88
 
60
- class Css < Brut::CLI::Commands::BaseCommand
61
- def default_rack_env = "development"
89
+
90
+ class Css < BaseCommand
62
91
  def description = "Builds a single CSS file suitable for sending to the browser"
63
92
 
64
93
  def detailed_description = %{
@@ -66,7 +95,6 @@ This is to ensure that any images your code references will end up in the public
66
95
 
67
96
  To ensure this happens correctly, your url() or other function must reference the file as a relative file from where your actual source CSS file is located. For example, a font named some-font.ttf would be in app/src/front_end/fonts and to reference this from app/src/front_end/css/index.css you'd use the url "../fonts/some-font.ttf"
68
97
  }
69
- def bootstrap? = false
70
98
 
71
99
  def run
72
100
  css_bundle = Brut.container.css_bundle_output_dir / "styles.css"
@@ -75,34 +103,37 @@ This is to ensure that any images your code references will end up in the public
75
103
  asset_metadata_file = Brut.container.asset_metadata_file
76
104
 
77
105
  if options.clean?(default: options.env != "production")
78
- puts "Cleaning old CSS files from #{Brut.container.css_bundle_output_dir}"
106
+ puts "Cleaning old CSS files from #{theme.code.render(friendly_name(Brut.container.css_bundle_output_dir.to_s))}"
79
107
  Dir[Brut.container.css_bundle_output_dir / "*.*"].each do |file|
80
108
  if File.file?(file)
81
- puts "Deleting #{file}"
109
+ puts theme.weak.render(" Deleting #{theme.code.render(friendly_name(file))}")
82
110
  FileUtils.rm(file)
83
111
  end
84
112
  end
85
113
  end
86
114
 
87
- command = "npx esbuild --loader:.ttf=copy --loader:.otf=copy --metafile=#{esbuild_metafile} --entry-names=[name]-[hash] --sourcemap --bundle #{css_bundle_source} --outfile=#{css_bundle}"
88
- puts "Building CSS bundle '#{css_bundle}' with '#{command}'"
115
+ # NOTE: esbuild outputs its normal messages on stderr which is fucking stupid
116
+ command = "npx esbuild --loader:.ttf=copy --loader:.otf=copy --metafile=#{esbuild_metafile} --entry-names=[name]-[hash] --sourcemap --bundle #{css_bundle_source} --outfile=#{css_bundle} 2>&1"
117
+ puts "Building CSS bundle '#{theme.code.render(friendly_name(css_bundle))}'"
118
+
89
119
  system!(command)
90
120
 
91
121
  if !File.exist?(esbuild_metafile)
92
- stderr.puts "'#{esbuild_metafile}' was not generated - cannot continue"
122
+ error "'#{esbuild_metafile}' was not generated"
123
+ puts theme.error.render("esbuild did not generate the metafile we asked for (#{esbuild_metafile})")
124
+ puts theme.error.render("This file is required to continue")
93
125
  return 1
94
126
  end
95
127
 
96
- asset_metadata = Brut::FrontEnd::AssetMetadata.new(asset_metadata_file:,out: stdout)
128
+ asset_metadata = Brut::FrontEnd::AssetMetadata.new(asset_metadata_file:,logger:execution_context.logger)
97
129
  asset_metadata.merge!(extension: ".css", esbuild_metafile:)
98
130
  asset_metadata.save!
99
131
  0
100
132
  end
101
133
  end
102
- class Js < Brut::CLI::Commands::BaseCommand
103
- def default_rack_env = "development"
134
+ class Js < BaseCommand
104
135
  def description = "Builds and bundles JavaScript destined for the browser"
105
- def opts = [
136
+ def opts = super + [
106
137
  [
107
138
  "--output-file=FILE",
108
139
  "Bundle to create that will be sent to the browser, relative to the JS public folder. Default is app.js",
@@ -110,9 +141,8 @@ This is to ensure that any images your code references will end up in the public
110
141
  [
111
142
  "--source-file=FILE",
112
143
  "Entry point used to create the bundle, relative to the source JS folder. Default is index.js",
113
- ]
144
+ ],
114
145
  ]
115
- def bootstrap? = false
116
146
 
117
147
  def run
118
148
  js_bundle = Brut.container.js_bundle_output_dir / options.output_file(default: "app.js")
@@ -122,25 +152,26 @@ This is to ensure that any images your code references will end up in the public
122
152
 
123
153
  name_with_hash_regexp = /app\/public\/(?<path>.+)\/(?<name>.+)\-(?<hash>.+)\.js/
124
154
  if options.clean?(default: options.env != "production")
125
- puts "Cleaning old JS files from #{Brut.container.js_bundle_output_dir}"
155
+ puts "Cleaning old JS files from #{friendly_name(Brut.container.js_bundle_output_dir)}"
126
156
  Dir[Brut.container.js_bundle_output_dir / "*.*"].each do |file|
127
157
  if File.file?(file)
128
- puts "Deleting #{file}"
158
+ puts theme.weak.render(" Deleting #{theme.code.render(friendly_name(file))}")
129
159
  FileUtils.rm(file)
130
160
  end
131
161
  end
132
162
  end
133
163
 
134
- command = "npx esbuild --metafile=#{esbuild_metafile} --entry-names=[name]-[hash] --sourcemap --bundle #{js_bundle_source} --outfile=#{js_bundle}"
135
- puts "Building JS bundle '#{js_bundle}' with '#{command}'"
164
+ # NOTE: esbuild outputs its normal messages on stderr which is fucking stupid
165
+ command = "npx esbuild --metafile=#{esbuild_metafile} --entry-names=[name]-[hash] --sourcemap --bundle #{js_bundle_source} --outfile=#{js_bundle} 2>&1"
166
+ puts "Building JS bundle '#{theme.code.render(friendly_name(js_bundle))}'"
136
167
  system!(command)
137
168
 
138
169
  if !File.exist?(esbuild_metafile)
139
- stderr.puts "'#{esbuild_metafile}' was not generated - cannot continue"
170
+ error "'#{esbuild_metafile}' was not generated - cannot continue"
140
171
  return 1
141
172
  end
142
173
 
143
- asset_metadata = Brut::FrontEnd::AssetMetadata.new(asset_metadata_file:,out: stdout)
174
+ asset_metadata = Brut::FrontEnd::AssetMetadata.new(asset_metadata_file:,logger: execution_context.logger)
144
175
  asset_metadata.merge!(extension: ".js", esbuild_metafile:)
145
176
  asset_metadata.save!
146
177
  0
@@ -7,8 +7,6 @@ class Brut::CLI::Apps::DB < Brut::CLI::Commands::BaseCommand
7
7
  def description = "Manage your database in development, test, and production"
8
8
  def name = "db"
9
9
 
10
- def default_command_class = Status
11
-
12
10
  class Status < Brut::CLI::Commands::BaseCommand
13
11
  def description = "Check the status of the database and migrations"
14
12
  def default_rack_env = "development"
@@ -17,8 +15,6 @@ class Brut::CLI::Apps::DB < Brut::CLI::Commands::BaseCommand
17
15
  def run
18
16
  database_name = URI(Brut.container.database_url).path.gsub(/^\//,"")
19
17
  connection = Brut.container.sequel_db_handle
20
- stdout.puts "Database server is up"
21
- stdout.puts "Database #{database_name} exists"
22
18
  migrations_run = if connection.table_exists?("schema_migrations")
23
19
  connection["select filename from schema_migrations order by filename"].all.map { |_| _[:filename] }
24
20
  else
@@ -27,16 +23,7 @@ class Brut::CLI::Apps::DB < Brut::CLI::Commands::BaseCommand
27
23
  migration_files = Dir[Brut.container.migrations_dir / "*.rb"].map { |file|
28
24
  filename = Pathname(file).basename.to_s
29
25
  }
30
- if migration_files.empty? && migrations_run.empty?
31
- stdout.puts("✅ NO MIGRATION FILES TO RUN")
32
- else
33
- max_length = migration_files.map(&:length).max
34
- printf_string = "%-#{max_length}s - %s\n"
35
- migration_files.each do |filename|
36
- applied = migrations_run.include?(filename)
37
- stdout.printf(printf_string,filename,applied ? "✅ APPLIED" : "❌ NOT APPLIED")
38
- end
39
- end
26
+ puts status_table(server_up: true, database_exists: true, database_name:, migrations_run:, migration_files:).render
40
27
  0
41
28
  rescue Sequel::DatabaseConnectionError => ex
42
29
  uri_no_database = URI(Brut.container.database_url.to_s)
@@ -44,15 +31,64 @@ class Brut::CLI::Apps::DB < Brut::CLI::Commands::BaseCommand
44
31
  uri_no_database.path = ""
45
32
  begin
46
33
  connection = Sequel.connect(uri_no_database.to_s)
47
- stdout.puts "Database server is up"
48
- stdout.puts "Database #{database_name} does not exist - run `brut db create` to create it"
34
+ puts status_table(server_up: true, database_exists: false, database_name:, migrations_run: [], migration_files: []).render
35
+ puts [
36
+ theme.warning.render("Try creating the database with"),
37
+ theme.code.render("brut db create"),
38
+ ].join(" ")
49
39
  0
50
40
  rescue => ex2
51
- stderr.puts "Database server is not running at #{uri_no_database}: #{ex2.message}"
52
- stderr.puts "This could be a problem with your dev environment generally, or your .env.test or .env.test.local files"
41
+ puts status_table(server_up: false, database_exists: false, database_name:, migrations_run: [], migration_files: []).render
42
+ puts theme.error.render("Database server is not running at #{uri_no_database}: #{ex2.message}")
43
+ puts theme.error.render("This could be a problem with your dev environment generally, or your .env.test or .env.test.local files")
53
44
  1
54
45
  end
55
46
  end
47
+
48
+ private
49
+
50
+ def status_table(server_up:, database_exists:, database_name:, migrations_run:, migration_files:)
51
+ rows = [
52
+ [
53
+ "Database Server",
54
+ server_up ? theme.success.render("✅ UP") : theme.error.render("❌ DOWN")
55
+ ],
56
+ ]
57
+ if server_up
58
+ rows << [
59
+ "Database #{theme.code.render(database_name)}",
60
+ database_exists ? theme.success.render("✅ Exists") : theme.error.render("❌ DOES NOT EXIST")
61
+ ]
62
+ end
63
+ if database_exists
64
+ if migration_files.empty? && migrations_run.empty?
65
+ rows << [
66
+ "Migrations",
67
+ "✅ NO MIGRATION FILES TO RUN"
68
+ ]
69
+ else
70
+ migration_files.each do |filename|
71
+ applied = if migrations_run.include?(filename)
72
+ theme.success.render("✅ APPLIED")
73
+ else
74
+ theme.warning.render("❌ NOT APPLIED")
75
+ end
76
+ rows << [ filename, applied ]
77
+ end
78
+ end
79
+ end
80
+ Lipgloss::Table.new.
81
+ headers([ "Check", "Status" ]).
82
+ rows(rows).
83
+ style_func(rows: rows.length, columns: 2) { |row,column|
84
+ if row == Lipgloss::Table::HEADER_ROW
85
+ Lipgloss::Style.new.inherit(theme.header).padding_left(1).padding_right(1)
86
+ else
87
+ Lipgloss::Style.new.inherit(theme.none).padding_left(1).padding_right(1)
88
+ end
89
+ }
90
+ end
91
+
56
92
  end
57
93
 
58
94
  class Create < Brut::CLI::Commands::BaseCommand
@@ -61,24 +97,56 @@ class Brut::CLI::Apps::DB < Brut::CLI::Commands::BaseCommand
61
97
  def bootstrap? = false
62
98
 
63
99
  def run
64
- connection = Sequel.connect(Brut.container.database_url)
65
- stdout.puts "Database already exists"
66
- connection.disconnect
67
- 0
68
- rescue Sequel::DatabaseConnectionError => ex
69
100
  uri_no_database = URI(Brut.container.database_url.to_s)
70
101
  database_name = uri_no_database.path.gsub(/^\//,"")
71
102
  uri_no_database.path = ""
72
103
  begin
73
- connection = Sequel.connect(uri_no_database.to_s)
74
- stdout.puts "Database #{database_name} does not exist. Creating..."
75
- connection.run("CREATE DATABASE \"#{database_name}\"")
104
+ connection = Sequel.connect(Brut.container.database_url)
105
+ puts [
106
+ theme.success.render(" Database"),
107
+ theme.code.render(database_name),
108
+ theme.success.render("already exists"),
109
+ ].join(" ")
76
110
  connection.disconnect
77
111
  0
78
- rescue Sequel::DatabaseConnectionError => ex2
79
- stderr.puts "Database server is not running at #{uri_no_database}: #{ex2.message}"
80
- stderr.puts "This could be a problem with your dev environment generally, or your .env.test or .env.test.local files"
81
- 1
112
+ rescue Sequel::DatabaseConnectionError => ex
113
+ begin
114
+ connection = Sequel.connect(uri_no_database.to_s)
115
+ puts [
116
+ "Database",
117
+ theme.code.render(database_name),
118
+ "does not exist. Creating...",
119
+ ].join(" ")
120
+ connection.run("CREATE DATABASE \"#{database_name}\"")
121
+ connection.disconnect
122
+ puts [
123
+ theme.success.render("✅ Database"),
124
+ theme.code.render(database_name),
125
+ theme.success.render("created"),
126
+ ].join(" ")
127
+ 0
128
+ rescue Sequel::DatabaseConnectionError => ex2
129
+ puts [
130
+ theme.error.render("Database server is not running at"),
131
+ theme.code.render(uri_no_database.to_s),
132
+ ].join(" ")
133
+ puts [
134
+ theme.error.render(ex2.class.name),
135
+ theme.exception.render(ex2.message),
136
+ ].join(": ")
137
+
138
+ puts theme.error.render("This could be a problem with your dev environment")
139
+ puts [
140
+ theme.error.render("Check"),
141
+ theme.code.render(".env.test"),
142
+ theme.error.render("and"),
143
+ theme.code.render(".env.test.local"),
144
+ theme.error.render("to see if "),
145
+ theme.code.render("DATABASE_URL"),
146
+ theme.error.render("is set correctly"),
147
+ ].join(" ")
148
+ 1
149
+ end
82
150
  end
83
151
  end
84
152
  end
@@ -94,20 +162,46 @@ class Brut::CLI::Apps::DB < Brut::CLI::Commands::BaseCommand
94
162
  uri_no_database.path = ""
95
163
  begin
96
164
  Brut.container.sequel_db_handle.disconnect
97
- stdout.puts "Database #{database_name} exists. Dropping..."
165
+ puts "Database #{theme.code.render(database_name)} exists. Dropping..."
98
166
  connection = Sequel.connect(uri_no_database.to_s)
99
167
  connection.run("DROP DATABASE IF EXISTS \"#{database_name}\"")
100
168
  connection.disconnect
169
+ puts [
170
+ theme.success.render("✅ Database"),
171
+ theme.code.render(database_name),
172
+ theme.success.render("dropped"),
173
+ ].join(" ")
101
174
  0
102
175
  rescue Sequel::DatabaseConnectionError => ex
103
176
  begin
104
177
  connection = Sequel.connect(uri_no_database.to_s)
105
- stdout.puts "Database #{database_name} does not exist"
178
+ puts [
179
+ theme.success.render("✅ Database"),
180
+ theme.code.render(database_name),
181
+ theme.success.render("has already been dropped"),
182
+ ].join(" ")
106
183
  connection.disconnect
107
184
  0
108
185
  rescue Sequel::DatabaseConnectionError => ex2
109
- stderr.puts "Database server is not running at #{uri_no_database}: #{ex2.message}"
110
- stderr.puts "This could be a problem with your dev environment generally, or your .env.test or .env.test.local files"
186
+ puts [
187
+ theme.error.render("Database server is not running at"),
188
+ theme.code.render(uri_no_database.to_s),
189
+ ].join(" ")
190
+ puts [
191
+ theme.error.render(ex2.class.name),
192
+ theme.exception.render(ex2.message),
193
+ ].join(": ")
194
+
195
+ puts theme.error.render("This could be a problem with your dev environment")
196
+ puts [
197
+ theme.error.render("Check"),
198
+ theme.code.render(".env.test"),
199
+ theme.error.render("and"),
200
+ theme.code.render(".env.test.local"),
201
+ theme.error.render("to see if "),
202
+ theme.code.render("DATABASE_URL"),
203
+ theme.error.render("is set correctly"),
204
+ ].join(" ")
111
205
  1
112
206
  end
113
207
  end
@@ -118,13 +212,30 @@ class Brut::CLI::Apps::DB < Brut::CLI::Commands::BaseCommand
118
212
  def description = "Apply any outstanding migrations to the database"
119
213
  def default_rack_env = "development"
120
214
 
215
+ class MessagingProxyLogger < SimpleDelegator
216
+ def initialize(logger, command)
217
+ super(logger)
218
+ @command = command
219
+ end
220
+ def info(msg)
221
+ if msg =~ /Finished applying migration (.*).rb/
222
+ @command.send(:puts,"Applied migration #{@command.send(:theme).code.render($1)}")
223
+ end
224
+ __getobj__.info(msg)
225
+ end
226
+ end
227
+
228
+ def opts = [
229
+ [ "--[no-]sequel-log", "Log Sequel activity at same level as --log-level. When disabled, Sequel will not log at all." ],
230
+ ]
231
+
121
232
  def run
122
233
  migrations_dir = Brut.container.migrations_dir
123
234
  if !migrations_dir.exist?
124
- stdout.puts "No migrations to run from #{migrations_dir}"
235
+ puts "No migrations to run from #{migrations_dir}"
125
236
  return 0
126
237
  elsif Dir[migrations_dir / "*.rb"].empty?
127
- stdout.puts "No migrations to run from #{migrations_dir}"
238
+ puts "No migrations to run from #{migrations_dir}"
128
239
  return 0
129
240
  end
130
241
 
@@ -132,25 +243,24 @@ class Brut::CLI::Apps::DB < Brut::CLI::Commands::BaseCommand
132
243
  Brut.container.sequel_db_handle.extension :brut_migrations
133
244
  Brut.container.sequel_db_handle.extension :pg_array
134
245
 
135
- logger = Logger.new(STDOUT)
136
- logger.level = self.options.log_level
137
- indent = ""
138
- logger.formatter = proc { |severity,time,progname,message|
139
- formatted = "#{indent} - #{message}\n"
140
- if message =~ /^Begin applying/
141
- indent = " "
142
- elsif message =~ /^Finished applying/
143
- indent = ""
144
- formatted = "#{indent} - #{message}\n"
145
- end
146
- formatted
147
- }
148
- Brut.container.sequel_db_handle.logger = logger
246
+ Brut.container.sequel_db_handle.logger = MessagingProxyLogger.new(
247
+ execution_context.logger.without_stderr,
248
+ self
249
+ )
149
250
  Sequel::Migrator.run(Brut.container.sequel_db_handle,migrations_dir)
150
- stdout.puts "All migrations have been applied"
251
+ puts theme.success.render("All migrations have been applied")
151
252
  0
152
253
  rescue Sequel::DatabaseConnectionError => ex
153
- stderr.puts "Database #{Brut.container.database_url} does not exist. Create it first with `brut db create`"
254
+ database_name = URI(Brut.container.database_url).path.gsub(/^\//,"")
255
+ puts [
256
+ theme.error.render("Database"),
257
+ theme.code.render(database_name),
258
+ theme.error.render("does not exist."),
259
+ ].join(" ")
260
+ puts [
261
+ theme.warning.render("Create it first with"),
262
+ theme.code.render("brut db create"),
263
+ ].join(" ")
154
264
  1
155
265
  rescue Sequel::DatabaseError => ex
156
266
  #if ex.cause.kind_of?(PG::UndefinedTable)
@@ -183,61 +293,71 @@ class Brut::CLI::Apps::DB < Brut::CLI::Commands::BaseCommand
183
293
 
184
294
  def run
185
295
  seeds_dir = Brut.container.db_seeds_dir
296
+ info "Using seeds from #{seeds_dir}"
186
297
  Dir["#{seeds_dir}/*.rb"].each do |file|
298
+ info "Loading seed file #{file}"
299
+ friendly_filename = Pathname(file).relative_path_from(Brut.container.project_root)
300
+ puts "Loading seed data from #{theme.code.render(friendly_filename.to_s)}"
187
301
  require file
188
302
  end
189
303
  seed_data = Brut::BackEnd::SeedData.new
190
304
  seed_data.setup!
191
305
  seed_data.load_seeds!
306
+ puts theme.success.render("✅ Seed data loaded")
192
307
  0
193
- rescue Sequel::DatabaseConnectionError => ex
194
- stderr.puts "Database doesn't exist. Create it with `brut db create`"
195
- 1
196
308
  rescue Sequel::UniqueConstraintViolation => ex
197
- stderr.puts "Seed data may have already been loaded: #{ex}. You can re-load it using `brut db rebuild`, then `brut db seed`"
309
+ puts theme.error.render("Seed data may have already been loaded:")
310
+ puts theme.exception.render(" #{ex}".strip)
311
+ puts [
312
+ theme.error.render("You can re-load it using"),
313
+ theme.code.render("brut db rebuild && brut db seed"),
314
+ ].join(" ")
198
315
  1
199
- rescue Sequel::DatabaseError => ex
200
- if ex.cause.kind_of?(PG::UndefinedTable)
201
- stderr.puts "Migrations need to be run. Use `brut db migrate` to run them"
202
- 1
203
- else
204
- raise ex
205
- end
206
316
  end
207
317
  end
208
318
 
209
319
  class NewMigration < Brut::CLI::Commands::BaseCommand
210
320
  def description = "Create a new migration file"
321
+ def opts = [
322
+ [ "--dry-run", "If true, only show what would happen, don't make any files" ],
323
+ ]
211
324
  def args_description = "migration_name"
212
325
  def bootstrap? = false
213
-
214
- def before_execute
215
- ENV["RACK_ENV"] = "development"
216
- end
326
+ def default_rack_env = "development"
217
327
 
218
328
  def run
219
329
  if argv.length == 0
220
- stderr.puts "You must provide a name for the migration"
330
+ puts theme.error.render("You must provide a name for the migration")
221
331
  return 1
222
332
  end
223
333
  if env["RACK_ENV"] != "development"
224
- stderr.puts "This only works in the development environment, not #{env["RACK_ENV"]}"
334
+ puts theme.error.render("This only works in the development environment, not #{theme.code.render(env["RACK_ENV"])}")
225
335
  return 1
226
336
  end
227
337
  migrations_dir = Brut.container.migrations_dir
228
338
  name = argv.join(" ").gsub(/[^\w\d\-]/,"-")
229
339
  date = DateTime.now.strftime("%Y%m%d%H%M%S")
230
340
  file_name = migrations_dir / "#{date}_#{name}.rb"
231
- File.open(file_name,"w") do |file|
232
- file.puts "Sequel.migration do"
233
- file.puts " up do"
234
- file.puts " # See https://brutrb.com/recipes/migrations.html"
235
- file.puts " # for a recipe on writing migrations"
236
- file.puts " end"
237
- file.puts "end"
238
- end
239
341
  relative_path = file_name.relative_path_from(Brut.container.project_root)
240
- stdout.puts "Migration created:\n #{relative_path}"
342
+ puts "Creating new migration file at #{theme.code.render(relative_path.to_s)}"
343
+ info "Creating new migration file at #{file_name}"
344
+ code = %{
345
+ Sequel.migration do
346
+ up do
347
+ # See https://brutrb.com/recipes/migrations.html
348
+ # for a recipe on writing migrations
349
+ end
350
+ end
351
+ }.strip
352
+ if options.dry_run?
353
+ puts theme.warning.render("Dry run - migration would contain this code:")
354
+ puts theme.code.render(code)
355
+ else
356
+ File.open(file_name,"w") do |file|
357
+ file.puts code
358
+ end
359
+ puts theme.success.render("✅ Migration created")
360
+ end
241
361
  0
242
362
  end
243
363
  end