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,123 @@
1
+ module Bulldozer
2
+ module Adapters
3
+ class Heroku
4
+ def initialize(app_builder)
5
+ @app_builder = app_builder
6
+ end
7
+
8
+ def set_heroku_remotes
9
+ remotes = <<~SHELL
10
+ #{command_to_join_heroku_app('staging')}
11
+ #{command_to_join_heroku_app('production')}
12
+
13
+ git config heroku.remote staging
14
+ SHELL
15
+
16
+ app_builder.append_file "bin/setup", remotes
17
+ end
18
+
19
+ def create_staging_heroku_app(flags)
20
+ app_name = heroku_app_name_for("staging")
21
+
22
+ run_toolbelt_command "create #{app_name} #{flags}", "staging"
23
+ end
24
+
25
+ def create_production_heroku_app(flags)
26
+ app_name = heroku_app_name_for("production")
27
+
28
+ run_toolbelt_command "create #{app_name} #{flags}", "production"
29
+ end
30
+
31
+ def set_heroku_rails_secrets
32
+ %w(staging production).each do |environment|
33
+ run_toolbelt_command(
34
+ "config:add SECRET_KEY_BASE=#{generate_secret}",
35
+ environment,
36
+ )
37
+ end
38
+ end
39
+
40
+ def set_heroku_honeybadger_env
41
+ %w(staging production).each do |environment|
42
+ run_toolbelt_command(
43
+ "config:add HONEYBADGER_ENV=#{environment}",
44
+ environment,
45
+ )
46
+ end
47
+ end
48
+
49
+ def set_heroku_backup_schedule
50
+ %w(staging production).each do |environment|
51
+ run_toolbelt_command(
52
+ "pg:backups:schedule DATABASE_URL --at '10:00 UTC'",
53
+ environment,
54
+ )
55
+ end
56
+ end
57
+
58
+ def create_heroku_pipeline
59
+ pipelines_plugin = `heroku help | grep pipelines`
60
+ if pipelines_plugin.empty?
61
+ puts "You need heroku pipelines plugin. Run: brew upgrade heroku-toolbelt"
62
+ exit 1
63
+ end
64
+
65
+ run_toolbelt_command(
66
+ "pipelines:create #{heroku_app_name} \
67
+ -a #{heroku_app_name}-staging --stage staging",
68
+ "staging",
69
+ )
70
+
71
+ run_toolbelt_command(
72
+ "pipelines:add #{heroku_app_name} \
73
+ -a #{heroku_app_name}-production --stage production",
74
+ "production",
75
+ )
76
+ end
77
+
78
+ def set_heroku_application_host
79
+ %w(staging production).each do |environment|
80
+ run_toolbelt_command(
81
+ "config:add APPLICATION_HOST=#{heroku_app_name}-#{environment}.herokuapp.com",
82
+ environment,
83
+ )
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ attr_reader :app_builder
90
+
91
+ def command_to_join_heroku_app(environment)
92
+ heroku_app_name = heroku_app_name_for(environment)
93
+ <<~SHELL
94
+
95
+ if heroku join --app #{heroku_app_name} > /dev/null 2>&1; then
96
+ git remote add #{environment} git@heroku.com:#{heroku_app_name}.git || true
97
+ printf 'You are a collaborator on the "#{heroku_app_name}" Heroku app\n'
98
+ else
99
+ printf 'Ask for access to the "#{heroku_app_name}" Heroku app\n'
100
+ fi
101
+ SHELL
102
+ end
103
+
104
+ def heroku_app_name
105
+ app_builder.app_name.dasherize
106
+ end
107
+
108
+ def heroku_app_name_for(environment)
109
+ "#{heroku_app_name}-#{environment}"
110
+ end
111
+
112
+ def generate_secret
113
+ SecureRandom.hex(64)
114
+ end
115
+
116
+ def run_toolbelt_command(command, environment)
117
+ app_builder.run(
118
+ "heroku #{command} --remote #{environment}",
119
+ )
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,321 @@
1
+ require "forwardable"
2
+
3
+ module Bulldozer
4
+ class AppBuilder < Rails::AppBuilder
5
+ include Bulldozer::Actions
6
+ extend Forwardable
7
+
8
+ def_delegators(
9
+ :heroku_adapter,
10
+ :create_heroku_pipeline,
11
+ :create_production_heroku_app,
12
+ :create_staging_heroku_app,
13
+ :set_heroku_application_host,
14
+ :set_heroku_backup_schedule,
15
+ :set_heroku_honeybadger_env,
16
+ :set_heroku_rails_secrets,
17
+ :set_heroku_remotes,
18
+ )
19
+
20
+ def readme
21
+ template 'README.md.erb', 'README.md'
22
+ end
23
+
24
+ def gitignore
25
+ copy_file "bulldozer_gitignore", ".gitignore"
26
+ end
27
+
28
+ def gemfile
29
+ template "Gemfile.erb", "Gemfile"
30
+ end
31
+
32
+ def setup_rack_mini_profiler
33
+ copy_file(
34
+ "rack_mini_profiler.rb",
35
+ "config/initializers/rack_mini_profiler.rb",
36
+ )
37
+ end
38
+
39
+ def raise_on_missing_assets_in_test
40
+ configure_environment "test", "config.assets.raise_runtime_errors = true"
41
+ end
42
+
43
+ def raise_on_delivery_errors
44
+ replace_in_file 'config/environments/development.rb',
45
+ 'raise_delivery_errors = false', 'raise_delivery_errors = true'
46
+ end
47
+
48
+ def set_test_delivery_method
49
+ inject_into_file(
50
+ "config/environments/development.rb",
51
+ "\n config.action_mailer.delivery_method = :file",
52
+ after: "config.action_mailer.raise_delivery_errors = true",
53
+ )
54
+ end
55
+
56
+ def raise_on_unpermitted_parameters
57
+ config = <<-RUBY
58
+ config.action_controller.action_on_unpermitted_parameters = :raise
59
+ RUBY
60
+
61
+ inject_into_class "config/application.rb", "Application", config
62
+ end
63
+
64
+ def configure_quiet_assets
65
+ config = <<-RUBY
66
+ config.assets.quiet = true
67
+ RUBY
68
+
69
+ inject_into_class "config/application.rb", "Application", config
70
+ end
71
+
72
+ def provide_setup_script
73
+ template "bin_setup", "bin/setup", force: true
74
+ run "chmod a+x bin/setup"
75
+ end
76
+
77
+ def configure_generators
78
+ config = <<-RUBY
79
+
80
+ config.generators do |generate|
81
+ generate.helper false
82
+ generate.javascripts false
83
+ generate.request_specs false
84
+ generate.routing_specs false
85
+ generate.stylesheets false
86
+ generate.test_framework :rspec
87
+ generate.view_specs false
88
+ end
89
+
90
+ RUBY
91
+
92
+ inject_into_class 'config/application.rb', 'Application', config
93
+ end
94
+
95
+ def configure_local_mail
96
+ copy_file "email.rb", "config/initializers/email.rb"
97
+ end
98
+
99
+ def enable_rack_canonical_host
100
+ config = <<-RUBY
101
+ config.middleware.use Rack::CanonicalHost, ENV.fetch("APPLICATION_HOST")
102
+ RUBY
103
+
104
+ configure_environment "production", config
105
+ end
106
+
107
+ def enable_rack_deflater
108
+ configure_environment "production", "config.middleware.use Rack::Deflater"
109
+ end
110
+
111
+ def setup_asset_host
112
+ replace_in_file 'config/environments/production.rb',
113
+ "# config.action_controller.asset_host = 'http://assets.example.com'",
114
+ 'config.action_controller.asset_host = ENV.fetch("ASSET_HOST", ENV.fetch("APPLICATION_HOST"))'
115
+
116
+ if File.exist?("config/initializers/assets.rb")
117
+ replace_in_file 'config/initializers/assets.rb',
118
+ "config.assets.version = '1.0'",
119
+ 'config.assets.version = (ENV["ASSETS_VERSION"] || "1.0")'
120
+ end
121
+
122
+ config = <<-EOD
123
+ config.public_file_server.headers = {
124
+ "Cache-Control" => "public, max-age=31557600",
125
+ }
126
+ EOD
127
+
128
+ configure_environment("production", config)
129
+ end
130
+
131
+ def setup_secret_token
132
+ template 'secrets.yml', 'config/secrets.yml', force: true
133
+ end
134
+
135
+ def disallow_wrapping_parameters
136
+ remove_file "config/initializers/wrap_parameters.rb"
137
+ end
138
+
139
+ def use_postgres_config_template
140
+ template 'postgresql_database.yml.erb', 'config/database.yml',
141
+ force: true
142
+ end
143
+
144
+ def create_database
145
+ bundle_command "exec rails db:create db:migrate"
146
+ end
147
+
148
+ def replace_gemfile(path)
149
+ template 'Gemfile.erb', 'Gemfile', force: true do |content|
150
+ if path
151
+ content.gsub(%r{gem .bulldozer.}) { |s| %{#{s}, path: "#{path}"} }
152
+ else
153
+ content
154
+ end
155
+ end
156
+ end
157
+
158
+ def ruby_version
159
+ create_file '.ruby-version', "#{Bulldozer::RUBY_VERSION}\n"
160
+ end
161
+
162
+ def configure_i18n_for_missing_translations
163
+ raise_on_missing_translations_in("development")
164
+ raise_on_missing_translations_in("test")
165
+ end
166
+
167
+ def configure_action_mailer_in_specs
168
+ copy_file 'action_mailer.rb', 'spec/support/action_mailer.rb'
169
+ end
170
+
171
+ def configure_time_formats
172
+ remove_file "config/locales/en.yml"
173
+ template "config_locales_en.yml.erb", "config/locales/en.yml"
174
+ end
175
+
176
+ def configure_action_mailer
177
+ action_mailer_host "development", %{"localhost:3000"}
178
+ action_mailer_asset_host "development", %{"http://localhost:3000"}
179
+ action_mailer_host "test", %{"www.example.com"}
180
+ action_mailer_asset_host "test", %{"http://www.example.com"}
181
+ action_mailer_host "production", %{ENV.fetch("APPLICATION_HOST")}
182
+ action_mailer_asset_host(
183
+ "production",
184
+ %{ENV.fetch("ASSET_HOST", ENV.fetch("APPLICATION_HOST"))},
185
+ )
186
+ end
187
+
188
+ def replace_default_puma_configuration
189
+ copy_file "puma.rb", "config/puma.rb", force: true
190
+ end
191
+
192
+ def set_up_forego
193
+ copy_file "Procfile", "Procfile"
194
+ end
195
+
196
+ def setup_default_directories
197
+ [
198
+ 'app/views/pages',
199
+ 'spec/lib',
200
+ 'spec/controllers',
201
+ 'spec/helpers',
202
+ 'spec/support/matchers',
203
+ 'spec/support/mixins',
204
+ 'spec/support/shared_examples'
205
+ ].each do |dir|
206
+ empty_directory_with_keep_file dir
207
+ end
208
+ end
209
+
210
+ def copy_dotfiles
211
+ directory("dotfiles", ".")
212
+ end
213
+
214
+ def create_heroku_apps(flags)
215
+ create_staging_heroku_app(flags)
216
+ create_production_heroku_app(flags)
217
+ end
218
+
219
+ def configure_automatic_deployment
220
+ deploy_command = <<-YML.strip_heredoc
221
+ deployment:
222
+ staging:
223
+ branch: master
224
+ commands:
225
+ - bin/deploy staging
226
+ YML
227
+
228
+ append_file "circle.yml", deploy_command
229
+ end
230
+
231
+ def create_github_repo(repo_name)
232
+ run "hub create #{repo_name}"
233
+ end
234
+
235
+ def setup_bundler_audit
236
+ copy_file "bundler_audit.rake", "lib/tasks/bundler_audit.rake"
237
+ append_file "Rakefile", %{\ntask default: "bundle:audit"\n}
238
+ end
239
+
240
+ def setup_spring
241
+ bundle_command "exec spring binstub --all"
242
+ end
243
+
244
+ def copy_miscellaneous_files
245
+ copy_file "browserslist", "browserslist"
246
+ copy_file "errors.rb", "config/initializers/errors.rb"
247
+ copy_file "json_encoding.rb", "config/initializers/json_encoding.rb"
248
+ end
249
+
250
+ def customize_error_pages
251
+ meta_tags =<<-EOS
252
+ <meta charset="utf-8" />
253
+ <meta name="viewport" content="initial-scale=1" />
254
+ EOS
255
+
256
+ %w(500 404 422).each do |page|
257
+ path = "public/#{page}.html"
258
+ if File.exist?(path)
259
+ inject_into_file path, meta_tags, after: "<head>\n"
260
+ replace_in_file path, /<!--.+-->\n/, ''
261
+ end
262
+ end
263
+ end
264
+
265
+ def remove_config_comment_lines
266
+ config_files = [
267
+ "application.rb",
268
+ "environment.rb",
269
+ "environments/development.rb",
270
+ "environments/production.rb",
271
+ "environments/test.rb",
272
+ ]
273
+
274
+ config_files.each do |config_file|
275
+ path = File.join(destination_root, "config/#{config_file}")
276
+
277
+ accepted_content = File.readlines(path).reject do |line|
278
+ line =~ /^.*#.*$/ || line =~ /^$\n/
279
+ end
280
+
281
+ File.open(path, "w") do |file|
282
+ accepted_content.each { |line| file.puts line }
283
+ end
284
+ end
285
+ end
286
+
287
+ def remove_routes_comment_lines
288
+ replace_in_file 'config/routes.rb',
289
+ /Rails\.application\.routes\.draw do.*end/m,
290
+ "Rails.application.routes.draw do\nend"
291
+ end
292
+
293
+ def setup_default_rake_task
294
+ append_file 'Rakefile' do
295
+ <<-EOS
296
+ task(:default).clear
297
+ task default: [:spec]
298
+
299
+ if defined? RSpec
300
+ task(:spec).clear
301
+ RSpec::Core::RakeTask.new(:spec) do |t|
302
+ t.verbose = false
303
+ end
304
+ end
305
+ EOS
306
+ end
307
+ end
308
+
309
+ private
310
+
311
+ def raise_on_missing_translations_in(environment)
312
+ config = 'config.action_view.raise_on_missing_translations = true'
313
+
314
+ uncomment_lines("config/environments/#{environment}.rb", config)
315
+ end
316
+
317
+ def heroku_adapter
318
+ @heroku_adapter ||= Adapters::Heroku.new(self)
319
+ end
320
+ end
321
+ end
@@ -0,0 +1,24 @@
1
+ require_relative "base"
2
+
3
+ module Bulldozer
4
+ class AnalyticsGenerator < Generators::Base
5
+ def install_partial
6
+ copy_file "_analytics.html.erb",
7
+ "app/views/application/_analytics.html.erb"
8
+ end
9
+
10
+ def render_partial
11
+ if File.exist?(js_partial)
12
+ inject_into_file js_partial,
13
+ %{\n\n<%= render "analytics" %>},
14
+ after: "<%= yield :javascript %>"
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def js_partial
21
+ "app/views/application/_javascript.html.erb"
22
+ end
23
+ end
24
+ end