suspenders 1.53.0 → 1.54.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +4 -3
- data/CONTRIBUTING.md +15 -15
- data/GOALS.md +65 -0
- data/NEWS.md +29 -0
- data/README.md +2 -4
- data/docs/heroku_deploy.md +2 -5
- data/lib/suspenders.rb +6 -0
- data/lib/suspenders/actions.rb +2 -2
- data/lib/suspenders/adapters/heroku.rb +14 -1
- data/lib/suspenders/app_builder.rb +3 -71
- data/lib/suspenders/generators/advisories_generator.rb +19 -0
- data/lib/suspenders/generators/app_generator.rb +7 -33
- data/lib/suspenders/generators/base.rb +39 -0
- data/lib/suspenders/generators/ci_generator.rb +19 -9
- data/lib/suspenders/generators/db_optimizations_generator.rb +3 -15
- data/lib/suspenders/generators/js_driver_generator.rb +1 -2
- data/lib/suspenders/generators/preloader_generator.rb +122 -0
- data/lib/suspenders/generators/production/compression_generator.rb +14 -0
- data/lib/suspenders/generators/production/deployment_generator.rb +1 -12
- data/lib/suspenders/generators/production/email_generator.rb +5 -8
- data/lib/suspenders/generators/production/manifest_generator.rb +1 -0
- data/lib/suspenders/generators/production/single_redirect.rb +15 -0
- data/lib/suspenders/generators/production/timeout_generator.rb +1 -0
- data/lib/suspenders/generators/profiler_generator.rb +35 -0
- data/lib/suspenders/generators/runner_generator.rb +48 -0
- data/lib/suspenders/generators/staging/pull_requests_generator.rb +2 -10
- data/lib/suspenders/generators/static_generator.rb +4 -0
- data/lib/suspenders/generators/stylesheet_base_generator.rb +5 -6
- data/lib/suspenders/generators/testing_generator.rb +0 -11
- data/lib/suspenders/version.rb +1 -1
- data/spec/adapters/heroku_spec.rb +28 -2
- data/spec/expand_json_spec.rb +89 -0
- data/spec/features/advisories_spec.rb +24 -0
- data/spec/features/ci_spec.rb +31 -0
- data/spec/features/db_optimizations_spec.rb +19 -0
- data/spec/features/heroku_spec.rb +6 -13
- data/spec/features/new_project_spec.rb +5 -27
- data/spec/features/preloader_spec.rb +25 -0
- data/spec/features/production/compression_spec.rb +23 -0
- data/spec/features/production/manifest_spec.rb +2 -0
- data/spec/features/production/single_redirect_spec.rb +25 -0
- data/spec/features/profiler_spec.rb +20 -0
- data/spec/features/runner_spec.rb +30 -0
- data/spec/features/static_spec.rb +17 -0
- data/spec/support/contain_json_matcher.rb +16 -10
- data/spec/support/project_files.rb +12 -0
- data/spec/support/rails_template.rb +1 -0
- data/spec/support/suspenders.rb +16 -13
- data/suspenders.gemspec +1 -2
- data/templates/Gemfile.erb +1 -6
- data/templates/application.scss +0 -1
- data/templates/bin_auto_migrate +5 -0
- data/templates/bin_deploy +0 -2
- data/templates/bin_setup +2 -2
- data/templates/bin_setup_review_app.erb +0 -1
- data/templates/chromedriver.rb +10 -0
- data/templates/descriptions/advisories.md +5 -0
- data/templates/descriptions/analytics.md +4 -0
- data/templates/descriptions/ci.md +4 -0
- data/templates/descriptions/compression.md +4 -0
- data/templates/descriptions/db_optimizations.md +2 -0
- data/templates/descriptions/deployment.md +5 -0
- data/templates/descriptions/email.md +9 -0
- data/templates/descriptions/factories.md +12 -0
- data/templates/descriptions/force_tls.md +1 -0
- data/templates/descriptions/forms.md +1 -0
- data/templates/descriptions/inline_svg.md +2 -0
- data/templates/descriptions/jobs.md +3 -0
- data/templates/descriptions/js_driver.md +4 -0
- data/templates/descriptions/json.md +1 -0
- data/templates/descriptions/lint.md +3 -0
- data/templates/descriptions/manifest.md +2 -0
- data/templates/descriptions/preloader.md +3 -0
- data/templates/descriptions/profiler.md +7 -0
- data/templates/descriptions/pull_requests.md +4 -0
- data/templates/descriptions/runner.md +10 -0
- data/templates/descriptions/single_redirect.md +1 -0
- data/templates/descriptions/static.md +5 -0
- data/templates/descriptions/stylelint.md +3 -0
- data/templates/descriptions/stylesheet_base.md +4 -0
- data/templates/descriptions/testing.md +9 -0
- data/templates/descriptions/timeout.md +4 -0
- data/templates/descriptions/views.md +8 -0
- data/templates/partials/ci_simplecov.rb +16 -0
- data/templates/partials/db_optimizations_configuration.rb +7 -0
- data/templates/partials/deployment_readme.md +8 -0
- data/templates/partials/email_smtp.rb +3 -0
- data/templates/partials/profiler_readme.md +8 -0
- data/templates/partials/pull_requests_config.rb +5 -0
- data/templates/partials/runner_readme.md +31 -0
- data/templates/partials/runner_setup.rb +3 -0
- data/templates/rack_mini_profiler.rb +2 -0
- data/templates/rails_helper.rb +4 -1
- data/templates/{dotfiles/.env → sample_env} +0 -1
- data/templates/spec_helper.rb +4 -7
- data/templates/spring.rb +6 -0
- data/templates/suspenders_gitignore +1 -1
- metadata +75 -25
- data/templates/dotfiles/.ctags +0 -2
- 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
|
-
|
7
|
-
|
11
|
+
prepend_template_to_file(test_helper_file, "partials/ci_simplecov.rb")
|
12
|
+
end
|
8
13
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
14
|
+
def configure_ci
|
15
|
+
template "circle.yml.erb", "circle.yml"
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
13
19
|
|
14
|
-
|
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
|
19
|
-
|
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
|
-
|
11
|
+
inject_template_into_file(
|
12
12
|
"config/environments/development.rb",
|
13
|
-
|
14
|
-
after:
|
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 "
|
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
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
@@ -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
|
@@ -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
|
-
|
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
|
-
|
9
|
+
"partials/pull_requests_config.rb",
|
18
10
|
after: "Rails.application.configure do\n",
|
19
11
|
)
|
20
12
|
end
|