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,37 @@
1
+ require_relative "../base"
2
+
3
+ module Bulldozer
4
+ module Production
5
+ class EmailGenerator < Generators::Base
6
+ def smtp_configuration
7
+ copy_file "smtp.rb", "config/smtp.rb"
8
+
9
+ prepend_file "config/environments/production.rb",
10
+ %{require Rails.root.join("config/smtp")\n}
11
+ end
12
+
13
+ def use_smtp
14
+ config = <<-RUBY
15
+
16
+ config.action_mailer.delivery_method = :smtp
17
+ config.action_mailer.smtp_settings = SMTP_SETTINGS
18
+ RUBY
19
+
20
+ inject_into_file "config/environments/production.rb", config,
21
+ after: "config.action_mailer.perform_caching = false"
22
+ end
23
+
24
+ def env_vars
25
+ expand_json(
26
+ "app.json",
27
+ env: {
28
+ SMTP_ADDRESS: { required: true },
29
+ SMTP_DOMAIN: { required: true },
30
+ SMTP_PASSWORD: { required: true },
31
+ SMTP_USERNAME: { required: true },
32
+ },
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,11 @@
1
+ require_relative "../base"
2
+
3
+ module Bulldozer
4
+ module Production
5
+ class ForceTlsGenerator < Generators::Base
6
+ def config_enforce_ssl
7
+ configure_environment "production", "config.force_ssl = true"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,24 @@
1
+ require_relative "../base"
2
+
3
+ module Bulldozer
4
+ module Production
5
+ class ManifestGenerator < Generators::Base
6
+ def render_manifest
7
+ expand_json(
8
+ "app.json",
9
+ name: app_name.dasherize,
10
+ scripts: {},
11
+ env: {
12
+ APPLICATION_HOST: { required: true },
13
+ EMAIL_RECIPIENTS: { required: true },
14
+ HEROKU_APP_NAME: { required: true },
15
+ HEROKU_PARENT_APP_NAME: { required: true },
16
+ RACK_ENV: { required: true },
17
+ SECRET_KEY_BASE: { generator: "secret" },
18
+ },
19
+ addons: ["heroku-postgresql"],
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ require_relative "../base"
2
+
3
+ module Bulldozer
4
+ module Production
5
+ class TimeoutGenerator < Generators::Base
6
+ def add_gem
7
+ gem "rack-timeout", group: :production
8
+ end
9
+
10
+ def configure_rack_timeout
11
+ append_file ".env", rack_timeout_config
12
+ end
13
+
14
+ private
15
+
16
+ def rack_timeout_config
17
+ %{RACK_TIMEOUT_SERVICE_TIMEOUT=10}
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,33 @@
1
+ require_relative "../base"
2
+
3
+ module Bulldozer
4
+ module Staging
5
+ class PullRequestsGenerator < Generators::Base
6
+ def configure_heroku_staging_pr_pipeline_host
7
+ config = <<-RUBY
8
+
9
+ if ENV.fetch("HEROKU_APP_NAME", "").include?("staging-pr-")
10
+ ENV["APPLICATION_HOST"] = ENV["HEROKU_APP_NAME"] + ".herokuapp.com"
11
+ ENV["ASSET_HOST"] = ENV["HEROKU_APP_NAME"] + ".herokuapp.com"
12
+ end
13
+ RUBY
14
+
15
+ inject_into_file(
16
+ "config/environments/production.rb",
17
+ config,
18
+ after: "Rails.application.configure do\n",
19
+ )
20
+ end
21
+
22
+ def create_review_apps_setup_script
23
+ template(
24
+ "bin_setup_review_app.erb",
25
+ "bin/setup_review_app",
26
+ force: true,
27
+ )
28
+
29
+ run "chmod a+x bin/setup_review_app"
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,10 @@
1
+ require_relative "base"
2
+
3
+ module Bulldozer
4
+ class StaticGenerator < Generators::Base
5
+ def add_high_voltage
6
+ gem "high_voltage"
7
+ Bundler.with_clean_env { run "bundle install" }
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,31 @@
1
+ require_relative "base"
2
+
3
+ module Bulldozer
4
+ class StylesheetBaseGenerator < Generators::Base
5
+ def add_stylesheet_gems
6
+ gem "bourbon", ">= 5.0.1"
7
+ gem "neat", ">= 3.0.1"
8
+ Bundler.with_clean_env { run "bundle install" }
9
+ end
10
+
11
+ def add_css_config
12
+ copy_file(
13
+ "application.scss",
14
+ "app/assets/stylesheets/application.scss",
15
+ force: true,
16
+ )
17
+ end
18
+
19
+ def remove_prior_config
20
+ remove_file "app/assets/stylesheets/application.css"
21
+ end
22
+
23
+ def install_bitters
24
+ run "bitters install --path app/assets/stylesheets"
25
+ end
26
+
27
+ def install_normalize_css
28
+ run "bin/yarn add normalize.css"
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,55 @@
1
+ require_relative "base"
2
+
3
+ module Bulldozer
4
+ class TestingGenerator < Generators::Base
5
+ def add_testing_gems
6
+ gem "spring-commands-rspec", group: :development
7
+ gem "rspec-rails", "~> 3.6", group: %i(development test)
8
+ gem "shoulda-matchers", group: :test
9
+
10
+ Bundler.with_clean_env { run "bundle install" }
11
+ end
12
+
13
+ def generate_rspec
14
+ generate "rspec:install"
15
+ end
16
+
17
+ def configure_rspec
18
+ remove_file "spec/rails_helper.rb"
19
+ remove_file "spec/spec_helper.rb"
20
+ copy_file "rails_helper.rb", "spec/rails_helper.rb"
21
+ copy_file "spec_helper.rb", "spec/spec_helper.rb"
22
+ end
23
+
24
+ def provide_shoulda_matchers_config
25
+ copy_file(
26
+ "shoulda_matchers_config_rspec.rb",
27
+ "spec/support/shoulda_matchers.rb",
28
+ )
29
+ end
30
+
31
+ def configure_spec_support_features
32
+ empty_directory_with_keep_file "spec/features"
33
+ empty_directory_with_keep_file "spec/support/features"
34
+ end
35
+
36
+ def configure_i18n_for_test_environment
37
+ copy_file "i18n.rb", "spec/support/i18n.rb"
38
+ end
39
+
40
+ def configure_action_mailer_in_specs
41
+ copy_file "action_mailer.rb", "spec/support/action_mailer.rb"
42
+ end
43
+
44
+ private
45
+
46
+ def empty_directory_with_keep_file(destination)
47
+ empty_directory(destination, {})
48
+ keep_file(destination)
49
+ end
50
+
51
+ def keep_file(destination)
52
+ create_file(File.join(destination, ".keep"))
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,30 @@
1
+ require_relative "base"
2
+
3
+ module Bulldozer
4
+ class ViewsGenerator < Generators::Base
5
+ def create_partials_directory
6
+ empty_directory "app/views/application"
7
+ end
8
+
9
+ def create_shared_flashes
10
+ copy_file "_flashes.html.erb", "app/views/application/_flashes.html.erb"
11
+ copy_file "flashes_helper.rb", "app/helpers/flashes_helper.rb"
12
+ end
13
+
14
+ def create_shared_javascripts
15
+ copy_file "_javascript.html.erb",
16
+ "app/views/application/_javascript.html.erb"
17
+ end
18
+
19
+ def create_shared_css_overrides
20
+ copy_file "_css_overrides.html.erb",
21
+ "app/views/application/_css_overrides.html.erb"
22
+ end
23
+
24
+ def create_application_layout
25
+ template "bulldozer_layout.html.erb.erb",
26
+ "app/views/layouts/application.html.erb",
27
+ force: true
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,8 @@
1
+ module Bulldozer
2
+ RAILS_VERSION = "~> 5.2.0".freeze
3
+ RUBY_VERSION = IO.
4
+ read("#{File.dirname(__FILE__)}/../../.ruby-version").
5
+ strip.
6
+ freeze
7
+ VERSION = "1.6.0".freeze
8
+ end
@@ -0,0 +1,72 @@
1
+ require "spec_helper"
2
+
3
+ module Bulldozer
4
+ module Adapters
5
+ RSpec.describe Heroku do
6
+ it "sets the heroku remotes" do
7
+ setup_file = "bin/setup"
8
+ app_builder = double(app_name: app_name)
9
+ allow(app_builder).to receive(:append_file)
10
+
11
+ Heroku.new(app_builder).set_heroku_remotes
12
+
13
+ expect(app_builder).to have_received(:append_file).
14
+ with(setup_file, /heroku join --app #{app_name.dasherize}-production/)
15
+ expect(app_builder).to have_received(:append_file).
16
+ with(setup_file, /heroku join --app #{app_name.dasherize}-staging/)
17
+ end
18
+
19
+ it "sets the heroku rails secrets" do
20
+ app_builder = double(app_name: app_name)
21
+ allow(app_builder).to receive(:run)
22
+
23
+ Heroku.new(app_builder).set_heroku_rails_secrets
24
+
25
+ expect(app_builder).to(
26
+ have_configured_var("staging", "SECRET_KEY_BASE"),
27
+ )
28
+ expect(app_builder).to(
29
+ have_configured_var("production", "SECRET_KEY_BASE"),
30
+ )
31
+ end
32
+
33
+ it "sets the heroku backup schedule" do
34
+ app_builder = double(app_name: app_name)
35
+ allow(app_builder).to receive(:run)
36
+
37
+ Heroku.new(app_builder).set_heroku_backup_schedule
38
+
39
+ expect(app_builder).to have_backup_schedule("staging")
40
+ expect(app_builder).to have_backup_schedule("production")
41
+ end
42
+
43
+ it "sets the application host" do
44
+ app_builder = double(app_name: app_name)
45
+ allow(app_builder).to receive(:run)
46
+
47
+ Heroku.new(app_builder).set_heroku_application_host
48
+
49
+ expect(app_builder).to(
50
+ have_configured_var("staging", "APPLICATION_HOST"),
51
+ )
52
+
53
+ expect(app_builder).to(
54
+ have_configured_var("production", "APPLICATION_HOST"),
55
+ )
56
+ end
57
+
58
+ def app_name
59
+ BulldozerTestHelpers::APP_NAME
60
+ end
61
+
62
+ def have_backup_schedule(remote_name)
63
+ have_received(:run).
64
+ with(/pg:backups:schedule DATABASE_URL --at '10:00 UTC' --remote #{remote_name}/)
65
+ end
66
+
67
+ def have_configured_var(remote_name, var)
68
+ have_received(:run).with(/config:add #{var}=.+ --remote #{remote_name}/)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join('..', '..', 'support', 'fake_heroku'), File.dirname(__FILE__))
4
+
5
+ FakeHeroku.new(ARGV).run!
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(File.join('..', '..', 'support', 'fake_github'), File.dirname(__FILE__))
4
+
5
+ FakeGithub.new(ARGV).run!
@@ -0,0 +1,18 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "Suspend a new project with --api flag" do
4
+ before(:all) do
5
+ drop_dummy_database
6
+ remove_project_directory
7
+ end
8
+
9
+ it "ensures project specs pass" do
10
+ run_bulldozer("--api")
11
+
12
+ Dir.chdir(project_path) do
13
+ Bundler.with_clean_env do
14
+ expect(`rake`).to include("0 failures")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,36 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "Command line help output" do
4
+ let(:help_text) { bulldozer_help_command }
5
+
6
+ it "does not contain the default rails usage statement" do
7
+ expect(help_text).not_to include("rails new APP_PATH [options]")
8
+ end
9
+
10
+ it "provides the correct usage statement for bulldozer" do
11
+ expect(help_text).to include <<~EOH
12
+ Usage:
13
+ bulldozer APP_PATH [options]
14
+ EOH
15
+ end
16
+
17
+ it "does not contain the default rails group" do
18
+ expect(help_text).not_to include("Rails options:")
19
+ end
20
+
21
+ it "provides help and version usage within the bulldozer group" do
22
+ expect(help_text).to include <<~EOH
23
+ Bulldozer options:
24
+ -h, [--help], [--no-help] # Show this help message and quit
25
+ -v, [--version], [--no-version] # Show Bulldozer version number and quit
26
+ EOH
27
+ end
28
+
29
+ it "does not show the default extended rails help section" do
30
+ expect(help_text).not_to include("Create bulldozer files for app generator.")
31
+ end
32
+
33
+ it "contains the usage statement from the bulldozer gem" do
34
+ expect(help_text).to include IO.read(usage_file)
35
+ end
36
+ end
@@ -0,0 +1,16 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "GitHub" do
4
+ before do
5
+ drop_dummy_database
6
+ remove_project_directory
7
+ end
8
+
9
+ it "suspends a project with --github option" do
10
+ repo_name = "test"
11
+ run_bulldozer("--github=#{repo_name}")
12
+ setup_app_dependencies
13
+
14
+ expect(FakeGithub).to have_created_repo(repo_name)
15
+ end
16
+ end
@@ -0,0 +1,71 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "Heroku" do
4
+ context "--heroku" do
5
+ before(:all) do
6
+ clean_up
7
+ run_bulldozer("--heroku=true")
8
+ setup_app_dependencies
9
+ end
10
+
11
+ it "suspends a project for Heroku" do
12
+ app_name = BulldozerTestHelpers::APP_NAME.dasherize
13
+
14
+ expect(FakeHeroku).to have_created_app_for("staging")
15
+ expect(FakeHeroku).to have_created_app_for("production")
16
+ expect(FakeHeroku).to have_configured_vars("staging", "SECRET_KEY_BASE")
17
+ expect(FakeHeroku).to have_configured_vars(
18
+ "production",
19
+ "SECRET_KEY_BASE",
20
+ )
21
+ %w(staging production).each do |env|
22
+ expect(FakeHeroku).to have_configured_vars(env, "APPLICATION_HOST")
23
+ expect(FakeHeroku).to have_configured_vars(env, "HONEYBADGER_ENV")
24
+ end
25
+ expect(FakeHeroku).to have_setup_pipeline_for(app_name)
26
+
27
+ bin_setup_path = "#{project_path}/bin/setup"
28
+ bin_setup = IO.read(bin_setup_path)
29
+
30
+ expect(bin_setup).to match(/^if heroku join --app #{app_name}-production/)
31
+ expect(bin_setup).to match(/^if heroku join --app #{app_name}-staging/)
32
+ expect(bin_setup).to match(/^git config heroku.remote staging/)
33
+ expect("bin/setup").to be_executable
34
+
35
+ readme = IO.read("#{project_path}/README.md")
36
+
37
+ expect(readme).to include("./bin/deploy staging")
38
+ expect(readme).to include("./bin/deploy production")
39
+
40
+ circle_yml_path = "#{project_path}/circle.yml"
41
+ circle_yml = IO.read(circle_yml_path)
42
+
43
+ expect(circle_yml).to include <<-YML.strip_heredoc
44
+ deployment:
45
+ staging:
46
+ branch: master
47
+ commands:
48
+ - bin/deploy staging
49
+ YML
50
+ end
51
+ end
52
+
53
+ context "--heroku with region flag" do
54
+ before(:all) do
55
+ clean_up
56
+ run_bulldozer(%{--heroku=true --heroku-flags="--region eu"})
57
+ setup_app_dependencies
58
+ end
59
+
60
+ it "suspends a project with extra Heroku flags" do
61
+ expect(FakeHeroku).to have_created_app_for("staging", "--region eu")
62
+ expect(FakeHeroku).to have_created_app_for("production", "--region eu")
63
+ end
64
+ end
65
+
66
+ def clean_up
67
+ drop_dummy_database
68
+ remove_project_directory
69
+ FakeHeroku.clear!
70
+ end
71
+ end