potassium 6.0.0 → 6.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +103 -38
  3. data/.circleci/setup-rubygems.sh +3 -0
  4. data/.gitignore +2 -1
  5. data/.rubocop.yml +530 -0
  6. data/CHANGELOG.md +57 -0
  7. data/README.md +51 -45
  8. data/lib/potassium/assets/.circleci/config.yml.erb +83 -34
  9. data/lib/potassium/assets/.eslintrc.json +13 -4
  10. data/lib/potassium/assets/.github/pull_request_template.md +9 -0
  11. data/lib/potassium/assets/.rubocop.yml +13 -0
  12. data/lib/potassium/assets/README.yml +7 -7
  13. data/lib/potassium/assets/app/graphql/graphql_controller.rb +55 -0
  14. data/lib/potassium/assets/app/graphql/mutations/login_mutation.rb +23 -0
  15. data/lib/potassium/assets/app/graphql/queries/base_query.rb +4 -0
  16. data/lib/potassium/assets/app/graphql/types/base/base_argument.rb +4 -0
  17. data/lib/potassium/assets/app/graphql/types/base/base_enum.rb +4 -0
  18. data/lib/potassium/assets/app/graphql/types/base/base_field.rb +5 -0
  19. data/lib/potassium/assets/app/graphql/types/base/base_input_object.rb +5 -0
  20. data/lib/potassium/assets/app/graphql/types/base/base_interface.rb +7 -0
  21. data/lib/potassium/assets/app/graphql/types/base/base_object.rb +5 -0
  22. data/lib/potassium/assets/app/graphql/types/base/base_scalar.rb +4 -0
  23. data/lib/potassium/assets/app/graphql/types/base/base_union.rb +4 -0
  24. data/lib/potassium/assets/app/graphql/types/mutation_type.rb +10 -0
  25. data/lib/potassium/assets/app/graphql/types/query_type.rb +13 -0
  26. data/lib/potassium/assets/app/javascript/app.spec.js +1 -1
  27. data/lib/potassium/assets/app/uploaders/base_uploader.rb +1 -3
  28. data/lib/potassium/assets/app/views/shared/_gtm_body.html.erb +4 -0
  29. data/lib/potassium/assets/app/views/shared/_gtm_head.html.erb +7 -0
  30. data/lib/potassium/assets/config/graphql_playground.rb +20 -0
  31. data/lib/potassium/assets/config/puma.rb +1 -1
  32. data/lib/potassium/assets/config/shrine.rb +4 -1
  33. data/lib/potassium/assets/redis.yml +1 -2
  34. data/lib/potassium/assets/testing/rails_helper.rb +2 -0
  35. data/lib/potassium/cli/commands/create.rb +11 -19
  36. data/lib/potassium/cli_options.rb +70 -10
  37. data/lib/potassium/helpers/template-helpers.rb +4 -0
  38. data/lib/potassium/newest_version_ensurer.rb +19 -36
  39. data/lib/potassium/node_version_ensurer.rb +30 -0
  40. data/lib/potassium/recipes/admin.rb +26 -16
  41. data/lib/potassium/recipes/api.rb +92 -27
  42. data/lib/potassium/recipes/background_processor.rb +62 -18
  43. data/lib/potassium/recipes/ci.rb +9 -39
  44. data/lib/potassium/recipes/database.rb +4 -0
  45. data/lib/potassium/recipes/draper.rb +0 -9
  46. data/lib/potassium/recipes/file_storage.rb +2 -1
  47. data/lib/potassium/recipes/front_end.rb +84 -9
  48. data/lib/potassium/recipes/github.rb +93 -15
  49. data/lib/potassium/recipes/google_tag_manager.rb +94 -0
  50. data/lib/potassium/recipes/heroku.rb +42 -29
  51. data/lib/potassium/recipes/mailer.rb +18 -5
  52. data/lib/potassium/recipes/monitoring.rb +5 -0
  53. data/lib/potassium/recipes/schedule.rb +16 -1
  54. data/lib/potassium/recipes/style.rb +2 -2
  55. data/lib/potassium/templates/application.rb +5 -2
  56. data/lib/potassium/version.rb +5 -2
  57. data/potassium.gemspec +5 -2
  58. data/spec/features/api_spec.rb +25 -0
  59. data/spec/features/background_processor_spec.rb +19 -6
  60. data/spec/features/ci_spec.rb +7 -4
  61. data/spec/features/draper_spec.rb +1 -6
  62. data/spec/features/file_storage_spec.rb +5 -0
  63. data/spec/features/front_end_spec.rb +32 -1
  64. data/spec/features/github_spec.rb +53 -8
  65. data/spec/features/google_tag_manager_spec.rb +36 -0
  66. data/spec/features/graphql_spec.rb +71 -0
  67. data/spec/features/mailer_spec.rb +16 -0
  68. data/spec/features/schedule_spec.rb +11 -4
  69. data/spec/spec_helper.rb +1 -0
  70. data/spec/support/fake_octokit.rb +31 -0
  71. data/spec/support/potassium_test_helpers.rb +0 -1
  72. data/tmp/.keep +0 -0
  73. metadata +80 -15
  74. data/lib/potassium/assets/Dockerfile.ci +0 -6
  75. data/lib/potassium/assets/api/api_error_concern.rb +0 -32
  76. data/lib/potassium/assets/api/base_controller.rb +0 -7
  77. data/lib/potassium/assets/api/draper_responder.rb +0 -62
  78. data/lib/potassium/assets/api/responder.rb +0 -41
  79. data/lib/potassium/assets/bin/cibuild.erb +0 -117
  80. data/lib/potassium/assets/docker-compose.ci.yml +0 -12
  81. data/lib/potassium/assets/sidekiq_scheduler.yml +0 -9
@@ -1,51 +1,34 @@
1
- require "term/ansicolor"
2
- require "gems"
3
- require "semantic"
4
- require "potassium/text_spinner"
1
+ require 'gems'
2
+ require 'semantic'
3
+ require 'potassium/text_spinner'
4
+
5
+ class VersionError < StandardError
6
+ end
5
7
 
6
8
  module Potassium
7
9
  class NewestVersionEnsurer
8
- include Term::ANSIColor
9
-
10
- def initialize(current_version = Potassium::VERSION)
11
- self.current_version = Semantic::Version.new(current_version)
10
+ def initialize
11
+ self.installed_version = Semantic::Version.new(Potassium::VERSION)
12
12
  self.text_spinner = Potassium::TextSpinner.new
13
13
  end
14
14
 
15
- def ensure(&success_block)
16
- spin_text("Checking your Potassium installation") { latest_version }
15
+ def ensure!
16
+ spin_text('Checking your Potassium installation') { published_version }
17
17
 
18
- self.latest_version = Semantic::Version.new(
19
- Gems.versions("potassium").first["number"]
20
- )
21
-
22
- if latest_version <= current_version
23
- up_to_date
24
- success_block.call
25
- else
26
- please_update
27
- end
18
+ self.published_version = Semantic::Version.new(Gems.versions('potassium').first['number'])
19
+ raise VersionError.new(update_message) if published_version > installed_version
28
20
  end
29
21
 
30
22
  private
31
23
 
32
- attr_accessor :latest_version, :current_version, :text_spinner
33
-
34
- def up_to_date
35
- puts green("\nYour Potassium installation is up to date.")
36
- end
24
+ attr_accessor :published_version, :installed_version, :text_spinner
37
25
 
38
- def please_update
39
- puts red("\nYour Potassium installation is not up to date.")
40
- puts red("Found: #{current_version}")
41
- puts red("Expected: #{latest_version}\n")
42
- print white("Please run: ")
43
- print green("gem update potassium ")
44
- print white("to upgrade your potassium installation.\n\n")
45
- print white("If you really need to run this outdated version of potassium anyway, ")
46
- print white("re-run this command with the ")
47
- print black("--no-version-check")
48
- puts white(" flag.")
26
+ def update_message
27
+ <<~HERE
28
+ Your potassium installation is not up to date.
29
+ The last available version is #{published_version} while the running version is #{installed_version}.
30
+ If you really need to run this outdated version of potassium, re-run this command with the `--no-version-check` flag.
31
+ HERE
49
32
  end
50
33
 
51
34
  def spin_text(message, &block)
@@ -0,0 +1,30 @@
1
+ module Potassium
2
+ class NodeVersionEnsurer
3
+ def ensure!
4
+ raise VersionError.new(install_message) if installed_node_version.nil?
5
+ raise VersionError.new(update_message) if Potassium::NODE_VERSION != installed_node_version
6
+ end
7
+
8
+ private
9
+
10
+ def installed_node_version
11
+ node_version = `node -v 2>&1`
12
+ return node_version.delete('^[0-9\.]').split('.').first if $?.success?
13
+ end
14
+
15
+ def install_message
16
+ <<~HERE
17
+ Node doesn't appear to be installed.
18
+ Please make sure you have node #{Potassium::NODE_VERSION} installed.
19
+ HERE
20
+ end
21
+
22
+ def update_message
23
+ <<~HERE
24
+ An unsupported version of node was found.
25
+ Please make sure you have node #{Potassium::NODE_VERSION} installed. Newer versions may work but potassium only supports that one.
26
+ If you really need to run potassium with a different version of node, re-run this command with the `--no-node-version-check` flag.
27
+ HERE
28
+ end
29
+ end
30
+ end
@@ -34,10 +34,9 @@ class Recipes::Admin < Rails::AppBuilder
34
34
  def add_active_admin
35
35
  gather_gem 'activeadmin', '~> 2.6'
36
36
  gather_gem 'activeadmin_addons'
37
- gather_gem 'active_skin', github: 'SoftwareBrothers/active_skin'
38
37
  add_readme_section :internal_dependencies, :active_admin
39
38
  after(:gem_install, wrap_in_action: :admin_install) do
40
- generate "active_admin:install"
39
+ generate "active_admin:install --use_webpacker"
41
40
  line = "ActiveAdmin.setup do |config|"
42
41
  initializer = "config/initializers/active_admin.rb"
43
42
  gsub_file initializer, /(#{Regexp.escape(line)})/mi do |_match|
@@ -50,26 +49,37 @@ class Recipes::Admin < Rails::AppBuilder
50
49
  end\n
51
50
  ActiveAdmin.setup do |config|
52
51
  config.view_factory.footer = CustomFooter
52
+ meta_tags_options = { viewport: 'width=device-width, initial-scale=1' }
53
+ config.meta_tags = meta_tags_options
54
+ config.meta_tags_for_logged_out_pages = meta_tags_options
53
55
  HERE
54
56
  end
55
57
 
56
- line = "@import \"active_admin/base\";"
57
- style = "app/assets/stylesheets/active_admin.css.scss"
58
- style = File.exist?(style) ? style : "app/assets/stylesheets/active_admin.scss"
58
+ generate "activeadmin_addons:install"
59
59
 
60
- gsub_file style, /(#{Regexp.escape(line)})/mi do |_match|
61
- <<~HERE
62
- #{line}
63
- $skinActiveColor: #001CEE;
64
- $skinHeaderBck: #002744;
65
- $panelHeaderBck: #002744;
66
- //$skinLogo: $skinHeaderBck image-url("logo_admin.png") no-repeat center center;
60
+ run "bin/yarn add arctic_admin @fortawesome/fontawesome-free"
67
61
 
68
- @import "active_skin";
69
- HERE
70
- end
62
+ aa_style = "app/javascript/stylesheets/active_admin.scss"
71
63
 
72
- generate "activeadmin_addons:install"
64
+ gsub_file(
65
+ aa_style,
66
+ "@import \"~@activeadmin/activeadmin/src/scss/mixins\";\n" +
67
+ "@import \"~@activeadmin/activeadmin/src/scss/base\";",
68
+ "@import '~arctic_admin/src/scss/main'; \n"
69
+ )
70
+
71
+ aa_js = "app/javascript/packs/active_admin.js"
72
+ js_line = "import \"@activeadmin/activeadmin\";\n"
73
+
74
+ gsub_file(
75
+ aa_js,
76
+ js_line,
77
+ <<~HERE
78
+ #{js_line}
79
+ import '@fortawesome/fontawesome-free/css/all.css';
80
+ import 'arctic_admin';
81
+ HERE
82
+ )
73
83
  end
74
84
  end
75
85
  end
@@ -1,51 +1,116 @@
1
1
  class Recipes::Api < Rails::AppBuilder
2
2
  def ask
3
- api_support = answer(:api) { Ask.confirm("Do you want to enable API support?") }
4
- set(:api_support, api_support)
3
+ api_interfaces = {
4
+ rest: "REST (with Power API)",
5
+ graphql: "GraphQL (beta)",
6
+ none: "None, thanks"
7
+ }
8
+ api_interface = answer(:api) do
9
+ api_interfaces.keys[Ask.list("Which API interface are you using?", api_interfaces.values)]
10
+ end
11
+ set :api, api_interface.to_sym
5
12
  end
6
13
 
7
14
  def create
8
- add_api if get(:api_support)
15
+ if get(:api) == :graphql
16
+ add_graphql
17
+ elsif get(:api) == :rest
18
+ add_power_api
19
+ end
9
20
  end
10
21
 
11
22
  def install
12
- add_api
23
+ ask
24
+ create
13
25
  end
14
26
 
15
27
  def installed?
16
- gem_exists?(/versionist/)
28
+ gem_exists?(/power_api/) || gem_exists?(/graphql/)
17
29
  end
18
30
 
19
31
  private
20
32
 
21
- def add_api
22
- gather_gem 'versionist'
23
- gather_gem 'responders'
24
- gather_gem 'active_model_serializers', '~> 0.9.3'
25
- gather_gem 'simple_token_authentication', '~> 1.0'
33
+ def add_power_api
34
+ gather_gem 'power_api'
35
+
36
+ gather_gems(:development, :test) do
37
+ gather_gem 'rswag-specs'
38
+ end
39
+
40
+ add_readme_section :internal_dependencies, :power_api
41
+ append_to_file('.rubocop.yml', "RSpec:\n Includes:\n Examples:\n - run_test!")
26
42
 
27
43
  after(:gem_install) do
28
- line = "Rails.application.routes.draw do\n"
29
- insert_into_file "config/routes.rb", after: line do
30
- <<-HERE.gsub(/^ {8}/, '')
31
- scope path: '/api' do
32
- api_version(module: "Api::V1", path: { value: "v1" }, defaults: { format: 'json' }) do
33
- end
34
- end
35
- HERE
36
- end
44
+ generate "power_api:install"
45
+ end
46
+ end
37
47
 
38
- api_error_concern_path = 'app/controllers/concerns/api_error_concern.rb'
48
+ def add_graphql
49
+ gather_gem 'graphql'
50
+ if get(:authentication)
51
+ gather_gem 'jwt'
52
+ end
53
+ gather_gems(:development, :test) do
54
+ gather_gem 'graphql_playground-rails'
55
+ end
39
56
 
40
- copy_file '../assets/api/base_controller.rb', 'app/controllers/api/v1/base_controller.rb'
41
- copy_file '../assets/api/api_error_concern.rb', api_error_concern_path
42
- copy_file '../assets/api/responder.rb', 'app/responders/api_responder.rb'
57
+ after(:gem_install) do
58
+ generate "graphql:install --skip_graphiql"
59
+ playground_route = <<~HEREDOC
60
+ \n
61
+ if Rails.env.development?
62
+ mount GraphqlPlayground::Rails::Engine, at: "/graphiql", graphql_path: "/graphql"
63
+ end
64
+ HEREDOC
65
+ inject_into_file(
66
+ 'config/routes.rb',
67
+ playground_route,
68
+ after: 'post "/graphql", to: "graphql#execute"'
69
+ )
70
+ copy_file(
71
+ "../assets/config/graphql_playground.rb",
72
+ "config/initializers/graphql_playground.rb"
73
+ )
74
+ remove_dir 'app/graphql/types'
75
+ directory '../assets/app/graphql/types', 'app/graphql/types'
76
+ gsub_file 'app/graphql/mutations/base_mutation.rb', 'Types::Base', 'Types::Base::Base'
77
+ directory '../assets/app/graphql/queries', 'app/graphql/queries'
78
+ gsub_file 'app/graphql/mutations/base_mutation.rb', 'RelayClassic', ''
79
+ gsub_file(
80
+ 'app/graphql/mutations/base_mutation.rb',
81
+ " input_object_class Types::Base::BaseInputObject\n", ''
82
+ )
43
83
 
44
- if selected?(:report_error)
45
- previous_line = 'logger.error exception.backtrace.join("\n")'
46
- new_line = "\n Raven.capture_exception(exception)"
47
- insert_into_file api_error_concern_path, new_line, after: previous_line
84
+ if get(:authentication)
85
+ copy_file(
86
+ '../assets/app/graphql/graphql_controller.rb',
87
+ 'app/controllers/graphql_controller.rb',
88
+ force: true
89
+ )
90
+ gsub_file(
91
+ 'app/controllers/graphql_controller.rb',
92
+ 'GqlSampleSchema',
93
+ "#{get(:titleized_app_name).delete(' ')}Schema"
94
+ )
95
+ copy_file(
96
+ '../assets/app/graphql/mutations/login_mutation.rb',
97
+ 'app/graphql/mutations/login_mutation.rb'
98
+ )
99
+ inject_into_file(
100
+ 'app/graphql/types/mutation_type.rb',
101
+ "\n field :login, mutation: Mutations::LoginMutation",
102
+ after: 'class MutationType < Types::Base::BaseObject'
103
+ )
104
+ append_to_file(".env.development", "HMAC_SECRET=\n")
48
105
  end
106
+
107
+ inject_into_file(
108
+ 'app/controllers/graphql_controller.rb',
109
+ "\n\n skip_before_action :verify_authenticity_token",
110
+ after: '# protect_from_forgery with: :null_session'
111
+ )
112
+
113
+ add_readme_section :internal_dependencies, :graphql
49
114
  end
50
115
  end
51
116
  end
@@ -1,18 +1,22 @@
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
13
13
 
14
14
  def create
15
- add_sidekiq if get(:background_processor)
15
+ if get(:background_processor)
16
+ add_sidekiq
17
+ add_docker_compose_redis_config
18
+ set_redis_dot_env
19
+ end
16
20
  end
17
21
 
18
22
  def install
@@ -27,19 +31,17 @@ class Recipes::BackgroundProcessor < Rails::AppBuilder
27
31
  end
28
32
 
29
33
  def add_sidekiq
30
- gather_gem("sidekiq")
31
- add_adapters("sidekiq")
32
- add_readme_section :internal_dependencies, :sidekiq
33
- edit_procfile("bundle exec sidekiq")
34
- append_to_file(".env.development", "DB_POOL=25\n")
35
- template("../assets/sidekiq.rb.erb", "config/initializers/sidekiq.rb", force: true)
36
- copy_file("../assets/sidekiq.yml", "config/sidekiq.yml", force: true)
37
- copy_file("../assets/redis.yml", "config/redis.yml", force: true)
38
-
39
- insert_into_file "config/routes.rb", after: "Rails.application.routes.draw do\n" do
40
- <<-HERE.gsub(/^ {6}/, '')
41
- mount Sidekiq::Web => '/queue'
42
- HERE
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
43
45
  end
44
46
  end
45
47
 
@@ -55,4 +57,46 @@ class Recipes::BackgroundProcessor < Rails::AppBuilder
55
57
  application "config.active_job.queue_adapter = :async", env: "development"
56
58
  application "config.active_job.queue_adapter = :test", env: "test"
57
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
+
69
+ private
70
+
71
+ def add_docker_compose_redis_config
72
+ compose = DockerHelpers.new('docker-compose.yml')
73
+
74
+ service_definition =
75
+ <<~YAML
76
+ image: redis
77
+ ports:
78
+ - 6379
79
+ volumes:
80
+ - redis_data:/data
81
+ YAML
82
+
83
+ compose.add_service('redis', service_definition)
84
+ compose.add_volume('redis_data')
85
+ end
86
+
87
+ def set_redis_dot_env
88
+ append_to_file(
89
+ '.env.development',
90
+ <<~TEXT
91
+ REDIS_HOST=127.0.0.1
92
+ REDIS_PORT=$(make services-port SERVICE=redis PORT=6379)
93
+ REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT}/1
94
+ TEXT
95
+ )
96
+ end
97
+
98
+ def enabled_mailer?
99
+ mailer_answer = get(:email_service)
100
+ mailer_answer && ![:none, :None].include?(mailer_answer.to_sym)
101
+ end
58
102
  end
@@ -1,50 +1,20 @@
1
1
  class Recipes::Ci < Rails::AppBuilder
2
2
  def create
3
- copy_file '../assets/Dockerfile.ci', 'Dockerfile.ci'
4
3
  template '../assets/.circleci/config.yml.erb', '.circleci/config.yml'
5
4
 
6
- template '../assets/bin/cibuild.erb', 'bin/cibuild'
7
- run "chmod a+x bin/cibuild"
8
-
9
- copy_file '../assets/docker-compose.ci.yml', 'docker-compose.ci.yml'
10
-
11
5
  gather_gems(:test) do
12
- gather_gem 'rspec_junit_formatter', '0.2.2'
13
- end
14
-
15
- compose = DockerHelpers.new('docker-compose.ci.yml')
16
-
17
- if selected?(:database, :mysql)
18
- srv =
19
- <<~YAML
20
- image: mysql:#{Potassium::MYSQL_VERSION}
21
- environment:
22
- MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
23
- YAML
24
- compose.add_service("mysql", srv)
25
- compose.add_link('test', 'mysql')
26
- compose.add_env('test', 'MYSQL_HOST', 'mysql')
27
- compose.add_env('test', 'MYSQL_PORT', '3306')
28
-
29
- elsif selected?(:database, :postgresql)
30
- srv =
31
- <<~YAML
32
- image: "postgres:#{Potassium::POSTGRES_VERSION}"
33
- environment:
34
- POSTGRES_USER: postgres
35
- POSTGRES_PASSWORD: ''
36
- YAML
37
- compose.add_service("postgresql", srv)
38
- compose.add_link('test', 'postgresql')
39
- compose.add_env('test', 'DB_USER', 'postgres')
40
- compose.add_env('test', 'DB_HOST', 'postgresql')
41
- compose.add_env('test', 'DB_PORT', '5432')
6
+ gather_gem 'rspec_junit_formatter', '~> 0.4'
42
7
  end
43
8
 
44
- compose.add_volume('test_data')
45
-
46
9
  add_readme_header :ci
47
-
48
10
  application 'config.assets.js_compressor = :uglifier', env: 'test'
49
11
  end
12
+
13
+ def install
14
+ create
15
+ end
16
+
17
+ def installed?
18
+ file_exist?('.circleci/config.yml')
19
+ end
50
20
  end