armadura 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +11 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE +19 -0
  7. data/README.md +218 -0
  8. data/Rakefile +8 -0
  9. data/USAGE +9 -0
  10. data/armadura.gemspec +29 -0
  11. data/bin/armadura +23 -0
  12. data/bin/rake +16 -0
  13. data/bin/rspec +16 -0
  14. data/bin/setup +13 -0
  15. data/lib/armadura.rb +7 -0
  16. data/lib/armadura/actions.rb +33 -0
  17. data/lib/armadura/adapters/heroku.rb +118 -0
  18. data/lib/armadura/app_builder.rb +484 -0
  19. data/lib/armadura/generators/app_generator.rb +245 -0
  20. data/lib/armadura/generators/static_generator.rb +10 -0
  21. data/lib/armadura/generators/stylesheet_base_generator.rb +37 -0
  22. data/lib/armadura/version.rb +8 -0
  23. data/spec/adapters/heroku_spec.rb +57 -0
  24. data/spec/fakes/bin/heroku +5 -0
  25. data/spec/fakes/bin/hub +5 -0
  26. data/spec/features/cli_help_spec.rb +36 -0
  27. data/spec/features/github_spec.rb +16 -0
  28. data/spec/features/heroku_spec.rb +75 -0
  29. data/spec/features/new_project_spec.rb +295 -0
  30. data/spec/features/sidekiq_spec.rb +30 -0
  31. data/spec/spec_helper.rb +20 -0
  32. data/spec/support/armadura.rb +83 -0
  33. data/spec/support/fake_github.rb +21 -0
  34. data/spec/support/fake_heroku.rb +53 -0
  35. data/templates/Gemfile.erb +60 -0
  36. data/templates/Procfile +2 -0
  37. data/templates/README.md.erb +28 -0
  38. data/templates/_analytics.html.erb +7 -0
  39. data/templates/_css_overrides.html.erb +7 -0
  40. data/templates/_flashes.html.erb +7 -0
  41. data/templates/_javascript.html.erb +12 -0
  42. data/templates/action_mailer.rb +5 -0
  43. data/templates/app.json.erb +39 -0
  44. data/templates/application.scss +9 -0
  45. data/templates/armadura_gitignore +14 -0
  46. data/templates/armadura_layout.html.erb.erb +22 -0
  47. data/templates/bin_deploy +12 -0
  48. data/templates/bin_setup +22 -0
  49. data/templates/bin_setup_review_app.erb +19 -0
  50. data/templates/browserslist +3 -0
  51. data/templates/bundler_audit.rake +12 -0
  52. data/templates/capybara_webkit.rb +5 -0
  53. data/templates/circle.yml.erb +6 -0
  54. data/templates/config_locales_en.yml.erb +19 -0
  55. data/templates/database_cleaner_rspec.rb +21 -0
  56. data/templates/dev.rake +12 -0
  57. data/templates/dotfiles/.ctags +2 -0
  58. data/templates/dotfiles/.env +14 -0
  59. data/templates/errors.rb +34 -0
  60. data/templates/factories.rb +2 -0
  61. data/templates/factory_girl_rspec.rb +3 -0
  62. data/templates/flashes_helper.rb +5 -0
  63. data/templates/hound.yml +14 -0
  64. data/templates/i18n.rb +3 -0
  65. data/templates/json_encoding.rb +1 -0
  66. data/templates/postgresql_database.yml.erb +21 -0
  67. data/templates/puma.rb +28 -0
  68. data/templates/rack_mini_profiler.rb +5 -0
  69. data/templates/rails_helper.rb +22 -0
  70. data/templates/secrets.yml +14 -0
  71. data/templates/shoulda_matchers_config_rspec.rb +6 -0
  72. data/templates/sidekiq.yml +6 -0
  73. data/templates/smtp.rb +13 -0
  74. data/templates/spec_helper.rb +29 -0
  75. metadata +188 -0
@@ -0,0 +1,118 @@
1
+ module Armadura
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.strip_heredoc
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 create_review_apps_setup_script
41
+ app_builder.template(
42
+ "bin_setup_review_app.erb",
43
+ "bin/setup_review_app",
44
+ force: true,
45
+ )
46
+ app_builder.run "chmod a+x bin/setup_review_app"
47
+ end
48
+
49
+ def create_heroku_application_manifest_file
50
+ app_builder.template "app.json.erb", "app.json"
51
+ end
52
+
53
+ def create_heroku_pipeline
54
+ pipelines_plugin = `heroku help | grep pipelines`
55
+ if pipelines_plugin.empty?
56
+ puts "You need heroku pipelines plugin. Run: brew upgrade heroku-toolbelt"
57
+ exit 1
58
+ end
59
+
60
+ run_toolbelt_command(
61
+ "pipelines:create #{heroku_app_name} \
62
+ -a #{heroku_app_name}-staging --stage staging",
63
+ "staging",
64
+ )
65
+
66
+ run_toolbelt_command(
67
+ "pipelines:add #{heroku_app_name} \
68
+ -a #{heroku_app_name}-production --stage production",
69
+ "production",
70
+ )
71
+ end
72
+
73
+ def set_heroku_application_host
74
+ %w(staging production).each do |environment|
75
+ run_toolbelt_command(
76
+ "config:add APPLICATION_HOST=#{heroku_app_name}-#{environment}.herokuapp.com",
77
+ environment,
78
+ )
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ attr_reader :app_builder
85
+
86
+ def command_to_join_heroku_app(environment)
87
+ heroku_app_name = heroku_app_name_for(environment)
88
+ <<-SHELL
89
+
90
+ if heroku join --app #{heroku_app_name} &> /dev/null; then
91
+ git remote add #{environment} git@heroku.com:#{heroku_app_name}.git || true
92
+ printf 'You are a collaborator on the "#{heroku_app_name}" Heroku app\n'
93
+ else
94
+ printf 'Ask for access to the "#{heroku_app_name}" Heroku app\n'
95
+ fi
96
+ SHELL
97
+ end
98
+
99
+ def heroku_app_name
100
+ app_builder.app_name.dasherize
101
+ end
102
+
103
+ def heroku_app_name_for(environment)
104
+ "#{heroku_app_name}-#{environment}"
105
+ end
106
+
107
+ def generate_secret
108
+ SecureRandom.hex(64)
109
+ end
110
+
111
+ def run_toolbelt_command(command, environment)
112
+ app_builder.run(
113
+ "heroku #{command} --remote #{environment}",
114
+ )
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,484 @@
1
+ require "forwardable"
2
+
3
+ module Armadura
4
+ class AppBuilder < Rails::AppBuilder
5
+ include Armadura::Actions
6
+ extend Forwardable
7
+
8
+ def_delegators :heroku_adapter,
9
+ :create_heroku_application_manifest_file,
10
+ :create_heroku_pipeline,
11
+ :create_production_heroku_app,
12
+ :create_staging_heroku_app,
13
+ :create_review_apps_setup_script,
14
+ :set_heroku_rails_secrets,
15
+ :set_heroku_remotes,
16
+ :set_heroku_application_host
17
+
18
+ def readme
19
+ template 'README.md.erb', 'README.md'
20
+ end
21
+
22
+ def gitignore
23
+ copy_file "armadura_gitignore", ".gitignore"
24
+ end
25
+
26
+ def gemfile
27
+ template "Gemfile.erb", "Gemfile"
28
+ end
29
+
30
+ def setup_rack_mini_profiler
31
+ copy_file(
32
+ "rack_mini_profiler.rb",
33
+ "config/initializers/rack_mini_profiler.rb",
34
+ )
35
+ end
36
+
37
+ def raise_on_missing_assets_in_test
38
+ configure_environment "test", "config.assets.raise_runtime_errors = true"
39
+ end
40
+
41
+ def raise_on_delivery_errors
42
+ replace_in_file 'config/environments/development.rb',
43
+ 'raise_delivery_errors = false', 'raise_delivery_errors = true'
44
+ end
45
+
46
+ def remove_turbolinks
47
+ replace_in_file(
48
+ "app/assets/javascripts/application.js",
49
+ "//= require turbolinks",
50
+ ""
51
+ )
52
+ end
53
+
54
+ def set_test_delivery_method
55
+ inject_into_file(
56
+ "config/environments/development.rb",
57
+ "\n config.action_mailer.delivery_method = :file",
58
+ after: "config.action_mailer.raise_delivery_errors = true",
59
+ )
60
+ end
61
+
62
+ def add_bullet_gem_configuration
63
+ config = <<-RUBY
64
+ config.after_initialize do
65
+ Bullet.enable = true
66
+ Bullet.bullet_logger = true
67
+ Bullet.rails_logger = true
68
+ end
69
+
70
+ RUBY
71
+
72
+ inject_into_file(
73
+ "config/environments/development.rb",
74
+ config,
75
+ after: "config.action_mailer.raise_delivery_errors = true\n",
76
+ )
77
+ end
78
+
79
+ def raise_on_unpermitted_parameters
80
+ config = <<-RUBY
81
+ config.action_controller.action_on_unpermitted_parameters = :raise
82
+ RUBY
83
+
84
+ inject_into_class "config/application.rb", "Application", config
85
+ end
86
+
87
+ def configure_quiet_assets
88
+ config = <<-RUBY
89
+ config.assets.quiet = true
90
+ RUBY
91
+
92
+ inject_into_class "config/application.rb", "Application", config
93
+ end
94
+
95
+ def provide_setup_script
96
+ template "bin_setup", "bin/setup", force: true
97
+ run "chmod a+x bin/setup"
98
+ end
99
+
100
+ def provide_dev_prime_task
101
+ copy_file 'dev.rake', 'lib/tasks/dev.rake'
102
+ end
103
+
104
+ def configure_generators
105
+ config = <<-RUBY
106
+
107
+ config.generators do |generate|
108
+ generate.helper false
109
+ generate.javascript_engine false
110
+ generate.request_specs false
111
+ generate.routing_specs false
112
+ generate.stylesheets false
113
+ generate.test_framework :rspec
114
+ generate.view_specs false
115
+ end
116
+
117
+ RUBY
118
+
119
+ inject_into_class 'config/application.rb', 'Application', config
120
+ end
121
+
122
+ def set_up_factory_girl_for_rspec
123
+ copy_file 'factory_girl_rspec.rb', 'spec/support/factory_girl.rb'
124
+ end
125
+
126
+ def generate_factories_file
127
+ copy_file "factories.rb", "spec/factories.rb"
128
+ end
129
+
130
+ def set_up_hound
131
+ copy_file "hound.yml", ".hound.yml"
132
+ end
133
+
134
+ def configure_smtp
135
+ copy_file 'smtp.rb', 'config/smtp.rb'
136
+
137
+ prepend_file 'config/environments/production.rb',
138
+ %{require Rails.root.join("config/smtp")\n}
139
+
140
+ config = <<-RUBY
141
+
142
+ config.action_mailer.delivery_method = :smtp
143
+ config.action_mailer.smtp_settings = SMTP_SETTINGS
144
+ RUBY
145
+
146
+ inject_into_file 'config/environments/production.rb', config,
147
+ after: "config.action_mailer.raise_delivery_errors = false"
148
+ end
149
+
150
+ def enable_rack_canonical_host
151
+ config = <<-RUBY
152
+
153
+ if ENV.fetch("HEROKU_APP_NAME", "").include?("staging-pr-")
154
+ ENV["APPLICATION_HOST"] = ENV["HEROKU_APP_NAME"] + ".herokuapp.com"
155
+ end
156
+
157
+ config.middleware.use Rack::CanonicalHost, ENV.fetch("APPLICATION_HOST")
158
+ RUBY
159
+
160
+ inject_into_file(
161
+ "config/environments/production.rb",
162
+ config,
163
+ after: "Rails.application.configure do",
164
+ )
165
+ end
166
+
167
+ def enable_rack_deflater
168
+ configure_environment "production", "config.middleware.use Rack::Deflater"
169
+ end
170
+
171
+ def setup_asset_host
172
+ replace_in_file 'config/environments/production.rb',
173
+ "# config.action_controller.asset_host = 'http://assets.example.com'",
174
+ 'config.action_controller.asset_host = ENV.fetch("ASSET_HOST", ENV.fetch("APPLICATION_HOST"))'
175
+
176
+ replace_in_file 'config/initializers/assets.rb',
177
+ "config.assets.version = '1.0'",
178
+ 'config.assets.version = (ENV["ASSETS_VERSION"] || "1.0")'
179
+
180
+ config = <<-EOD
181
+ config.public_file_server.headers = {
182
+ "Cache-Control" => "public, max-age=31557600",
183
+ }
184
+ EOD
185
+
186
+ configure_environment("production", config)
187
+ end
188
+
189
+ def setup_secret_token
190
+ template 'secrets.yml', 'config/secrets.yml', force: true
191
+ end
192
+
193
+ def disallow_wrapping_parameters
194
+ remove_file "config/initializers/wrap_parameters.rb"
195
+ end
196
+
197
+ def create_partials_directory
198
+ empty_directory 'app/views/application'
199
+ end
200
+
201
+ def create_shared_flashes
202
+ copy_file "_flashes.html.erb", "app/views/application/_flashes.html.erb"
203
+ copy_file "flashes_helper.rb", "app/helpers/flashes_helper.rb"
204
+ end
205
+
206
+ def create_shared_javascripts
207
+ copy_file '_javascript.html.erb', 'app/views/application/_javascript.html.erb'
208
+ end
209
+
210
+ def create_shared_css_overrides
211
+ copy_file(
212
+ "_css_overrides.html.erb",
213
+ "app/views/application/_css_overrides.html.erb",
214
+ )
215
+ end
216
+
217
+ def create_application_layout
218
+ template 'armadura_layout.html.erb.erb',
219
+ 'app/views/layouts/application.html.erb',
220
+ force: true
221
+ end
222
+
223
+ def use_postgres_config_template
224
+ template 'postgresql_database.yml.erb', 'config/database.yml',
225
+ force: true
226
+ end
227
+
228
+ def create_database
229
+ bundle_command 'exec rake db:create db:migrate'
230
+ end
231
+
232
+ def replace_gemfile(path)
233
+ template 'Gemfile.erb', 'Gemfile', force: true do |content|
234
+ if path
235
+ content.gsub(%r{gem .armadura.}) { |s| %{#{s}, path: "#{path}"} }
236
+ else
237
+ content
238
+ end
239
+ end
240
+ end
241
+
242
+ def set_ruby_to_version_being_used
243
+ create_file '.ruby-version', "#{Armadura::RUBY_VERSION}\n"
244
+ end
245
+
246
+ def enable_database_cleaner
247
+ copy_file 'database_cleaner_rspec.rb', 'spec/support/database_cleaner.rb'
248
+ end
249
+
250
+ def provide_shoulda_matchers_config
251
+ copy_file(
252
+ "shoulda_matchers_config_rspec.rb",
253
+ "spec/support/shoulda_matchers.rb"
254
+ )
255
+ end
256
+
257
+ def configure_spec_support_features
258
+ empty_directory_with_keep_file 'spec/features'
259
+ empty_directory_with_keep_file 'spec/support/features'
260
+ end
261
+
262
+ def configure_rspec
263
+ remove_file "spec/rails_helper.rb"
264
+ remove_file "spec/spec_helper.rb"
265
+ copy_file "rails_helper.rb", "spec/rails_helper.rb"
266
+ copy_file "spec_helper.rb", "spec/spec_helper.rb"
267
+ end
268
+
269
+ def configure_ci
270
+ template "circle.yml.erb", "circle.yml"
271
+ end
272
+
273
+ def configure_i18n_for_test_environment
274
+ copy_file "i18n.rb", "spec/support/i18n.rb"
275
+ end
276
+
277
+ def configure_i18n_for_missing_translations
278
+ raise_on_missing_translations_in("development")
279
+ raise_on_missing_translations_in("test")
280
+ end
281
+
282
+ def configure_action_mailer_in_specs
283
+ copy_file 'action_mailer.rb', 'spec/support/action_mailer.rb'
284
+ end
285
+
286
+ def configure_capybara_webkit
287
+ copy_file "capybara_webkit.rb", "spec/support/capybara_webkit.rb"
288
+ end
289
+
290
+ def configure_time_formats
291
+ remove_file "config/locales/en.yml"
292
+ template "config_locales_en.yml.erb", "config/locales/en.yml"
293
+ end
294
+
295
+ def configure_rack_timeout
296
+ rack_timeout_config = <<-RUBY
297
+ Rack::Timeout.timeout = (ENV["RACK_TIMEOUT"] || 10).to_i
298
+ RUBY
299
+
300
+ append_file "config/environments/production.rb", rack_timeout_config
301
+ end
302
+
303
+ def configure_simple_form
304
+ bundle_command "exec rails generate simple_form:install"
305
+ end
306
+
307
+ def configure_action_mailer
308
+ action_mailer_host "development", %{"localhost:3000"}
309
+ action_mailer_host "test", %{"www.example.com"}
310
+ action_mailer_host "production", %{ENV.fetch("APPLICATION_HOST")}
311
+ end
312
+
313
+ def configure_active_job
314
+ configure_application_file "config.active_job.queue_adapter = :sidekiq"
315
+ configure_environment "test", "config.active_job.queue_adapter = :inline"
316
+ end
317
+
318
+ def configure_sidekiq
319
+ copy_file "sidekiq.yml", "config/sidekiq.yml", force: true
320
+ end
321
+
322
+ def generate_rspec
323
+ generate 'rspec:install'
324
+ end
325
+
326
+ def replace_default_puma_configuration
327
+ copy_file "puma.rb", "config/puma.rb", force: true
328
+ end
329
+
330
+ def set_up_forego
331
+ copy_file "Procfile", "Procfile"
332
+ end
333
+
334
+ def setup_default_directories
335
+ [
336
+ 'app/views/pages',
337
+ 'spec/lib',
338
+ 'spec/controllers',
339
+ 'spec/helpers',
340
+ 'spec/support/matchers',
341
+ 'spec/support/mixins',
342
+ 'spec/support/shared_examples'
343
+ ].each do |dir|
344
+ empty_directory_with_keep_file dir
345
+ end
346
+ end
347
+
348
+ def copy_dotfiles
349
+ directory("dotfiles", ".")
350
+ end
351
+
352
+ def init_git
353
+ run 'git init'
354
+ end
355
+
356
+ def create_heroku_apps(flags)
357
+ create_staging_heroku_app(flags)
358
+ create_production_heroku_app(flags)
359
+ end
360
+
361
+ def create_deploy_script
362
+ copy_file "bin_deploy", "bin/deploy"
363
+
364
+ instructions = <<-MARKDOWN
365
+
366
+ ## Deploying
367
+
368
+ If you have previously run the `./bin/setup` script,
369
+ you can deploy to staging and production with:
370
+
371
+ $ ./bin/deploy staging
372
+ $ ./bin/deploy production
373
+ MARKDOWN
374
+
375
+ append_file "README.md", instructions
376
+ run "chmod a+x bin/deploy"
377
+ end
378
+
379
+ def configure_automatic_deployment
380
+ deploy_command = <<-YML.strip_heredoc
381
+ deployment:
382
+ staging:
383
+ branch: master
384
+ commands:
385
+ - bin/deploy staging
386
+ YML
387
+
388
+ append_file "circle.yml", deploy_command
389
+ end
390
+
391
+ def create_github_repo(repo_name)
392
+ run "hub create #{repo_name}"
393
+ end
394
+
395
+ def setup_segment
396
+ copy_file '_analytics.html.erb',
397
+ 'app/views/application/_analytics.html.erb'
398
+ end
399
+
400
+ def setup_bundler_audit
401
+ copy_file "bundler_audit.rake", "lib/tasks/bundler_audit.rake"
402
+ append_file "Rakefile", %{\ntask default: "bundler:audit"\n}
403
+ end
404
+
405
+ def setup_spring
406
+ bundle_command "exec spring binstub --all"
407
+ end
408
+
409
+ def copy_miscellaneous_files
410
+ copy_file "browserslist", "browserslist"
411
+ copy_file "errors.rb", "config/initializers/errors.rb"
412
+ copy_file "json_encoding.rb", "config/initializers/json_encoding.rb"
413
+ end
414
+
415
+ def customize_error_pages
416
+ meta_tags =<<-EOS
417
+ <meta charset="utf-8" />
418
+ <meta name="ROBOTS" content="NOODP" />
419
+ <meta name="viewport" content="initial-scale=1" />
420
+ EOS
421
+
422
+ %w(500 404 422).each do |page|
423
+ inject_into_file "public/#{page}.html", meta_tags, after: "<head>\n"
424
+ replace_in_file "public/#{page}.html", /<!--.+-->\n/, ''
425
+ end
426
+ end
427
+
428
+ def remove_config_comment_lines
429
+ config_files = [
430
+ "application.rb",
431
+ "environment.rb",
432
+ "environments/development.rb",
433
+ "environments/production.rb",
434
+ "environments/test.rb",
435
+ ]
436
+
437
+ config_files.each do |config_file|
438
+ path = File.join(destination_root, "config/#{config_file}")
439
+
440
+ accepted_content = File.readlines(path).reject do |line|
441
+ line =~ /^.*#.*$/ || line =~ /^$\n/
442
+ end
443
+
444
+ File.open(path, "w") do |file|
445
+ accepted_content.each { |line| file.puts line }
446
+ end
447
+ end
448
+ end
449
+
450
+ def remove_routes_comment_lines
451
+ replace_in_file 'config/routes.rb',
452
+ /Rails\.application\.routes\.draw do.*end/m,
453
+ "Rails.application.routes.draw do\nend"
454
+ end
455
+
456
+ def setup_default_rake_task
457
+ append_file 'Rakefile' do
458
+ <<-EOS
459
+ task(:default).clear
460
+ task default: [:spec]
461
+
462
+ if defined? RSpec
463
+ task(:spec).clear
464
+ RSpec::Core::RakeTask.new(:spec) do |t|
465
+ t.verbose = false
466
+ end
467
+ end
468
+ EOS
469
+ end
470
+ end
471
+
472
+ private
473
+
474
+ def raise_on_missing_translations_in(environment)
475
+ config = 'config.action_view.raise_on_missing_translations = true'
476
+
477
+ uncomment_lines("config/environments/#{environment}.rb", config)
478
+ end
479
+
480
+ def heroku_adapter
481
+ @heroku_adapter ||= Adapters::Heroku.new(self)
482
+ end
483
+ end
484
+ end