Skip to content

Commit b547ef9

Browse files
ihabadhamclaude
andauthored
Fix doctor command false version mismatch for beta/prerelease versions (#2064)
## Summary Fixes #2063 - The doctor command was incorrectly reporting version mismatches for beta/alpha/rc versions even when gem and NPM package versions were identical. ## Problem When running `rake react_on_rails:doctor` with matching prerelease versions: - Gem: `16.2.0.beta.10` - NPM: `16.2.0-beta.10` The doctor would show a false warning: ``` ⚠️ Version mismatch detected: • Gem version: 16.2.0.beta.10 • NPM version: 16.2.0-beta.10 ``` ## Root Cause The original code from PR #1787 used `gsub(/[^0-9.]/, "")` to strip all non-numeric/non-dot characters: - Input: `"16.2.0-beta.10"` - Output: `"16.2.0.10"` (both dash AND "beta" removed) - Compared with: `"16.2.0.beta.10"` (gem version) - Result: Never matches! ❌ **Note:** CodeRabbit [caught this bug during the original PR review](#1787 (review)) but the suggestion was not implemented. ## Solution Use the existing `VersionSyntaxConverter.npm_to_rubygem` utility which properly handles NPM-to-Ruby version format conversion: - Removes version prefixes (`^`, `~`, `=`) - Converts NPM semver format (`-beta`) to Ruby gem format (`.beta`) - Already has comprehensive test coverage ## Changes **File:** `lib/react_on_rails/system_checker.rb` **Before:** ```ruby clean_npm_version = npm_version.gsub(/[^0-9.]/, "") ``` **After:** ```ruby converter = ReactOnRails::VersionSyntaxConverter.new normalized_npm_version = converter.npm_to_rubygem(npm_version) ``` ## Testing Verified with test app using both gem and NPM at `16.2.0.beta.10`: ✅ **Matching versions:** No false warning ``` ✅ React on Rails gem and NPM package versions match (16.2.0.beta.10) ``` ✅ **Caret/tilde prefixes:** Handled correctly with appropriate warning ``` ✅ React on Rails gem and NPM package versions match (16.2.0.beta.10) ⚠️ NPM package uses caret (^) version pattern ``` ✅ **Real mismatches:** Still detected properly ``` ⚠️ Version mismatch detected: • Gem version: 16.2.0.beta.10 • NPM version: 16.2.0-beta.9 ``` ✅ **All prerelease formats:** Tested beta, alpha, rc, pre (16/16 scenarios pass) ## Benefits - ✅ Fixes false warnings for all beta/alpha/rc/pre versions - ✅ Uses existing, battle-tested utility (no code duplication) - ✅ Already has test coverage (VersionSyntaxConverter specs) - ✅ More accurate variable naming (`normalized_npm_version` vs `clean_npm_version`) - ✅ No breaking changes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved npm and gem version compatibility checking to accurately handle prerelease and format variations, reducing false version mismatch warnings. * **Tests** * Enhanced test coverage for version synchronization validation across various version formats. <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude <[email protected]>
1 parent 463a31f commit b547ef9

File tree

2 files changed

+166
-4
lines changed

2 files changed

+166
-4
lines changed

lib/react_on_rails/system_checker.rb

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -213,17 +213,20 @@ def check_package_version_sync # rubocop:disable Metrics/CyclomaticComplexity
213213

214214
return unless npm_version && defined?(ReactOnRails::VERSION)
215215

216-
# Clean version strings for comparison (remove ^, ~, =, etc.)
217-
clean_npm_version = npm_version.gsub(/[^0-9.]/, "")
216+
# Normalize NPM version format to Ruby gem format for comparison
217+
# Uses existing VersionSyntaxConverter to handle dash/dot differences
218+
# (e.g., "16.2.0-beta.10" → "16.2.0.beta.10")
219+
converter = ReactOnRails::VersionSyntaxConverter.new
220+
normalized_npm_version = converter.npm_to_rubygem(npm_version)
218221
gem_version = ReactOnRails::VERSION
219222

220-
if clean_npm_version == gem_version
223+
if normalized_npm_version == gem_version
221224
add_success("✅ React on Rails gem and NPM package versions match (#{gem_version})")
222225
check_version_patterns(npm_version, gem_version)
223226
else
224227
# Check for major version differences
225228
gem_major = gem_version.split(".")[0].to_i
226-
npm_major = clean_npm_version.split(".")[0].to_i
229+
npm_major = normalized_npm_version.split(".")[0].to_i
227230

228231
if gem_major != npm_major # rubocop:disable Style/NegatedIfElseCondition
229232
add_error(<<~MSG.strip)

spec/lib/react_on_rails/system_checker_spec.rb

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,165 @@
321321
end
322322
end
323323

324+
describe "#check_package_version_sync" do
325+
before do
326+
stub_const("ReactOnRails::VERSION", "16.2.0.beta.10")
327+
end
328+
329+
context "when package.json does not exist" do
330+
before do
331+
allow(File).to receive(:exist?).with("package.json").and_return(false)
332+
end
333+
334+
it "does not add any messages" do
335+
messages_count_before = checker.messages.count
336+
checker.send(:check_package_version_sync)
337+
expect(checker.messages.count).to eq(messages_count_before)
338+
end
339+
end
340+
341+
context "when package.json exists with matching beta versions" do
342+
let(:package_json_content) do
343+
{ "dependencies" => { "react-on-rails" => "16.2.0-beta.10" } }.to_json
344+
end
345+
346+
before do
347+
allow(File).to receive(:exist?).with("package.json").and_return(true)
348+
allow(File).to receive(:read).with("package.json").and_return(package_json_content)
349+
end
350+
351+
it "adds a success message" do
352+
checker.send(:check_package_version_sync)
353+
expect(checker.messages.any? do |msg|
354+
msg[:type] == :success && msg[:content].include?("versions match")
355+
end).to be true
356+
end
357+
end
358+
359+
context "when package.json has beta version with caret prefix" do
360+
let(:package_json_content) do
361+
{ "dependencies" => { "react-on-rails" => "^16.2.0-beta.10" } }.to_json
362+
end
363+
364+
before do
365+
allow(File).to receive(:exist?).with("package.json").and_return(true)
366+
allow(File).to receive(:read).with("package.json").and_return(package_json_content)
367+
end
368+
369+
it "adds a success message and version pattern warning" do
370+
checker.send(:check_package_version_sync)
371+
expect(checker.messages.any? do |msg|
372+
msg[:type] == :success && msg[:content].include?("versions match")
373+
end).to be true
374+
expect(checker.warnings?).to be true
375+
end
376+
end
377+
378+
context "when package.json has alpha version" do
379+
let(:package_json_content) do
380+
{ "dependencies" => { "react-on-rails" => "16.2.0-alpha.5" } }.to_json
381+
end
382+
383+
before do
384+
stub_const("ReactOnRails::VERSION", "16.2.0.alpha.5")
385+
allow(File).to receive(:exist?).with("package.json").and_return(true)
386+
allow(File).to receive(:read).with("package.json").and_return(package_json_content)
387+
end
388+
389+
it "correctly matches alpha versions" do
390+
checker.send(:check_package_version_sync)
391+
expect(checker.messages.any? do |msg|
392+
msg[:type] == :success && msg[:content].include?("versions match")
393+
end).to be true
394+
end
395+
end
396+
397+
context "when package.json has rc version" do
398+
let(:package_json_content) do
399+
{ "dependencies" => { "react-on-rails" => "16.2.0-rc.1" } }.to_json
400+
end
401+
402+
before do
403+
stub_const("ReactOnRails::VERSION", "16.2.0.rc.1")
404+
allow(File).to receive(:exist?).with("package.json").and_return(true)
405+
allow(File).to receive(:read).with("package.json").and_return(package_json_content)
406+
end
407+
408+
it "correctly matches rc versions" do
409+
checker.send(:check_package_version_sync)
410+
expect(checker.messages.any? do |msg|
411+
msg[:type] == :success && msg[:content].include?("versions match")
412+
end).to be true
413+
end
414+
end
415+
416+
context "when package.json has stable version" do
417+
let(:package_json_content) do
418+
{ "dependencies" => { "react-on-rails" => "16.2.0" } }.to_json
419+
end
420+
421+
before do
422+
stub_const("ReactOnRails::VERSION", "16.2.0")
423+
allow(File).to receive(:exist?).with("package.json").and_return(true)
424+
allow(File).to receive(:read).with("package.json").and_return(package_json_content)
425+
end
426+
427+
it "correctly matches stable versions" do
428+
checker.send(:check_package_version_sync)
429+
expect(checker.messages.any? do |msg|
430+
msg[:type] == :success && msg[:content].include?("versions match")
431+
end).to be true
432+
end
433+
end
434+
435+
context "when versions have minor mismatch" do
436+
let(:package_json_content) do
437+
{ "dependencies" => { "react-on-rails" => "16.2.0-beta.9" } }.to_json
438+
end
439+
440+
before do
441+
allow(File).to receive(:exist?).with("package.json").and_return(true)
442+
allow(File).to receive(:read).with("package.json").and_return(package_json_content)
443+
end
444+
445+
it "adds a warning message" do
446+
checker.send(:check_package_version_sync)
447+
expect(checker.warnings?).to be true
448+
expect(checker.messages.last[:content]).to include("Version mismatch detected")
449+
end
450+
end
451+
452+
context "when versions have major mismatch" do
453+
let(:package_json_content) do
454+
{ "dependencies" => { "react-on-rails" => "15.0.0" } }.to_json
455+
end
456+
457+
before do
458+
allow(File).to receive(:exist?).with("package.json").and_return(true)
459+
allow(File).to receive(:read).with("package.json").and_return(package_json_content)
460+
end
461+
462+
it "adds an error message" do
463+
checker.send(:check_package_version_sync)
464+
expect(checker.errors?).to be true
465+
expect(checker.messages.last[:content]).to include("Major version mismatch")
466+
end
467+
end
468+
469+
context "when package.json has invalid JSON" do
470+
before do
471+
allow(File).to receive(:exist?).with("package.json").and_return(true)
472+
allow(File).to receive(:read).with("package.json").and_return("invalid json")
473+
end
474+
475+
it "handles parsing errors gracefully" do
476+
expect do
477+
checker.send(:check_package_version_sync)
478+
end.not_to raise_error
479+
end
480+
end
481+
end
482+
324483
describe "private methods" do
325484
describe "#cli_exists?" do
326485
it "returns true when command exists" do

0 commit comments

Comments
 (0)