bulldozer 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
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