suspenders 1.53.0 → 1.54.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +4 -3
  4. data/CONTRIBUTING.md +15 -15
  5. data/GOALS.md +65 -0
  6. data/NEWS.md +29 -0
  7. data/README.md +2 -4
  8. data/docs/heroku_deploy.md +2 -5
  9. data/lib/suspenders.rb +6 -0
  10. data/lib/suspenders/actions.rb +2 -2
  11. data/lib/suspenders/adapters/heroku.rb +14 -1
  12. data/lib/suspenders/app_builder.rb +3 -71
  13. data/lib/suspenders/generators/advisories_generator.rb +19 -0
  14. data/lib/suspenders/generators/app_generator.rb +7 -33
  15. data/lib/suspenders/generators/base.rb +39 -0
  16. data/lib/suspenders/generators/ci_generator.rb +19 -9
  17. data/lib/suspenders/generators/db_optimizations_generator.rb +3 -15
  18. data/lib/suspenders/generators/js_driver_generator.rb +1 -2
  19. data/lib/suspenders/generators/preloader_generator.rb +122 -0
  20. data/lib/suspenders/generators/production/compression_generator.rb +14 -0
  21. data/lib/suspenders/generators/production/deployment_generator.rb +1 -12
  22. data/lib/suspenders/generators/production/email_generator.rb +5 -8
  23. data/lib/suspenders/generators/production/manifest_generator.rb +1 -0
  24. data/lib/suspenders/generators/production/single_redirect.rb +15 -0
  25. data/lib/suspenders/generators/production/timeout_generator.rb +1 -0
  26. data/lib/suspenders/generators/profiler_generator.rb +35 -0
  27. data/lib/suspenders/generators/runner_generator.rb +48 -0
  28. data/lib/suspenders/generators/staging/pull_requests_generator.rb +2 -10
  29. data/lib/suspenders/generators/static_generator.rb +4 -0
  30. data/lib/suspenders/generators/stylesheet_base_generator.rb +5 -6
  31. data/lib/suspenders/generators/testing_generator.rb +0 -11
  32. data/lib/suspenders/version.rb +1 -1
  33. data/spec/adapters/heroku_spec.rb +28 -2
  34. data/spec/expand_json_spec.rb +89 -0
  35. data/spec/features/advisories_spec.rb +24 -0
  36. data/spec/features/ci_spec.rb +31 -0
  37. data/spec/features/db_optimizations_spec.rb +19 -0
  38. data/spec/features/heroku_spec.rb +6 -13
  39. data/spec/features/new_project_spec.rb +5 -27
  40. data/spec/features/preloader_spec.rb +25 -0
  41. data/spec/features/production/compression_spec.rb +23 -0
  42. data/spec/features/production/manifest_spec.rb +2 -0
  43. data/spec/features/production/single_redirect_spec.rb +25 -0
  44. data/spec/features/profiler_spec.rb +20 -0
  45. data/spec/features/runner_spec.rb +30 -0
  46. data/spec/features/static_spec.rb +17 -0
  47. data/spec/support/contain_json_matcher.rb +16 -10
  48. data/spec/support/project_files.rb +12 -0
  49. data/spec/support/rails_template.rb +1 -0
  50. data/spec/support/suspenders.rb +16 -13
  51. data/suspenders.gemspec +1 -2
  52. data/templates/Gemfile.erb +1 -6
  53. data/templates/application.scss +0 -1
  54. data/templates/bin_auto_migrate +5 -0
  55. data/templates/bin_deploy +0 -2
  56. data/templates/bin_setup +2 -2
  57. data/templates/bin_setup_review_app.erb +0 -1
  58. data/templates/chromedriver.rb +10 -0
  59. data/templates/descriptions/advisories.md +5 -0
  60. data/templates/descriptions/analytics.md +4 -0
  61. data/templates/descriptions/ci.md +4 -0
  62. data/templates/descriptions/compression.md +4 -0
  63. data/templates/descriptions/db_optimizations.md +2 -0
  64. data/templates/descriptions/deployment.md +5 -0
  65. data/templates/descriptions/email.md +9 -0
  66. data/templates/descriptions/factories.md +12 -0
  67. data/templates/descriptions/force_tls.md +1 -0
  68. data/templates/descriptions/forms.md +1 -0
  69. data/templates/descriptions/inline_svg.md +2 -0
  70. data/templates/descriptions/jobs.md +3 -0
  71. data/templates/descriptions/js_driver.md +4 -0
  72. data/templates/descriptions/json.md +1 -0
  73. data/templates/descriptions/lint.md +3 -0
  74. data/templates/descriptions/manifest.md +2 -0
  75. data/templates/descriptions/preloader.md +3 -0
  76. data/templates/descriptions/profiler.md +7 -0
  77. data/templates/descriptions/pull_requests.md +4 -0
  78. data/templates/descriptions/runner.md +10 -0
  79. data/templates/descriptions/single_redirect.md +1 -0
  80. data/templates/descriptions/static.md +5 -0
  81. data/templates/descriptions/stylelint.md +3 -0
  82. data/templates/descriptions/stylesheet_base.md +4 -0
  83. data/templates/descriptions/testing.md +9 -0
  84. data/templates/descriptions/timeout.md +4 -0
  85. data/templates/descriptions/views.md +8 -0
  86. data/templates/partials/ci_simplecov.rb +16 -0
  87. data/templates/partials/db_optimizations_configuration.rb +7 -0
  88. data/templates/partials/deployment_readme.md +8 -0
  89. data/templates/partials/email_smtp.rb +3 -0
  90. data/templates/partials/profiler_readme.md +8 -0
  91. data/templates/partials/pull_requests_config.rb +5 -0
  92. data/templates/partials/runner_readme.md +31 -0
  93. data/templates/partials/runner_setup.rb +3 -0
  94. data/templates/rack_mini_profiler.rb +2 -0
  95. data/templates/rails_helper.rb +4 -1
  96. data/templates/{dotfiles/.env → sample_env} +0 -1
  97. data/templates/spec_helper.rb +4 -7
  98. data/templates/spring.rb +6 -0
  99. data/templates/suspenders_gitignore +1 -1
  100. metadata +75 -25
  101. data/templates/dotfiles/.ctags +0 -2
  102. data/templates/puma.rb +0 -28
@@ -10,11 +10,50 @@ module Suspenders
10
10
  File.expand_path(File.join("..", "..", "..", "templates"), __dir__)
11
11
  end
12
12
 
13
+ def self.inherited(subclass)
14
+ super
15
+
16
+ description_file = File.expand_path(
17
+ File.join(
18
+ default_source_root,
19
+ "descriptions",
20
+ "#{subclass.generator_name}.md",
21
+ ),
22
+ )
23
+
24
+ subclass.desc File.read(description_file)
25
+ rescue Errno::ENOENT
26
+ end
27
+
13
28
  private
14
29
 
15
30
  def app_name
16
31
  Rails.app_class.module_parent_name.demodulize.underscore.dasherize
17
32
  end
33
+
34
+ def empty_directory_with_keep_file(destination)
35
+ empty_directory(destination, {})
36
+ keep_file(destination)
37
+ end
38
+
39
+ def keep_file(destination)
40
+ create_file(File.join(destination, ".keep"))
41
+ end
42
+
43
+ def append_template_to_file(destination, source, *args)
44
+ partial = File.expand_path(find_in_source_paths(source))
45
+ append_to_file(destination, File.read(partial, *args))
46
+ end
47
+
48
+ def prepend_template_to_file(destination, source, *args)
49
+ partial = File.expand_path(find_in_source_paths(source))
50
+ prepend_to_file(destination, File.read(partial, *args))
51
+ end
52
+
53
+ def inject_template_into_file(destination, source, *args)
54
+ partial = File.expand_path(find_in_source_paths(source))
55
+ inject_into_file(destination, File.read(partial), *args)
56
+ end
18
57
  end
19
58
  end
20
59
  end
@@ -2,21 +2,31 @@ require_relative "base"
2
2
 
3
3
  module Suspenders
4
4
  class CiGenerator < Generators::Base
5
+ def simplecov_gem
6
+ gem "simplecov", require: false, group: [:test]
7
+ Bundler.with_clean_env { run "bundle install" }
8
+ end
9
+
5
10
  def simplecov_test_integration
6
- inject_into_file "spec/spec_helper.rb", before: 'SimpleCov.start "rails"' do
7
- <<-RUBY
11
+ prepend_template_to_file(test_helper_file, "partials/ci_simplecov.rb")
12
+ end
8
13
 
9
- if ENV["CIRCLE_ARTIFACTS"]
10
- dir = File.join(ENV["CIRCLE_ARTIFACTS"], "coverage")
11
- SimpleCov.coverage_dir(dir)
12
- end
14
+ def configure_ci
15
+ template "circle.yml.erb", "circle.yml"
16
+ end
17
+
18
+ private
13
19
 
14
- RUBY
20
+ def test_helper_file
21
+ if using_rspec?
22
+ "spec/spec_helper.rb"
23
+ else
24
+ "test/test_helper.rb"
15
25
  end
16
26
  end
17
27
 
18
- def configure_ci
19
- template "circle.yml.erb", "circle.yml"
28
+ def using_rspec?
29
+ File.exist?("spec/spec_helper.rb")
20
30
  end
21
31
  end
22
32
  end
@@ -8,23 +8,11 @@ module Suspenders
8
8
  end
9
9
 
10
10
  def configure_bullet
11
- inject_into_file(
11
+ inject_template_into_file(
12
12
  "config/environments/development.rb",
13
- configuration,
14
- after: "config.action_mailer.raise_delivery_errors = true\n",
13
+ "partials/db_optimizations_configuration.rb",
14
+ after: /config.action_mailer.raise_delivery_errors = .*/,
15
15
  )
16
16
  end
17
-
18
- private
19
-
20
- def configuration
21
- <<-RUBY
22
- config.after_initialize do
23
- Bullet.enable = true
24
- Bullet.bullet_logger = true
25
- Bullet.rails_logger = true
26
- end
27
- RUBY
28
- end
29
17
  end
30
18
  end
@@ -3,8 +3,7 @@ require_relative "base"
3
3
  module Suspenders
4
4
  class JsDriverGenerator < Generators::Base
5
5
  def add_gems
6
- gem "capybara-selenium", group: :test
7
- gem "chromedriver-helper", group: :test
6
+ gem "webdrivers", group: :test
8
7
  Bundler.with_clean_env { run "bundle install" }
9
8
  end
10
9
 
@@ -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_clean_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_clean_env do
35
+ @base.send(:always_run, "spring binstub --all")
36
+ end
37
+ end
38
+
39
+ def revoke!
40
+ Bundler.with_clean_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,14 +11,11 @@ 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,
21
- after: "config.action_mailer.perform_caching = false"
14
+ inject_template_into_file(
15
+ "config/environments/production.rb",
16
+ "partials/email_smtp.rb",
17
+ after: "config.action_mailer.perform_caching = false",
18
+ )
22
19
  end
23
20
 
24
21
  def env_vars
@@ -10,6 +10,7 @@ module Suspenders
10
10
  scripts: {},
11
11
  env: {
12
12
  APPLICATION_HOST: { required: true },
13
+ AUTO_MIGRATE_DB: { value: true },
13
14
  EMAIL_RECIPIENTS: { required: true },
14
15
  HEROKU_APP_NAME: { required: true },
15
16
  HEROKU_PARENT_APP_NAME: { required: true },
@@ -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,6 +5,7 @@ module Suspenders
5
5
  class TimeoutGenerator < Generators::Base
6
6
  def add_gem
7
7
  gem "rack-timeout", group: :production
8
+ Bundler.with_clean_env { run "bundle install" }
8
9
  end
9
10
 
10
11
  def configure_rack_timeout
@@ -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 ".env", "RACK_MINI_PROFILER=0\n"
7
+ rescue Errno::ENOENT
8
+ create_file ".env", "RACK_MINI_PROFILER=0\n"
9
+ rescue Thor::Error => e
10
+ if e.message.match?(/does not appear to exist/)
11
+ create_file ".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_clean_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,17 +4,9 @@ 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,
9
+ "partials/pull_requests_config.rb",
18
10
  after: "Rails.application.configure do\n",
19
11
  )
20
12
  end