suspenders 1.51.0 → 1.55.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +6 -3
  4. data/CONTRIBUTING.md +15 -15
  5. data/GOALS.md +65 -0
  6. data/Gemfile +1 -1
  7. data/NEWS.md +52 -0
  8. data/README.md +5 -4
  9. data/Rakefile +6 -5
  10. data/docs/heroku_deploy.md +19 -0
  11. data/lib/suspenders.rb +7 -0
  12. data/lib/suspenders/actions.rb +2 -2
  13. data/lib/suspenders/adapters/heroku.rb +27 -14
  14. data/lib/suspenders/app_builder.rb +42 -111
  15. data/lib/suspenders/generators/advisories_generator.rb +15 -0
  16. data/lib/suspenders/generators/analytics_generator.rb +1 -1
  17. data/lib/suspenders/generators/app_generator.rb +36 -58
  18. data/lib/suspenders/generators/base.rb +40 -1
  19. data/lib/suspenders/generators/ci_generator.rb +19 -9
  20. data/lib/suspenders/generators/db_optimizations_generator.rb +5 -17
  21. data/lib/suspenders/generators/factories_generator.rb +2 -2
  22. data/lib/suspenders/generators/forms_generator.rb +1 -1
  23. data/lib/suspenders/generators/inline_svg_generator.rb +14 -0
  24. data/lib/suspenders/generators/jobs_generator.rb +4 -4
  25. data/lib/suspenders/generators/js_driver_generator.rb +7 -4
  26. data/lib/suspenders/generators/json_generator.rb +1 -1
  27. data/lib/suspenders/generators/lint_generator.rb +5 -0
  28. data/lib/suspenders/generators/preloader_generator.rb +122 -0
  29. data/lib/suspenders/generators/production/compression_generator.rb +14 -0
  30. data/lib/suspenders/generators/production/deployment_generator.rb +1 -12
  31. data/lib/suspenders/generators/production/email_generator.rb +9 -12
  32. data/lib/suspenders/generators/production/manifest_generator.rb +8 -7
  33. data/lib/suspenders/generators/production/single_redirect.rb +15 -0
  34. data/lib/suspenders/generators/production/timeout_generator.rb +3 -2
  35. data/lib/suspenders/generators/profiler_generator.rb +35 -0
  36. data/lib/suspenders/generators/runner_generator.rb +48 -0
  37. data/lib/suspenders/generators/staging/pull_requests_generator.rb +4 -12
  38. data/lib/suspenders/generators/static_generator.rb +5 -1
  39. data/lib/suspenders/generators/stylelint_generator.rb +2 -1
  40. data/lib/suspenders/generators/stylesheet_base_generator.rb +7 -8
  41. data/lib/suspenders/generators/testing_generator.rb +6 -17
  42. data/lib/suspenders/version.rb +6 -6
  43. data/spec/adapters/heroku_spec.rb +36 -10
  44. data/spec/expand_json_spec.rb +89 -0
  45. data/spec/fakes/bin/heroku +1 -1
  46. data/spec/fakes/bin/hub +1 -1
  47. data/spec/features/advisories_spec.rb +24 -0
  48. data/spec/features/api_spec.rb +1 -1
  49. data/spec/features/ci_spec.rb +31 -0
  50. data/spec/features/cli_help_spec.rb +4 -4
  51. data/spec/features/db_optimizations_spec.rb +19 -0
  52. data/spec/features/heroku_spec.rb +9 -16
  53. data/spec/features/inline_svg_spec.rb +10 -0
  54. data/spec/features/lint_spec.rb +26 -0
  55. data/spec/features/new_project_spec.rb +38 -60
  56. data/spec/features/preloader_spec.rb +25 -0
  57. data/spec/features/production/compression_spec.rb +23 -0
  58. data/spec/features/production/email_spec.rb +10 -10
  59. data/spec/features/production/manifest_spec.rb +16 -14
  60. data/spec/features/production/single_redirect_spec.rb +25 -0
  61. data/spec/features/profiler_spec.rb +20 -0
  62. data/spec/features/runner_spec.rb +30 -0
  63. data/spec/features/staging/pull_requests_spec.rb +1 -1
  64. data/spec/features/static_spec.rb +17 -0
  65. data/spec/features/stylelint_spec.rb +7 -7
  66. data/spec/spec_helper.rb +3 -3
  67. data/spec/support/contain_json_matcher.rb +16 -10
  68. data/spec/support/fake_github.rb +3 -3
  69. data/spec/support/fake_heroku.rb +8 -8
  70. data/spec/support/project_files.rb +12 -0
  71. data/spec/support/rails_template.rb +1 -0
  72. data/spec/support/suspenders.rb +21 -19
  73. data/suspenders.gemspec +20 -20
  74. data/templates/Gemfile.erb +2 -10
  75. data/templates/_javascript.html.erb +1 -1
  76. data/templates/application.scss +0 -1
  77. data/templates/bin_auto_migrate +5 -0
  78. data/templates/bin_deploy +0 -2
  79. data/templates/bin_setup +2 -2
  80. data/templates/bin_setup_review_app.erb +0 -1
  81. data/templates/capybara_silence_puma.rb +1 -0
  82. data/templates/chromedriver.rb +14 -4
  83. data/templates/descriptions/advisories.md +5 -0
  84. data/templates/descriptions/analytics.md +4 -0
  85. data/templates/descriptions/ci.md +4 -0
  86. data/templates/descriptions/compression.md +4 -0
  87. data/templates/descriptions/db_optimizations.md +2 -0
  88. data/templates/descriptions/deployment.md +5 -0
  89. data/templates/descriptions/email.md +9 -0
  90. data/templates/descriptions/factories.md +12 -0
  91. data/templates/descriptions/force_tls.md +1 -0
  92. data/templates/descriptions/forms.md +1 -0
  93. data/templates/descriptions/inline_svg.md +2 -0
  94. data/templates/descriptions/jobs.md +3 -0
  95. data/templates/descriptions/js_driver.md +4 -0
  96. data/templates/descriptions/json.md +1 -0
  97. data/templates/descriptions/lint.md +3 -0
  98. data/templates/descriptions/manifest.md +2 -0
  99. data/templates/descriptions/preloader.md +3 -0
  100. data/templates/descriptions/profiler.md +7 -0
  101. data/templates/descriptions/pull_requests.md +4 -0
  102. data/templates/descriptions/runner.md +10 -0
  103. data/templates/descriptions/single_redirect.md +1 -0
  104. data/templates/descriptions/static.md +5 -0
  105. data/templates/descriptions/stylelint.md +3 -0
  106. data/templates/descriptions/stylesheet_base.md +4 -0
  107. data/templates/descriptions/testing.md +9 -0
  108. data/templates/descriptions/timeout.md +4 -0
  109. data/templates/descriptions/views.md +8 -0
  110. data/templates/errors.rb +3 -3
  111. data/templates/hound.yml +1 -2
  112. data/templates/inline_svg.rb +3 -0
  113. data/templates/partials/ci_simplecov.rb +14 -0
  114. data/templates/partials/db_optimizations_configuration.rb +5 -0
  115. data/templates/partials/deployment_readme.md +8 -0
  116. data/templates/partials/email_smtp.rb +2 -0
  117. data/templates/partials/profiler_readme.md +8 -0
  118. data/templates/partials/pull_requests_config.rb +4 -0
  119. data/templates/partials/runner_readme.md +31 -0
  120. data/templates/partials/runner_setup.rb +2 -0
  121. data/templates/rack_mini_profiler.rb +2 -0
  122. data/templates/rails_helper.rb +7 -4
  123. data/templates/{dotfiles/.env → sample_env} +0 -2
  124. data/templates/spec_helper.rb +4 -7
  125. data/templates/spring.rb +6 -0
  126. data/templates/suspenders_gitignore +1 -1
  127. metadata +99 -14
  128. data/templates/browserslist +0 -3
  129. data/templates/dotfiles/.ctags +0 -2
  130. data/templates/puma.rb +0 -28
@@ -4,7 +4,7 @@ module Suspenders
4
4
  class JsonGenerator < Generators::Base
5
5
  def add_oj
6
6
  gem "oj"
7
- Bundler.with_clean_env { run "bundle install" }
7
+ Bundler.with_unbundled_env { run "bundle install" }
8
8
  end
9
9
  end
10
10
  end
@@ -5,5 +5,10 @@ module Suspenders
5
5
  def set_up_hound
6
6
  copy_file "hound.yml", ".hound.yml"
7
7
  end
8
+
9
+ def set_up_standard
10
+ gem "standard", group: :development
11
+ prepend_to_file("Rakefile", 'require "standard/rake"')
12
+ end
8
13
  end
9
14
  end
@@ -0,0 +1,122 @@
1
+ require_relative "base"
2
+
3
+ module Suspenders
4
+ class PreloaderGenerator < Generators::Base
5
+ def spring_config
6
+ template "spring.rb", "config/spring.rb", force: false, skip: true
7
+ end
8
+
9
+ def spring_gem
10
+ always_gsub_file("Gemfile", /# Spring speeds up development.*/, "")
11
+
12
+ gem_group :development do
13
+ gem "spring"
14
+ gem "spring-watcher-listen", "~> 2.0.0"
15
+ end
16
+
17
+ Bundler.with_unbundled_env { run "bundle install" }
18
+ end
19
+
20
+ def cache_classes_test
21
+ action UncacheClasses.new(self, "config/environments/test.rb")
22
+ end
23
+
24
+ def spring_binstubs
25
+ action SpringBinstubs.new(self)
26
+ end
27
+
28
+ class SpringBinstubs
29
+ def initialize(base)
30
+ @base = base
31
+ end
32
+
33
+ def invoke!
34
+ Bundler.with_unbundled_env do
35
+ @base.send(:always_run, "spring binstub --all")
36
+ end
37
+ end
38
+
39
+ def revoke!
40
+ Bundler.with_unbundled_env do
41
+ @base.send(:always_run, "spring binstub --remove --all")
42
+ end
43
+ end
44
+ end
45
+
46
+ class UncacheClasses
47
+ def initialize(base, config_file)
48
+ @base = base
49
+ @config_file = config_file
50
+ end
51
+
52
+ def invoke!
53
+ @base.send(
54
+ :always_gsub_file,
55
+ @config_file,
56
+ "config.cache_classes = true",
57
+ "config.cache_classes = false"
58
+ )
59
+ end
60
+
61
+ def revoke!
62
+ @base.send(
63
+ :always_gsub_file,
64
+ @config_file,
65
+ "config.cache_classes = false",
66
+ "config.cache_classes = true"
67
+ )
68
+ end
69
+ end
70
+
71
+ protected
72
+
73
+ def always_run(command, with: nil, verbose: true, env: nil, capture: nil,
74
+ abort_on_failure: nil)
75
+ destination = relative_to_original_destination_root(
76
+ destination_root,
77
+ false
78
+ )
79
+ desc = "#{command} from #{destination.inspect}"
80
+
81
+ if with
82
+ desc = "#{File.basename(with.to_s)} #{desc}"
83
+ command = "#{with} #{command}"
84
+ end
85
+
86
+ say_status :run, desc, verbose
87
+
88
+ return if options[:pretend]
89
+
90
+ env_splat = [env] if env
91
+
92
+ if capture
93
+ result, status = Open3.capture2e(*env_splat, command.to_s)
94
+ success = status.success?
95
+ else
96
+ result = system(*env_splat, command.to_s)
97
+ success = result
98
+ end
99
+
100
+ if abort_on_failure.nil?
101
+ abort_on_failure = self.class.send(:exit_on_failure?)
102
+ end
103
+
104
+ if !success && abort_on_failure
105
+ abort
106
+ end
107
+
108
+ result
109
+ end
110
+
111
+ def always_gsub_file(path, flag, replacement, verbose: true)
112
+ path = File.expand_path(path, destination_root)
113
+ say_status :gsub, relative_to_original_destination_root(path), verbose
114
+
115
+ unless options[:pretend]
116
+ content = File.binread(path)
117
+ content.gsub!(flag, replacement)
118
+ File.open(path, "wb") { |file| file.write(content) }
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,14 @@
1
+ require_relative "../base"
2
+
3
+ module Suspenders
4
+ module Production
5
+ class CompressionGenerator < Generators::Base
6
+ def add_rack_deflater
7
+ configure_environment(
8
+ :production,
9
+ %(config.middleware.use Rack::Deflater)
10
+ )
11
+ end
12
+ end
13
+ end
14
+ end
@@ -9,18 +9,7 @@ module Suspenders
9
9
  end
10
10
 
11
11
  def inform_user
12
- instructions = <<~MARKDOWN
13
-
14
- ## Deploying
15
-
16
- If you have previously run the `./bin/setup` script,
17
- you can deploy to staging and production with:
18
-
19
- % ./bin/deploy staging
20
- % ./bin/deploy production
21
- MARKDOWN
22
-
23
- append_file "README.md", instructions
12
+ append_template_to_file "README.md", "partials/deployment_readme.md"
24
13
  end
25
14
  end
26
15
  end
@@ -11,25 +11,22 @@ module Suspenders
11
11
  end
12
12
 
13
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,
14
+ inject_template_into_file(
15
+ "config/environments/production.rb",
16
+ "partials/email_smtp.rb",
21
17
  after: "config.action_mailer.perform_caching = false"
18
+ )
22
19
  end
23
20
 
24
21
  def env_vars
25
22
  expand_json(
26
23
  "app.json",
27
24
  env: {
28
- SMTP_ADDRESS: { required: true },
29
- SMTP_DOMAIN: { required: true },
30
- SMTP_PASSWORD: { required: true },
31
- SMTP_USERNAME: { required: true },
32
- },
25
+ SMTP_ADDRESS: {required: true},
26
+ SMTP_DOMAIN: {required: true},
27
+ SMTP_PASSWORD: {required: true},
28
+ SMTP_USERNAME: {required: true}
29
+ }
33
30
  )
34
31
  end
35
32
  end
@@ -9,14 +9,15 @@ module Suspenders
9
9
  name: app_name.dasherize,
10
10
  scripts: {},
11
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" },
12
+ APPLICATION_HOST: {required: true},
13
+ AUTO_MIGRATE_DB: {value: true},
14
+ EMAIL_RECIPIENTS: {required: true},
15
+ HEROKU_APP_NAME: {required: true},
16
+ HEROKU_PARENT_APP_NAME: {required: true},
17
+ RACK_ENV: {required: true},
18
+ SECRET_KEY_BASE: {generator: "secret"}
18
19
  },
19
- addons: ["heroku-postgresql"],
20
+ addons: ["heroku-postgresql"]
20
21
  )
21
22
  end
22
23
  end
@@ -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