potassium 6.1.0 → 6.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 6.2.0
6
+
7
+ Features:
8
+ - Add Google Tag Manager [#326](https://github.com/platanus/potassium/pull/326)
9
+ - Update rubocop and rubocop-rspec for potassium and generated projects [#337](https://github.com/platanus/potassium/pull/337)
10
+ - Adds `mailers` queue to `sidekiq.yml` when installing or creating mailer recipe [#341](https://github.com/platanus/potassium/pull/341)
11
+
12
+ Fixes:
13
+ - Change `backgroud_processor` cli option to a switch. As of [#137](https://github.com/platanus/potassium/pull/137) we no longer have `delayed_jobs` as an option, it's only `sidekiq` now [#340](https://github.com/platanus/potassium/pull/340)
14
+ - Fixes mailer recipe install when background_processor wasn't installed [#341](https://github.com/platanus/potassium/pull/341)
15
+ - Update `heroku` recipe to check if an app or pipeline name is valid before creating it on Heroku [#344](https://github.com/platanus/potassium/pull/344)
16
+
5
17
  ## 6.1.0
6
18
 
7
19
  Features:
data/README.md CHANGED
@@ -60,13 +60,13 @@ The following optional integrations are also added:
60
60
  - [ActiveAdmin](http://activeadmin.info) for admin interfaces
61
61
  - [ActiveAdminAddons](https://github.com/platanus/activeadmin_addons) for some help with ActiveAdmin
62
62
  - [Pundit](https://github.com/elabs/pundit) for role-based authorization
63
- - [DelayedJob](https://github.com/collectiveidea/delayed_job) to execute longer tasks in the background
64
63
  - [Sidekiq](https://github.com/mperham/sidekiq) a simple, efficient background processing for Ruby
65
64
  - [Sidekiq-scheduler](https://github.com/moove-it/sidekiq-scheduler) to run scheduled processes
66
65
  - Mailing configuration for [AWS SES](https://github.com/aws/aws-sdk-rails)
67
66
  and [Sendgrid](https://github.com/platanus/send_grid_mailer) with recipient interceptor support
68
67
  - [Sentry](https://sentry.io) to monitor exceptions and errors
69
68
  - [Vue.js](https://vuejs.org) or [Angular 2](https://angular.io/) for frontend development
69
+ - [Google Tag Manager](https://tagmanager.google.com/) for analytics
70
70
  - Creates the Github repository of your choice for the project. A local git repository will always be created.
71
71
 
72
72
  A few more things are added to the project:
@@ -502,6 +502,19 @@ Performance/RedundantBlockCall:
502
502
  Description: Use `yield` instead of `block.call`.
503
503
  Reference: https://github.com/JuanitoFatas/fast-ruby#proccall-vs-yield-code
504
504
  Enabled: false
505
+ Style/OptionalBooleanParameter:
506
+ Description: 'Use keyword arguments when defining method with boolean argument.'
507
+ Enabled: false
508
+ Lint/MissingSuper:
509
+ Description: >-
510
+ This cop checks for the presence of constructors and lifecycle callbacks
511
+ without calls to `super`'.
512
+ Enabled: false
513
+ Style/RedundantFileExtensionInRequire:
514
+ Description: >-
515
+ Checks for the presence of superfluous `.rb` extension in
516
+ the filename provided to `require` and `require_relative`.
517
+ Enabled: false
505
518
  RSpec/MultipleExpectations:
506
519
  Max: 5
507
520
  RSpec/NestedGroups:
@@ -112,9 +112,6 @@ readme:
112
112
  pundit:
113
113
  title: "Authorization"
114
114
  body: "For defining which parts of the system each user has access to, we have chosen to include the [Pundit](https://github.com/elabs/pundit) gem, by [Elabs](http://elabs.se/)."
115
- delayed_job:
116
- title: "Queue System"
117
- body: "For managing tasks in the background, this project uses [DelayedJob](https://github.com/collectiveidea/delayed_job)"
118
115
  sidekiq:
119
116
  title: "Queue System"
120
117
  body: "For managing tasks in the background, this project uses [Sidekiq](https://github.com/mperham/sidekiq)"
@@ -0,0 +1,4 @@
1
+ <!-- Google Tag Manager (noscript) -->
2
+ <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=<%= ENV['GTM_CONTAINER_ID'] %>"
3
+ height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
4
+ <!-- End Google Tag Manager (noscript) -->
@@ -0,0 +1,7 @@
1
+ <!-- Google Tag Manager -->
2
+ <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
3
+ new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
4
+ j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
5
+ 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
6
+ })(window,document,'script','dataLayer','<%=ENV['GTM_CONTAINER_ID']%>');</script>
7
+ <!-- End Google Tag Manager -->
@@ -97,10 +97,10 @@ module Potassium::CliOptions # rubocop:disable Metrics/ModuleLength
97
97
  default_test_value: false
98
98
  },
99
99
  {
100
- type: :flag,
100
+ type: :switch,
101
101
  name: "background_processor",
102
- desc: "Decides which background processor to use. Available: sidekiq, delayed_job, none",
103
- default_test_value: "None"
102
+ desc: "Whether to use Sidekiq for background processing or not",
103
+ default_test_value: false
104
104
  },
105
105
  {
106
106
  type: :switch,
@@ -177,6 +177,14 @@ module Potassium::CliOptions # rubocop:disable Metrics/ModuleLength
177
177
  desc: "Decides which front-end framework to use. Available: Vue, Angular 2, None",
178
178
  default_test_value: "None"
179
179
  },
180
+ {
181
+ type: :switch,
182
+ name: 'google_tag_manager',
183
+ desc: 'Whether to use google tag manager',
184
+ negatable: true,
185
+ default_value: 'none',
186
+ default_test_value: false
187
+ },
180
188
  {
181
189
  type: :switch,
182
190
  name: "test",
@@ -1,12 +1,12 @@
1
1
  class Recipes::BackgroundProcessor < Rails::AppBuilder
2
2
  def ask
3
- response = if selected?(:email_service, :none)
3
+ response = if enabled_mailer?
4
+ info "Note: Emails should be sent on background jobs. We'll install sidekiq"
5
+ true
6
+ else
4
7
  answer(:background_processor) do
5
8
  Ask.confirm("Do you want to use Sidekiq for background job processing?")
6
9
  end
7
- else
8
- info "Note: Emails should be sent on background jobs. We'll install sidekiq"
9
- true
10
10
  end
11
11
  set(:background_processor, response)
12
12
  end
@@ -30,6 +30,42 @@ class Recipes::BackgroundProcessor < Rails::AppBuilder
30
30
  gem_exists?(/sidekiq/)
31
31
  end
32
32
 
33
+ def add_sidekiq
34
+ recipe = self
35
+ run_action(:install_sidekiq) do
36
+ gather_gem("sidekiq")
37
+ recipe.add_adapters("sidekiq")
38
+ add_readme_section :internal_dependencies, :sidekiq
39
+ recipe.edit_procfile("bundle exec sidekiq")
40
+ append_to_file(".env.development", "DB_POOL=25\n")
41
+ template("../assets/sidekiq.rb.erb", "config/initializers/sidekiq.rb", force: true)
42
+ copy_file("../assets/sidekiq.yml", "config/sidekiq.yml", force: true)
43
+ copy_file("../assets/redis.yml", "config/redis.yml", force: true)
44
+ recipe.mount_sidekiq_routes
45
+ end
46
+ end
47
+
48
+ def edit_procfile(cmd)
49
+ heroku = load_recipe(:heroku)
50
+ if selected?(:heroku) || heroku.installed?
51
+ gsub_file('Procfile', /^.*$/m) { |match| "#{match}worker: #{cmd}" }
52
+ end
53
+ end
54
+
55
+ def add_adapters(name)
56
+ application("config.active_job.queue_adapter = :#{name}")
57
+ application "config.active_job.queue_adapter = :async", env: "development"
58
+ application "config.active_job.queue_adapter = :test", env: "test"
59
+ end
60
+
61
+ def mount_sidekiq_routes
62
+ insert_into_file "config/routes.rb", after: "Rails.application.routes.draw do\n" do
63
+ <<-HERE.gsub(/^ {6}/, '')
64
+ mount Sidekiq::Web => '/queue'
65
+ HERE
66
+ end
67
+ end
68
+
33
69
  private
34
70
 
35
71
  def add_docker_compose_redis_config
@@ -59,33 +95,8 @@ class Recipes::BackgroundProcessor < Rails::AppBuilder
59
95
  )
60
96
  end
61
97
 
62
- def add_sidekiq
63
- gather_gem("sidekiq")
64
- add_adapters("sidekiq")
65
- add_readme_section :internal_dependencies, :sidekiq
66
- edit_procfile("bundle exec sidekiq")
67
- append_to_file(".env.development", "DB_POOL=25\n")
68
- template("../assets/sidekiq.rb.erb", "config/initializers/sidekiq.rb", force: true)
69
- copy_file("../assets/sidekiq.yml", "config/sidekiq.yml", force: true)
70
- copy_file("../assets/redis.yml", "config/redis.yml", force: true)
71
-
72
- insert_into_file "config/routes.rb", after: "Rails.application.routes.draw do\n" do
73
- <<-HERE.gsub(/^ {6}/, '')
74
- mount Sidekiq::Web => '/queue'
75
- HERE
76
- end
77
- end
78
-
79
- def edit_procfile(cmd)
80
- heroku = load_recipe(:heroku)
81
- if selected?(:heroku) || heroku.installed?
82
- gsub_file('Procfile', /^.*$/m) { |match| "#{match}worker: #{cmd}" }
83
- end
84
- end
85
-
86
- def add_adapters(name)
87
- application("config.active_job.queue_adapter = :#{name}")
88
- application "config.active_job.queue_adapter = :async", env: "development"
89
- application "config.active_job.queue_adapter = :test", env: "test"
98
+ def enabled_mailer?
99
+ mailer_answer = get(:email_service)
100
+ mailer_answer && ![:none, :None].include?(mailer_answer.to_sym)
90
101
  end
91
102
  end
@@ -0,0 +1,90 @@
1
+ class Recipes::GoogleTagManager < Rails::AppBuilder
2
+ def ask
3
+ use_google_tag_manager = answer(:google_tag_manager) do
4
+ Ask.confirm 'Do you want to use Google Tag Manager?'
5
+ end
6
+
7
+ set(:google_tag_manager, use_google_tag_manager)
8
+ end
9
+
10
+ def create
11
+ install if selected?(:google_tag_manager)
12
+ end
13
+
14
+ def install
15
+ add_google_tag_manager
16
+ end
17
+
18
+ def add_google_tag_manager
19
+ copy_tag_manager_files
20
+ append_to_file '.env.development', "GTM_CONTAINER_ID=\n"
21
+ include_tag_manager
22
+ add_content_security_policy
23
+ end
24
+
25
+ def add_content_security_policy
26
+ inject_into_file(
27
+ 'config/initializers/content_security_policy.rb',
28
+ content_security_policy_code
29
+ )
30
+ end
31
+
32
+ def copy_tag_manager_files
33
+ copy_file(
34
+ '../assets/app/views/shared/_gtm_head.html.erb',
35
+ 'app/views/shared/_gtm_head.html.erb',
36
+ force: true
37
+ )
38
+
39
+ copy_file(
40
+ '../assets/app/views/shared/_gtm_body.html.erb',
41
+ 'app/views/shared/_gtm_body.html.erb',
42
+ force: true
43
+ )
44
+ end
45
+
46
+ def include_tag_manager
47
+ inject_into_file(
48
+ 'app/views/layouts/application.html.erb',
49
+ render_string('shared/gtm_head'),
50
+ before: '</head>'
51
+ )
52
+
53
+ inject_into_file(
54
+ 'app/views/layouts/application.html.erb',
55
+ render_string('shared/gtm_body'),
56
+ after: '<body>'
57
+ )
58
+ end
59
+
60
+ private
61
+
62
+ def render_string(file_path)
63
+ " <%if Rails.env.production? %>
64
+ <%= render \"#{file_path}\" %>
65
+ <% end %>\n "
66
+ end
67
+
68
+ def content_security_policy_code
69
+ <<~HERE
70
+ Rails.application.config.content_security_policy do |policy|
71
+ if Rails.env.development?
72
+ policy.connect_src :self, :https, 'http://localhost:3035', 'ws://localhost:3035'
73
+ policy.script_src :self, :https, :unsafe_eval
74
+ else
75
+ policy.script_src :self, :https
76
+ # google tag manager requires to enable unsafe inline:
77
+ # https://developers.google.com/tag-manager/web/csp
78
+ policy.connect_src :self, :https, 'https://www.google-analytics.com'
79
+ policy.script_src :self,
80
+ :https,
81
+ :unsafe_inline,
82
+ 'https://www.googletagmanager.com',
83
+ 'https://www.google-analytics.com',
84
+ 'https://ssl.google-analytics.com'
85
+ policy.img_src :self, :https, 'https://www.googletagmanager.com', 'https://www.google-analytics.com'
86
+ end
87
+ end
88
+ HERE
89
+ end
90
+ end
@@ -1,13 +1,7 @@
1
1
  class Recipes::Heroku < Rails::AppBuilder
2
2
  NAME_PREFIX = 'pl'
3
-
4
- attr_accessor :app_name_staging
5
-
6
- def initialize(args)
7
- super(args)
8
- set(:heroku_app_name_staging, app_name_for('staging'))
9
- set(:heroku_app_name_production, app_name_for('production'))
10
- end
3
+ ENVIRONMENTS = ['staging', 'production']
4
+ HEROKU_NAMES_MAX_CHARS = 30
11
5
 
12
6
  def ask
13
7
  heroku = answer(:heroku) do
@@ -16,6 +10,8 @@ class Recipes::Heroku < Rails::AppBuilder
16
10
 
17
11
  if heroku
18
12
  set(:heroku, heroku)
13
+
14
+ ENVIRONMENTS.each { |environment| set_app_name_for(environment) }
19
15
  end
20
16
  end
21
17
 
@@ -47,31 +43,34 @@ class Recipes::Heroku < Rails::AppBuilder
47
43
  template "../assets/bin/setup_heroku.erb", "bin/setup_heroku", force: true
48
44
  run "chmod a+x bin/setup_heroku"
49
45
 
50
- if logged_in?
51
- %w(staging production).each do |environment|
52
- create_app_on_heroku(environment)
53
- end
54
- puts "Remember to connect the github repository to the new pipeline"
55
- open_pipeline_command = "\e[33mheroku pipelines:open #{heroku_pipeline_name}\e[0m"
56
- puts "run #{open_pipeline_command} to open the dashboard"
57
- else
58
- puts "You are not logged in into heroku"
59
- login_command = "\e[33mheroku login\e[0m"
60
- puts "Run #{login_command} and enter your credentials"
61
- puts "You can install the heroku recipe again create the app in heroku"
62
- install_command = "\e[33mpostassium install heroku --force\e[0m"
63
- puts "Just run #{install_command}"
64
- end
46
+ logged_in? ? create_apps : puts_not_logged_in_msg
65
47
 
66
48
  add_readme_header :deployment
67
49
  end
68
50
 
51
+ def create_apps
52
+ ENVIRONMENTS.each { |environment| create_app_on_heroku(environment) }
53
+ puts "Remember to connect the github repository to the new pipeline"
54
+ open_pipeline_command = "\e[33mheroku pipelines:open #{heroku_pipeline_name}\e[0m"
55
+ puts "run #{open_pipeline_command} to open the dashboard"
56
+ end
57
+
58
+ def puts_not_logged_in_msg
59
+ puts "You are not logged in into heroku"
60
+ login_command = "\e[33mheroku login\e[0m"
61
+ puts "Run #{login_command} and enter your credentials"
62
+ puts "You can install the heroku recipe again to create the app in heroku"
63
+ install_command = "\e[33mpostassium install heroku --force\e[0m"
64
+ puts "Just run #{install_command}"
65
+ end
66
+
69
67
  def heroku_pipeline_name
70
- app_name.dasherize
68
+ @heroku_pipeline_name ||= valid_heroku_name(app_name.dasherize, 'pipeline', false)
71
69
  end
72
70
 
73
- def app_name_for(environment)
74
- "#{NAME_PREFIX}-#{app_name.dasherize}-#{environment}"
71
+ def set_app_name_for(environment)
72
+ default_name = "#{NAME_PREFIX}-#{app_name.dasherize}-#{environment}"
73
+ set("heroku_app_name_#{environment}".to_sym, valid_heroku_name(default_name, environment))
75
74
  end
76
75
 
77
76
  def logged_in?
@@ -84,7 +83,7 @@ class Recipes::Heroku < Rails::AppBuilder
84
83
 
85
84
  def create_app_on_heroku(environment)
86
85
  rack_env = "RACK_ENV=production"
87
- staged_app_name = app_name_for(environment)
86
+ staged_app_name = get("heroku_app_name_#{environment}".to_sym)
88
87
 
89
88
  run_toolbelt_command "create #{staged_app_name} --remote #{environment}"
90
89
  run_toolbelt_command "labs:enable runtime-dyno-metadata", staged_app_name
@@ -99,14 +98,14 @@ class Recipes::Heroku < Rails::AppBuilder
99
98
  def set_rails_secrets(environment)
100
99
  run_toolbelt_command(
101
100
  "config:add SECRET_KEY_BASE=#{generate_secret}",
102
- app_name_for(environment)
101
+ get("heroku_app_name_#{environment}".to_sym)
103
102
  )
104
103
  end
105
104
 
106
105
  def set_app_multi_buildpack(environment)
107
106
  run_toolbelt_command(
108
107
  "buildpacks:set https://github.com/heroku/heroku-buildpack-multi.git",
109
- app_name_for(environment)
108
+ get("heroku_app_name_#{environment}".to_sym)
110
109
  )
111
110
  end
112
111
 
@@ -133,4 +132,18 @@ class Recipes::Heroku < Rails::AppBuilder
133
132
  `heroku #{command} --app #{app_env_name}`
134
133
  end
135
134
  end
135
+
136
+ def valid_heroku_name(name, element, force_suffix = true)
137
+ suffix = "-#{element}"
138
+ while name.length > HEROKU_NAMES_MAX_CHARS
139
+ puts "Heroku names must be shorter than #{HEROKU_NAMES_MAX_CHARS} chars."
140
+ if force_suffix
141
+ puts "Potassium uses the heroku-stage gem, because of that '#{suffix}' will be "\
142
+ "added to your app name. The suffix, #{suffix}, counts towards the app name length."
143
+ end
144
+ name = Ask.input("Please enter a valid name for #{element}:")
145
+ name += suffix if force_suffix && !name.end_with?(suffix)
146
+ end
147
+ name
148
+ end
136
149
  end
@@ -24,11 +24,7 @@ class Recipes::Mailer < Rails::AppBuilder
24
24
  dependencies(email_service)
25
25
  config(email_service)
26
26
 
27
- background_processor_recipe = load_recipe(:background_processor)
28
- background_processor_answer = get(:background_processor)
29
-
30
- background_processor_recipe.add_sidekiq unless background_processor_recipe.installed? ||
31
- background_processor_answer
27
+ ensure_sidekiq_install_and_add_mailers_queue
32
28
  end
33
29
 
34
30
  def install
@@ -36,6 +32,10 @@ class Recipes::Mailer < Rails::AppBuilder
36
32
  create
37
33
  end
38
34
 
35
+ def add_mailer_queue
36
+ insert_into_file "config/sidekiq.yml", " - mailers", after: "- default\n"
37
+ end
38
+
39
39
  private
40
40
 
41
41
  def email_services(service_name)
@@ -102,4 +102,17 @@ class Recipes::Mailer < Rails::AppBuilder
102
102
  gather_gems(:development) { gather_gem("letter_opener") }
103
103
  application "config.action_mailer.delivery_method = :letter_opener", env: "development"
104
104
  end
105
+
106
+ def ensure_sidekiq_install_and_add_mailers_queue
107
+ background_processor_recipe = load_recipe(:background_processor)
108
+ background_processor_answer = get(:background_processor)
109
+
110
+ if background_processor_recipe.installed?
111
+ add_mailer_queue
112
+ else
113
+ recipe = self
114
+ after(:install_sidekiq) { recipe.add_mailer_queue }
115
+ background_processor_recipe.add_sidekiq unless background_processor_answer
116
+ end
117
+ end
105
118
  end