rolemodel-rails 2.0.1 → 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.
- checksums.yaml +4 -4
- data/README.md +121 -2
- data/lib/generators/rolemodel/all_generator.rb +1 -0
- data/lib/generators/rolemodel/core_setup/README.md +27 -0
- data/lib/generators/rolemodel/core_setup/USAGE +12 -0
- data/lib/generators/rolemodel/core_setup/core_setup_generator.rb +19 -0
- data/lib/generators/rolemodel/github/README.md +4 -0
- data/lib/generators/rolemodel/github/templates/workflows/deploy-production.yml +34 -0
- data/lib/generators/rolemodel/github/templates/workflows/deploy-staging.yml +35 -0
- data/lib/generators/rolemodel/good_job/good_job_generator.rb +0 -2
- data/lib/generators/rolemodel/heroku/README.md +20 -1
- data/lib/generators/rolemodel/heroku/heroku_generator.rb +35 -0
- data/lib/generators/rolemodel/heroku/templates/app.json.tt +5 -2
- data/lib/generators/rolemodel/heroku/templates/deploy-app/SKILL.md +202 -0
- data/lib/generators/rolemodel/linters/eslint/eslint_generator.rb +1 -0
- data/lib/generators/rolemodel/linters/rubocop/rubocop_generator.rb +5 -1
- data/lib/generators/rolemodel/lograge/README.md +1 -1
- data/lib/generators/rolemodel/lograge/templates/config/initializers/lograge.rb +1 -1
- data/lib/generators/rolemodel/optics/base/base_generator.rb +1 -0
- data/lib/generators/rolemodel/react/react_generator.rb +1 -0
- data/lib/generators/rolemodel/readme/templates/README.md.erb +2 -4
- data/lib/generators/rolemodel/sentry/README.md +25 -0
- data/lib/generators/rolemodel/sentry/USAGE +13 -0
- data/lib/generators/rolemodel/sentry/sentry_generator.rb +126 -0
- data/lib/generators/rolemodel/sentry/templates/app/javascript/initializers/sentry.js +34 -0
- data/lib/generators/rolemodel/sentry/templates/config/initializers/sentry.rb +68 -0
- data/lib/generators/rolemodel/simple_form/README.md +6 -0
- data/lib/generators/rolemodel/simple_form/USAGE +3 -0
- data/lib/generators/rolemodel/simple_form/simple_form_generator.rb +11 -0
- data/lib/generators/rolemodel/simple_form/templates/app/inputs/switch_checkbox_input.rb +1 -1
- data/lib/generators/rolemodel/tailored_select/USAGE +2 -1
- data/lib/generators/rolemodel/tailored_select/tailored_select_generator.rb +15 -0
- data/lib/generators/rolemodel/testing/factory_bot/templates/support/factory_bot.rb +2 -0
- data/lib/generators/rolemodel/testing/jasmine_playwright/jasmine_playwright_generator.rb +2 -2
- data/lib/generators/rolemodel/testing/rspec/templates/spec/rails_helper.rb.tt +4 -1
- data/lib/generators/rolemodel/testing/rspec/templates/spec/support/capybara_drivers.rb.tt +2 -0
- data/lib/generators/rolemodel/testing/rspec/templates/spec/support/helpers/action_cable_helper.rb +2 -0
- data/lib/generators/rolemodel/testing/rspec/templates/spec/support/helpers/test_element_helper.rb +2 -0
- data/lib/generators/rolemodel/testing/rspec/templates/spec/support/helpers.rb.tt +3 -1
- data/lib/generators/rolemodel/testing/vitest/vitest_generator.rb +1 -0
- data/lib/generators/rolemodel/ui_components/modals/modals_generator.rb +1 -0
- data/lib/generators/rolemodel/ui_components/modals/templates/app/helpers/turbo_frame_link_helper.rb +2 -0
- data/lib/generators/rolemodel/ui_components/navbar/navbar_generator.rb +1 -0
- data/lib/generators/rolemodel/webpack/README.md +2 -1
- data/lib/generators/rolemodel/webpack/templates/webpack.config.js +2 -12
- data/lib/generators/rolemodel/webpack/webpack_generator.rb +19 -11
- data/lib/rolemodel/generator_base.rb +37 -0
- data/lib/rolemodel/utility/task_tools.rb +27 -0
- data/lib/rolemodel/version.rb +1 -1
- data/lib/rolemodel-rails.rb +1 -0
- metadata +13 -12
- data/lib/generators/rolemodel/linters/rubocop/templates/lib/cops/form_error_response.rb +0 -54
- data/lib/generators/rolemodel/linters/rubocop/templates/lib/cops/no_chrome_tag.rb +0 -45
- data/lib/generators/rolemodel/semaphore/README.md +0 -15
- data/lib/generators/rolemodel/semaphore/USAGE +0 -11
- data/lib/generators/rolemodel/semaphore/semaphore_generator.rb +0 -25
- data/lib/generators/rolemodel/semaphore/templates/heroku-deployment-commands.sh +0 -3
- data/lib/generators/rolemodel/semaphore/templates/production-deploy.yml.erb +0 -17
- data/lib/generators/rolemodel/semaphore/templates/semaphore.yml.erb +0 -145
- data/lib/generators/rolemodel/semaphore/templates/staging-deploy.yml.erb +0 -17
- data/lib/generators/rolemodel/webpack/templates/app/javascript/initializers/honeybadger.js +0 -26
- /data/lib/generators/rolemodel/{simple_form → tailored_select}/templates/app/inputs/tailored_select_input.rb +0 -0
|
@@ -81,12 +81,10 @@ When finished with the feature and the code has been reviewed, the commits shoul
|
|
|
81
81
|
## [Staging](http://staging.<applicationname>.com)
|
|
82
82
|
## [Production](http://app.<applicationname>.com)
|
|
83
83
|
## External services
|
|
84
|
-
* [
|
|
85
|
-
* [Skylight](http://skylight.io)
|
|
84
|
+
* [Sentry](https://sentry.io)
|
|
86
85
|
* [SendGrid](http://sendgrid.com/RoleModel)
|
|
87
86
|
* [Heroku](http://herokuapp.com)
|
|
88
87
|
|
|
89
|
-
## [CI](http://semaphoreci.com/RoleModel)
|
|
90
88
|
## [Core project presentation](http://docs.google.com)
|
|
91
89
|
## [List of contributors](http://github.com/RoleModel)
|
|
92
90
|
## [Change log](file://./docs/change_log.md)
|
|
@@ -113,4 +111,4 @@ The application is deployed to Heroku. They are also hosting the DNS. We certifi
|
|
|
113
111
|
* Larry Anderson - 919-555-1213
|
|
114
112
|
|
|
115
113
|
# Copyright & licensing
|
|
116
|
-
Copyright (c)
|
|
114
|
+
Copyright (c) 2026 Closed Source @CompanyName
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Sentry Generator
|
|
2
|
+
|
|
3
|
+
## What you get
|
|
4
|
+
|
|
5
|
+
Error monitoring and performance tracing via [Sentry](https://sentry.io) for both the Ruby and JavaScript sides of the app.
|
|
6
|
+
|
|
7
|
+
### Ruby
|
|
8
|
+
|
|
9
|
+
* The `sentry-rails` gem
|
|
10
|
+
* `config/initializers/sentry.rb` with sensible defaults: traces sampling, profiling, PII filtering via Rails' parameter filter, noisy-exception exclusion, and health-check transaction filtering
|
|
11
|
+
* Sentry user context wired into `app/controllers/application_controller.rb` via a `set_sentry_user` before_action (Devise-friendly, gated on `user_signed_in?`)
|
|
12
|
+
|
|
13
|
+
### JavaScript
|
|
14
|
+
|
|
15
|
+
* `@sentry/browser` and `@sentry/webpack-plugin` dependencies
|
|
16
|
+
* `app/javascript/initializers/sentry.js`, which initializes Sentry in production and staging only (avoids ad-blocker noise in development) and attaches the current user from a `current-user-id` meta tag
|
|
17
|
+
* The `sentryWebpackPlugin` wired into `webpack.config.js` to upload source maps in production
|
|
18
|
+
|
|
19
|
+
Depends on the `rolemodel:webpack` generator having already created `webpack.config.js` and `app/javascript/application.js`.
|
|
20
|
+
|
|
21
|
+
## After running
|
|
22
|
+
|
|
23
|
+
* Update the `project` and `applicationKey` in `webpack.config.js`, and the matching `filterKeys` in `app/javascript/initializers/sentry.js`, to match your Sentry project.
|
|
24
|
+
* Set the `SENTRY_DSN`, `SENTRY_ENVIRONMENT`, and `SENTRY_AUTH_TOKEN` environment variables.
|
|
25
|
+
* Render a `current-user-id` meta tag in your layout so the browser SDK can attach user context.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Description:
|
|
2
|
+
Sets up Sentry error monitoring for both Ruby and JavaScript
|
|
3
|
+
|
|
4
|
+
Example:
|
|
5
|
+
rails generate rolemodel:sentry
|
|
6
|
+
|
|
7
|
+
This will:
|
|
8
|
+
Add the sentry-rails gem
|
|
9
|
+
Create config/initializers/sentry.rb
|
|
10
|
+
Add Sentry user context to app/controllers/application_controller.rb
|
|
11
|
+
Add @sentry/browser and @sentry/webpack-plugin JS dependencies
|
|
12
|
+
Create app/javascript/initializers/sentry.js
|
|
13
|
+
Wire the Sentry webpack plugin into webpack.config.js
|
|
@@ -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.
|
|
@@ -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
|
|
@@ -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
|
|
@@ -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
|
|
22
|
-
|
|
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
|
-
|
|
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
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|