Skip to content

Fix create-react-on-rails-app follow-ups from PR #2375#2571

Merged
justin808 merged 3 commits intomasterfrom
jg-codex/fix-2383-followups
Mar 13, 2026
Merged

Fix create-react-on-rails-app follow-ups from PR #2375#2571
justin808 merged 3 commits intomasterfrom
jg-codex/fix-2383-followups

Conversation

@justin808
Copy link
Copy Markdown
Member

@justin808 justin808 commented Mar 9, 2026

Summary

  • enforce stricter app name validation so generated project names must start with a letter
  • require Rails 7.0+ for generated apps
  • replace in-helper process exits with composable thrown errors
  • simplify generator args construction and improve command execution utility/docs
  • update tests and docs for these follow-up fixes

Fixes #2383.

Manual testing

  • pnpm --filter create-react-on-rails-app test
  • pnpm --filter create-react-on-rails-app type-check
  • pnpm exec eslint packages/create-react-on-rails-app/src/**/*.ts
  • pnpm exec prettier --check packages/create-react-on-rails-app/src//*.ts packages/create-react-on-rails-app/tests//*.ts
  • smoke: invalid app name run (node packages/create-react-on-rails-app/lib/index.js 123bad) fails with validation error
  • smoke: full app generation run (node packages/create-react-on-rails-app/lib/index.js smokeapp --template javascript --package-manager npm) succeeds

Summary by CodeRabbit

  • Bug Fixes

    • Tightened app-name validation: names must start with a letter and leading/trailing whitespace is trimmed.
    • Added explicit Rails 7.0+ prerequisite validation.
  • Documentation

    • Updated minimum Rails guidance to "Rails 7.0+" and recommended using the most current Rails version.
    • Clarified option descriptions and completion list (adds server-side rendering readiness and development scripts).
  • Tests

    • Added and expanded tests for name validation and Rails version checks.
  • Chores

    • Updated changelog entry.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Trimmed app names are now validated (must start with a letter), Rails version validation requires 7.0+, tests extended for success and failure paths, and related docs and package.json formatting were updated.

Changes

Cohort / File(s) Summary
Validation Logic
packages/create-react-on-rails-app/src/create-app.ts, packages/create-react-on-rails-app/src/index.ts
Normalize app name by trimming and run all validations/messages/path resolution against the trimmed name; enforce "starts with a letter" rule; surface Rails 7.0+ requirement in validator.
Tests
packages/create-react-on-rails-app/tests/create-app.test.ts, packages/create-react-on-rails-app/tests/validators.test.ts
Added assertions checking error messages for names starting with -, _, or digits; added test rejecting Rails 6.1 and accepting Rails 7.0.x; expanded validateAll coverage for success/failure.
Docs & Metadata
CHANGELOG.md, analysis/create-react-on-rails-app-proposal.md, docs/oss/getting-started/create-react-on-rails-app.md, packages/create-react-on-rails-app/package.json
Changelog entry added; proposal doc wording clarified for interactive/--ignore-warnings; docs updated Rails prerequisite and completion list; package.json clean script reformat (no behavioral change).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I trim the name, I check the start,
I hop for letters, no leading dash or chart,
Rails at seven, standing tall and bright,
Tests hop in, asserting day and night,
A tidy generator — I nibble with delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly references the PR it follows up on (#2375) and indicates the nature of the changes (follow-ups/fixes), which aligns with the validation, code quality, testing, and documentation improvements in the changeset.
Linked Issues check ✅ Passed The PR addresses core objectives from #2383: enforces app name validation with letter-start requirement, adds Rails 7.0+ version check, normalizes app name handling, adds validation and Rails version tests, and updates documentation. Process.exit removal was scoped out intentionally.
Out of Scope Changes check ✅ Passed All changes directly support #2383 follow-ups: app name validation, Rails version checks, test coverage, documentation updates, and minor formatting fixes (package.json script reformatting). No unrelated changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch jg-codex/fix-2383-followups
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Comment thread packages/create-react-on-rails-app/src/utils.ts Outdated
Comment thread packages/create-react-on-rails-app/src/create-app.ts Outdated
Comment thread packages/create-react-on-rails-app/src/validators.ts
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 9, 2026

Overall the direction is good. Tighter app name validation, composable errors instead of process.exit, cross-platform clean, and the new Rails 7.0+ prerequisite check all improve the package. Test coverage additions are appreciated. Three things worth addressing before merge: (1) execLive is exported but never called anywhere in the codebase - dead code; either remove it or document the use-case, and note why shell:true is needed over the tokenised execLiveArgs. (2) createStepError discards the original stack trace - prefer new Error(message, { cause: error }) (Node 16.9+) so the full chain stays accessible. (3) MIN_RAILS_MINOR and MIN_RUBY_MINOR are both 0, making the minor < MIN_*_MINOR branch always-false dead code; harmless now but could mislead a future maintainer who bumps the minimum. Inline comments posted for all three.

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 9, 2026

PR Review: Fix create-react-on-rails-app follow-ups

Overall this is a clean, focused PR. The refactors are well-motivated and the test coverage additions are good. A few issues worth addressing:

1. execLive is exported but never used — utils.ts adds execLive(command, cwd) with shell: true, but it is never called. A dead export with shell: true is a latent security risk. Remove it now and add it in the PR that actually needs it, or at minimum leave it unexported until it has a caller.

2. createStepError drops the original stack trace — Constructing a brand-new Error with only the message string discards the original call stack. Node 16.9+ supports the cause option: new Error(message, { cause: error }). This preserves the full stack while still surfacing a user-friendly message.

3. Missing happy-path test for createApp — All three new createApp tests cover failure branches. There is no test verifying that when all execLiveArgs calls succeed the function completes without throwing. A simple fourth case would catch regressions in the success path.

4. Minor: misleading error message for names with leading whitespace — validateAppName(' myapp') returns 'must start with a letter' because the un-trimmed string is compared against the regex after the whitespace guard. Trimming before the regex (or adjusting the guard) would produce a more accurate error.

Positives:

  • Replacing process.exit(1) inside helper functions with thrown errors is the right design; index.ts now owns the exit boundary cleanly.
  • buildGeneratorArgs refactor to spread syntax is more concise.
  • Rails 7.0+ version check is well-implemented with good test coverage.
  • Cross-platform clean script via node -e is a nice improvement.

Comment thread packages/create-react-on-rails-app/src/utils.ts Outdated
Comment thread packages/create-react-on-rails-app/src/create-app.ts Outdated
Comment thread packages/create-react-on-rails-app/tests/create-app.test.ts
Comment thread packages/create-react-on-rails-app/src/create-app.ts Outdated
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Mar 9, 2026

Greptile Summary

This PR is a follow-up to #2375, delivering a set of polish and correctness improvements to the create-react-on-rails-app CLI package. The changes are clean, well-tested, and fit naturally into the existing codebase patterns.

Key changes:

  • Stricter app name validation: Regex updated from /^[a-zA-Z0-9_-]+$/ to /^[a-zA-Z][a-zA-Z0-9_-]*$/, ensuring names must start with a letter — consistent with Ruby/Rails directory naming requirements.
  • Rails 7.0+ prerequisite check: validateRails() now parses and compares the rails --version output, rejecting older versions with a clear error message. The version comparison logic is correct and mirrors the existing Ruby validator pattern.
  • Composable error propagation: createApp no longer calls process.exit(1) directly on step failures; instead it throws errors via a createStepError helper. These bubble up to the top-level action() catch handler in index.ts, which logs and exits — a much cleaner design.
  • Cross-platform clean script: rm -rf ./lib replaced with a node -e equivalent using fs.rmSync.
  • Test coverage expanded: New tests for rejecting names starting with digits/hyphens/underscores, each createApp failure step, Rails version validation (6.1 rejection, unparseable version), and the validateAll success path.

Two minor improvements recommended:

  1. The newly exported execLive function in utils.ts is unused dead code and should be removed or deferred to when it's actually needed.
  2. Add a happy-path test case for createApp to verify successful completion when all steps pass.

Confidence Score: 4/5

  • This PR is safe to merge. All functional changes are correct; the two flagged items are minor improvements that don't affect correctness.
  • The functional changes are solid: app name validation correctly enforces leading-letter requirement, Rails 7.0+ version check is properly implemented, and error handling refactoring is clean. Test coverage is comprehensive for the main functionality. The only items flagged are: (1) unused execLive export in utils.ts—dead code that should be removed or deferred, and (2) missing happy-path test for createApp—a testing completeness suggestion. Neither impacts functionality or introduces bugs.
  • packages/create-react-on-rails-app/src/utils.ts (remove unused execLive export) and packages/create-react-on-rails-app/tests/create-app.test.ts (add happy-path test)

Sequence Diagram

sequenceDiagram
    participant CLI as index.ts (CLI entry)
    participant V as validators.ts
    participant CA as create-app.ts
    participant OS as OS / Shell

    CLI->>CA: validateAppName(appName)
    note over CA: Regex: /^[a-zA-Z][a-zA-Z0-9_-]*$/
    CA-->>CLI: { success, error? }

    CLI->>V: validateAll(packageManager)
    V->>V: validateNode()
    V->>V: validateRuby() — ruby --version, ≥3.0
    V->>V: validateRails() — rails --version, ≥7.0 (NEW)
    V->>V: validatePackageManager()
    V-->>CLI: { allValid, results }

    alt allValid
        CLI->>CA: createApp(appName, options)
        CA->>OS: rails new appName
        OS-->>CA: exit 0 (or throws)
        CA->>OS: bundle add react_on_rails (in appPath)
        OS-->>CA: exit 0 (or throws)
        CA->>OS: bundle exec rails generate react_on_rails:install (in appPath)
        OS-->>CA: exit 0 (or throws)
        CA-->>CLI: (returns)
    else not allValid
        CLI->>CLI: logError + process.exit(1)
    end

    note over CLI: Errors thrown by createApp bubble up<br/>to action() catch → logError + exit(1)
Loading

Last reviewed commit: cdd0027

Comment thread packages/create-react-on-rails-app/src/utils.ts Outdated
Comment thread packages/create-react-on-rails-app/tests/create-app.test.ts
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 9, 2026

size-limit report 📦

Path Size
react-on-rails/client bundled (gzip) 62.63 KB (0%)
react-on-rails/client bundled (gzip) (time) 62.63 KB (0%)
react-on-rails/client bundled (brotli) 53.74 KB (0%)
react-on-rails/client bundled (brotli) (time) 53.74 KB (0%)
react-on-rails-pro/client bundled (gzip) 63.65 KB (0%)
react-on-rails-pro/client bundled (gzip) (time) 63.65 KB (0%)
react-on-rails-pro/client bundled (brotli) 54.68 KB (0%)
react-on-rails-pro/client bundled (brotli) (time) 54.68 KB (0%)
registerServerComponent/client bundled (gzip) 127.46 KB (0%)
registerServerComponent/client bundled (gzip) (time) 127.46 KB (0%)
registerServerComponent/client bundled (brotli) 61.64 KB (0%)
registerServerComponent/client bundled (brotli) (time) 61.64 KB (0%)
wrapServerComponentRenderer/client bundled (gzip) 122.02 KB (0%)
wrapServerComponentRenderer/client bundled (gzip) (time) 122.02 KB (0%)
wrapServerComponentRenderer/client bundled (brotli) 56.77 KB (0%)
wrapServerComponentRenderer/client bundled (brotli) (time) 56.77 KB (0%)

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (2)
packages/create-react-on-rails-app/tests/validators.test.ts (1)

58-77: Add the exact 7.0 boundary case.

These tests cover below-minimum and comfortably above-minimum versions, but not the minimum supported version itself. A Rails 7.0.x fixture would pin the off-by-one boundary this change introduces.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/create-react-on-rails-app/tests/validators.test.ts` around lines 58
- 77, Add a test that verifies the boundary case Rails 7.0.x is accepted: in
tests/validators.test.ts add a case where
mockedGetCommandVersion.mockReturnValue('Rails 7.0.6' or similar 7.0.x) then
call validateRails() and assert result.valid === true and result.message
includes or equals the version string; reference the existing test pattern using
mockedGetCommandVersion and validateRails to mirror the other cases.
packages/create-react-on-rails-app/tests/create-app.test.ts (1)

50-58: Consider adding error message assertions for consistency.

The test for names starting with a number (line 47) asserts the specific error message. For consistency and better documentation of expected behavior, consider adding similar assertions for the hyphen and underscore cases.

♻️ Suggested improvement
   it('rejects names that start with a hyphen', () => {
     const result = validateAppName('-myapp');
     expect(result.success).toBe(false);
+    expect(result.error).toContain('must start with a letter');
   });
 
   it('rejects names that start with an underscore', () => {
     const result = validateAppName('_myapp');
     expect(result.success).toBe(false);
+    expect(result.error).toContain('must start with a letter');
   });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/create-react-on-rails-app/tests/create-app.test.ts` around lines 50
- 58, Add the same error-message assertions to the hyphen and underscore tests
as the numeric-start test uses: after calling validateAppName('-myapp') and
validateAppName('_myapp'), assert the returned error field (the same property
the numeric test checks, e.g. result.error or result.message) equals the
expected validation message; this ensures the tests for validateAppName
consistently assert both failure and the exact error text.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@analysis/create-react-on-rails-app-proposal.md`:
- Around line 124-129: The doc claims interactive prompt-driven flows and uses a
default `--interactive` behavior but the dependencies block only lists
"commander" and "chalk" (no prompt library); either add a prompt dependency
(e.g., include "inquirer" or "prompts" alongside "commander" in the dependencies
block) and update references to `--interactive` to reflect that the prompt
library will be used, or explicitly mark the prompt-driven sections as "future
work" and remove/qualify the default `--interactive` behavior; ensure you
reference the `--interactive` flag and the dependencies JSON block so reviewers
can verify consistency.

In `@CHANGELOG.md`:
- Line 35: The changelog entry mixes user-facing fixes with internal
implementation and test details; edit the entry for "create-react-on-rails-app
follow-up fixes" so it only lists externally observable changes (e.g., tightened
app name validation must start with a letter, added Rails 7.0+ prerequisite
validation, and CLI setup now surfaces composable errors instead of exiting),
and remove internal-only items such as the `clean` script implementation, test
coverage changes (including `validateAll` success path), and other
refactor/lint/test details; ensure the remaining text is concise and focused on
user-visible behavior and keep references (PR 2571 and author) intact.

In `@docs/getting-started/create-react-on-rails-app.md`:
- Line 63: Update the wording that promises Rails 8 to match the prerequisite
change: replace the line "- **Rails 7.0+** (`gem install rails`)" and any
occurrence of the phrase "A Rails 8 app" in this document with a neutral phrase
such as "Rails 7+" or "the installed Rails version" so the copy remains
accurate; search for the literal strings "A Rails 8 app" and "- **Rails 7.0+**
(`gem install rails`)" and update them accordingly.

In `@packages/create-react-on-rails-app/src/create-app.ts`:
- Around line 43-47: The current regex in validateAppName allows names that
Rails later rejects; update validateAppName to mirror Rails'
AppName#valid_const? checks by (1) computing the CamelCased constant from the
supplied name (split on -/_ and capitalize then join) and ensuring it matches
/^[A-Z]\w*$/ (replace the simple /^[a-zA-Z][a-zA-Z0-9_-]*$/ check), (2)
rejecting names whose CamelCased constant equals or contains reserved class
names like "Rails", "Application" or other Rails reserved constants, and (3)
rejecting Ruby reserved keywords (e.g., def, class, module, begin, end, etc.);
return the same error object when these validations fail so upfront validation
mirrors rails/generators/app_name.rb behavior.

---

Nitpick comments:
In `@packages/create-react-on-rails-app/tests/create-app.test.ts`:
- Around line 50-58: Add the same error-message assertions to the hyphen and
underscore tests as the numeric-start test uses: after calling
validateAppName('-myapp') and validateAppName('_myapp'), assert the returned
error field (the same property the numeric test checks, e.g. result.error or
result.message) equals the expected validation message; this ensures the tests
for validateAppName consistently assert both failure and the exact error text.

In `@packages/create-react-on-rails-app/tests/validators.test.ts`:
- Around line 58-77: Add a test that verifies the boundary case Rails 7.0.x is
accepted: in tests/validators.test.ts add a case where
mockedGetCommandVersion.mockReturnValue('Rails 7.0.6' or similar 7.0.x) then
call validateRails() and assert result.valid === true and result.message
includes or equals the version string; reference the existing test pattern using
mockedGetCommandVersion and validateRails to mirror the other cases.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e7a9fd79-04e2-4c7d-9459-5455d15bfae0

📥 Commits

Reviewing files that changed from the base of the PR and between d9e5d7b and cdd0027.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • analysis/create-react-on-rails-app-proposal.md
  • docs/getting-started/create-react-on-rails-app.md
  • packages/create-react-on-rails-app/package.json
  • packages/create-react-on-rails-app/src/create-app.ts
  • packages/create-react-on-rails-app/src/utils.ts
  • packages/create-react-on-rails-app/src/validators.ts
  • packages/create-react-on-rails-app/tests/create-app.test.ts
  • packages/create-react-on-rails-app/tests/validators.test.ts

Comment thread analysis/create-react-on-rails-app-proposal.md
Comment thread CHANGELOG.md Outdated
Comment thread docs/oss/getting-started/create-react-on-rails-app.md
Comment thread packages/create-react-on-rails-app/src/create-app.ts Outdated
@justin808 justin808 added bug P0 Merge this week release:16.4.0-must-have Must-have for 16.4.0: critical bug/perf/usability labels Mar 11, 2026
@justin808
Copy link
Copy Markdown
Member Author

Addressed in commit e058edc:\n\n- removed unused exported \n- preserved wrapped error context by setting \n- simplified the Rails minimum-version check to avoid dead minor-version logic\n- normalized app name handling (trim before validation and use normalized name in orchestration)\n- added a happy-path \ test plus additional validation assertions\n- added an explicit Rails 7.0.x acceptance test\n- updated docs/changelog/proposal wording for consistency\n\nI intentionally kept full Rails \ parity out of this follow-up so this stays scoped to #2383; \Usage:
rails new APP_PATH [options]

Options:
[--skip-namespace] # Skip namespace (affects only isolated engines)
# Default: false
[--skip-collision-check] # Skip collision check
# Default: false
-r, [--ruby=PATH] # Path to the Ruby binary of your choice
# Default: /Users/justin/.local/share/mise/installs/ruby/3.4.3/bin/ruby
-n, [--name=NAME] # Name of the app
-m, [--template=TEMPLATE] # Path to some application template (can be a filesystem path or URL)
-d, [--database=DATABASE] # Preconfigure for selected database
# Default: sqlite3
# Possible values: mysql, trilogy, postgresql, sqlite3, mariadb-mysql, mariadb-trilogy
-G, [--skip-git] # Skip git init, .gitignore and .gitattributes
[--skip-docker] # Skip Dockerfile, .dockerignore and bin/docker-entrypoint
[--skip-keeps] # Skip source control .keep files
-M, [--skip-action-mailer] # Skip Action Mailer files
[--skip-action-mailbox] # Skip Action Mailbox gem
[--skip-action-text] # Skip Action Text gem
-O, [--skip-active-record] # Skip Active Record files
[--skip-active-job] # Skip Active Job
[--skip-active-storage] # Skip Active Storage files
-C, [--skip-action-cable] # Skip Action Cable files
-A, [--skip-asset-pipeline] # Skip the asset pipeline setup
-J, --skip-js, [--skip-javascript] # Skip JavaScript files
[--skip-hotwire] # Skip Hotwire integration
[--skip-jbuilder] # Skip jbuilder gem
-T, [--skip-test] # Skip test files
[--skip-system-test] # Skip system test files
[--skip-bootsnap] # Skip bootsnap gem
[--skip-dev-gems] # Skip development gems (e.g., web-console)
[--skip-thruster] # Skip Thruster setup
[--skip-rubocop] # Skip RuboCop setup
[--skip-brakeman] # Skip brakeman setup
[--skip-bundler-audit] # Skip bundler-audit setup
[--skip-ci] # Skip GitHub CI files
[--skip-kamal] # Skip Kamal setup
[--skip-solid] # Skip Solid Cache, Queue, and Cable setup
[--dev], [--no-dev], [--skip-dev] # Set up the application with Gemfile pointing to your Rails checkout
[--devcontainer], [--no-devcontainer], [--skip-devcontainer] # Generate devcontainer files
[--edge], [--no-edge], [--skip-edge] # Set up the application with a Gemfile pointing to the 8-1-stable branch on the Rails repository
--master, [--main], [--no-main], [--skip-main] # Set up the application with Gemfile pointing to Rails repository main branch
[--rc=RC] # Path to file containing extra configuration options for rails command
[--no-rc] # Skip loading of extra configuration options from .railsrc file
[--api], [--no-api], [--skip-api] # Preconfigure smaller stack for API only apps
# Default: false
[--minimal], [--no-minimal], [--skip-minimal] # Preconfigure a minimal rails app
-j, --js, [--javascript=JAVASCRIPT] # Choose JavaScript approach
# Default: importmap
# Possible values: importmap, bun, webpack, esbuild, rollup
-c, [--css=CSS] # Choose CSS processor. Check https://github.com/rails/cssbundling-rails for more options
# Possible values: tailwind, bootstrap, bulma, postcss, sass
-B, [--skip-bundle] # Don't run bundle install
[--skip-decrypted-diffs] # Don't configure git to show decrypted diffs of encrypted credentials

Runtime options:
-f, [--force] # Overwrite files that already exist
-p, [--pretend], [--no-pretend], [--skip-pretend] # Run but do not make any changes
-q, [--quiet], [--no-quiet], [--skip-quiet] # Suppress status output
-s, [--skip], [--no-skip], [--skip-skip] # Skip files that already exist

Rails options:
-h, [--help], [--no-help], [--skip-help] # Show this help message and quit
-v, [--version], [--no-version], [--skip-version] # Show Rails version number and quit

Description:
The rails new command creates a new Rails application with a default
directory structure and configuration at the path you specify.

You can specify extra command-line arguments to be used every time
`rails new` runs in the .railsrc configuration file in your home directory,
or in $XDG_CONFIG_HOME/rails/railsrc if XDG_CONFIG_HOME is set.

Note that the arguments specified in the .railsrc file don't affect the
default values shown above in this help message.

You can specify which version to use when creating a new rails application 
using `rails _<version>_ new`.

Examples:
rails new ~/Code/Ruby/weblog

This generates a new Rails app in ~/Code/Ruby/weblog.

`rails _<version>_ new weblog`

This generates a new Rails app with the provided version in ./weblog.

`rails new weblog --api`

This generates a new Rails app in API mode in ./weblog.

`rails new weblog --skip-action-mailer`

This generates a new Rails app without Action Mailer in ./weblog.
Any part of Rails can be skipped during app generation. remains the authoritative guard for reserved-name collisions.

@justin808
Copy link
Copy Markdown
Member Author

Addressed in commit e058edc:

  • removed unused exported execLive
  • preserved wrapped error context by setting wrappedError.cause
  • simplified the Rails minimum-version check to avoid dead minor-version logic
  • normalized app name handling (trim before validation and use normalized name in orchestration)
  • added a happy-path createApp test plus additional validation assertions
  • added an explicit Rails 7.0.x acceptance test
  • updated docs/changelog/proposal wording for consistency

I intentionally kept full Rails AppName valid_const parity out of this follow-up so this stays scoped to #2383; rails new remains the authoritative guard for reserved-name collisions.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/create-react-on-rails-app/src/utils.ts (1)

9-24: ⚠️ Potential issue | 🟠 Major

Both functions need Windows-safe process invocation.

On Windows, npm, pnpm, rails, and bundle are .cmd launchers that cannot be executed via spawnSync() or execFileSync() without shell: true. The current implementation will fail on Windows when detecting package managers in detectPackageManager() (lines 37–38) and when invoking tools in execLiveArgs(). Use a Windows-safe helper like cross-spawn, or conditionally add { shell: true } on Windows in both execLiveArgs() and getCommandVersion().

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/create-react-on-rails-app/src/utils.ts` around lines 9 - 24, Windows
process launches fail because spawnSync/execFileSync can't run .cmd shims;
update execLiveArgs and getCommandVersion to be Windows-safe by either switching
to cross-spawn or adding { shell: true } when process.platform === 'win32'.
Specifically, modify execLiveArgs (the spawnSync call) to pass shell:true on
Windows (or use cross-spawn.sync) and modify getCommandVersion (the execFileSync
call) to also use shell:true on Windows (or use cross-spawn to get the version),
ensuring stdio/encoding behavior is preserved; this will make
detectPackageManager and tool invocations (npm/pnpm/rails/bundle) work on
Windows.
♻️ Duplicate comments (1)
packages/create-react-on-rails-app/src/create-app.ts (1)

47-60: ⚠️ Potential issue | 🟡 Minor

Mirror Rails' reserved-name checks in validateAppName().

This still accepts names that rails new rejects later, such as reserved constants or Ruby keywords, so users can pass validation here and then fail in Step 1 anyway. If this helper is meant to be authoritative, it should mirror Rails' app-name validation rather than only the character pattern.

In Rails generators, what names are rejected by Rails::Generators::AppName (for example reserved constants like Rails/Application or Ruby keywords), and how does Rails validate generated app names?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/create-react-on-rails-app/src/create-app.ts` around lines 47 - 60,
validateAppName() currently only checks characters but must mirror
Rails::Generators::AppName rules so names rejected by `rails new` are rejected
here; update validateAppName() to also reject names that contain Ruby namespace
separators ('::'), names that look like Ruby constants (start with an uppercase
letter or include uppercase segments like "Rails" or "Application"), known
Rails/reserved app names (e.g., "application", "rails", "config", "lib", "test",
etc.—create a RESERVED_NAMES array), and Ruby reserved keywords (use a small set
or import a canonical list) and return the same failure shape; implement these
additional checks inside validateAppName() (add checks for /::/, /^[A-Z]/,
membership in RESERVED_NAMES, and membership in RUBY_KEYWORDS) before returning
success so validation matches Rails behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/create-react-on-rails-app/src/utils.ts`:
- Around line 9-24: Windows process launches fail because spawnSync/execFileSync
can't run .cmd shims; update execLiveArgs and getCommandVersion to be
Windows-safe by either switching to cross-spawn or adding { shell: true } when
process.platform === 'win32'. Specifically, modify execLiveArgs (the spawnSync
call) to pass shell:true on Windows (or use cross-spawn.sync) and modify
getCommandVersion (the execFileSync call) to also use shell:true on Windows (or
use cross-spawn to get the version), ensuring stdio/encoding behavior is
preserved; this will make detectPackageManager and tool invocations
(npm/pnpm/rails/bundle) work on Windows.

---

Duplicate comments:
In `@packages/create-react-on-rails-app/src/create-app.ts`:
- Around line 47-60: validateAppName() currently only checks characters but must
mirror Rails::Generators::AppName rules so names rejected by `rails new` are
rejected here; update validateAppName() to also reject names that contain Ruby
namespace separators ('::'), names that look like Ruby constants (start with an
uppercase letter or include uppercase segments like "Rails" or "Application"),
known Rails/reserved app names (e.g., "application", "rails", "config", "lib",
"test", etc.—create a RESERVED_NAMES array), and Ruby reserved keywords (use a
small set or import a canonical list) and return the same failure shape;
implement these additional checks inside validateAppName() (add checks for /::/,
/^[A-Z]/, membership in RESERVED_NAMES, and membership in RUBY_KEYWORDS) before
returning success so validation matches Rails behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a38ac26b-4ef1-4be4-bb93-91fc6d5eefc0

📥 Commits

Reviewing files that changed from the base of the PR and between cdd0027 and e058edc.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • analysis/create-react-on-rails-app-proposal.md
  • docs/getting-started/create-react-on-rails-app.md
  • packages/create-react-on-rails-app/src/create-app.ts
  • packages/create-react-on-rails-app/src/index.ts
  • packages/create-react-on-rails-app/src/utils.ts
  • packages/create-react-on-rails-app/src/validators.ts
  • packages/create-react-on-rails-app/tests/create-app.test.ts
  • packages/create-react-on-rails-app/tests/validators.test.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/create-react-on-rails-app/tests/validators.test.ts
  • docs/getting-started/create-react-on-rails-app.md
  • analysis/create-react-on-rails-app-proposal.md

@justin808 justin808 force-pushed the jg-codex/fix-2383-followups branch from e058edc to cac8976 Compare March 13, 2026 03:00
Comment on lines +91 to +95
it('rejects names that start with a number', () => {
const result = validateAppName('123app');
expect(result.success).toBe(false);
expect(result.error).toContain('must start with a letter');
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is a near-duplicate of the existing 'rejects names starting with a digit' case on line 65 (which tests '1app'). Both inputs follow the same code path and assert the same error substring. Consider removing one to keep the suite non-redundant.

if (!name || name.trim() === '') {
const trimmedName = name.trim();

if (!name || trimmedName === '') {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since name is typed as string, !name is only ever true when name === '', which is already subsumed by trimmedName === ''. The condition can be simplified:

Suggested change
if (!name || trimmedName === '') {
if (!trimmedName) {

Comment on lines 95 to 98
success: false,
error:
'App name must start with a letter, end with a letter or number, and may contain hyphens or underscores only between alphanumeric characters.',
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This single error message is returned for every regex failure, including names like 'my app' (which does start with a letter but contains a space). The test on line 50 of the test file asserts .toContain('must start with a letter') for the 'my app' case, which passes today only because the message happens to contain that phrase — but the wording is misleading for that input.

Consider a more general message such as:

'App name must start with a letter and may only contain letters, numbers, hyphens, or underscores (no spaces or special characters).'

or keep the current message but add a note about disallowed characters so it's accurate for all failure modes.

const packageJson = require('../package.json') as { version: string };

function run(appName: string, rawOpts: Record<string, unknown>): void {
const normalizedAppName = appName.trim();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

validateAppName already trims the name internally (line 87 of create-app.ts), so the trim here and the one inside the function are redundant. They're harmless, but if validateAppName is meant to be an exported/reusable helper that handles normalization internally, consider either: (a) relying solely on the internal trim and removing this one, or (b) having validateAppName accept only an already-normalized name and moving the trim responsibility entirely to the caller. Either way, the API contract should be clear.

Comment thread CHANGELOG.md Outdated

### [Unreleased]

Changes since the last non-beta release.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The line Changes since the last non-beta release. appears to have been accidentally inserted here. It doesn't match the established CHANGELOG format used by other release sections in this file and adds no actionable information.

Suggested change
Changes since the last non-beta release.

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 13, 2026

Review Summary

The changes are generally clean and well-tested. A few minor issues worth addressing:

Bug-free / no security concerns — the app name validation, Rails 7.0+ enforcement, and trimming logic are all correct.

Nits / minor issues (inline comments posted)

  1. Duplicate test (tests/create-app.test.ts): 'rejects names that start with a number' is a near-duplicate of the existing 'rejects names starting with a digit' test.

  2. Redundant empty-string check (src/create-app.ts:89): !name is always false when trimmedName === '' covers it — can simplify to if (!trimmedName).

  3. Misleading error message (src/create-app.ts:95-98): The single regex-failure message says "must start with a letter" but is also returned for names like 'my app' that start with a letter but contain a space. The test asserts toContain('must start with a letter') for that case, which only passes incidentally.

  4. Double-trim (src/index.ts:13): appName.trim() is called here and inside validateAppName. One of the two is redundant — worth clarifying the API contract.

  5. Spurious CHANGELOG line (CHANGELOG.md:28): "Changes since the last non-beta release." was accidentally inserted and doesn't match the established format.

After completion, you get:

- A Rails app with PostgreSQL (Rails 7+)
- A Rails 7+ app with PostgreSQL
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be current version of Rails listed and indicate to use the most current for a new app

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 13, 2026

PR Review: Fix create-react-on-rails-app follow-ups

Overall these are clean, focused improvements. Trimming behavior, stricter regex enforcement, and Rails 7.0+ validation are all solid. Test coverage expansion is good. A few minor issues below.

1. Redundant null-check in validateAppName

name.trim() is called before the falsy guard on line 89 of create-app.ts. This means name cannot be null/undefined at that point. The !name branch only guards against name === '', which is already covered by trimmedName === ''. The !name check is redundant for any valid TypeScript caller and can be removed.

2. Duplicate digit-start test case

create-app.test.ts now has two nearly identical tests: one testing 1app and one testing 123app. Both hit the same regex branch. One should be removed.

3. Missing test for leading/trailing whitespace trimming

validateAppName now trims input internally, but no test exercises this path directly. A test passing a name with leading/trailing spaces (e.g., ' myapp ') and expecting success: true would confirm the trim behavior and guard against accidental regression.

if (!name || name.trim() === '') {
const trimmedName = name.trim();

if (!name || trimmedName === '') {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The !name check is redundant here. Since name.trim() was already called on line 87 without throwing, name is guaranteed to be a non-null string. The only additional case !name covers beyond trimmedName === '' is name === '', but ''.trim() === '', so trimmedName === '' already handles that. Consider simplifying:

Suggested change
if (!name || trimmedName === '') {
if (trimmedName === '') {

expect(result.success).toBe(false);
});

it('rejects names that start with a number', () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test is a near-duplicate of the existing 'rejects names starting with a digit' test above (which tests '1app'). Both exercise the same regex branch and the same error message. One of them should be removed to avoid redundancy.

const result = validateAppName('123app');
expect(result.success).toBe(false);
expect(result.error).toContain('must start with a letter');
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR adds internal trimming to validateAppName, but there is no test that exercises this path directly. Consider adding a test after the digit-start tests:

it('accepts names with leading/trailing whitespace after trimming', () => {
  const result = validateAppName('  myapp  ');
  expect(result.success).toBe(true);
});

This would guard against a regression if the name.trim() call were accidentally removed.

…e double trim

- Move null check before .trim() in validateAppName to prevent TypeError
  on null/undefined input from JS callers
- Simplify empty-string guard to just check !trimmedName (falsy covers it)
- Remove redundant .trim() in index.ts since validateAppName trims internally;
  also remove the now-pointless normalizedAppName alias
- Remove duplicate "rejects names that start with a number" test (123app)
  since "rejects names starting with a digit" (1app) already covers the case

Co-Authored-By: Claude Opus 4.6 <[email protected]>
}

if (!/^[a-zA-Z][a-zA-Z0-9]*([_-][a-zA-Z0-9]+)*$/.test(name)) {
const trimmedName = name.trim();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trimmed name is validated but not propagated to the caller.

validateAppName now validates against trimmedName internally, but still returns only { success: true } — not the trimmed value. In index.ts, the caller passes the original appName to createApp:

// index.ts
const nameValidation = validateAppName(appName); // validates trimmed internally
// ...
createApp(appName, options);                     // uses original, untrimmed!

Before this PR, a name like " myapp " (surrounding spaces) would have been rejected at the regex check because the raw string starts with a space. After this PR, .trim() causes it to pass validation, but createApp still receives the space-padded string and would call rails new " myapp ", which fails or produces an unexpected directory.

Suggested fix: either return the trimmed name from this function (e.g., { success: true, name: trimmedName }) and use it in the caller, or trim appName in index.ts before both calls.

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Mar 13, 2026

Overall this is a clean, focused set of follow-up fixes. The validation tightening and test additions are well done. One functional issue and a couple of minor observations below.

Bug: trimmed name not propagated to createApp

The main issue is in validateAppName: it validates against trimmedName internally but returns success:true without the trimmed value. The caller in index.ts then passes the original appName (untrimmed) to createApp (see inline comment).

Before this PR, a whitespace-padded input would have been rejected by the regex since the raw string starts with a space. After this PR it silently passes validation, but createApp still receives the space-padded string and would call rails new with it. Fix: return the trimmed name from validateAppName (e.g. return { success: true, name: trimmedName }), or trim appName in index.ts before both calls.

Minor observations

Two tests for Rails 6.1: validators.test.ts now has both 'returns invalid for Rails 6.1' (new, 6.1.7.10) and 'returns invalid for Rails 6.x' (existing, 6.1.7). The new one is better since it asserts message wording, but they cover the same code path and are largely redundant.

Redundant null guard: the first if (!name) handles null/undefined/empty-string, and the second if (!trimmedName) handles whitespace-only. The original single expression was equivalent and more concise.

What is good

Stricter regex applied to trimmedName is the right call. Test assertions on error message wording are a solid improvement over bare false checks. The new Rails 7.0.x test with a four-part version string (7.0.8.7) adds genuine coverage for a format the previous tests missed. Docs and proposal cleanup are clear and accurate.

@justin808 justin808 merged commit 13fbb6a into master Mar 13, 2026
35 checks passed
@justin808 justin808 deleted the jg-codex/fix-2383-followups branch March 13, 2026 08:46
justin808 added a commit that referenced this pull request Mar 30, 2026
## Summary
- enforce stricter app name validation so generated project names must
start with a letter
- require Rails 7.0+ for generated apps
- replace in-helper process exits with composable thrown errors
- simplify generator args construction and improve command execution
utility/docs
- update tests and docs for these follow-up fixes

Fixes #2383.

## Manual testing
- pnpm --filter create-react-on-rails-app test
- pnpm --filter create-react-on-rails-app type-check
- pnpm exec eslint packages/create-react-on-rails-app/src/**/*.ts
- pnpm exec prettier --check
packages/create-react-on-rails-app/src/**/*.ts
packages/create-react-on-rails-app/tests/**/*.ts
- smoke: invalid app name run (`node
packages/create-react-on-rails-app/lib/index.js 123bad`) fails with
validation error
- smoke: full app generation run (`node
packages/create-react-on-rails-app/lib/index.js smokeapp --template
javascript --package-manager npm`) succeeds


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Tightened app-name validation: names must start with a letter and
leading/trailing whitespace is trimmed.
  * Added explicit Rails 7.0+ prerequisite validation.

* **Documentation**
* Updated minimum Rails guidance to "Rails 7.0+" and recommended using
the most current Rails version.
* Clarified option descriptions and completion list (adds server-side
rendering readiness and development scripts).

* **Tests**
* Added and expanded tests for name validation and Rails version checks.

* **Chores**
  * Updated changelog entry.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
justin808 added a commit that referenced this pull request Apr 6, 2026
## Summary
- enforce stricter app name validation so generated project names must
start with a letter
- require Rails 7.0+ for generated apps
- replace in-helper process exits with composable thrown errors
- simplify generator args construction and improve command execution
utility/docs
- update tests and docs for these follow-up fixes

Fixes #2383.

## Manual testing
- pnpm --filter create-react-on-rails-app test
- pnpm --filter create-react-on-rails-app type-check
- pnpm exec eslint packages/create-react-on-rails-app/src/**/*.ts
- pnpm exec prettier --check
packages/create-react-on-rails-app/src/**/*.ts
packages/create-react-on-rails-app/tests/**/*.ts
- smoke: invalid app name run (`node
packages/create-react-on-rails-app/lib/index.js 123bad`) fails with
validation error
- smoke: full app generation run (`node
packages/create-react-on-rails-app/lib/index.js smokeapp --template
javascript --package-manager npm`) succeeds


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Tightened app-name validation: names must start with a letter and
leading/trailing whitespace is trimmed.
  * Added explicit Rails 7.0+ prerequisite validation.

* **Documentation**
* Updated minimum Rails guidance to "Rails 7.0+" and recommended using
the most current Rails version.
* Clarified option descriptions and completion list (adds server-side
rendering readiness and development scripts).

* **Tests**
* Added and expanded tests for name validation and Rails version checks.

* **Chores**
  * Updated changelog entry.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.6 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug P0 Merge this week release:16.4.0-must-have Must-have for 16.4.0: critical bug/perf/usability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

create-react-on-rails-app: Follow-ups from PR #2375

1 participant