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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4af0b11896cbe75d88cf7934e0322e9c8145656e115d13888473c642aac86716
4
- data.tar.gz: ad3a3ab92a726d9ce7333ae9db2f45762ce02d5ded8a1c8b86b0923eb685c3c0
3
+ metadata.gz: f1cf52276aac5192b4349379e6f7792232fbdabae218e3d3ac6fbde81cd1ae01
4
+ data.tar.gz: 3d13d291529c487fb93b8d7c7816acb05e3b686fd4129173208611c0c714558d
5
5
  SHA512:
6
- metadata.gz: 0c3cbd55ba285c70505447bc34b0509b98df66093e4fe35b5ae1007cb2252cd080a5779c434a4033302096d461a63c0365ea11b6b288d488adbbc8e31267a797
7
- data.tar.gz: 41ba42965a2ab047098f450cf3c1fe3d7b69cffda6b4a634da757c6df229f4173b0b1dabc403d77493d1feb891ad9b364f221b6ef61e6c739e76663658feb91f
6
+ metadata.gz: 2fdd780f5256aac4772e65e9282569956b2b7e8a2fd663caa068d072a720a7905a721953725df5ccf07d671f600e31d7ec781831782128b63182b8cf586af61b
7
+ data.tar.gz: 453e93d083a80ff9ce7a2b2ea1f02079e334e2a4ae5028ee91d7b67d0c6cb7d7d8f013e9bab777a9da2f77b767f354af38688a2ac101ba8e94dd28c5979e22ee
data/README.md CHANGED
@@ -40,7 +40,14 @@ And then execute:
40
40
 
41
41
  ## Usage
42
42
 
43
- Run all generators (useful on a new app)
43
+ Run the core generators (recommended on a new app — sets up our standard
44
+ baseline so the app is ready to push straight to Heroku)
45
+
46
+ ```shell
47
+ bin/rails g rolemodel:core_setup
48
+ ```
49
+
50
+ Or run every generator, including app-specific extras like React, SaaS/Devise, and GoodJob
44
51
 
45
52
  ```shell
46
53
  bin/rails g rolemodel:all
@@ -66,6 +73,7 @@ bin/rails g
66
73
 
67
74
  ## Generators
68
75
 
76
+ * [Core Setup](./lib/generators/rolemodel/core_setup)
69
77
  * [Github](./lib/generators/rolemodel/github)
70
78
  * [Semaphore](./lib/generators/rolemodel/semaphore)
71
79
  * [Heroku](./lib/generators/rolemodel/heroku)
@@ -215,7 +223,7 @@ DRY_RUN=true rake users:dev[_,bob@example.com]
215
223
 
216
224
  ## Development
217
225
 
218
- Install the versions of Node and Ruby specified in `.node-version` and `.ruby-version` on your machine. https://asdf-vm.com/ is a great tool for managing language versions. Then run `npm install -g yarn`.
226
+ Install the versions of Node and Ruby specified in `.node-version` and `.ruby-version` on your machine. https://asdf-vm.com/ is a great tool for managing language versions. Then run `corepack enable` to activate the Yarn 4+ version pinned by each project's `packageManager` field.
219
227
 
220
228
  ## Adding new Generators
221
229
 
@@ -8,6 +8,7 @@ module Rolemodel
8
8
  generate 'rolemodel:heroku'
9
9
  generate 'rolemodel:readme'
10
10
  generate 'rolemodel:webpack'
11
+ generate 'rolemodel:sentry'
11
12
  generate 'rolemodel:react'
12
13
  generate 'rolemodel:slim'
13
14
  generate 'rolemodel:optics:all'
@@ -0,0 +1,27 @@
1
+ # Core Setup Generator
2
+
3
+ Runs the core generators every new Rails app should have. Unlike
4
+ `rolemodel:all`, this skips the app-specific extras (React, SaaS/Devise,
5
+ GoodJob, Kaminari, etc.) so you get just the standard baseline — a new Rails
6
+ app you can push straight to Heroku right after generation.
7
+
8
+ ## Prerequisites
9
+
10
+ - A freshly generated Rails app
11
+
12
+ ## What you get
13
+
14
+ - [GitHub](../github) — standard GitHub configuration
15
+ - [Heroku](../heroku) — standard Heroku deployment configuration
16
+ - [Readme](../readme) — standard project README
17
+ - [Webpack](../webpack) — Webpack v5 for JS and CSS
18
+ - [Sentry](../sentry) — error monitoring for Ruby and JavaScript
19
+ - [Slim](../slim) — Slim templates
20
+ - [Optics](../optics) — Optics design system
21
+ - [Testing](../testing) — RSpec, FactoryBot, parallel_tests, TestProf
22
+ (pass `--js-runner` to include jasmine-playwright-runner)
23
+ - [SimpleForm](../simple_form) — SimpleForm with our configuration
24
+ - [Linters](../linters) — Rubocop and ESLint
25
+ - [UI Components](../ui_components) — flash, the modal pattern, & Turbo 8 support
26
+ - [Editors](../editors) — EditorConfig and recommended VSCode extensions
27
+ - [Lograge](../lograge) — condensed request logging
@@ -0,0 +1,12 @@
1
+ Description:
2
+ Runs the core generators every new Rails app should have
3
+
4
+ Sets up GitHub config, Heroku deployment, README, Webpack, Sentry, Slim,
5
+ Optics, testing (RSpec & friends), SimpleForm, linters, Turbo 8+ support,
6
+ and Lograge. The result is an app that is ready to push directly to Heroku.
7
+
8
+ Pass --js-runner to also include jasmine-playwright-runner for browser
9
+ JS testing.
10
+
11
+ Example:
12
+ rails generate rolemodel:core_setup
@@ -0,0 +1,19 @@
1
+ module Rolemodel
2
+ class CoreSetupGenerator < ::Rolemodel::GeneratorBase
3
+ def run_the_core_generators
4
+ generate 'rolemodel:github'
5
+ generate 'rolemodel:heroku'
6
+ generate 'rolemodel:readme'
7
+ generate 'rolemodel:webpack'
8
+ generate 'rolemodel:sentry'
9
+ generate 'rolemodel:slim'
10
+ generate 'rolemodel:optics:all'
11
+ generate 'rolemodel:testing:all'
12
+ generate 'rolemodel:simple_form'
13
+ generate 'rolemodel:linters:all'
14
+ generate 'rolemodel:ui_components:flash'
15
+ generate 'rolemodel:ui_components:modals'
16
+ generate 'rolemodel:lograge'
17
+ end
18
+ end
19
+ end
@@ -9,6 +9,10 @@ It doesn't need to be run first, but the parallel_tests generator must be run in
9
9
  - CI workflow
10
10
  - A sensible default `ci.yml` to get you started with Github Actions. This will run linters, model tests, and system tests.
11
11
  - Along with the `ci.yml`, your `database.yml` will be modified to be able to be run in GHA.
12
+ - Deploy workflows
13
+ - `deploy-staging.yml` deploys to Heroku on every push to `main` (or manually); `deploy-production.yml` deploys manually via `workflow_dispatch`.
14
+ - Both target a GitHub deployment environment (`Staging`/`Production`) that provides `HEROKU_APP_NAME` and `HEROKU_APP_URL` variables, and authenticate with the org-level `HEROKU_IT_SUPPORT_API_KEY` secret and `HEROKU_IT_SUPPORT_EMAIL` variable.
15
+ - Note: the staging workflow will fail on pushes to `main` until the environment exists — run the `deploy-app` agent skill (installed by the heroku generator) to create the Heroku app and the GitHub environment.
12
16
  - Pull Request Template
13
17
  - When you open a Pull Request in Github it will use the Markdown file as a [template](./templates/pull_request_template.md) for the content of the PR.
14
18
  - Helpful for reminding collaborators to add specific details to the PR.
@@ -0,0 +1,34 @@
1
+ name: Deploy to Heroku Production
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ sha:
7
+ description: 'Specific SHA or Branch name (optional)'
8
+ required: false
9
+ type: string
10
+
11
+ concurrency: production-deployment
12
+
13
+ jobs:
14
+ deploy:
15
+ name: Deploy
16
+ runs-on: ubuntu-latest
17
+ timeout-minutes: 20
18
+ environment:
19
+ name: Production
20
+ url: ${{ vars.HEROKU_APP_URL }}
21
+ steps:
22
+ - uses: actions/checkout@v6
23
+
24
+ - name: Install Heroku CLI
25
+ run: |
26
+ curl https://cli-assets.heroku.com/install.sh | sh
27
+
28
+ - uses: akhileshns/heroku-deploy@v3.15.15
29
+ with:
30
+ heroku_api_key: ${{ secrets.HEROKU_IT_SUPPORT_API_KEY }}
31
+ heroku_app_name: ${{ vars.HEROKU_APP_NAME }}
32
+ heroku_email: ${{ vars.HEROKU_IT_SUPPORT_EMAIL }}
33
+ branch: ${{ inputs.sha || 'HEAD' }}
34
+ healthcheck: '${{ vars.HEROKU_APP_URL }}/up'
@@ -0,0 +1,35 @@
1
+ name: Deploy to Heroku Staging
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ sha:
7
+ description: 'Specific SHA or Branch name (optional)'
8
+ required: false
9
+ type: string
10
+ push: { branches: [ staging ] }
11
+
12
+ concurrency: staging-deployment
13
+
14
+ jobs:
15
+ deploy:
16
+ name: Deploy
17
+ runs-on: ubuntu-latest
18
+ timeout-minutes: 20
19
+ environment:
20
+ name: Staging
21
+ url: ${{ vars.HEROKU_APP_URL }}
22
+ steps:
23
+ - uses: actions/checkout@v6
24
+
25
+ - name: Install Heroku CLI
26
+ run: |
27
+ curl https://cli-assets.heroku.com/install.sh | sh
28
+
29
+ - uses: akhileshns/heroku-deploy@v3.15.15
30
+ with:
31
+ heroku_api_key: ${{ secrets.HEROKU_IT_SUPPORT_API_KEY }}
32
+ heroku_app_name: ${{ vars.HEROKU_APP_NAME }}
33
+ heroku_email: ${{ vars.HEROKU_IT_SUPPORT_EMAIL }}
34
+ branch: ${{ inputs.sha || 'HEAD' }}
35
+ healthcheck: "${{ vars.HEROKU_APP_URL }}/up"
@@ -64,8 +64,6 @@ module Rolemodel
64
64
 
65
65
  def finishing_notes
66
66
  say <<~NOTES
67
- *** Reminder to update Honeybadger gem to version 5.7.0 or later to get correct GoodJob error notifications in Honeybadger
68
-
69
67
  *** Reminder to also update your job classes to include appropriate concurrency controls (enqueue_limit/perform_limit with keys)
70
68
  NOTES
71
69
  end
@@ -4,5 +4,24 @@
4
4
 
5
5
  * Procfile configured for a server process and a release command to run migrations
6
6
  * Basic app.json preconfigured with a script to initialize the database and common environment variables, addons, and buildpacks
7
-
8
7
  This is the basic config needed to deploy to Heroku.
8
+
9
+ ## Deploying the app
10
+
11
+ Deployment setup itself is handled by the `deploy-app` agent skill, which ships inside the
12
+ `rolemodel-rails` gem rather than being copied into your repo — it's one-time setup, so nothing
13
+ skill-related is committed to the app. When the generator finishes, it prints the path to the
14
+ skill's `SKILL.md`; point any coding agent at that file to deploy the app. You can also locate
15
+ it yourself:
16
+
17
+ ```sh
18
+ $(bundle show rolemodel-rails)/lib/generators/rolemodel/heroku/templates/deploy-app/SKILL.md
19
+ ```
20
+
21
+ The skill cleans up the generated Gemfile (merges duplicate groups, removes comments,
22
+ alphabetizes), verifies the test suite and RuboCop pass, creates the Sentry project and wires
23
+ up the DSN, creates and deploys the Heroku staging app (buildpacks, dynos, Postgres,
24
+ Papertrail), and creates the GitHub `Staging` environment with the
25
+ `HEROKU_APP_NAME`/`HEROKU_APP_URL` variables the deploy workflow consumes. The skill is
26
+ LLM-agnostic (Agent Skills format). Requires the Heroku CLI, the GitHub CLI, and the Sentry
27
+ MCP server.
@@ -15,6 +15,33 @@ module Rolemodel
15
15
  template 'Procfile'
16
16
  end
17
17
 
18
+ def pin_ruby_version_for_buildpack
19
+ say 'Pin the Ruby version in the Gemfile so the Heroku buildpack respects it.', :green
20
+
21
+ # A bare .ruby-version file is not enough: without a `ruby` directive the version
22
+ # never lands in Gemfile.lock, so the Heroku Ruby buildpack falls back to its own
23
+ # default and can install an incompatible Ruby. Tie the Gemfile to .ruby-version.
24
+ gemfile = File.join(destination_root, 'Gemfile')
25
+ return if File.exist?(gemfile) && File.read(gemfile).match?(/^\s*ruby\s/)
26
+
27
+ inject_into_file 'Gemfile', "\nruby file: '.ruby-version'\n", after: /^source .*$/
28
+ end
29
+
30
+ def use_database_url_in_production
31
+ say 'Point the production database at DATABASE_URL for Heroku.', :green
32
+
33
+ # Rails' generated production block hardcodes database/username/<APP>_DATABASE_PASSWORD,
34
+ # none of which exist on Heroku, where the Postgres add-on provides a full DATABASE_URL.
35
+ # Replace the whole block with a url-based config.
36
+ gsub_file 'config/database.yml',
37
+ /^production:\n(?:[ \t]+.*\n?)+/,
38
+ <<~YAML
39
+ production:
40
+ <<: *default
41
+ url: <%= ENV["DATABASE_URL"] %>
42
+ YAML
43
+ end
44
+
18
45
  def force_ssl
19
46
  say 'Require SSL for production environment.', :green
20
47
 
@@ -48,5 +75,13 @@ module Rolemodel
48
75
  end
49
76
  RAKE
50
77
  end
78
+
79
+ def show_deploy_app_skill_path
80
+ # The deploy-app skill is one-time deployment setup, so it ships inside the
81
+ # rolemodel-rails gem instead of being copied into the app. Point the user
82
+ # at it so they can hand it to a coding agent when they're ready to deploy.
83
+ say "\nWhen you're ready to deploy, point your coding agent at the deploy-app skill:", :green
84
+ say File.expand_path('templates/deploy-app/SKILL.md', __dir__), :yellow
85
+ end
51
86
  end
52
87
  end
@@ -4,12 +4,15 @@
4
4
  "postdeploy": "bin/rails db:seed"
5
5
  },
6
6
  "env": {
7
- "HONEYBADGER_API_KEY": {
7
+ "SENTRY_DSN": {
8
8
  "required": true
9
9
  },
10
- "HONEYBADGER_ENV": {
10
+ "SENTRY_ENVIRONMENT": {
11
11
  "required": true,
12
12
  "value": "review-app"
13
+ },
14
+ "SENTRY_AUTH_TOKEN": {
15
+ "required": false
13
16
  }
14
17
  },
15
18
  "addons": [
@@ -0,0 +1,202 @@
1
+ ---
2
+ name: deploy-app
3
+ description: >
4
+ Set up deployment for a RoleModel Rails app after running the rolemodel_rails
5
+ core_setup generator. Cleans up the generated Gemfile, verifies RuboCop, the test
6
+ suite, and a local production smoke test pass, creates the Sentry project and wires
7
+ up the DSN, creates and deploys the Heroku app (buildpacks, dynos, Postgres,
8
+ Papertrail), and creates the GitHub deployment environment + deploy workflow. Use
9
+ when the user says "deploy app",
10
+ "set up staging", "set up heroku", "set up sentry for deploy", or "set up production
11
+ deploy". Defaults to staging; pass "production" for the production flow.
12
+ ---
13
+
14
+ # Deploy App
15
+
16
+ Completes deployment setup for a RoleModel Rails app in five phases: Gemfile cleanup,
17
+ green build (RuboCop + tests + production smoke test), Sentry, Heroku, GitHub. Default
18
+ mode is **staging**. If invoked with `production`, follow the
19
+ [Production mode](#production-mode) differences.
20
+
21
+ Every step must be **idempotent**: check whether the resource already exists before
22
+ creating it, and skip or repair rather than failing. Report each phase's outcome as you
23
+ go. Ask the user questions **one at a time**.
24
+
25
+ ## Phase 0: Preflight
26
+
27
+ Run these checks before touching anything. Stop and tell the user how to fix any that
28
+ fail:
29
+
30
+ 1. `git remote get-url origin` — must be a GitHub repo. Derive `REPO_NAME` from
31
+ `gh repo view --json name -q .name` (fallback: basename of the origin URL).
32
+ 2. `gh auth status` — GitHub CLI authenticated.
33
+ 3. `heroku auth:whoami` — Heroku CLI installed and logged in.
34
+ 4. Sentry MCP tools available (try listing organizations). If no Sentry MCP server is
35
+ connected, ask the user to add/authorize the Sentry MCP server
36
+ (https://mcp.sentry.dev) in their coding agent's MCP configuration and stop — do not
37
+ fall back to another mechanism.
38
+ 5. Read `Procfile` — note whether a `worker` process exists.
39
+ 6. Read `config/initializers/sentry.rb` — determine how the DSN is consumed:
40
+ - reads `ENV['SENTRY_DSN']` → **env-var mode** (RoleModel standard)
41
+ - reads `Rails.application.credentials...` → **credentials mode**
42
+ 7. Deploy-readiness (recent `rolemodel-rails` bakes these in; older apps need the manual
43
+ fixes noted in each phase). Confirm the generated app already has:
44
+ - `terser-webpack-plugin` in `package.json` (webpack config imports it)
45
+ - custom cops under `.rubocop/cops/`, not `lib/cops/` (the latter crashes prod boot)
46
+ - `config/initializers/lograge.rb` uses `controller.try(:current_user)`
47
+ - `config/database.yml` `production:` block uses `url: <%= ENV["DATABASE_URL"] %>`
48
+ - a `ruby` directive in the `Gemfile` (so the version reaches `Gemfile.lock`)
49
+ - `stackprof` in the `Gemfile` (Sentry profiling)
50
+ If any are missing, apply the fix inline and note the app predates the generator update.
51
+
52
+ Set `APP_NAME` = `<REPO_NAME>-staging` (staging) or `<REPO_NAME>` (production), but
53
+ confirm it with the user before creating anything on Heroku.
54
+
55
+ ## Phase 1: Gemfile cleanup
56
+
57
+ The rolemodel_rails generators append gems as they run, leaving multiple blocks for the
58
+ same group and top-level gems scattered between them. Normalize the Gemfile first (skip
59
+ this phase if it's already clean):
60
+
61
+ 1. Merge duplicate `group` blocks so each unique group combination
62
+ (e.g. `:development, :test`, `:development`, `:test`) appears exactly once.
63
+ 2. Remove all comments, including commented-out gems (e.g. `# gem "redis" ...`).
64
+ 3. Alphabetize the gems — the top-level list and the list within each group.
65
+ 4. Preserve every gem's version constraint and options (`require:`, `platforms:`,
66
+ `path:`, etc.) exactly as written. File order: `source`, then `ruby` (if present),
67
+ then top-level gems, then the group blocks.
68
+ 5. Run `bundle install` and confirm it succeeds. `git diff Gemfile.lock` must show no
69
+ changes — this is an ordering-only cleanup; if the lockfile changed, you altered a
70
+ dependency and must fix it.
71
+
72
+ ## Phase 2: Green build + production smoke test
73
+
74
+ Do not deploy a broken app. This is a **verification gate**: on a current generated app
75
+ these should pass with no changes. A failure here usually means a generator regression —
76
+ fix the root cause (and flag it for the generator), never paper over it.
77
+
78
+ 1. RuboCop: `bin/rubocop` (fallback: `bundle exec rubocop`). If there are offenses,
79
+ apply safe autocorrections (`rubocop -a`), then fix the remainder by hand.
80
+ 2. Test suite: `bundle exec rspec` (fallback: `bin/rspec`). Fix any failures at the
81
+ root cause — never skip, pend, or delete tests to get to green.
82
+ 3. **Local production smoke test** — this is what catches the boot/asset/DB failures that
83
+ only surface under `RAILS_ENV=production` (eager loading, `force_ssl`, DATABASE_URL).
84
+ Run against a throwaway local Postgres DB (do not touch development/test data):
85
+ ```
86
+ export PROD="RAILS_ENV=production SECRET_KEY_BASE=smoketest \
87
+ DATABASE_URL=postgres://$(whoami)@localhost/<repo>_prodsmoke"
88
+ env $PROD bundle exec rails db:create
89
+ env $PROD bundle exec rails assets:precompile # webpack + asset pipeline
90
+ env $PROD RAILS_SERVE_STATIC_FILES=true PORT=3999 bundle exec puma -C config/puma.rb &
91
+ sleep 6
92
+ curl -s -o /dev/null -w '%{http_code}\n' -H 'X-Forwarded-Proto: https' \
93
+ http://localhost:3999/up # expect 200 (plain http returns 301 due to force_ssl)
94
+ kill %1; env $PROD bundle exec rails db:drop
95
+ ```
96
+ `assets:precompile` succeeding and `/up` → 200 is the gate. If `/up` 500s, read the
97
+ server log for the root cause before proceeding.
98
+ 4. Re-run until clean, then commit the Gemfile cleanup and any fixes (the deploy later
99
+ pushes this branch, so everything must be committed).
100
+
101
+ ## Phase 3: Sentry project + DSN
102
+
103
+ 1. List the Sentry organizations/teams via the Sentry MCP. Ask the user which team the
104
+ project belongs under (skip the question if there is only one option).
105
+ 2. Check whether a project named `REPO_NAME` already exists in that org. If it does,
106
+ reuse it and fetch its DSN instead of creating a duplicate.
107
+ 3. Otherwise create the project: slug/name = `REPO_NAME`, platform `ruby-rails`,
108
+ assigned to the confirmed team.
109
+ 4. Fetch the project's client key (DSN).
110
+ 5. Deliver the DSN according to the mode detected in preflight:
111
+ - **env-var mode**: no code change needed. The DSN is set as a Heroku config var in
112
+ Phase 4 (`SENTRY_DSN`, plus `SENTRY_ENVIRONMENT=staging`). Mention that local
113
+ error reporting (if ever wanted) uses the same `SENTRY_DSN` env var.
114
+ - **credentials mode**: do NOT edit credentials yourself. Print the DSN and exact
115
+ instructions — e.g. `bin/rails credentials:edit --environment production`, add
116
+ `sentry_dsn: <DSN>` — then wait for the user to confirm they've saved it. Verify
117
+ afterwards with
118
+ `bin/rails runner "abort 'missing' unless Rails.application.credentials.sentry_dsn"`
119
+ (adjust env/key path to match the initializer) before moving on.
120
+
121
+ ## Phase 4: Heroku app
122
+
123
+ 1. `heroku teams` — ask the user which team to create the app in. **Do not create the
124
+ app until they confirm the team and the app name** (default `<REPO_NAME>-staging`).
125
+ 2. Ensure the current branch is pushed: check `git status` and
126
+ `git rev-parse @ @{u}`. If there's no upstream or local is ahead, ask the user to
127
+ push (or push for them if they say so) before continuing.
128
+ 3. Create the app if it doesn't exist (`heroku apps:info -a $APP_NAME` to check):
129
+ `heroku apps:create $APP_NAME --team <team>`
130
+ 4. Buildpacks, in this exact order (check `heroku buildpacks -a $APP_NAME` first):
131
+ 1. `heroku buildpacks:add heroku/nodejs -a $APP_NAME`
132
+ 2. `heroku buildpacks:add heroku/ruby -a $APP_NAME`
133
+ 5. Add-ons (skip any that already exist per `heroku addons -a $APP_NAME`):
134
+ - `heroku addons:create heroku-postgresql:essential-0 -a $APP_NAME`
135
+ - Papertrail: run `heroku addons:plans papertrail` and pick the plan whose
136
+ name/description matches "Development" (case-insensitive); if no plan matches,
137
+ show the plan list and ask the user which to use. Then
138
+ `heroku addons:create papertrail:<plan> -a $APP_NAME`.
139
+ 6. Config vars:
140
+ - `RAILS_MASTER_KEY`: **never read `config/master.key` (or
141
+ `config/credentials/production.key`) — the key must not enter the model's context.**
142
+ Pause and tell the user to run this in their own terminal:
143
+ `heroku config:set RAILS_MASTER_KEY=$(cat config/master.key) -a $APP_NAME`
144
+ (substitute `config/credentials/production.key` if per-environment credentials are
145
+ in use). Once they confirm, verify presence without exposing the value:
146
+ `heroku config --json -a $APP_NAME | jq 'has("RAILS_MASTER_KEY")'` — must be `true`.
147
+ - env-var mode only, set these yourself:
148
+ `heroku config:set SENTRY_DSN=<dsn> SENTRY_ENVIRONMENT=staging -a $APP_NAME`
149
+ 7. Enable runtime dyno metadata (idempotent) so Sentry can detect releases:
150
+ `heroku labs:enable runtime-dyno-metadata -a $APP_NAME`. This adds `HEROKU_*`
151
+ env vars (e.g. `HEROKU_SLUG_COMMIT`) on the next release; without it the release
152
+ command logs a warning about dyno metadata.
153
+ 8. Initial deploy from the local machine:
154
+ - `heroku git:remote -a $APP_NAME -r heroku-staging`
155
+ - `git push heroku-staging <current-branch>:main`
156
+ - If the build or release phase fails, read the build output / `heroku logs` and fix
157
+ the root cause before proceeding — do not create the GitHub environment until the
158
+ app deploys and boots.
159
+ 9. Dyno formation (after the first successful deploy):
160
+ - `heroku ps:type web=basic -a $APP_NAME`
161
+ - If the Procfile has a `worker` process: `heroku ps:scale worker=1:basic -a $APP_NAME`
162
+ 10. Verify: get the app URL from `heroku apps:info -a $APP_NAME --json` (`web_url`), then
163
+ curl `<web_url>/up` and confirm a 200. Save the URL as `APP_URL` (no trailing slash).
164
+
165
+ ## Phase 5: GitHub environment + deploy workflow
166
+
167
+ 1. Create the environment (idempotent PUT):
168
+ `gh api -X PUT repos/{owner}/{repo}/environments/Staging`
169
+ 2. Set the environment variables:
170
+ - `gh variable set HEROKU_APP_NAME --env Staging --body "$APP_NAME"`
171
+ - `gh variable set HEROKU_APP_URL --env Staging --body "$APP_URL"`
172
+ 3. Confirm `.github/workflows/deploy-staging.yml` exists (the rolemodel-rails github
173
+ generator installs it). If it's missing, the app skipped the github generator — tell
174
+ the user to run it (`rails generate rolemodel:github`) rather than hand-writing the
175
+ workflow. The workflow relies on the RoleModel **org-level**
176
+ `HEROKU_IT_SUPPORT_API_KEY` secret and `HEROKU_IT_SUPPORT_EMAIL` variable — do not
177
+ create per-repo copies.
178
+ 4. Verify end-to-end: `gh workflow run deploy-staging.yml` then `gh run watch` the run.
179
+ A green run (including its `/up` healthcheck) is the definition of done.
180
+
181
+ ## Production mode
182
+
183
+ Same phases with these differences — ask, don't assume, on every sizing choice:
184
+
185
+ - App name defaults to `<REPO_NAME>` (confirm with the user).
186
+ - Sentry: reuse the existing project; set `SENTRY_ENVIRONMENT=production`.
187
+ - Heroku tiers: ask the user for dyno type (basic/standard-1x/standard-2x/performance),
188
+ Postgres plan, and Papertrail plan instead of assuming the staging defaults.
189
+ - GitHub environment is `Production`; the workflow is
190
+ `.github/workflows/deploy-production.yml` (also installed by the github generator; manual
191
+ `workflow_dispatch` only — production never auto-deploys on push).
192
+
193
+ ## Notes
194
+
195
+ - Never run destructive Heroku commands (`apps:destroy`, `addons:destroy`,
196
+ `pg:reset`) as part of this skill.
197
+ - Never read secret material into the model's context: `config/master.key`,
198
+ `config/credentials/*.key`, or the output of `heroku config` without `--json | jq`
199
+ filtering. When a secret must be set, have the user run the command themselves and
200
+ verify only the key's presence afterwards.
201
+ - If any phase was already completed on a previous run, say so and continue with the
202
+ next phase rather than starting over.
@@ -18,6 +18,7 @@ module Rolemodel
18
18
  ].freeze
19
19
 
20
20
  def install_eslint
21
+ ensure_yarn
21
22
  run "yarn add --dev #{DEV_DEPENDENCIES.join(' ')}"
22
23
  end
23
24
 
@@ -16,7 +16,11 @@ module Rolemodel
16
16
 
17
17
  def add_config
18
18
  copy_file '.rubocop.yml', force: true
19
- directory 'lib/cops'
19
+ # Custom cops live in .rubocop/cops (loaded via .rubocop.yml's require:),
20
+ # not lib/cops — keeping them out of Rails' autoload/eager-load paths so
21
+ # they don't crash production boot referencing the dev/test-only RuboCop
22
+ # constant.
23
+ directory '.rubocop/cops'
20
24
  end
21
25
  end
22
26
  end
@@ -7,4 +7,4 @@
7
7
  Installs [Lograge](https://github.com/roidrage/lograge).
8
8
 
9
9
  Lograge is an opinionated gem that slims down the amount of logs that Rails generates, with the goal of reducing
10
- noise that's handled by other services. For instance, it removes view rendering logs, which is a duplication of Skylight.
10
+ noise that's handled by other services.
@@ -4,7 +4,7 @@ Rails.application.configure do
4
4
  config.lograge.enabled = true
5
5
  config.lograge.custom_payload do |controller|
6
6
  {
7
- user_id: controller.current_user.try(:id)
7
+ user_id: controller.try(:current_user).try(:id)
8
8
  }
9
9
  end
10
10
  config.lograge.custom_options = lambda do |event|
@@ -6,6 +6,7 @@ module Rolemodel
6
6
  def add_optics_package
7
7
  say 'installing Optics package', :green
8
8
 
9
+ ensure_yarn
9
10
  run 'yarn add @rolemodel/optics'
10
11
  end
11
12
 
@@ -6,6 +6,7 @@ module Rolemodel
6
6
  @add_react = yes?('Would you like to add react?')
7
7
 
8
8
  if @add_react
9
+ ensure_yarn
9
10
  run 'yarn add react react-dom'
10
11
  end
11
12
  end
@@ -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
- * [HoneyBadger](http://honeybadger.io)
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) 2019 Closed Source @CompanyName
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