suspenders 1.52.0 → 1.55.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.standard.yml +2 -0
  4. data/.travis.yml +6 -3
  5. data/CONTRIBUTING.md +15 -15
  6. data/GOALS.md +65 -0
  7. data/Gemfile +1 -1
  8. data/NEWS.md +52 -0
  9. data/README.md +6 -5
  10. data/Rakefile +6 -5
  11. data/docs/heroku_deploy.md +19 -0
  12. data/lib/suspenders.rb +7 -0
  13. data/lib/suspenders/actions.rb +2 -2
  14. data/lib/suspenders/adapters/heroku.rb +27 -14
  15. data/lib/suspenders/app_builder.rb +42 -111
  16. data/lib/suspenders/generators/advisories_generator.rb +15 -0
  17. data/lib/suspenders/generators/analytics_generator.rb +1 -1
  18. data/lib/suspenders/generators/app_generator.rb +36 -58
  19. data/lib/suspenders/generators/base.rb +40 -1
  20. data/lib/suspenders/generators/ci_generator.rb +19 -9
  21. data/lib/suspenders/generators/db_optimizations_generator.rb +5 -17
  22. data/lib/suspenders/generators/factories_generator.rb +2 -2
  23. data/lib/suspenders/generators/forms_generator.rb +1 -1
  24. data/lib/suspenders/generators/inline_svg_generator.rb +14 -0
  25. data/lib/suspenders/generators/jobs_generator.rb +4 -4
  26. data/lib/suspenders/generators/js_driver_generator.rb +7 -4
  27. data/lib/suspenders/generators/json_generator.rb +1 -1
  28. data/lib/suspenders/generators/lint_generator.rb +5 -0
  29. data/lib/suspenders/generators/preloader_generator.rb +122 -0
  30. data/lib/suspenders/generators/production/compression_generator.rb +14 -0
  31. data/lib/suspenders/generators/production/deployment_generator.rb +1 -12
  32. data/lib/suspenders/generators/production/email_generator.rb +9 -12
  33. data/lib/suspenders/generators/production/manifest_generator.rb +8 -7
  34. data/lib/suspenders/generators/production/single_redirect.rb +15 -0
  35. data/lib/suspenders/generators/production/timeout_generator.rb +3 -2
  36. data/lib/suspenders/generators/profiler_generator.rb +35 -0
  37. data/lib/suspenders/generators/runner_generator.rb +48 -0
  38. data/lib/suspenders/generators/staging/pull_requests_generator.rb +4 -12
  39. data/lib/suspenders/generators/static_generator.rb +5 -1
  40. data/lib/suspenders/generators/stylelint_generator.rb +2 -1
  41. data/lib/suspenders/generators/stylesheet_base_generator.rb +7 -8
  42. data/lib/suspenders/generators/testing_generator.rb +3 -14
  43. data/lib/suspenders/version.rb +6 -6
  44. data/spec/adapters/heroku_spec.rb +36 -10
  45. data/spec/expand_json_spec.rb +89 -0
  46. data/spec/fakes/bin/heroku +1 -1
  47. data/spec/fakes/bin/hub +1 -1
  48. data/spec/features/advisories_spec.rb +24 -0
  49. data/spec/features/api_spec.rb +1 -1
  50. data/spec/features/ci_spec.rb +31 -0
  51. data/spec/features/cli_help_spec.rb +4 -4
  52. data/spec/features/db_optimizations_spec.rb +19 -0
  53. data/spec/features/heroku_spec.rb +9 -16
  54. data/spec/features/inline_svg_spec.rb +10 -0
  55. data/spec/features/lint_spec.rb +26 -0
  56. data/spec/features/new_project_spec.rb +40 -60
  57. data/spec/features/preloader_spec.rb +25 -0
  58. data/spec/features/production/compression_spec.rb +23 -0
  59. data/spec/features/production/email_spec.rb +10 -10
  60. data/spec/features/production/manifest_spec.rb +16 -14
  61. data/spec/features/production/single_redirect_spec.rb +25 -0
  62. data/spec/features/profiler_spec.rb +20 -0
  63. data/spec/features/runner_spec.rb +30 -0
  64. data/spec/features/staging/pull_requests_spec.rb +1 -1
  65. data/spec/features/static_spec.rb +17 -0
  66. data/spec/features/stylelint_spec.rb +7 -7
  67. data/spec/spec_helper.rb +3 -3
  68. data/spec/support/contain_json_matcher.rb +16 -10
  69. data/spec/support/fake_github.rb +3 -3
  70. data/spec/support/fake_heroku.rb +8 -8
  71. data/spec/support/project_files.rb +12 -0
  72. data/spec/support/rails_template.rb +1 -0
  73. data/spec/support/suspenders.rb +21 -19
  74. data/suspenders.gemspec +20 -20
  75. data/templates/Gemfile.erb +2 -10
  76. data/templates/_javascript.html.erb +1 -1
  77. data/templates/application.scss +0 -1
  78. data/templates/bin_auto_migrate +5 -0
  79. data/templates/bin_deploy +0 -2
  80. data/templates/bin_setup +2 -2
  81. data/templates/bin_setup_review_app.erb +0 -1
  82. data/templates/capybara_silence_puma.rb +1 -0
  83. data/templates/chromedriver.rb +14 -4
  84. data/templates/descriptions/advisories.md +5 -0
  85. data/templates/descriptions/analytics.md +4 -0
  86. data/templates/descriptions/ci.md +4 -0
  87. data/templates/descriptions/compression.md +4 -0
  88. data/templates/descriptions/db_optimizations.md +2 -0
  89. data/templates/descriptions/deployment.md +5 -0
  90. data/templates/descriptions/email.md +9 -0
  91. data/templates/descriptions/factories.md +12 -0
  92. data/templates/descriptions/force_tls.md +1 -0
  93. data/templates/descriptions/forms.md +1 -0
  94. data/templates/descriptions/inline_svg.md +2 -0
  95. data/templates/descriptions/jobs.md +3 -0
  96. data/templates/descriptions/js_driver.md +4 -0
  97. data/templates/descriptions/json.md +1 -0
  98. data/templates/descriptions/lint.md +3 -0
  99. data/templates/descriptions/manifest.md +2 -0
  100. data/templates/descriptions/preloader.md +3 -0
  101. data/templates/descriptions/profiler.md +7 -0
  102. data/templates/descriptions/pull_requests.md +4 -0
  103. data/templates/descriptions/runner.md +10 -0
  104. data/templates/descriptions/single_redirect.md +1 -0
  105. data/templates/descriptions/static.md +5 -0
  106. data/templates/descriptions/stylelint.md +3 -0
  107. data/templates/descriptions/stylesheet_base.md +4 -0
  108. data/templates/descriptions/testing.md +9 -0
  109. data/templates/descriptions/timeout.md +4 -0
  110. data/templates/descriptions/views.md +8 -0
  111. data/templates/errors.rb +3 -3
  112. data/templates/hound.yml +1 -2
  113. data/templates/inline_svg.rb +3 -0
  114. data/templates/partials/ci_simplecov.rb +14 -0
  115. data/templates/partials/db_optimizations_configuration.rb +7 -0
  116. data/templates/partials/deployment_readme.md +8 -0
  117. data/templates/partials/email_smtp.rb +2 -0
  118. data/templates/partials/profiler_readme.md +8 -0
  119. data/templates/partials/pull_requests_config.rb +4 -0
  120. data/templates/partials/runner_readme.md +31 -0
  121. data/templates/partials/runner_setup.rb +2 -0
  122. data/templates/rack_mini_profiler.rb +2 -0
  123. data/templates/rails_helper.rb +4 -1
  124. data/templates/{dotfiles/.env → sample_env} +0 -2
  125. data/templates/spec_helper.rb +4 -7
  126. data/templates/spring.rb +6 -0
  127. data/templates/suspenders_gitignore +1 -1
  128. metadata +103 -17
  129. data/templates/browserslist +0 -3
  130. data/templates/dotfiles/.ctags +0 -2
  131. data/templates/puma.rb +0 -28
@@ -0,0 +1,15 @@
1
+ require_relative "../base"
2
+
3
+ module Suspenders
4
+ module Production
5
+ class SingleRedirect < Generators::Base
6
+ def add_rack_canonical_host
7
+ inject_into_file(
8
+ "config/environments/production.rb",
9
+ %{\n config.middleware.use Rack::CanonicalHost, ENV.fetch("APPLICATION_HOST")},
10
+ before: "\nend"
11
+ )
12
+ end
13
+ end
14
+ end
15
+ end
@@ -5,16 +5,17 @@ module Suspenders
5
5
  class TimeoutGenerator < Generators::Base
6
6
  def add_gem
7
7
  gem "rack-timeout", group: :production
8
+ Bundler.with_unbundled_env { run "bundle install" }
8
9
  end
9
10
 
10
11
  def configure_rack_timeout
11
- append_file ".env", rack_timeout_config
12
+ append_file ".sample.env", rack_timeout_config
12
13
  end
13
14
 
14
15
  private
15
16
 
16
17
  def rack_timeout_config
17
- %{RACK_TIMEOUT_SERVICE_TIMEOUT=10}
18
+ %(RACK_TIMEOUT_SERVICE_TIMEOUT=10)
18
19
  end
19
20
  end
20
21
  end
@@ -0,0 +1,35 @@
1
+ require_relative "base"
2
+
3
+ module Suspenders
4
+ class ProfilerGenerator < Generators::Base
5
+ def augment_default_env
6
+ append_to_file ".sample.env", "RACK_MINI_PROFILER=0\n"
7
+ rescue Errno::ENOENT
8
+ create_file ".sample.env", "RACK_MINI_PROFILER=0\n"
9
+ rescue Thor::Error => e
10
+ if e.message.match?(/does not appear to exist/)
11
+ create_file ".sample.env", "RACK_MINI_PROFILER=0\n"
12
+ else
13
+ raise
14
+ end
15
+ end
16
+
17
+ def add_gem
18
+ gem "rack-mini-profiler", require: false
19
+ Bundler.with_unbundled_env { run "bundle install" }
20
+ end
21
+
22
+ def configure_rack_mini_profiler
23
+ copy_file(
24
+ "rack_mini_profiler.rb",
25
+ "config/initializers/rack_mini_profiler.rb",
26
+ force: false,
27
+ skip: true
28
+ )
29
+ end
30
+
31
+ def update_readme
32
+ append_template_to_file "README.md", "partials/profiler_readme.md"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,48 @@
1
+ require_relative "base"
2
+
3
+ module Suspenders
4
+ class RunnerGenerator < Generators::Base
5
+ def procfile
6
+ copy_file "Procfile", "Procfile"
7
+ end
8
+
9
+ def sample_env
10
+ copy_file "sample_env", ".sample.env"
11
+ end
12
+
13
+ def copy_sample_env
14
+ if bin_setup_is_ruby?
15
+ inject_template_into_file(
16
+ "bin/setup",
17
+ "partials/runner_setup.rb",
18
+ before: %( puts "\\n== Preparing database ==")
19
+ )
20
+ elsif bin_setup_mentions_ci?
21
+ inject_into_file(
22
+ "bin/setup",
23
+ %( cp -i .sample.env .env\n),
24
+ after: %(if [ -z "$CI" ]; then\n)
25
+ )
26
+ else
27
+ append_to_file(
28
+ "bin/setup",
29
+ %(\nif [ -z "$CI" ]; then\n cp -i .sample.env .env\nfi)
30
+ )
31
+ end
32
+ end
33
+
34
+ def update_readme
35
+ append_template_to_file "README.md", "partials/runner_readme.md"
36
+ end
37
+
38
+ private
39
+
40
+ def bin_setup_is_ruby?
41
+ File.read("bin/setup", 20).match?(%r{#!/usr/bin/env ruby})
42
+ end
43
+
44
+ def bin_setup_mentions_ci?
45
+ File.read("bin/setup").match?(/if \[ -z "\$CI" \]/)
46
+ end
47
+ end
48
+ end
@@ -4,18 +4,10 @@ module Suspenders
4
4
  module Staging
5
5
  class PullRequestsGenerator < Generators::Base
6
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(
7
+ inject_template_into_file(
16
8
  "config/environments/production.rb",
17
- config,
18
- after: "Rails.application.configure do\n",
9
+ "partials/pull_requests_config.rb",
10
+ after: "Rails.application.configure do\n"
19
11
  )
20
12
  end
21
13
 
@@ -23,7 +15,7 @@ module Suspenders
23
15
  template(
24
16
  "bin_setup_review_app.erb",
25
17
  "bin/setup_review_app",
26
- force: true,
18
+ force: true
27
19
  )
28
20
 
29
21
  run "chmod a+x bin/setup_review_app"
@@ -4,7 +4,11 @@ module Suspenders
4
4
  class StaticGenerator < Generators::Base
5
5
  def add_high_voltage
6
6
  gem "high_voltage"
7
- Bundler.with_clean_env { run "bundle install" }
7
+ Bundler.with_unbundled_env { run "bundle install" }
8
+ end
9
+
10
+ def make_placeholder_directory
11
+ empty_directory_with_keep_file "app/views/pages"
8
12
  end
9
13
  end
10
14
  end
@@ -26,7 +26,8 @@ module Suspenders
26
26
  @base.invoke @generator
27
27
  end
28
28
 
29
- def revoke!; end
29
+ def revoke!
30
+ end
30
31
  end
31
32
 
32
33
  class ToggleComments
@@ -3,23 +3,22 @@ require_relative "base"
3
3
  module Suspenders
4
4
  class StylesheetBaseGenerator < Generators::Base
5
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" }
6
+ gem "bourbon", ">= 6.0.0"
7
+ Bundler.with_unbundled_env { run "bundle install" }
8
+ end
9
+
10
+ def remove_prior_config
11
+ remove_file "app/assets/stylesheets/application.css"
9
12
  end
10
13
 
11
14
  def add_css_config
12
15
  copy_file(
13
16
  "application.scss",
14
17
  "app/assets/stylesheets/application.scss",
15
- force: true,
18
+ force: true
16
19
  )
17
20
  end
18
21
 
19
- def remove_prior_config
20
- remove_file "app/assets/stylesheets/application.css"
21
- end
22
-
23
22
  def install_bitters
24
23
  run "bitters install --path app/assets/stylesheets"
25
24
  end
@@ -4,10 +4,10 @@ module Suspenders
4
4
  class TestingGenerator < Generators::Base
5
5
  def add_testing_gems
6
6
  gem "spring-commands-rspec", group: :development
7
- gem "rspec-rails", "~> 3.6", group: %i(development test)
7
+ gem "rspec-rails", "~> 3.6", group: %i[development test]
8
8
  gem "shoulda-matchers", group: :test
9
9
 
10
- Bundler.with_clean_env { run "bundle install" }
10
+ Bundler.with_unbundled_env { run "bundle install" }
11
11
  end
12
12
 
13
13
  def generate_rspec
@@ -24,7 +24,7 @@ module Suspenders
24
24
  def provide_shoulda_matchers_config
25
25
  copy_file(
26
26
  "shoulda_matchers_config_rspec.rb",
27
- "spec/support/shoulda_matchers.rb",
27
+ "spec/support/shoulda_matchers.rb"
28
28
  )
29
29
  end
30
30
 
@@ -40,16 +40,5 @@ module Suspenders
40
40
  def configure_action_mailer_in_specs
41
41
  copy_file "action_mailer.rb", "spec/support/action_mailer.rb"
42
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
43
  end
55
44
  end
@@ -1,8 +1,8 @@
1
1
  module Suspenders
2
- RAILS_VERSION = "~> 5.2.3".freeze
3
- RUBY_VERSION = IO.
4
- read("#{File.dirname(__FILE__)}/../../.ruby-version").
5
- strip.
6
- freeze
7
- VERSION = "1.52.0".freeze
2
+ RAILS_VERSION = "~> 6.0.0".freeze
3
+ RUBY_VERSION = IO
4
+ .read("#{File.dirname(__FILE__)}/../../.ruby-version")
5
+ .strip
6
+ .freeze
7
+ VERSION = "1.55.1".freeze
8
8
  end
@@ -10,10 +10,10 @@ module Suspenders
10
10
 
11
11
  Heroku.new(app_builder).set_heroku_remotes
12
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/)
13
+ expect(app_builder).to have_received(:append_file)
14
+ .with(setup_file, /heroku apps:info --app #{app_name.dasherize}-production/)
15
+ expect(app_builder).to have_received(:append_file)
16
+ .with(setup_file, /heroku apps:info --app #{app_name.dasherize}-staging/)
17
17
  end
18
18
 
19
19
  it "sets the heroku rails secrets" do
@@ -23,10 +23,10 @@ module Suspenders
23
23
  Heroku.new(app_builder).set_heroku_rails_secrets
24
24
 
25
25
  expect(app_builder).to(
26
- have_configured_var("staging", "SECRET_KEY_BASE"),
26
+ have_configured_var("staging", "SECRET_KEY_BASE")
27
27
  )
28
28
  expect(app_builder).to(
29
- have_configured_var("production", "SECRET_KEY_BASE"),
29
+ have_configured_var("production", "SECRET_KEY_BASE")
30
30
  )
31
31
  end
32
32
 
@@ -47,26 +47,52 @@ module Suspenders
47
47
  Heroku.new(app_builder).set_heroku_application_host
48
48
 
49
49
  expect(app_builder).to(
50
- have_configured_var("staging", "APPLICATION_HOST"),
50
+ have_configured_var("staging", "APPLICATION_HOST")
51
51
  )
52
52
 
53
53
  expect(app_builder).to(
54
- have_configured_var("production", "APPLICATION_HOST"),
54
+ have_configured_var("production", "APPLICATION_HOST")
55
55
  )
56
56
  end
57
57
 
58
+ it "configures nodejs and ruby packs" do
59
+ app_builder = double(app_name: app_name)
60
+ allow(app_builder).to receive(:run)
61
+
62
+ Heroku.new(app_builder).set_heroku_buildpacks
63
+
64
+ %w[staging production].each do |remote|
65
+ expect(app_builder).to(
66
+ have_configured_buildpack(
67
+ remote_name: remote, index: 1, packname: "heroku/nodejs"
68
+ )
69
+ )
70
+ expect(app_builder).to(
71
+ have_configured_buildpack(
72
+ remote_name: remote, index: 2, packname: "heroku/ruby"
73
+ )
74
+ )
75
+ end
76
+ end
77
+
58
78
  def app_name
59
79
  SuspendersTestHelpers::APP_NAME
60
80
  end
61
81
 
62
82
  def have_backup_schedule(remote_name)
63
- have_received(:run).
64
- with(/pg:backups:schedule DATABASE_URL --at '10:00 UTC' --remote #{remote_name}/)
83
+ have_received(:run)
84
+ .with(/pg:backups:schedule DATABASE_URL --at '10:00 UTC' --remote #{remote_name}/)
65
85
  end
66
86
 
67
87
  def have_configured_var(remote_name, var)
68
88
  have_received(:run).with(/config:add #{var}=.+ --remote #{remote_name}/)
69
89
  end
90
+
91
+ def have_configured_buildpack(remote_name:, index:, packname:)
92
+ have_received(:run).with(
93
+ /buildpacks:add --index #{index} #{packname} --remote #{remote_name}/
94
+ )
95
+ end
70
96
  end
71
97
  end
72
98
  end
@@ -0,0 +1,89 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe Suspenders::Actions::ExpandJson do
4
+ let(:destination_root) { File.join(root_path, "tmp") }
5
+ let(:destination_file_name) { "app.json" }
6
+ let(:destination_path) { File.join(destination_root, destination_file_name) }
7
+
8
+ before do
9
+ FileUtils.rm destination_path if File.exist?(destination_path)
10
+ end
11
+
12
+ describe "#invoke!" do
13
+ context "when calling multiple times with the same root key" do
14
+ before do
15
+ described_class.new(
16
+ destination_root,
17
+ destination_file_name,
18
+ env: {
19
+ SMTP_ADDRESS: {required: true}
20
+ }
21
+ ).invoke!
22
+ end
23
+
24
+ it "deep merges the hash" do
25
+ described_class.new(
26
+ destination_root,
27
+ destination_file_name,
28
+ env: {
29
+ HEROKU_APP_NAME: {required: true}
30
+ }
31
+ ).invoke!
32
+
33
+ expected = <<~JSON
34
+ {
35
+ "env": {
36
+ "SMTP_ADDRESS": {
37
+ "required": true
38
+ },
39
+ "HEROKU_APP_NAME": {
40
+ "required": true
41
+ }
42
+ }
43
+ }
44
+ JSON
45
+ expect(existing_json).to eq(expected.chomp)
46
+ end
47
+ end
48
+ end
49
+
50
+ describe "#revoke!" do
51
+ before do
52
+ described_class.new(
53
+ destination_root,
54
+ destination_file_name,
55
+ env: {
56
+ foo: {required: true},
57
+ bar: {required: true}
58
+ }
59
+ ).invoke!
60
+ end
61
+
62
+ it "removes data from the JSON" do
63
+ described_class.new(
64
+ destination_root,
65
+ destination_file_name,
66
+ env: {
67
+ foo: {required: true}
68
+ }
69
+ ).revoke!
70
+
71
+ expected = <<~JSON
72
+ {
73
+ "env": {
74
+ "bar": {
75
+ "required": true
76
+ }
77
+ }
78
+ }
79
+ JSON
80
+ expect(existing_json).to eq(expected.chomp)
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def existing_json
87
+ IO.read(destination_path)
88
+ end
89
+ end
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require File.expand_path(File.join('..', '..', 'support', 'fake_heroku'), File.dirname(__FILE__))
3
+ require File.expand_path(File.join("..", "..", "support", "fake_heroku"), File.dirname(__FILE__))
4
4
 
5
5
  FakeHeroku.new(ARGV).run!
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require File.expand_path(File.join('..', '..', 'support', 'fake_github'), File.dirname(__FILE__))
3
+ require File.expand_path(File.join("..", "..", "support", "fake_github"), File.dirname(__FILE__))
4
4
 
5
5
  FakeGithub.new(ARGV).run!