welaika-suspenders 2.33 → 2.34

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/.travis.yml +4 -2
  4. data/CHANGELOG_WELAIKA.md +15 -0
  5. data/NEWS.md +8 -0
  6. data/README.md +12 -16
  7. data/docs/heroku_deploy.md +22 -0
  8. data/lib/suspenders.rb +2 -0
  9. data/lib/suspenders/actions.rb +2 -2
  10. data/lib/suspenders/app_builder.rb +19 -11
  11. data/lib/suspenders/generators/app_generator.rb +9 -8
  12. data/lib/suspenders/generators/factories_generator.rb +1 -1
  13. data/lib/suspenders/generators/inline_svg_generator.rb +14 -0
  14. data/lib/suspenders/generators/jobs_generator.rb +53 -0
  15. data/lib/suspenders/generators/production/email_generator.rb +1 -1
  16. data/lib/suspenders/generators/production/error_reporting_generator.rb +6 -6
  17. data/lib/suspenders/generators/production/manifest_generator.rb +2 -1
  18. data/lib/suspenders/generators/production/timeout_generator.rb +1 -0
  19. data/lib/suspenders/generators/staging/pull_requests_generator.rb +3 -3
  20. data/lib/suspenders/generators/stylelint_generator.rb +11 -8
  21. data/lib/suspenders/version.rb +2 -2
  22. data/spec/expand_json_spec.rb +89 -0
  23. data/spec/features/inline_svg_spec.rb +10 -0
  24. data/spec/features/new_project_spec.rb +7 -28
  25. data/spec/features/production/email_spec.rb +1 -1
  26. data/spec/features/production/manifest_spec.rb +2 -0
  27. data/suspenders.gemspec +5 -0
  28. data/templates/Dockerfile.gitlab.erb +1 -1
  29. data/templates/Gemfile.erb +3 -2
  30. data/templates/Procfile +0 -1
  31. data/templates/README.md.erb +12 -6
  32. data/templates/bin_auto_migrate +5 -0
  33. data/templates/bin_deploy +0 -2
  34. data/templates/bin_setup_review_app.erb +0 -1
  35. data/templates/config_sidekiq.yml +14 -0
  36. data/templates/dotfiles/.env +0 -1
  37. data/templates/email.rb +2 -2
  38. data/templates/factory_bot_rspec.rb +1 -1
  39. data/templates/gitlab-ci.yml.erb +50 -50
  40. data/templates/inline_svg.rb +3 -0
  41. data/templates/rack_mini_profiler.rb +2 -2
  42. data/templates/rubocop.yml +11 -1
  43. data/templates/smtp.rb +7 -7
  44. data/templates/spec_support_sidekiq.rb +4 -0
  45. metadata +32 -6
  46. data/templates/browserslist +0 -1
@@ -6,9 +6,9 @@ module Suspenders
6
6
  def configure_heroku_staging_pr_pipeline_host
7
7
  config = <<-RUBY
8
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"
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
12
  end
13
13
  RUBY
14
14
 
@@ -3,11 +3,13 @@ require_relative "base"
3
3
  module Suspenders
4
4
  class StylelintGenerator < Generators::Base
5
5
  def install_stylelint
6
- dependencies = ["stylelint",
7
- "stylelint-config-recommended",
8
- "stylelint-order",
9
- "stylelint-declaration-block-no-ignored-properties",
10
- "stylelint-scss"]
6
+ dependencies = [
7
+ { name: "stylelint", version: '10' }, # FIXME: config-recommened is not ready for stylelint 11 yet
8
+ { name: "stylelint-config-recommended" },
9
+ { name: "stylelint-order" },
10
+ { name: "stylelint-declaration-block-no-ignored-properties" },
11
+ { name: "stylelint-scss" }
12
+ ]
11
13
  action YarnInstall.new(self, dependencies, "--dev")
12
14
  end
13
15
 
@@ -18,17 +20,18 @@ module Suspenders
18
20
  class YarnInstall
19
21
  def initialize(base, dependencies, flags)
20
22
  @base = base
21
- @dependencies = dependencies.join(" ")
23
+ @dependencies = dependencies
22
24
  @flags = flags
23
25
  end
24
26
 
25
27
  def invoke!
26
- @base.run "bin/yarn add #{@dependencies} #{@flags}"
28
+ dependencies_with_versions = @dependencies.map { |d| d[:version] ? "#{d[:name]}@#{d[:version]}" : d[:name] }.join(' ')
29
+ @base.run "bin/yarn add #{dependencies_with_versions} #{@flags}"
27
30
  end
28
31
 
29
32
  def revoke!
30
33
  @base.behavior = :invoke
31
- @base.run "bin/yarn remove #{@dependencies}"
34
+ @base.run "bin/yarn remove #{@dependencies.map { |d| d[:name] }.join(' ')}"
32
35
  ensure
33
36
  @base.behavior = :revoke
34
37
  end
@@ -1,9 +1,9 @@
1
1
  module Suspenders
2
2
  RAILS_VERSION = "~> 5.2.3".freeze
3
- POSTGRES_VERSION = "10.6".freeze # Used in CI
3
+ POSTGRES_VERSION = "11".freeze # Used in CI
4
4
  RUBY_VERSION = IO.
5
5
  read("#{File.dirname(__FILE__)}/../../.ruby-version").
6
6
  strip.
7
7
  freeze
8
- VERSION = "2.33".freeze
8
+ VERSION = "2.34".freeze
9
9
  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
@@ -0,0 +1,10 @@
1
+ require "spec_helper"
2
+
3
+ RSpec.describe "suspenders:inline_svg", type: :generator do
4
+ it "generates the configuration for inline_svg" do
5
+ with_app { generate("suspenders:inline_svg") }
6
+
7
+ expect("config/initializers/inline_svg.rb").to match_contents(/InlineSvg/)
8
+ expect("Gemfile").to match_contents(/inline_svg/)
9
+ end
10
+ end
@@ -13,9 +13,6 @@ RSpec.describe "Suspend a new project with default configuration" do
13
13
  expect(gemfile_file).to match(
14
14
  /^ruby '#{Suspenders::RUBY_VERSION}'$/,
15
15
  )
16
- expect(gemfile_file).to match(
17
- /^gem 'active_interaction'/,
18
- )
19
16
  expect(gemfile_file).to match(
20
17
  /^gem 'rails', '#{Suspenders::RAILS_VERSION}'$/,
21
18
  )
@@ -96,7 +93,7 @@ RSpec.describe "Suspend a new project with default configuration" do
96
93
  it "configs locale and timezone" do
97
94
  result = IO.read("#{project_path}/config/application.rb")
98
95
 
99
- expect(result).to match(/^ +config.i18n.available_locales = \[:en, :it\]$/)
96
+ expect(result).to match(/^ +config.i18n.available_locales = %i\[en it\]$/)
100
97
  expect(result).to match(/^ +config.i18n.default_locale = :it$/)
101
98
 
102
99
  expect(result).to match(/^ +config.time_zone = 'Rome'$/)
@@ -118,7 +115,7 @@ RSpec.describe "Suspend a new project with default configuration" do
118
115
 
119
116
  it "configures public_file_server.headers in production" do
120
117
  expect(production_config).to match(
121
- /^ +config.public_file_server.headers = {\n +"Cache-Control" => "public,/,
118
+ /^ +config.public_file_server.headers = {\n +'Cache-Control' => 'public,/,
122
119
  )
123
120
  end
124
121
 
@@ -156,13 +153,13 @@ RSpec.describe "Suspend a new project with default configuration" do
156
153
  it "sets action mailer default host and asset host" do
157
154
  config_key = 'config\.action_mailer\.asset_host'
158
155
  config_value =
159
- %q{ENV\.fetch\("ASSET_HOST", ENV\.fetch\("APPLICATION_HOST"\)\)}
156
+ %q{ENV\.fetch\('ASSET_HOST', ENV\.fetch\('APPLICATION_HOST'\)\)}
160
157
  expect(production_config).to match(/#{config_key} = #{config_value}/)
161
158
  end
162
159
 
163
160
  it "uses APPLICATION_HOST, not HOST in the production config" do
164
- expect(production_config).to match(/"APPLICATION_HOST"/)
165
- expect(production_config).not_to match(/"HOST"/)
161
+ expect(production_config).to match(/'APPLICATION_HOST'/)
162
+ expect(production_config).not_to match(/'HOST'/)
166
163
  end
167
164
 
168
165
  it "configures email interceptor" do
@@ -170,7 +167,7 @@ RSpec.describe "Suspend a new project with default configuration" do
170
167
  email_config = IO.read(email_file)
171
168
 
172
169
  expect(email_config).
173
- to include(%{RecipientInterceptor.new(ENV["EMAIL_RECIPIENTS"], subject_prefix: '[STAGING]'})
170
+ to include(%{RecipientInterceptor.new(ENV['EMAIL_RECIPIENTS'], subject_prefix: '[STAGING]'})
174
171
  end
175
172
 
176
173
  it "configs bullet gem in development" do
@@ -201,30 +198,12 @@ RSpec.describe "Suspend a new project with default configuration" do
201
198
  expect(File).to exist("#{project_path}/bin/overcommit")
202
199
  end
203
200
 
204
- it "removes comments and extra newlines from config files" do
205
- config_files = [
206
- IO.read("#{project_path}/config/application.rb"),
207
- IO.read("#{project_path}/config/environment.rb"),
208
- development_config,
209
- test_config,
210
- production_config,
211
- ]
212
-
213
- config_files.each do |file|
214
- expect(file).not_to match(%r{.*#.*})
215
- expect(file).not_to eq(file.strip)
216
- expect(file).not_to match(%r{^$\n\n})
217
- end
218
- end
219
-
220
201
  it "creates review apps setup script" do
221
202
  bin_setup_path = "#{project_path}/bin/setup_review_app"
222
203
  bin_setup = IO.read(bin_setup_path)
223
204
 
224
205
  expect(bin_setup).to include("PARENT_APP_NAME=#{app_name.dasherize}-staging")
225
206
  expect(bin_setup).to include("APP_NAME=#{app_name.dasherize}-staging-pr-$1")
226
- expect(bin_setup).
227
- to include("heroku run rails db:migrate --exit-code --app $APP_NAME")
228
207
  expect(bin_setup).to include("heroku ps:scale worker=1 --app $APP_NAME")
229
208
  expect(bin_setup).to include("heroku restart --app $APP_NAME")
230
209
 
@@ -235,7 +214,7 @@ RSpec.describe "Suspend a new project with default configuration" do
235
214
  bin_deploy_path = "#{project_path}/bin/deploy"
236
215
  bin_deploy = IO.read(bin_deploy_path)
237
216
 
238
- expect(bin_deploy).to include("heroku run rails db:migrate --exit-code")
217
+ expect(bin_deploy).to include('git push $force "$target" "$branch:master"')
239
218
  expect("bin/deploy").to be_executable
240
219
  end
241
220
 
@@ -7,7 +7,7 @@ RSpec.describe "suspenders:production:email", type: :generator do
7
7
  expect("config/smtp.rb").to match_contents(%r{SMTP_SETTINGS\s*=})
8
8
 
9
9
  expect("config/environments/production.rb").to \
10
- match_contents(%r{require.+config/smtp})
10
+ match_contents(%r{require.+'config', 'smtp'})
11
11
  expect("config/environments/production.rb").to \
12
12
  match_contents(%r{action_mailer.delivery_method\s*=\s*:smtp})
13
13
  expect("config/environments/production.rb").to \
@@ -8,6 +8,7 @@ RSpec.describe "suspenders:production:manifest", type: :generator do
8
8
  name: SuspendersTestHelpers::APP_NAME.dasherize,
9
9
  env: {
10
10
  APPLICATION_HOST: { required: true },
11
+ AUTO_MIGRATE_DB: { value: "true" },
11
12
  EMAIL_RECIPIENTS: { required: true },
12
13
  HEROKU_APP_NAME: { required: true },
13
14
  HEROKU_PARENT_APP_NAME: { required: true },
@@ -24,6 +25,7 @@ RSpec.describe "suspenders:production:manifest", type: :generator do
24
25
  name: SuspendersTestHelpers::APP_NAME.dasherize,
25
26
  env: {
26
27
  APPLICATION_HOST: { required: true },
28
+ AUTO_MIGRATE_DB: { value: "true" },
27
29
  EMAIL_RECIPIENTS: { required: true },
28
30
  HEROKU_APP_NAME: { required: true },
29
31
  HEROKU_PARENT_APP_NAME: { required: true },
data/suspenders.gemspec CHANGED
@@ -18,6 +18,10 @@ weLaika's fork of the famous thoughbot suspenders gem.
18
18
  s.extra_rdoc_files = %w[README.md LICENSE]
19
19
  s.files = `git ls-files`.split("\n")
20
20
  s.homepage = 'http://github.com/welaika/welaika-suspenders'
21
+ s.metadata = {
22
+ 'changelog_uri' => 'https://github.com/welaika/welaika-suspenders/blob/master/CHANGELOG_WELAIKA.md',
23
+ 'source_code_uri' => 'https://github.com/welaika/welaika-suspenders'
24
+ }
21
25
  s.license = 'MIT'
22
26
  s.name = 'welaika-suspenders'
23
27
  s.rdoc_options = ['--charset=UTF-8']
@@ -27,6 +31,7 @@ weLaika's fork of the famous thoughbot suspenders gem.
27
31
  s.version = Suspenders::VERSION
28
32
 
29
33
  s.add_dependency 'rails', Suspenders::RAILS_VERSION
34
+ s.add_dependency 'bundler', '>= 2'
30
35
 
31
36
  s.add_development_dependency 'rspec', '~> 3.8'
32
37
  s.add_development_dependency 'pry-byebug', '~> 3.6'
@@ -48,7 +48,7 @@ RUN curl -sS -O https://dl.google.com/linux/direct/google-chrome-stable_current_
48
48
  && apt-get clean \
49
49
  && rm -rf /var/lib/apt/lists/*
50
50
 
51
- RUN gem install bundler --version '< 2' \
51
+ RUN gem install bundler --version '>= 2' \
52
52
  && bundle config git.allow_insecure true \
53
53
  && bundle config silence_root_warning true \
54
54
  && bundle config jobs "$(nproc)"
@@ -9,13 +9,13 @@ end
9
9
 
10
10
  ruby '<%= Suspenders::RUBY_VERSION %>'
11
11
 
12
- gem 'active_interaction', '~> 3.7'
13
12
  <% unless options[:api] %>
14
13
  gem 'autoprefixer-rails'
15
14
  <% end %>
16
15
  gem 'bootsnap', '>= 1.1.0', require: false
17
16
  gem 'coffee-rails', '~> 4.2'
18
17
  gem 'email_validator'
18
+ gem 'interactor'
19
19
  gem 'pg', '>= 0.18', '< 2.0'
20
20
  gem 'puma', '~> 3.11'
21
21
  gem 'rack-canonical-host'
@@ -23,6 +23,7 @@ gem 'rails', '<%= Suspenders::RAILS_VERSION %>'
23
23
  gem 'rails-i18n'
24
24
  gem 'recipient_interceptor'
25
25
  gem 'sassc-rails'
26
+ gem 'sprockets', '< 4'
26
27
  gem 'title'
27
28
  gem 'uglifier', '>= 1.3.0'
28
29
  <% if options[:webpack] %>
@@ -41,6 +42,7 @@ group :development, :test do
41
42
  gem 'dotenv-rails'
42
43
  gem 'pry-byebug'
43
44
  gem 'pry-rails'
45
+ gem 'welaika-suspenders', '< 3'
44
46
  end
45
47
 
46
48
  group :test do
@@ -50,4 +52,3 @@ group :test do
50
52
  gem 'webmock'
51
53
  end
52
54
 
53
- gem 'welaika-suspenders', group: %i[development test]
data/templates/Procfile CHANGED
@@ -1,2 +1 @@
1
1
  web: bundle exec puma -p $PORT -C ./config/puma.rb
2
- # worker:
@@ -4,9 +4,15 @@
4
4
 
5
5
  To install Ruby, read the [rbenv guide](https://github.com/rbenv/rbenv#installation)
6
6
 
7
- To install Postgres:
7
+ To install Postgres 11:
8
8
 
9
- $ brew install postgresql@10
9
+ $ brew install postgresql@<%= Suspenders::POSTGRES_VERSION %>
10
+
11
+ or
12
+
13
+ $ brew install postgresql
14
+
15
+ if `postgresql@<%= Suspenders::POSTGRES_VERSION %>` does not exist yet.
10
16
 
11
17
  To install Node:
12
18
 
@@ -44,10 +50,10 @@ Run the quality checks and the tests suite:
44
50
 
45
51
  https://elements.heroku.com/addons/heroku-postgresql
46
52
 
47
- Choose the PostgreSQL version you want to use (e.g. 10):
53
+ Choose the PostgreSQL version you want to use:
48
54
 
49
- $ heroku addons:create heroku-postgresql:hobby-dev --version=10 --remote=staging
50
- $ heroku addons:create heroku-postgresql:hobby-dev --version=10 --remote=production
55
+ $ heroku addons:create heroku-postgresql:hobby-dev --version=<%= Suspenders::POSTGRES_VERSION %> --remote=staging
56
+ $ heroku addons:create heroku-postgresql:hobby-dev --version=<%= Suspenders::POSTGRES_VERSION %> --remote=production
51
57
 
52
58
  Further reading:
53
59
 
@@ -88,7 +94,7 @@ Since our `Dockerfile` is called `Dockerfile.gitlab`, the commands are slightly
88
94
 
89
95
 
90
96
  ```bash
91
- $ docker build -f Dockerfile.gitlab -t registry.gitlab.com/welaika/<%= app_name %> .
97
+ $ docker build --no-cache -f Dockerfile.gitlab -t registry.gitlab.com/welaika/<%= app_name %> .
92
98
  $ docker push registry.gitlab.com/welaika/<%= app_name %>
93
99
  ```
94
100
 
@@ -0,0 +1,5 @@
1
+ set -e
2
+
3
+ if [ -n "$AUTO_MIGRATE_DB" ]; then
4
+ bundle exec rake db:migrate
5
+ fi
data/templates/bin_deploy CHANGED
@@ -20,5 +20,3 @@ branch="$(git symbolic-ref HEAD --short)"
20
20
  target="${1:-staging}"
21
21
 
22
22
  git push $force "$target" "$branch:master"
23
- heroku run rails db:migrate --exit-code --remote "$target"
24
- heroku restart --remote "$target"
@@ -17,6 +17,5 @@ heroku pg:backups:capture --app $PARENT_APP_NAME
17
17
  URL=`heroku pg:backups public-url --app $PARENT_APP_NAME`
18
18
 
19
19
  heroku pg:backups restore $URL DATABASE_URL --confirm $APP_NAME --app $APP_NAME
20
- heroku run rails db:migrate --exit-code --app $APP_NAME
21
20
  heroku ps:scale worker=1 --app $APP_NAME
22
21
  heroku restart --app $APP_NAME
@@ -0,0 +1,14 @@
1
+ ---
2
+ :concurrency: 5
3
+
4
+ # Keep in mind that Heroku puts a hard limit of 30 seconds on a process restart,
5
+ # The -t 25 tells Sidekiq to give the jobs 25 seconds to finish before starting
6
+ # the "force shutdown" procedure.
7
+ :timeout: 25
8
+
9
+ # Sidekiq will run this file through ERB when reading it so you can
10
+ # even put in dynamic logic, like a host-specific queue.
11
+ # http://www.mikeperham.com/2013/11/13/advanced-sidekiq-host-specific-queues/
12
+ :queues:
13
+ - default
14
+ - mailers # for ActionMailer
@@ -5,7 +5,6 @@ PORT=3000
5
5
  RACK_ENV=development
6
6
  RACK_MINI_PROFILER=0
7
7
  SECRET_KEY_BASE=development_secret
8
- EXECJS_RUNTIME=Node
9
8
  SMTP_ADDRESS=smtp.example.com
10
9
  SMTP_DOMAIN=example.com
11
10
  SMTP_PASSWORD=password