Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .github/workflows/examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ jobs:
echo "Node version: "; node -v
echo "pnpm version: "; pnpm --version
echo "Bundler version: "; bundle --version
- name: run conversion script to support shakapacker v6
- name: Run conversion script for older Node compatibility
if: matrix.dependency-level == 'minimum'
run: script/convert
- name: Save root ruby gems to cache
Expand Down Expand Up @@ -180,8 +180,12 @@ jobs:
- name: Set packer version environment variable
run: |
echo "CI_DEPENDENCY_LEVEL=${{ matrix.dependency-level }}" >> $GITHUB_ENV
- name: Main CI
run: cd react_on_rails && bundle exec rake run_rspec:shakapacker_examples
- name: Main CI - Latest version examples
if: matrix.dependency-level == 'latest'
run: cd react_on_rails && bundle exec rake run_rspec:shakapacker_examples_latest
- name: Main CI - Minimum version examples
if: matrix.dependency-level == 'minimum'
run: cd react_on_rails && bundle exec rake run_rspec:shakapacker_examples_minimum
- name: Store test results
uses: actions/upload-artifact@v4
with:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/integration-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ jobs:
echo "Node version: "; node -v
echo "pnpm version: "; pnpm --version
echo "Bundler version: "; bundle --version
- name: run conversion script to support shakapacker v6
- name: Run conversion script for older Node compatibility
if: matrix.dependency-level == 'minimum'
run: script/convert
- name: Install Node modules with pnpm for renderer package
Expand Down Expand Up @@ -229,7 +229,7 @@ jobs:
echo "Node version: "; node -v
echo "pnpm version: "; pnpm --version
echo "Bundler version: "; bundle --version
- name: run conversion script to support shakapacker v6
- name: Run conversion script for older Node compatibility
if: matrix.dependency-level == 'minimum'
run: script/convert
- name: Save root ruby gems to cache
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@
"prettier": "^3.5.2",
"prop-types": "^15.8.1",
"publint": "^0.3.4",
"react": "18.0.0",
"react-dom": "18.0.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-on-rails-rsc": "19.0.2",
"redux": "^4.2.1",
"stylelint": "^16.14.0",
Expand Down
53 changes: 9 additions & 44 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ def copy_tests

def add_test_related_gems_to_gemfile
gem("rspec-rails", group: :test)
gem("chromedriver-helper", group: :test)
# NOTE: chromedriver-helper was deprecated in 2019. Modern selenium-webdriver (4.x)
# and GitHub Actions have built-in driver management, so no driver helper is needed.
gem("coveralls", require: false)
end

Expand Down
14 changes: 12 additions & 2 deletions react_on_rails/rakelib/example_type.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,22 @@ def self.all
@all ||= { shakapacker_examples: [] }
end

attr_reader :packer_type, :name, :generator_options
# Minimum supported versions for compatibility testing
MINIMUM_REACT_VERSION = "18.0.0"
MINIMUM_SHAKAPACKER_VERSION = "8.2.0"

def initialize(packer_type: nil, name: nil, generator_options: nil)
attr_reader :packer_type, :name, :generator_options, :minimum_versions

# Ruby convention: predicate method with ? suffix for boolean checks
def minimum_versions?
minimum_versions
end

def initialize(packer_type: nil, name: nil, generator_options: nil, minimum_versions: false)
@packer_type = packer_type
@name = name
@generator_options = generator_options
@minimum_versions = minimum_versions
self.class.all[packer_type.to_sym] << self
end

Expand Down
7 changes: 7 additions & 0 deletions react_on_rails/rakelib/examples_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,10 @@ example_type_data:
generator_options: --redux
- name: redux-server-rendering
generator_options: --redux --example-server-rendering
# Minimum version compatibility tests - tests React 18 and Shakapacker 8.2.0
- name: basic-minimum
generator_options: ''
minimum_versions: true
- name: basic-server-rendering-minimum
generator_options: --example-server-rendering
minimum_versions: true
38 changes: 36 additions & 2 deletions react_on_rails/rakelib/run_rspec.rake
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ namespace :run_rspec do
puts "Creating #{example_type.rspec_task_name} task"
desc "Runs RSpec for #{example_type.name_pretty} only"
task example_type.rspec_task_name_short => example_type.gen_task_name do
run_tests_in(File.join(examples_dir, example_type.name)) # have to use relative path
# Use unbundled mode for minimum version examples to ensure the example app's
# Gemfile and gem versions are used, not the parent workspace's bundle
run_tests_in(File.join(examples_dir, example_type.name),
unbundled: example_type.minimum_versions?)
end
end

Expand All @@ -90,6 +93,25 @@ namespace :run_rspec do
ExampleType.all[:shakapacker_examples].each { |example_type| Rake::Task[example_type.rspec_task_name].invoke }
end

# Helper methods for filtering examples
def latest_examples
ExampleType.all[:shakapacker_examples].reject(&:minimum_versions?)
end

def minimum_examples
ExampleType.all[:shakapacker_examples].select(&:minimum_versions?)
end

desc "Runs Rspec for latest version example apps only (excludes minimum version tests)"
task shakapacker_examples_latest: latest_examples.map(&:gen_task_name) do
latest_examples.each { |example_type| Rake::Task[example_type.rspec_task_name].invoke }
end

desc "Runs Rspec for minimum version example apps only (React 18, Shakapacker 8.2.0)"
task shakapacker_examples_minimum: minimum_examples.map(&:gen_task_name) do
minimum_examples.each { |example_type| Rake::Task[example_type.rspec_task_name].invoke }
end

Coveralls::RakeTask.new if ENV["USE_COVERALLS"] == "TRUE"

desc "run all tests no examples"
Expand Down Expand Up @@ -138,11 +160,17 @@ end
# If string is passed and it's not absolute, it's converted relative to root of the gem.
# TEST_ENV_COMMAND_NAME is used to make SimpleCov.command_name unique in order to
# prevent a name collision. Defaults to the given directory's name.
# Options:
# :command_name - name for SimpleCov (default: dir basename)
# :rspec_args - additional rspec arguments (default: "")
# :env_vars - additional environment variables (default: "")
# :unbundled - run with unbundled_sh_in_dir for Bundler isolation (default: false)
def run_tests_in(dir, options = {})
path = calc_path(dir)

command_name = options.fetch(:command_name, path.basename)
rspec_args = options.fetch(:rspec_args, "")
unbundled = options.fetch(:unbundled, false)

# Build environment variables as an array for proper spacing
env_tokens = []
Expand All @@ -151,5 +179,11 @@ def run_tests_in(dir, options = {})
env_tokens << "COVERAGE=true" if ENV["USE_COVERALLS"]

env_vars = env_tokens.join(" ")
sh_in_dir(path.realpath, "#{env_vars} bundle exec rspec #{rspec_args}")
command = "#{env_vars} bundle exec rspec #{rspec_args}"

if unbundled
unbundled_sh_in_dir(path.realpath, command)
else
sh_in_dir(path.realpath, command)
end
end
73 changes: 70 additions & 3 deletions react_on_rails/rakelib/shakapacker_examples.rake
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,72 @@
require "yaml"
require "rails/version"
require "pathname"
require "json"

require_relative "example_type"
require_relative "task_helpers"

namespace :shakapacker_examples do # rubocop:disable Metrics/BlockLength
include ReactOnRails::TaskHelpers

# Updates package.json and Gemfile to use minimum supported versions for compatibility testing
# rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
def apply_minimum_versions(dir)
# Update package.json
package_json_path = File.join(dir, "package.json")
if File.exist?(package_json_path)
begin
package_json = JSON.parse(File.read(package_json_path))
rescue JSON::ParserError => e
puts " ERROR: Failed to parse package.json in #{dir}: #{e.message}"
raise
end

deps = package_json["dependencies"]
dev_deps = package_json["devDependencies"]

# Update React versions to minimum supported
if deps
deps["react"] = ExampleType::MINIMUM_REACT_VERSION
deps["react-dom"] = ExampleType::MINIMUM_REACT_VERSION
# Shakapacker 8.2.0 requires webpack-assets-manifest ^5.x
deps["webpack-assets-manifest"] = "^5.0.6" if deps.key?("webpack-assets-manifest")
end

# Shakapacker 8.2.0 requires webpack-assets-manifest ^5.x (check devDependencies too)
dev_deps["webpack-assets-manifest"] = "^5.0.6" if dev_deps&.key?("webpack-assets-manifest")

# Update Shakapacker to minimum supported version in package.json
if dev_deps&.key?("shakapacker")
dev_deps["shakapacker"] = ExampleType::MINIMUM_SHAKAPACKER_VERSION
elsif deps&.key?("shakapacker")
deps["shakapacker"] = ExampleType::MINIMUM_SHAKAPACKER_VERSION
end

File.write(package_json_path, "#{JSON.pretty_generate(package_json)}\n")
end

# Update Gemfile to pin shakapacker to minimum version
# (must match the npm package version exactly)
gemfile_path = File.join(dir, "Gemfile")
if File.exist?(gemfile_path)
gemfile_content = File.read(gemfile_path)
# Replace any shakapacker gem line with exact version pin
gemfile_content = gemfile_content.gsub(
/gem ['"]shakapacker['"].*$/,
"gem 'shakapacker', '#{ExampleType::MINIMUM_SHAKAPACKER_VERSION}'"
)
File.write(gemfile_path, gemfile_content)
end

puts " Updated package.json with minimum versions:"
puts " React: #{ExampleType::MINIMUM_REACT_VERSION}"
puts " Shakapacker: #{ExampleType::MINIMUM_SHAKAPACKER_VERSION}"
end
# rubocop:enable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity

# Define tasks for each example type
ExampleType.all[:shakapacker_examples].each do |example_type|
ExampleType.all[:shakapacker_examples].each do |example_type| # rubocop:disable Metrics/BlockLength
relative_gem_root = Pathname(gem_root).relative_path_from(Pathname(example_type.dir))
# CLOBBER
desc "Clobbers (deletes) #{example_type.name_pretty}"
Expand Down Expand Up @@ -46,10 +103,20 @@ namespace :shakapacker_examples do # rubocop:disable Metrics/BlockLength
"REACT_ON_RAILS_SKIP_VALIDATION=true #{cmd}"
end
sh_in_dir(example_type.dir, generator_commands)

# Apply minimum versions for compatibility testing examples
if example_type.minimum_versions
apply_minimum_versions(example_type.dir)
# Re-run bundle install since Gemfile was updated with pinned shakapacker version
bundle_install_in(example_type.dir)
end

sh_in_dir(example_type.dir, "npm install")
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use pnpm install instead of npm install.

Based on learnings, this repository should NEVER use npm commands; only pnpm equivalents should be used for JavaScript package management.

-      sh_in_dir(example_type.dir, "npm install")
+      sh_in_dir(example_type.dir, "pnpm install")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
sh_in_dir(example_type.dir, "npm install")
sh_in_dir(example_type.dir, "pnpm install")
🤖 Prompt for AI Agents
In react_on_rails/rakelib/shakapacker_examples.rake around line 114, replace the
call that runs "npm install" with the pnpm equivalent; update the sh_in_dir
invocation to execute "pnpm install" so the project uses pnpm for package
installation consistently across scripts. Ensure the command string is exactly
"pnpm install" and leave any surrounding helpers/arguments unchanged.

# Generate the component packs after running the generator to ensure all
# auto-bundled components have corresponding pack files created
sh_in_dir(example_type.dir, "bundle exec rake react_on_rails:generate_packs")
# auto-bundled components have corresponding pack files created.
# Use unbundled_sh_in_dir to ensure we're using the generated app's Gemfile
# and gem versions, not the parent workspace's bundle context.
unbundled_sh_in_dir(example_type.dir, "bundle exec rake react_on_rails:generate_packs")
end
end

Expand Down
Loading
Loading