bulldozer 1.6.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.
Files changed (108) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +12 -0
  5. data/CONTRIBUTING.md +59 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +21 -0
  8. data/NEWS.md +638 -0
  9. data/README.md +225 -0
  10. data/RELEASING.md +18 -0
  11. data/Rakefile +8 -0
  12. data/USAGE +13 -0
  13. data/bin/bulldozer +23 -0
  14. data/bin/rake +16 -0
  15. data/bin/rspec +16 -0
  16. data/bin/setup +13 -0
  17. data/bulldozer.gemspec +35 -0
  18. data/docker-compose.yml +8 -0
  19. data/dockerfile +11 -0
  20. data/lib/bulldozer.rb +24 -0
  21. data/lib/bulldozer/actions.rb +85 -0
  22. data/lib/bulldozer/adapters/heroku.rb +123 -0
  23. data/lib/bulldozer/app_builder.rb +321 -0
  24. data/lib/bulldozer/generators/analytics_generator.rb +24 -0
  25. data/lib/bulldozer/generators/app_generator.rb +231 -0
  26. data/lib/bulldozer/generators/base.rb +20 -0
  27. data/lib/bulldozer/generators/ci_generator.rb +22 -0
  28. data/lib/bulldozer/generators/db_optimizations_generator.rb +30 -0
  29. data/lib/bulldozer/generators/factories_generator.rb +22 -0
  30. data/lib/bulldozer/generators/forms_generator.rb +18 -0
  31. data/lib/bulldozer/generators/jobs_generator.rb +38 -0
  32. data/lib/bulldozer/generators/js_driver_generator.rb +15 -0
  33. data/lib/bulldozer/generators/json_generator.rb +10 -0
  34. data/lib/bulldozer/generators/lint_generator.rb +9 -0
  35. data/lib/bulldozer/generators/production/deployment_generator.rb +27 -0
  36. data/lib/bulldozer/generators/production/email_generator.rb +37 -0
  37. data/lib/bulldozer/generators/production/force_tls_generator.rb +11 -0
  38. data/lib/bulldozer/generators/production/manifest_generator.rb +24 -0
  39. data/lib/bulldozer/generators/production/timeout_generator.rb +21 -0
  40. data/lib/bulldozer/generators/staging/pull_requests_generator.rb +33 -0
  41. data/lib/bulldozer/generators/static_generator.rb +10 -0
  42. data/lib/bulldozer/generators/stylesheet_base_generator.rb +31 -0
  43. data/lib/bulldozer/generators/testing_generator.rb +55 -0
  44. data/lib/bulldozer/generators/views_generator.rb +30 -0
  45. data/lib/bulldozer/version.rb +8 -0
  46. data/spec/adapters/heroku_spec.rb +72 -0
  47. data/spec/fakes/bin/heroku +5 -0
  48. data/spec/fakes/bin/hub +5 -0
  49. data/spec/features/api_spec.rb +18 -0
  50. data/spec/features/cli_help_spec.rb +36 -0
  51. data/spec/features/github_spec.rb +16 -0
  52. data/spec/features/heroku_spec.rb +71 -0
  53. data/spec/features/json_spec.rb +15 -0
  54. data/spec/features/new_project_spec.rb +341 -0
  55. data/spec/features/production/deployment_spec.rb +22 -0
  56. data/spec/features/production/email_spec.rb +47 -0
  57. data/spec/features/production/manifest_spec.rb +35 -0
  58. data/spec/features/staging/pull_requests_spec.rb +22 -0
  59. data/spec/spec_helper.rb +21 -0
  60. data/spec/support/be_executable_matcher.rb +7 -0
  61. data/spec/support/bulldozer.rb +182 -0
  62. data/spec/support/contain_json_matcher.rb +24 -0
  63. data/spec/support/exist_as_a_file_matcher.rb +7 -0
  64. data/spec/support/fake_github.rb +21 -0
  65. data/spec/support/fake_heroku.rb +53 -0
  66. data/spec/support/generators.rb +5 -0
  67. data/spec/support/match_contents_matcher.rb +6 -0
  68. data/spec/support/project_files.rb +13 -0
  69. data/templates/Gemfile.erb +51 -0
  70. data/templates/Procfile +2 -0
  71. data/templates/README.md.erb +28 -0
  72. data/templates/_analytics.html.erb +8 -0
  73. data/templates/_css_overrides.html.erb +7 -0
  74. data/templates/_flashes.html.erb +7 -0
  75. data/templates/_javascript.html.erb +3 -0
  76. data/templates/action_mailer.rb +5 -0
  77. data/templates/active_job.rb +13 -0
  78. data/templates/application.scss +8 -0
  79. data/templates/bin_deploy +12 -0
  80. data/templates/bin_setup +28 -0
  81. data/templates/bin_setup_review_app.erb +22 -0
  82. data/templates/browserslist +3 -0
  83. data/templates/bulldozer_gitignore +18 -0
  84. data/templates/bulldozer_layout.html.erb.erb +21 -0
  85. data/templates/bundler_audit.rake +4 -0
  86. data/templates/chromedriver.rb +17 -0
  87. data/templates/circle.yml.erb +6 -0
  88. data/templates/config_locales_en.yml.erb +19 -0
  89. data/templates/dev.rake +12 -0
  90. data/templates/dotfiles/.ctags +2 -0
  91. data/templates/dotfiles/.env +13 -0
  92. data/templates/email.rb +3 -0
  93. data/templates/errors.rb +34 -0
  94. data/templates/factories.rb +2 -0
  95. data/templates/factory_bot_rspec.rb +5 -0
  96. data/templates/flashes_helper.rb +5 -0
  97. data/templates/hound.yml +14 -0
  98. data/templates/i18n.rb +3 -0
  99. data/templates/json_encoding.rb +1 -0
  100. data/templates/postgresql_database.yml.erb +19 -0
  101. data/templates/puma.rb +28 -0
  102. data/templates/rack_mini_profiler.rb +5 -0
  103. data/templates/rails_helper.rb +22 -0
  104. data/templates/secrets.yml +8 -0
  105. data/templates/shoulda_matchers_config_rspec.rb +6 -0
  106. data/templates/smtp.rb +9 -0
  107. data/templates/spec_helper.rb +28 -0
  108. metadata +197 -0
@@ -0,0 +1,15 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "bulldozer:json", type: :generator do
4
+ it "generates the Gemfile for JSON parsing" do
5
+ with_app { generate("bulldozer:json") }
6
+
7
+ expect("Gemfile").to match_contents(%r{gem .oj.})
8
+ end
9
+
10
+ it "cleans up the Gemfile from JSON parsing" do
11
+ with_app { destroy("bulldozer:json") }
12
+
13
+ expect("Gemfile").not_to match_contents(%r{gem .oj.})
14
+ end
15
+ end
@@ -0,0 +1,341 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "Suspend a new project with default configuration" do
4
+ before(:all) do
5
+ drop_dummy_database
6
+ remove_project_directory
7
+ run_bulldozer
8
+ setup_app_dependencies
9
+ end
10
+
11
+ it "uses custom Gemfile" do
12
+ gemfile_file = IO.read("#{project_path}/Gemfile")
13
+ expect(gemfile_file).to match(
14
+ /^ruby "#{Bulldozer::RUBY_VERSION}"$/,
15
+ )
16
+ expect(gemfile_file).to match(
17
+ /^gem "autoprefixer-rails"$/,
18
+ )
19
+ expect(gemfile_file).to match(
20
+ /^gem "rails", "#{Bulldozer::RAILS_VERSION}"$/,
21
+ )
22
+ end
23
+
24
+ it "ensures project specs pass" do
25
+ Dir.chdir(project_path) do
26
+ Bundler.with_clean_env do
27
+ expect(`rake`).to include('0 failures')
28
+ end
29
+ end
30
+ end
31
+
32
+ it "includes the bundle:audit task" do
33
+ Dir.chdir(project_path) do
34
+ Bundler.with_clean_env do
35
+ expect(`rails -T`).to include("rails bundle:audit")
36
+ end
37
+ end
38
+ end
39
+
40
+ it "creates .ruby-version from Bulldozer .ruby-version" do
41
+ ruby_version_file = IO.read("#{project_path}/.ruby-version")
42
+
43
+ expect(ruby_version_file).to eq "#{RUBY_VERSION}\n"
44
+ end
45
+
46
+ it "copies dotfiles" do
47
+ %w[.ctags .env].each do |dotfile|
48
+ expect(File).to exist("#{project_path}/#{dotfile}")
49
+ end
50
+ end
51
+
52
+ it "doesn't generate test directory" do
53
+ expect(File).not_to exist("#{project_path}/test")
54
+ end
55
+
56
+ it "loads secret_key_base from env" do
57
+ secrets_file = IO.read("#{project_path}/config/secrets.yml")
58
+
59
+ expect(secrets_file).
60
+ to match(/secret_key_base: <%= ENV\["SECRET_KEY_BASE"\] %>/)
61
+ end
62
+
63
+ it "adds bin/setup file" do
64
+ expect(File).to exist("#{project_path}/bin/setup")
65
+ end
66
+
67
+ it "makes bin/setup executable" do
68
+ expect("bin/setup").to be_executable
69
+ end
70
+
71
+ it "adds support file for action mailer" do
72
+ expect(File).to exist("#{project_path}/spec/support/action_mailer.rb")
73
+ end
74
+
75
+ it "configures capybara-chromedriver" do
76
+ expect(File).to exist("#{project_path}/spec/support/chromedriver.rb")
77
+ end
78
+
79
+ it "adds support file for i18n" do
80
+ expect(File).to exist("#{project_path}/spec/support/i18n.rb")
81
+ end
82
+
83
+ it "creates good default .hound.yml" do
84
+ hound_config_file = IO.read("#{project_path}/.hound.yml")
85
+
86
+ expect(hound_config_file).to include "enabled: true"
87
+ end
88
+
89
+ it "ensures Gemfile contains `rack-mini-profiler`" do
90
+ gemfile = IO.read("#{project_path}/Gemfile")
91
+
92
+ expect(gemfile).to include %{gem "rack-mini-profiler", require: false}
93
+ end
94
+
95
+ it "ensures .sample.env defaults to RACK_MINI_PROFILER=0" do
96
+ env = IO.read("#{project_path}/.env")
97
+
98
+ expect(env).to include "RACK_MINI_PROFILER=0"
99
+ end
100
+
101
+ it "initializes ActiveJob to avoid memory bloat" do
102
+ expect(File).
103
+ to exist("#{project_path}/config/initializers/active_job.rb")
104
+ end
105
+
106
+ it "creates a rack-mini-profiler initializer" do
107
+ expect(File).
108
+ to exist("#{project_path}/config/initializers/rack_mini_profiler.rb")
109
+ end
110
+
111
+ it "records pageviews through Segment if ENV variable set" do
112
+ expect(analytics_partial).
113
+ to include(%{<% if ENV["SEGMENT_KEY"] %>})
114
+ expect(analytics_partial).
115
+ to include(%{analytics.load("<%= ENV["SEGMENT_KEY"] %>");})
116
+ end
117
+
118
+ it "raises on unpermitted parameters in all environments" do
119
+ result = IO.read("#{project_path}/config/application.rb")
120
+
121
+ expect(result).to match(
122
+ /^ +config.action_controller.action_on_unpermitted_parameters = :raise$/
123
+ )
124
+ end
125
+
126
+ it "adds explicit quiet_assets configuration" do
127
+ result = IO.read("#{project_path}/config/application.rb")
128
+
129
+ expect(result).to match(/^ +config.assets.quiet = true$/)
130
+ end
131
+
132
+ it "configures public_file_server.headers in production" do
133
+ expect(production_config).to match(
134
+ /^ +config.public_file_server.headers = {\n +"Cache-Control" => "public,/,
135
+ )
136
+ end
137
+
138
+ it "configures production environment to enforce SSL" do
139
+ expect(production_config).to match(
140
+ /^ +config.force_ssl = true/,
141
+ )
142
+ end
143
+
144
+ it "raises on missing translations in development and test" do
145
+ [development_config, test_config].each do |environment_file|
146
+ expect(environment_file).to match(
147
+ /^ +config.action_view.raise_on_missing_translations = true$/
148
+ )
149
+ end
150
+ end
151
+
152
+ it "evaluates en.yml.erb" do
153
+ locales_en_file = IO.read("#{project_path}/config/locales/en.yml")
154
+
155
+ expect(locales_en_file).to match(/application: #{app_name.humanize}/)
156
+ end
157
+
158
+ it "configs simple_form" do
159
+ expect(File).to exist("#{project_path}/config/initializers/simple_form.rb")
160
+ end
161
+
162
+ it "configs :test email delivery method for development" do
163
+ expect(development_config).
164
+ to match(/^ +config.action_mailer.delivery_method = :file$/)
165
+ end
166
+
167
+ it "sets action mailer default host and asset host" do
168
+ config_key = 'config\.action_mailer\.asset_host'
169
+ config_value =
170
+ %q{ENV\.fetch\("ASSET_HOST", ENV\.fetch\("APPLICATION_HOST"\)\)}
171
+ expect(production_config).to match(/#{config_key} = #{config_value}/)
172
+ end
173
+
174
+ it "uses APPLICATION_HOST, not HOST in the production config" do
175
+ expect(production_config).to match(/"APPLICATION_HOST"/)
176
+ expect(production_config).not_to match(/"HOST"/)
177
+ end
178
+
179
+ it "configures email interceptor" do
180
+ email_file = File.join(project_path, "config", "initializers", "email.rb")
181
+ email_config = IO.read(email_file)
182
+
183
+ expect(email_config).
184
+ to include(%{RecipientInterceptor.new(ENV["EMAIL_RECIPIENTS"])})
185
+ end
186
+
187
+ it "configures language in html element" do
188
+ layout_path = "/app/views/layouts/application.html.erb"
189
+ layout_file = IO.read("#{project_path}#{layout_path}")
190
+ expect(layout_file).to match(/<html lang="en">/)
191
+ end
192
+
193
+ it "configs active job queue adapter" do
194
+ application_config = IO.read("#{project_path}/config/application.rb")
195
+
196
+ expect(application_config).to match(
197
+ /^ +config.active_job.queue_adapter = :delayed_job$/
198
+ )
199
+ expect(test_config).to match(
200
+ /^ +config.active_job.queue_adapter = :inline$/
201
+ )
202
+ end
203
+
204
+ it "configs background jobs for rspec" do
205
+ delayed_job = IO.read("#{project_path}/bin/delayed_job")
206
+
207
+ expect(delayed_job).to match(
208
+ /^require 'delayed\/command'$/,
209
+ )
210
+ end
211
+
212
+ it "configs bullet gem in development" do
213
+ expect(development_config).to match /^ +Bullet.enable = true$/
214
+ expect(development_config).to match /^ +Bullet.bullet_logger = true$/
215
+ expect(development_config).to match /^ +Bullet.rails_logger = true$/
216
+ end
217
+
218
+ it "configs missing assets to raise in test" do
219
+ expect(test_config).to match(
220
+ /^ +config.assets.raise_runtime_errors = true$/,
221
+ )
222
+ end
223
+
224
+ it "adds spring to binstubs" do
225
+ expect(File).to exist("#{project_path}/bin/spring")
226
+
227
+ bin_stubs = %w(rake rails rspec)
228
+ bin_stubs.each do |bin_stub|
229
+ expect(IO.read("#{project_path}/bin/#{bin_stub}")).to match(/spring/)
230
+ end
231
+ end
232
+
233
+ it "removes comments and extra newlines from config files" do
234
+ config_files = [
235
+ IO.read("#{project_path}/config/application.rb"),
236
+ IO.read("#{project_path}/config/environment.rb"),
237
+ development_config,
238
+ test_config,
239
+ production_config,
240
+ ]
241
+
242
+ config_files.each do |file|
243
+ expect(file).not_to match(%r{.*#.*})
244
+ expect(file).not_to eq(file.strip)
245
+ expect(file).not_to match(%r{^$\n\n})
246
+ end
247
+ end
248
+
249
+ it "copies factories.rb" do
250
+ expect(File).to exist("#{project_path}/spec/factories.rb")
251
+ end
252
+
253
+ it "creates review apps setup script" do
254
+ bin_setup_path = "#{project_path}/bin/setup_review_app"
255
+ bin_setup = IO.read(bin_setup_path)
256
+
257
+ expect(bin_setup).to include("PARENT_APP_NAME=#{app_name.dasherize}-staging")
258
+ expect(bin_setup).to include("APP_NAME=#{app_name.dasherize}-staging-pr-$1")
259
+ expect(bin_setup).
260
+ to include("heroku run rails db:migrate --exit-code --app $APP_NAME")
261
+ expect(bin_setup).to include("heroku ps:scale worker=1 --app $APP_NAME")
262
+ expect(bin_setup).to include("heroku restart --app $APP_NAME")
263
+
264
+ expect("bin/setup_review_app").to be_executable
265
+ end
266
+
267
+ it "creates deploy script" do
268
+ bin_deploy_path = "#{project_path}/bin/deploy"
269
+ bin_deploy = IO.read(bin_deploy_path)
270
+
271
+ expect(bin_deploy).to include("heroku run rails db:migrate --exit-code")
272
+ expect("bin/deploy").to be_executable
273
+ end
274
+
275
+ it "creates heroku application manifest file with application name in it" do
276
+ app_json_file = IO.read("#{project_path}/app.json")
277
+
278
+ expect(app_json_file).to match(/"name":\s*"#{app_name.dasherize}"/)
279
+ end
280
+
281
+ def app_name
282
+ BulldozerTestHelpers::APP_NAME
283
+ end
284
+
285
+ it "adds high_voltage" do
286
+ gemfile = IO.read("#{project_path}/Gemfile")
287
+ expect(gemfile).to match(/high_voltage/)
288
+ end
289
+
290
+ it "adds sassc-rails" do
291
+ gemfile = read_project_file("Gemfile")
292
+
293
+ expect(gemfile).to match(/sassc-rails/)
294
+ end
295
+
296
+ it "adds and configures bourbon and neat" do
297
+ gemfile = read_project_file("Gemfile")
298
+
299
+ expect(gemfile).to match(/bourbon/)
300
+ expect(gemfile).to match(/neat/)
301
+ end
302
+
303
+ it "configures bourbon, neat, and bitters" do
304
+ app_css = read_project_file(%w(app assets stylesheets application.scss))
305
+ expect(app_css).to match(
306
+ /normalize\.css\/normalize.*bourbon.*neat.*base/m,
307
+ )
308
+ end
309
+
310
+ it "doesn't use turbolinks" do
311
+ app_js = read_project_file(%w(app assets javascripts application.js))
312
+ expect(app_js).not_to match(/turbolinks/)
313
+ end
314
+
315
+ it "configures Timecop safe mode" do
316
+ spec_helper = read_project_file(%w(spec spec_helper.rb))
317
+ expect(spec_helper).to match(/Timecop.safe_mode = true/)
318
+ end
319
+
320
+ def development_config
321
+ @_development_config ||=
322
+ read_project_file %w(config environments development.rb)
323
+ end
324
+
325
+ def test_config
326
+ @_test_config ||= read_project_file %w(config environments test.rb)
327
+ end
328
+
329
+ def production_config
330
+ @_production_config ||=
331
+ read_project_file %w(config environments production.rb)
332
+ end
333
+
334
+ def analytics_partial
335
+ IO.read("#{project_path}/app/views/application/_analytics.html.erb")
336
+ end
337
+
338
+ def read_project_file(path)
339
+ IO.read(File.join(project_path, *path))
340
+ end
341
+ end
@@ -0,0 +1,22 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "bulldozer:production:deployment", type: :generator do
4
+ it "generates the configuration for a production deployment" do
5
+ rm "bin/deploy"
6
+
7
+ with_app { generate("bulldozer:production:deployment") }
8
+
9
+ expect("bin/deploy").to exist_as_a_file
10
+ expect("bin/deploy").to be_executable
11
+ expect("README.md").to match_contents(%r{bin/deploy})
12
+ end
13
+
14
+ it "destroys the configuration for a production deployment" do
15
+ touch "bin/deploy"
16
+
17
+ with_app { destroy("bulldozer:production:deployment") }
18
+
19
+ expect("bin/deploy").not_to exist_as_a_file
20
+ expect("README.md").not_to match_contents(%r{bin/deploy})
21
+ end
22
+ end
@@ -0,0 +1,47 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "bulldozer:production:email", type: :generator do
4
+ it "generates the configuration for a production email deployment" do
5
+ with_app { generate("bulldozer:production:email") }
6
+
7
+ expect("config/smtp.rb").to match_contents(%r{SMTP_SETTINGS\s*=})
8
+
9
+ expect("config/environments/production.rb").to \
10
+ match_contents(%r{require.+config/smtp})
11
+ expect("config/environments/production.rb").to \
12
+ match_contents(%r{action_mailer.delivery_method\s*=\s*:smtp})
13
+ expect("config/environments/production.rb").to \
14
+ match_contents(%r{action_mailer.smtp_settings\s*=\s*SMTP_SETTINGS})
15
+
16
+ expect("app.json").to contain_json(
17
+ env: {
18
+ SMTP_ADDRESS: { required: true },
19
+ SMTP_DOMAIN: { required: true },
20
+ SMTP_PASSWORD: { required: true },
21
+ SMTP_USERNAME: { required: true },
22
+ },
23
+ )
24
+ end
25
+
26
+ it "destroys the configuration for a production email deployment" do
27
+ with_app { destroy("bulldozer:production:email") }
28
+
29
+ expect("config/smtp.rb").not_to exist_as_a_file
30
+
31
+ expect("config/environments/production.rb").not_to \
32
+ match_contents(%r{require.+config/smtp})
33
+ expect("config/environments/production.rb").not_to \
34
+ match_contents(%r{action_mailer.delivery_method\s*=\s*:smtp})
35
+ expect("config/environments/production.rb").not_to \
36
+ match_contents(%r{action_mailer.smtp_settings\s*=\s*SMTP_SETTINGS})
37
+
38
+ expect("app.json").not_to contain_json(
39
+ env: {
40
+ SMTP_ADDRESS: { required: true },
41
+ SMTP_DOMAIN: { required: true },
42
+ SMTP_PASSWORD: { required: true },
43
+ SMTP_USERNAME: { required: true },
44
+ },
45
+ )
46
+ end
47
+ end
@@ -0,0 +1,35 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "bulldozer:production:manifest", type: :generator do
4
+ it "generates the manifest for a production build" do
5
+ with_app { generate("bulldozer:production:manifest") }
6
+
7
+ expect("app.json").to contain_json(
8
+ name: BulldozerTestHelpers::APP_NAME.dasherize,
9
+ env: {
10
+ APPLICATION_HOST: { required: true },
11
+ EMAIL_RECIPIENTS: { required: true },
12
+ HEROKU_APP_NAME: { required: true },
13
+ HEROKU_PARENT_APP_NAME: { required: true },
14
+ RACK_ENV: { required: true },
15
+ SECRET_KEY_BASE: { generator: "secret" },
16
+ },
17
+ )
18
+ end
19
+
20
+ it "destroys the manifest for a production build" do
21
+ with_app { destroy("bulldozer:production:manifest") }
22
+
23
+ expect("app.json").not_to contain_json(
24
+ name: BulldozerTestHelpers::APP_NAME.dasherize,
25
+ env: {
26
+ APPLICATION_HOST: { required: true },
27
+ EMAIL_RECIPIENTS: { required: true },
28
+ HEROKU_APP_NAME: { required: true },
29
+ HEROKU_PARENT_APP_NAME: { required: true },
30
+ RACK_ENV: { required: true },
31
+ SECRET_KEY_BASE: { generator: "secret" },
32
+ },
33
+ )
34
+ end
35
+ end