rolemodel-rails 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +10 -2
  3. data/lib/generators/rolemodel/all_generator.rb +1 -0
  4. data/lib/generators/rolemodel/core_setup/README.md +27 -0
  5. data/lib/generators/rolemodel/core_setup/USAGE +12 -0
  6. data/lib/generators/rolemodel/core_setup/core_setup_generator.rb +19 -0
  7. data/lib/generators/rolemodel/github/README.md +4 -0
  8. data/lib/generators/rolemodel/github/templates/workflows/deploy-production.yml +34 -0
  9. data/lib/generators/rolemodel/github/templates/workflows/deploy-staging.yml +35 -0
  10. data/lib/generators/rolemodel/good_job/good_job_generator.rb +0 -2
  11. data/lib/generators/rolemodel/heroku/README.md +20 -1
  12. data/lib/generators/rolemodel/heroku/heroku_generator.rb +35 -0
  13. data/lib/generators/rolemodel/heroku/templates/app.json.tt +5 -2
  14. data/lib/generators/rolemodel/heroku/templates/deploy-app/SKILL.md +202 -0
  15. data/lib/generators/rolemodel/linters/eslint/eslint_generator.rb +1 -0
  16. data/lib/generators/rolemodel/linters/rubocop/rubocop_generator.rb +5 -1
  17. data/lib/generators/rolemodel/lograge/README.md +1 -1
  18. data/lib/generators/rolemodel/lograge/templates/config/initializers/lograge.rb +1 -1
  19. data/lib/generators/rolemodel/optics/base/base_generator.rb +1 -0
  20. data/lib/generators/rolemodel/react/react_generator.rb +1 -0
  21. data/lib/generators/rolemodel/readme/templates/README.md.erb +2 -4
  22. data/lib/generators/rolemodel/sentry/README.md +25 -0
  23. data/lib/generators/rolemodel/sentry/USAGE +13 -0
  24. data/lib/generators/rolemodel/sentry/sentry_generator.rb +126 -0
  25. data/lib/generators/rolemodel/sentry/templates/app/javascript/initializers/sentry.js +34 -0
  26. data/lib/generators/rolemodel/sentry/templates/config/initializers/sentry.rb +68 -0
  27. data/lib/generators/rolemodel/simple_form/README.md +6 -0
  28. data/lib/generators/rolemodel/simple_form/USAGE +3 -0
  29. data/lib/generators/rolemodel/simple_form/simple_form_generator.rb +11 -0
  30. data/lib/generators/rolemodel/simple_form/templates/app/inputs/switch_checkbox_input.rb +1 -1
  31. data/lib/generators/rolemodel/tailored_select/USAGE +2 -1
  32. data/lib/generators/rolemodel/tailored_select/tailored_select_generator.rb +15 -0
  33. data/lib/generators/rolemodel/testing/factory_bot/templates/support/factory_bot.rb +2 -0
  34. data/lib/generators/rolemodel/testing/jasmine_playwright/jasmine_playwright_generator.rb +2 -2
  35. data/lib/generators/rolemodel/testing/rspec/templates/spec/rails_helper.rb.tt +4 -1
  36. data/lib/generators/rolemodel/testing/rspec/templates/spec/support/capybara_drivers.rb.tt +2 -0
  37. data/lib/generators/rolemodel/testing/rspec/templates/spec/support/helpers/action_cable_helper.rb +2 -0
  38. data/lib/generators/rolemodel/testing/rspec/templates/spec/support/helpers/test_element_helper.rb +2 -0
  39. data/lib/generators/rolemodel/testing/rspec/templates/spec/support/helpers.rb.tt +3 -1
  40. data/lib/generators/rolemodel/testing/vitest/vitest_generator.rb +1 -0
  41. data/lib/generators/rolemodel/ui_components/modals/modals_generator.rb +1 -0
  42. data/lib/generators/rolemodel/ui_components/modals/templates/app/helpers/turbo_frame_link_helper.rb +2 -0
  43. data/lib/generators/rolemodel/ui_components/navbar/navbar_generator.rb +1 -0
  44. data/lib/generators/rolemodel/webpack/README.md +2 -1
  45. data/lib/generators/rolemodel/webpack/templates/webpack.config.js +2 -12
  46. data/lib/generators/rolemodel/webpack/webpack_generator.rb +19 -11
  47. data/lib/rolemodel/generator_base.rb +37 -0
  48. data/lib/rolemodel/version.rb +1 -1
  49. data/lib/rolemodel-rails.rb +1 -0
  50. metadata +13 -12
  51. data/lib/generators/rolemodel/linters/rubocop/templates/lib/cops/form_error_response.rb +0 -54
  52. data/lib/generators/rolemodel/linters/rubocop/templates/lib/cops/no_chrome_tag.rb +0 -45
  53. data/lib/generators/rolemodel/semaphore/README.md +0 -15
  54. data/lib/generators/rolemodel/semaphore/USAGE +0 -11
  55. data/lib/generators/rolemodel/semaphore/semaphore_generator.rb +0 -25
  56. data/lib/generators/rolemodel/semaphore/templates/heroku-deployment-commands.sh +0 -3
  57. data/lib/generators/rolemodel/semaphore/templates/production-deploy.yml.erb +0 -17
  58. data/lib/generators/rolemodel/semaphore/templates/semaphore.yml.erb +0 -145
  59. data/lib/generators/rolemodel/semaphore/templates/staging-deploy.yml.erb +0 -17
  60. data/lib/generators/rolemodel/webpack/templates/app/javascript/initializers/honeybadger.js +0 -26
  61. /data/lib/generators/rolemodel/{simple_form → tailored_select}/templates/app/inputs/tailored_select_input.rb +0 -0
@@ -0,0 +1,126 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Rolemodel
4
+ class SentryGenerator < GeneratorBase
5
+ source_root File.expand_path('templates', __dir__)
6
+
7
+ JS_DEPS = %w[
8
+ @sentry/browser
9
+ @sentry/webpack-plugin
10
+ ]
11
+
12
+ def install_gem
13
+ say 'Adding sentry-rails gem', :green
14
+
15
+ bundle_command 'add sentry-rails'
16
+ end
17
+
18
+ def install_profiler_gem
19
+ say 'Adding stackprof gem for Sentry profiling', :green
20
+
21
+ # config/initializers/sentry.rb sets profiles_sample_rate; without stackprof
22
+ # the SDK logs a warning on every boot and profiling is silently disabled.
23
+ # stackprof is a native MRI extension, so restrict it to compatible platforms.
24
+ gem 'stackprof', platforms: :ruby
25
+ run_bundle
26
+ end
27
+
28
+ def add_ruby_initializer
29
+ say 'Setting up Sentry for Ruby error reporting', :green
30
+
31
+ copy_file 'config/initializers/sentry.rb'
32
+ end
33
+
34
+ def add_user_context
35
+ say 'Adding Sentry user context to ApplicationController', :green
36
+
37
+ inject_into_class 'app/controllers/application_controller.rb', 'ApplicationController',
38
+ " before_action :set_sentry_user\n"
39
+
40
+ inject_into_file 'app/controllers/application_controller.rb', before: /^end\b/ do
41
+ <<-RUBY
42
+
43
+ private
44
+
45
+ # Guarded so it works whether or not an authentication generator has been run.
46
+ def set_sentry_user
47
+ return unless respond_to?(:current_user) && current_user
48
+
49
+ Sentry.set_user(id: current_user.id)
50
+ end
51
+ RUBY
52
+ end
53
+ end
54
+
55
+ def add_js_dependencies
56
+ say 'Adding Sentry JS dependencies to package.json', :green
57
+
58
+ ensure_yarn
59
+ run "yarn add --dev #{JS_DEPS.join(' ')}"
60
+ end
61
+
62
+ def add_js_initializer
63
+ say 'Setting up Sentry for JS error reporting', :green
64
+
65
+ copy_file 'app/javascript/initializers/sentry.js'
66
+ append_to_file 'app/javascript/application.js', <<~JS
67
+ import './initializers/sentry'
68
+ JS
69
+ end
70
+
71
+ # Wire the Sentry webpack plugin into webpack.config.js. Idempotent and a
72
+ # no-op if there's no webpack.config.js or it's already wired, so the
73
+ # webpack generator can safely invoke this generator via its --sentry option.
74
+ def configure_webpack
75
+ config = File.expand_path('webpack.config.js', destination_root)
76
+ return unless File.exist?(config)
77
+ return if File.read(config).include?('@sentry/webpack-plugin')
78
+
79
+ say 'Wiring Sentry into webpack.config.js', :green
80
+
81
+ inject_into_file 'webpack.config.js',
82
+ "import { sentryWebpackPlugin } from '@sentry/webpack-plugin'\n",
83
+ after: "import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'\n"
84
+
85
+ inject_into_file 'webpack.config.js', after: "'process.env.RAILS_ENV': JSON.stringify(process.env.RAILS_ENV),\n" do
86
+ <<-JS
87
+ 'process.env.SENTRY_DSN': JSON.stringify(process.env.SENTRY_DSN),
88
+ 'process.env.SENTRY_ENVIRONMENT': JSON.stringify(process.env.SENTRY_ENVIRONMENT),
89
+ 'process.env.SENTRY_AUTH_TOKEN': JSON.stringify(process.env.SENTRY_AUTH_TOKEN),
90
+ JS
91
+ end
92
+
93
+ gsub_file 'webpack.config.js', " })\n ].filter(Boolean)", <<-JS.chomp
94
+ }),
95
+
96
+ // Upload source maps to Sentry in production for easier debugging
97
+ mode === 'production' &&
98
+ !process.env.CI &&
99
+ sentryWebpackPlugin({
100
+ authToken: process.env.SENTRY_AUTH_TOKEN,
101
+ org: 'rolemodel-software',
102
+ project: '#{sentry_app_name}',
103
+ telemetry: false,
104
+ applicationKey: 'app-frontend'
105
+ })
106
+ ].filter(Boolean)
107
+ JS
108
+ end
109
+
110
+ def finishing_notes
111
+ say <<~NOTES
112
+
113
+ *** Update the sentryWebpackPlugin `project` and `applicationKey` in webpack.config.js,
114
+ and the matching `filterKeys` in app/javascript/initializers/sentry.js, to your Sentry project.
115
+
116
+ *** Set the SENTRY_DSN, SENTRY_ENVIRONMENT, and SENTRY_AUTH_TOKEN environment variables.
117
+ NOTES
118
+ end
119
+
120
+ private
121
+
122
+ def sentry_app_name
123
+ Rails.application.class.module_parent_name.underscore
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,34 @@
1
+ import * as Sentry from '@sentry/browser'
2
+
3
+ // Only initialize Sentry in production and staging environments
4
+ // This avoids issues with ad blockers during local development
5
+ const environment = process.env.SENTRY_ENVIRONMENT || process.env.RAILS_ENV
6
+ const shouldInitialize = environment === 'production' || environment === 'staging'
7
+
8
+ if (shouldInitialize) {
9
+ Sentry.init({
10
+ dsn: process.env.SENTRY_DSN,
11
+ environment: environment,
12
+ sendDefaultPii: false,
13
+ tracesSampleRate: 0.1,
14
+ replaysOnErrorSampleRate: 1.0,
15
+ integrations: [
16
+ Sentry.thirdPartyErrorFilterIntegration({
17
+ // Must match the applicationKey in the sentryWebpackPlugin configuration
18
+ filterKeys: ['app-frontend'],
19
+ behaviour: 'drop-error-if-exclusively-contains-third-party-frames'
20
+ })
21
+ ],
22
+
23
+ // Capture user context from meta tags rendered by Rails
24
+ beforeSend(event) {
25
+ const userId = document.querySelector('meta[name="current-user-id"]')?.content
26
+
27
+ if (userId) {
28
+ event.user = { id: userId }
29
+ }
30
+
31
+ return event
32
+ }
33
+ })
34
+ }
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ Sentry.init do |config|
4
+ config.dsn = ENV['SENTRY_DSN']
5
+ config.enable_logs = true
6
+ config.debug = Rails.env.local?
7
+
8
+ # Traces sampling — 5% for HTTP requests, 10% for other transactions
9
+ config.traces_sampler = lambda do |sampling_context|
10
+ transaction_context = sampling_context[:transaction_context]
11
+
12
+ case transaction_context[:op]
13
+ when /^http/
14
+ 0.05
15
+ else
16
+ 0.10
17
+ end
18
+ end
19
+
20
+ # Enable profiling at 100% of sampled transactions
21
+ config.profiles_sample_rate = 1.0
22
+
23
+ # Breadcrumbs configuration
24
+ config.breadcrumbs_logger = [:sentry_logger, :http_logger]
25
+
26
+ # Exclude common noisy exceptions
27
+ config.excluded_exceptions += [
28
+ 'ActiveRecord::RecordNotFound',
29
+ 'ActionController::RoutingError',
30
+ ]
31
+
32
+ # Include local variables for debugging
33
+ config.include_local_variables = true
34
+
35
+ # Filter out health check transactions
36
+ config.before_send_transaction = lambda do |event, _hint|
37
+ return nil if event.transaction&.match?(/health|ping|metrics/)
38
+ event
39
+ end
40
+
41
+ # Filter sensitive data using Rails' parameter filter
42
+ filter = ActiveSupport::ParameterFilter.new(Rails.application.config.filter_parameters)
43
+ config.send_default_pii = false
44
+
45
+ config.before_send = lambda do |event, _hint|
46
+ if event.extra
47
+ event.extra = filter.filter(event.extra)
48
+ end
49
+
50
+ if event.user
51
+ event.user = filter.filter(event.user)
52
+ end
53
+
54
+ if event.contexts
55
+ event.contexts = filter.filter(event.contexts)
56
+ end
57
+
58
+ event
59
+ end
60
+
61
+ config.enabled_patches << :logger
62
+ config.std_lib_logger_filter = proc do |_logger, _message, severity|
63
+ [:warn, :error, :fatal].include?(severity)
64
+ end
65
+
66
+ config.rails.register_error_subscriber = Rails.env.production?
67
+ config.rails.structured_logging.subscribers = config.rails.structured_logging.subscribers.slice(:action_controller)
68
+ end
@@ -8,3 +8,9 @@
8
8
  * SimpleForm scaffold generation template
9
9
 
10
10
  Adds SimpleForm for simplified and unified styling that automatically wraps inputs with classes
11
+
12
+ ## Custom inputs
13
+
14
+ All standard custom inputs are included by default. The generator prompts you to
15
+ confirm whether to include the `tailored_select` custom input, since not every
16
+ project uses the tailored-select web component.
@@ -9,3 +9,6 @@ Example:
9
9
  config/locales/simple_form.en.yml
10
10
  simple_form input wrapper classes
11
11
  simple_form template file
12
+
13
+ Pass --tailored_select to also install the experimental tailored_select
14
+ component input (via the rolemodel:tailored_select generator).
@@ -4,6 +4,9 @@ module Rolemodel
4
4
  class SimpleFormGenerator < GeneratorBase
5
5
  source_root File.expand_path('templates', __dir__)
6
6
 
7
+ class_option :tailored_select, type: :boolean, default: false,
8
+ desc: 'Install the tailored_select experimental component input'
9
+
7
10
  def add_gem
8
11
  Bundler.with_unbundled_env do
9
12
  bundle_command 'add simple_form'
@@ -16,5 +19,13 @@ module Rolemodel
16
19
  copy_file 'config/initializers/simple_form.rb'
17
20
  copy_file 'config/locales/simple_form.en.yml'
18
21
  end
22
+
23
+ def install_tailored_select
24
+ return unless options.tailored_select?
25
+
26
+ # The tailored_select generator owns the input template and installs it
27
+ # here because simple_form is now present.
28
+ generate 'rolemodel:tailored_select'
29
+ end
19
30
  end
20
31
  end
@@ -14,7 +14,7 @@
14
14
  # <%= f.input :my_field, as: :switch_checkbox, label_after_input: true %>
15
15
  # <%= f.input :my_field, as: :switch_checkbox, wrapper: :switch_wrapper %>
16
16
  class SwitchCheckboxInput < SimpleForm::Inputs::BooleanInput
17
- def input(wrapper_options = nil)
17
+ def input(wrapper_options = nil) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
18
18
  merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
19
19
 
20
20
  switch_group = template.content_tag(:div, class: "switch #{'switch--small' if options[:small]}") do
@@ -1,5 +1,6 @@
1
1
  Description:
2
- runs the tailored select generator
2
+ Installs the @rolemodel/tailored-select package. If the app is using
3
+ SimpleForm, it also installs the matching tailored_select SimpleForm input.
3
4
 
4
5
  Example:
5
6
  rails generate rolemodel:tailored_select
@@ -7,7 +7,22 @@ module Rolemodel
7
7
  def add_tailored_select_package
8
8
  say 'Installing Tailored Select package', :green
9
9
 
10
+ ensure_yarn
10
11
  run 'yarn add @rolemodel/tailored-select'
11
12
  end
13
+
14
+ def add_simple_form_input
15
+ return unless simple_form?
16
+
17
+ say 'Installing the Tailored Select SimpleForm input', :green
18
+
19
+ copy_file 'app/inputs/tailored_select_input.rb'
20
+ end
21
+
22
+ private
23
+
24
+ def simple_form?
25
+ File.exist?(File.expand_path('config/initializers/simple_form.rb', destination_root))
26
+ end
12
27
  end
13
28
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.configure do |config|
2
4
  config.include FactoryBot::Syntax::Methods
3
5
  end
@@ -18,8 +18,8 @@ module Rolemodel
18
18
  raise Thor::InvocationError, 'a --github_package_token option or GITHUB_PACKAGES_TOKEN environment variable is required' if options[:github_package_token].blank?
19
19
  end
20
20
 
21
- def yarn_init_unless_package_json_exists
22
- run 'yarn init' unless File.exist?(File.expand_path('package.json', destination_root))
21
+ def enable_corepack_and_yarn
22
+ ensure_yarn
23
23
  end
24
24
 
25
25
  def add_browser_test_script
@@ -70,7 +70,10 @@ RSpec.configure do |config|
70
70
  prep_passed = system 'rails spec:prepare'
71
71
  ENV['ASSET_PRECOMPILE_SUCCESSFUL'] = 'true'
72
72
 
73
- abort "\nYour assets didn't compile. Exiting WITHOUT running any tests. Review the output above to resolve any errors." unless prep_passed
73
+ unless prep_passed
74
+ abort "\nYour assets didn't compile. Exiting WITHOUT running any tests. " \
75
+ 'Review the output above to resolve any errors.'
76
+ end
74
77
  end
75
78
  end
76
79
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  RSpec.configure do |config|
2
4
  Capybara.register_driver :playwright_headless do |app|
3
5
  create_driver(app)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionCableHelper
2
4
  def wait_for_stream_connection
3
5
  expect(page).to have_selector('turbo-cable-stream-source[connected]', visible: false)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TestElementHelper
2
4
  include ActionView::RecordIdentifier
3
5
 
@@ -1,4 +1,6 @@
1
- Dir[Rails.root.join('spec', 'support', 'helpers', '**', '*.rb')].each { |f| require f }
1
+ # frozen_string_literal: true
2
+
3
+ Rails.root.glob('spec/support/helpers/**/*.rb').each { |f| require f }
2
4
 
3
5
  # this is a place to pull in all your app specific DSL methods.
4
6
 
@@ -23,6 +23,7 @@ module Rolemodel
23
23
  def add_dev_dependencies
24
24
  say 'Adding new dev dependency to package.json', :green
25
25
 
26
+ ensure_yarn
26
27
  run "yarn add --dev #{DEV_DEPENDENCIES.join(' ')}"
27
28
  end
28
29
 
@@ -7,6 +7,7 @@ module Rolemodel
7
7
  def turbo_confirm
8
8
  say 'Installing Turbo Confirm package', :green
9
9
 
10
+ ensure_yarn
10
11
  run 'yarn add @rolemodel/turbo-confirm'
11
12
  end
12
13
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TurboFrameLinkHelper
2
4
  def panel_link_to(...)
3
5
  link_to_frame('panel', ...)
@@ -25,6 +25,7 @@ module Rolemodel
25
25
  def install_shoelace
26
26
  say 'Installing Shoelace package', :green
27
27
 
28
+ ensure_yarn
28
29
  run 'yarn add @shoelace-style/shoelace'
29
30
  end
30
31
 
@@ -5,4 +5,5 @@
5
5
  * Webpack v5
6
6
  * Uses `esbuild-loader` instead of Babel to transpile JS
7
7
  * Uses PostCSS to compile CSS and SCSS
8
- * Honeybadger error monitoring for JS. Run `bundle exec honeybadger install [YOUR API KEY HERE]` to setup Honeybadger for Ruby.
8
+
9
+ For error monitoring, run the `rolemodel:sentry` generator.
@@ -1,7 +1,5 @@
1
1
  import path from 'path'
2
2
  import webpack from 'webpack'
3
- import TerserPlugin from 'terser-webpack-plugin'
4
- import HoneybadgerSourceMapPlugin from '@honeybadger-io/webpack'
5
3
  import MiniCssExtractPlugin from 'mini-css-extract-plugin'
6
4
  import CssMinimizerPlugin from 'css-minimizer-webpack-plugin'
7
5
 
@@ -73,7 +71,8 @@ export default {
73
71
  optimization: {
74
72
  minimize: mode === 'production',
75
73
  minimizer: [
76
- new TerserPlugin(),
74
+ // '...' keeps webpack 5's built-in JS minimizer (terser)
75
+ '...',
77
76
  new CssMinimizerPlugin()
78
77
  ]
79
78
  },
@@ -88,17 +87,8 @@ export default {
88
87
 
89
88
  // Replace ENV variables at build time
90
89
  new webpack.DefinePlugin({
91
- 'process.env.HONEYBADGER_API_KEY': JSON.stringify(process.env.HONEYBADGER_API_KEY),
92
- 'process.env.HONEYBADGER_ENV': JSON.stringify(process.env.HONEYBADGER_ENV),
93
90
  'process.env.RAILS_ENV': JSON.stringify(process.env.RAILS_ENV),
94
91
  'process.env.SOURCE_VERSION': JSON.stringify(process.env.SOURCE_VERSION)
95
- }),
96
-
97
- // Send source maps to HoneyBadger in production for easier debugging
98
- (mode === 'production' && !process.env.CI) && new HoneybadgerSourceMapPlugin({
99
- apiKey: process.env.HONEYBADGER_API_KEY,
100
- assetsUrl: process.env.ASSETS_URL,
101
- revision: process.env.SOURCE_VERSION
102
92
  })
103
93
  ].filter(Boolean)
104
94
  }
@@ -2,9 +2,10 @@ module Rolemodel
2
2
  class WebpackGenerator < GeneratorBase
3
3
  source_root File.expand_path('templates', __dir__)
4
4
 
5
+ class_option :sentry, type: :boolean, default: false,
6
+ desc: 'Also run the rolemodel:sentry generator'
7
+
5
8
  DEV_DEPS = %w[
6
- @honeybadger-io/webpack
7
- @honeybadger-io/js
8
9
  esbuild
9
10
  esbuild-loader
10
11
  webpack
@@ -31,6 +32,12 @@ module Rolemodel
31
32
  create_file '.node-version', NODE_VERSION, force: true
32
33
  end
33
34
 
35
+ def enable_corepack_and_yarn
36
+ say 'Enabling Corepack and pinning the project to Yarn 4+', :green
37
+
38
+ ensure_yarn
39
+ end
40
+
34
41
  def force_node_to_use_es_modules
35
42
  say 'Configuring project to use ES Modules instead of CommonJS', :green
36
43
 
@@ -50,15 +57,6 @@ module Rolemodel
50
57
  run "yarn add --dev #{dependencies.join(' ')}"
51
58
  end
52
59
 
53
- def honeybadger_setup
54
- say 'Setting up Honeybadger for JS error reporting', :green
55
-
56
- copy_file 'app/javascript/initializers/honeybadger.js'
57
- append_to_file 'app/javascript/application.js', <<~JS
58
- import './initializers/honeybadger'
59
- JS
60
- end
61
-
62
60
  def replace_css_entrypoint_with_scss
63
61
  say 'Replacing CSS entrypoint file with SCSS version', :green
64
62
 
@@ -72,5 +70,15 @@ module Rolemodel
72
70
  copy_file 'postcss.config.cjs', force: true
73
71
  copy_file 'webpack.config.js', force: true
74
72
  end
73
+
74
+ # All Sentry concerns (including wiring its plugin into webpack.config.js)
75
+ # live in the Sentry generator; this option is a convenience for setting up
76
+ # both in one run. Runs last so webpack.config.js exists before the Sentry
77
+ # generator injects into it.
78
+ def run_sentry_generator
79
+ return unless options.sentry?
80
+
81
+ generate 'rolemodel:sentry'
82
+ end
75
83
  end
76
84
  end
@@ -13,5 +13,42 @@ module Rolemodel
13
13
  def run_bundle
14
14
  bundle_command("install --quiet", "BUNDLE_IGNORE_MESSAGES" => "1")
15
15
  end
16
+
17
+ # Enable Corepack and pin the project to Yarn 4+ (instead of the classic
18
+ # Yarn 1.22). Idempotent, so any generator can call it before running a
19
+ # `yarn` command to guarantee the modern toolchain is in place.
20
+ def ensure_yarn
21
+ return if @yarn_ensured
22
+
23
+ run 'corepack enable'
24
+
25
+ unless File.exist?(File.expand_path('package.json', destination_root))
26
+ create_file 'package.json', JSON.pretty_generate({}) + "\n"
27
+ end
28
+
29
+ # Configure the node-modules linker so webpack, Playwright, and the Rails
30
+ # asset pipeline keep working (Yarn 4 defaults to Plug'n'Play otherwise).
31
+ create_file '.yarnrc.yml', "nodeLinker: node-modules\n", force: true
32
+
33
+ pin_yarn_version
34
+ ignore_yarn_install_state
35
+
36
+ @yarn_ensured = true
37
+ end
38
+
39
+ def pin_yarn_version
40
+ modify_json_file('package.json') do |hash|
41
+ hash['packageManager'] = "yarn@#{YARN_VERSION}"
42
+ hash
43
+ end
44
+ end
45
+
46
+ def ignore_yarn_install_state
47
+ gitignore = File.expand_path('.gitignore', destination_root)
48
+ return create_file '.gitignore', "/.yarn/install-state.gz\n" unless File.exist?(gitignore)
49
+ return if File.read(gitignore).include?('/.yarn/install-state.gz')
50
+
51
+ append_to_file '.gitignore', "\n/.yarn/install-state.gz\n"
52
+ end
16
53
  end
17
54
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rolemodel
4
- VERSION = '2.1.0'
4
+ VERSION = '2.2.0'
5
5
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module Rolemodel
4
4
  NODE_VERSION = '24.12.0'
5
+ YARN_VERSION = '4.13.0'
5
6
  RUBY_VERSION = '4.0.1'
6
7
 
7
8
  GEM_LIB = File.expand_path(__dir__)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rolemodel-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - RoleModel Software Inc
@@ -163,6 +163,9 @@ files:
163
163
  - lib/generators/rolemodel/README.md
164
164
  - lib/generators/rolemodel/USAGE
165
165
  - lib/generators/rolemodel/all_generator.rb
166
+ - lib/generators/rolemodel/core_setup/README.md
167
+ - lib/generators/rolemodel/core_setup/USAGE
168
+ - lib/generators/rolemodel/core_setup/core_setup_generator.rb
166
169
  - lib/generators/rolemodel/editors/README.md
167
170
  - lib/generators/rolemodel/editors/USAGE
168
171
  - lib/generators/rolemodel/editors/editors_generator.rb
@@ -175,6 +178,8 @@ files:
175
178
  - lib/generators/rolemodel/github/templates/instructions
176
179
  - lib/generators/rolemodel/github/templates/pull_request_template.md
177
180
  - lib/generators/rolemodel/github/templates/workflows/ci.yml.tt
181
+ - lib/generators/rolemodel/github/templates/workflows/deploy-production.yml
182
+ - lib/generators/rolemodel/github/templates/workflows/deploy-staging.yml
178
183
  - lib/generators/rolemodel/good_job/README.md
179
184
  - lib/generators/rolemodel/good_job/USAGE
180
185
  - lib/generators/rolemodel/good_job/good_job_generator.rb
@@ -185,6 +190,7 @@ files:
185
190
  - lib/generators/rolemodel/heroku/heroku_generator.rb
186
191
  - lib/generators/rolemodel/heroku/templates/Procfile
187
192
  - lib/generators/rolemodel/heroku/templates/app.json.tt
193
+ - lib/generators/rolemodel/heroku/templates/deploy-app/SKILL.md
188
194
  - lib/generators/rolemodel/kaminari/README.md
189
195
  - lib/generators/rolemodel/kaminari/USAGE
190
196
  - lib/generators/rolemodel/kaminari/kaminari_generator.rb
@@ -205,8 +211,6 @@ files:
205
211
  - lib/generators/rolemodel/linters/rubocop/README.md
206
212
  - lib/generators/rolemodel/linters/rubocop/USAGE
207
213
  - lib/generators/rolemodel/linters/rubocop/rubocop_generator.rb
208
- - lib/generators/rolemodel/linters/rubocop/templates/lib/cops/form_error_response.rb
209
- - lib/generators/rolemodel/linters/rubocop/templates/lib/cops/no_chrome_tag.rb
210
214
  - lib/generators/rolemodel/lograge/README.md
211
215
  - lib/generators/rolemodel/lograge/USAGE
212
216
  - lib/generators/rolemodel/lograge/lograge_generator.rb
@@ -282,13 +286,11 @@ files:
282
286
  - lib/generators/rolemodel/saas/devise/templates/spec/models/user_spec.rb
283
287
  - lib/generators/rolemodel/saas/devise/templates/spec/support/devise.rb
284
288
  - lib/generators/rolemodel/saas/devise/templates/spec/system/users_spec.rb
285
- - lib/generators/rolemodel/semaphore/README.md
286
- - lib/generators/rolemodel/semaphore/USAGE
287
- - lib/generators/rolemodel/semaphore/semaphore_generator.rb
288
- - lib/generators/rolemodel/semaphore/templates/heroku-deployment-commands.sh
289
- - lib/generators/rolemodel/semaphore/templates/production-deploy.yml.erb
290
- - lib/generators/rolemodel/semaphore/templates/semaphore.yml.erb
291
- - lib/generators/rolemodel/semaphore/templates/staging-deploy.yml.erb
289
+ - lib/generators/rolemodel/sentry/README.md
290
+ - lib/generators/rolemodel/sentry/USAGE
291
+ - lib/generators/rolemodel/sentry/sentry_generator.rb
292
+ - lib/generators/rolemodel/sentry/templates/app/javascript/initializers/sentry.js
293
+ - lib/generators/rolemodel/sentry/templates/config/initializers/sentry.rb
292
294
  - lib/generators/rolemodel/simple_form/README.md
293
295
  - lib/generators/rolemodel/simple_form/USAGE
294
296
  - lib/generators/rolemodel/simple_form/simple_form_generator.rb
@@ -297,7 +299,6 @@ files:
297
299
  - lib/generators/rolemodel/simple_form/templates/app/inputs/grouped_collection_select_input.rb
298
300
  - lib/generators/rolemodel/simple_form/templates/app/inputs/segmented_control_input.rb
299
301
  - lib/generators/rolemodel/simple_form/templates/app/inputs/switch_checkbox_input.rb
300
- - lib/generators/rolemodel/simple_form/templates/app/inputs/tailored_select_input.rb
301
302
  - lib/generators/rolemodel/simple_form/templates/config/initializers/simple_form.rb
302
303
  - lib/generators/rolemodel/simple_form/templates/config/locales/simple_form.en.yml
303
304
  - lib/generators/rolemodel/simple_form/templates/lib/templates/slim/scaffold/_form.html.slim.tt
@@ -324,6 +325,7 @@ files:
324
325
  - lib/generators/rolemodel/tailored_select/README.md
325
326
  - lib/generators/rolemodel/tailored_select/USAGE
326
327
  - lib/generators/rolemodel/tailored_select/tailored_select_generator.rb
328
+ - lib/generators/rolemodel/tailored_select/templates/app/inputs/tailored_select_input.rb
327
329
  - lib/generators/rolemodel/testing/README.md
328
330
  - lib/generators/rolemodel/testing/USAGE
329
331
  - lib/generators/rolemodel/testing/all_generator.rb
@@ -391,7 +393,6 @@ files:
391
393
  - lib/generators/rolemodel/webpack/README.md
392
394
  - lib/generators/rolemodel/webpack/USAGE
393
395
  - lib/generators/rolemodel/webpack/templates/app/assets/stylesheets/application.scss
394
- - lib/generators/rolemodel/webpack/templates/app/javascript/initializers/honeybadger.js
395
396
  - lib/generators/rolemodel/webpack/templates/postcss.config.cjs
396
397
  - lib/generators/rolemodel/webpack/templates/webpack.config.js
397
398
  - lib/generators/rolemodel/webpack/webpack_generator.rb