Skip to content

Conversation

@justin808
Copy link
Member

@justin808 justin808 commented Sep 29, 2025

Summary

Reorganized repository into a monorepo structure to support multiple React on Rails demo applications with shared tooling, comprehensive version management, and robust automation scripts.

Key Changes

🏗️ Monorepo Structure

  • packages/shakacode_demo_common/ - Shared configuration and utilities gem
  • demos/ - Directory for demo applications (to be populated)
  • bin/ - Ruby executables for demo management (new-demo, scaffold-demo, update-all-demos)
  • scripts/ - Legacy bash scripts (kept for compatibility)
  • lib/demo_scripts/ - Ruby modules for demo creation with full RSpec test coverage
  • docs/ - Comprehensive documentation for contributors and version management

🔒 Security Fixes (PR Review)

  • ✅ Added input validation to prevent path traversal attacks in demo creation scripts
  • ✅ Fixed variable expansion syntax error in scaffold-demo.sh
  • ✅ Fixed subshell issue in test-all.sh that prevented failure tracking

📦 Package Renaming & Standardization

  • Gem name: demo_commonshakacode_demo_common (follows Ruby convention: gem name matches directory)
  • Module: ReactOnRailsDemoCommonDemoCommonShakacodeDemoCommon
  • Files: lib/demo_common/lib/shakacode_demo_common/
  • Generator: demo_common:installshakacode_demo_common:install
  • Updated all references throughout codebase (Ruby, bash scripts, docs)
  • README updated with correct monorepo installation examples

🛠️ Development Infrastructure

Ruby Scripts (Recommended):

  • bin/new-demo - Create basic demo with version control
  • bin/scaffold-demo - Create advanced demo with options
  • bin/update-all-demos - Bulk update gem versions across demos
  • Enhanced help messages with descriptions for each command

Shared Dependencies:

  • Created Gemfile.development_dependencies for inherited dev gems
  • Demos use eval_gemfile pattern for consistency

Code Quality:

  • ✅ RuboCop configured and passing (0 offenses)
  • ✅ Prettier installed for code formatting at monorepo root
  • ✅ Lefthook pre-commit hooks for quality checks
  • ✅ Full RSpec test suite for Ruby scripts

📚 Documentation Updates

  • VERSION_MANAGEMENT.md - Comprehensive guide for managing gem versions
  • CONTRIBUTING_SETUP.md - Development setup and hooks
  • ✅ Updated all references from bash scripts to Ruby programs
  • ✅ Added cypress-playwright-on-rails reference
  • ✅ Documented Gemfile.development_dependencies pattern
  • ✅ Enhanced command help messages and README with usage examples

🎨 Code Formatting

  • Added Prettier at monorepo root with configuration
  • Formatted all markdown, YAML, JSON, and JavaScript files
  • Added npm scripts: npm run format and npm run format:check

🔧 Script Improvements

  • ✅ JavaScript dependencies now install after Rails generators
  • ✅ Scripts support both npm and pnpm
  • ✅ Dry-run mode for safe testing
  • ✅ Pre-flight checks (git status, directory existence)
  • ✅ Proper error handling and reporting
  • ✅ Renamed .demo-versions to .new-demo-versions for clarity

⚙️ CI/CD Configuration

  • Updated GitHub Actions workflow for monorepo structure
  • Fixed shallow clone issues for PR diffs
  • Proper shell quoting for robustness
  • Tests run selectively for changed demos (max 5)

Version Management

Global Defaults (.new-demo-versions)

SHAKAPACKER_VERSION="~> 8.0"
REACT_ON_RAILS_VERSION="~> 16.0"

Creating Demos

Basic demo:

bin/new-demo my-demo

With custom versions:

bin/new-demo my-demo \
  --shakapacker-version '~> 8.1' \
  --react-on-rails-version '~> 16.1'

Advanced scaffolding:

bin/scaffold-demo my-advanced-demo \
  --react-on-rails-version '16.0.0.beta.1'

With custom Rails/generator arguments:

bin/new-demo my-demo \
  --rails-args="--skip-test,--api" \
  --react-on-rails-args="--redux,--node"

Bulk Updates

bin/update-all-demos \
  --react-on-rails-version '~> 16.1' \
  --shakapacker-version '~> 8.1'

Breaking Changes

Gem Name Change

Before:

gem 'demo_common', path: '../../packages/demo_common'

After:

gem 'shakacode_demo_common', path: '../../packages/shakacode_demo_common'

Module Rename

Before:

require 'demo_common'
DemoCommon.root
rails generate demo_common:install

After:

require 'shakacode_demo_common'
ShakacodeDemoCommon.root
rails generate shakacode_demo_common:install

Installation in Monorepo

Within monorepo:

# In demo's Gemfile
eval_gemfile File.expand_path("../../Gemfile.development_dependencies", __dir__)
gem 'shakacode_demo_common', path: '../../packages/shakacode_demo_common'

From GitHub:

gem 'shakacode_demo_common', github: 'shakacode/react_on_rails-demos', glob: 'packages/shakacode_demo_common/*.gemspec'

Test Plan

  • ✅ All RSpec tests pass
  • ✅ RuboCop linting passes (0 offenses)
  • ✅ Prettier formatting applied
  • ✅ Demo creation scripts validated
  • ✅ Security vulnerabilities addressed
  • ✅ CI workflow updated and tested
  • ✅ Module renaming completed throughout codebase
  • ✅ Gem renamed to shakacode_demo_common following Ruby conventions

Migration Guide

For existing consumers:

  1. Update Gemfile:

    # Old
    gem 'demo_common', path: '../../packages/demo_common'
    
    # New
    gem 'shakacode_demo_common', path: '../../packages/shakacode_demo_common'
  2. Update require statements:

    # Old
    require 'demo_common'
    
    # New
    require 'shakacode_demo_common'
  3. Update module references:

    # Old
    DemoCommon.root
    
    # New
    ShakacodeDemoCommon.root
  4. Update generator commands:

    # Old
    rails generate demo_common:install
    
    # New
    rails generate shakacode_demo_common:install

Future Enhancements

  • Git branch support in bin/new-demo (planned)
  • Additional demo templates
  • Automated demo deployment workflows
  • Integration with cypress-playwright-on-rails

Related Documentation


🤖 Generated with Claude Code

Co-Authored-By: Claude [email protected]

Summary by CodeRabbit

  • New Features

    • Converted to a monorepo with shared tooling and a common config package.
    • Added CLIs/scripts to create, scaffold, bootstrap, test, and bulk-update demos with version management.
    • Introduced Git hooks, Prettier, ESLint, RuboCop, and standardized Rake tasks.
  • CI

    • Added GitHub Actions to lint and test the shared package and changed demos.
  • Documentation

    • Overhauled README; added contributing/setup and version management guides; included migration notes.
  • Bug Fixes

    • Hardened demo scripts; fixed install order and subshell/variable handling.
  • Tests

    • Added RSpec coverage for demo tooling.
  • Refactor (Breaking)

    • Renamed modules/generator and updated usage across the repo.

- Move all existing code to packages/demo_common/
- Rename gem from react_on_rails_demo_common to demo_common
- Add root-level README and CONTRIBUTING documentation
- Create demos/ directory for future demo applications
- Add helper scripts for managing demos:
  - bootstrap-all.sh: Install dependencies for all demos
  - test-all.sh: Run tests across all demos
  - new-demo.sh: Create new demo application
  - apply-shared.sh: Apply shared configs to demos
- Update CI workflow for monorepo structure
- Prepare for hosting multiple React on Rails demo applications

This sets up the foundation for a monorepo containing multiple
demo applications that share common configuration and utilities.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
@coderabbitai
Copy link

coderabbitai bot commented Sep 29, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Converts the repo into a monorepo for multiple React on Rails demos with shared tooling. Adds CI, bash and Ruby utilities for creating, scaffolding, testing, and updating demos; introduces a shared package (Ruby gem + npm), extensive documentation, test suites, and standardized lint/test configurations.

Changes

Cohort / File(s) Summary of Changes
CI/CD
.github/workflows/ci.yml
Adds multi-job GitHub Actions CI for linting, tests, and selective demo test matrix based on changed paths.
Repository scaffolding & housekeeping
.gitignore, README.md, CONTRIBUTING.md, demos/.gitkeep, package.json
Expands ignore rules; rewrites README for monorepo; adds contributing guide; placeholder for demos; switches to private monorepo package with Prettier scripts.
Docs
docs/VERSION_MANAGEMENT.md, docs/CONTRIBUTING_SETUP.md, pr_description.md, commit_message.txt
Adds version management and contributor setup docs; PR description; migration notes and change log text.
Shell scripts (legacy ops)
scripts/apply-shared.sh, scripts/bootstrap-all.sh, scripts/new-demo.sh, scripts/scaffold-demo.sh, scripts/test-all.sh, scripts/update-all-demos.sh
Introduces/updates scripts to apply shared configs, bootstrap, create/scaffold demos, run tests, and bulk-update versions with dry-run, validation, and optional features.
Ruby CLI entrypoints
bin/new-demo, bin/scaffold-demo, bin/update-all-demos
New CLIs that parse options and delegate to Ruby library classes for create/scaffold/update workflows with error handling.
Ruby library: demo_scripts (core orchestration)
lib/demo_scripts.rb, lib/demo_scripts/version.rb, lib/demo_scripts/command_runner.rb, lib/demo_scripts/config.rb, lib/demo_scripts/pre_flight_checks.rb, lib/demo_scripts/demo_creator.rb, lib/demo_scripts/demo_scaffolder.rb, lib/demo_scripts/demo_updater.rb
Adds modular library for command execution, config, preflight checks, demo creation/scaffolding, and bulk updates; defines module, version, and custom errors.
Tests for demo_scripts
spec/spec_helper.rb, spec/demo_scripts/*_spec.rb, .rspec, spec/examples.txt
Adds RSpec setup and specs for command runner, config loader, preflight checks, and readme generation; configures RSpec output.
Build and lint config
.rubocop.yml, .prettierrc, .prettierignore, Gemfile, Gemfile.development_dependencies, Rakefile, lefthook.yml
Standardizes RuboCop and Prettier; centralizes dev gems; updates Rake tasks to run specs+rubocop; enables Lefthook hooks. Removes prior release tasks from root Rakefile.
Shared package: shakacode_demo_common (gem + npm)
packages/shakacode_demo_common/...
Adds gemspec, Gemfile, README, Railtie, version, tasks, generator with templates, configs (rubocop, lefthook, eslint/prettier), npm package.json, Playwright helpers, and release workflow for the shared package.
Generator and tasks inside shared package
packages/shakacode_demo_common/lib/generators/..., packages/shakacode_demo_common/lib/tasks/shakacode_demo_common.rake
Provides Rails generator to install shared tooling/configs; namespaces tasks; minor formatting/template updates.
Placeholders & minor edits
packages/shakacode_demo_common/RELEASING.md, packages/shakacode_demo_common/index.js, .../configs/*.js, .../playwright/helpers.js
Formatting/trailing newline adjustments; no logic changes.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User
  participant CLI as bin/new-demo
  participant Lib as DemoScripts::DemoCreator
  participant PF as PreFlightChecks
  participant CR as CommandRunner
  participant FS as Filesystem

  U->>CLI: new-demo DEMO_NAME [options]
  CLI->>Lib: create!(options)
  Lib->>PF: run!
  PF-->>Lib: ok
  Lib->>CR: rails new (with skips)
  CR-->>Lib: status
  Lib->>CR: bin/rails db:prepare
  Lib->>CR: bundle add shakapacker/react_on_rails (versions)
  Lib->>FS: Append shared gem, symlink configs
  Lib->>CR: rails webpacker/shakapacker install
  Lib->>CR: rails react_on_rails:install [args]
  Lib->>FS: Generate README
  Lib-->>CLI: done
  CLI-->>U: Success message
Loading
sequenceDiagram
  autonumber
  participant U as User
  participant CLI as bin/update-all-demos
  participant Up as DemoScripts::DemoUpdater
  participant CR as CommandRunner
  participant D as demos/*

  U->>CLI: update-all-demos --ror --shakapacker [flags]
  CLI->>Up: update!
  Up->>Up: validate versions
  loop for each demo
    Up->>D: locate Gemfile
    alt has Gemfile
      Up->>CR: bundle add react_on_rails --version X
      Up->>CR: bundle add shakapacker --version Y
      Up->>CR: bundle install
      Up->>D: Update README Gem Versions (unless dry-run)
      alt run tests
        Up->>CR: rspec
        CR-->>Up: pass/fail
      end
      Up-->>Up: record result
    else
      Up-->>Up: skip
    end
  end
  Up-->>CLI: summary
Loading
sequenceDiagram
  autonumber
  participant GH as GitHub Actions
  participant CI as ci.yml
  participant Repo as Git diff
  participant Matrix as Demos Matrix
  participant Job as Test Job

  GH->>CI: PR opened/updated
  CI->>Repo: git diff origin/main
  Repo-->>CI: changed paths
  CI->>Matrix: build list of changed demos (<=5)
  alt demos found
    loop each demo
      CI->>Job: setup Ruby/Node, install deps
      Job->>Job: run rspec/rails tests
    end
  else
    CI-->>GH: skip demo tests
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120+ minutes

Poem

I thump my paws on monorepo ground,
New demos sprout where scripts abound.
CI hums, hooks swing in tune,
Rubies dance with Nodes by noon.
Versions hop in tidy rows—
I nibble docs, then off I go! 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.41% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “Reorganize into monorepo structure” succinctly and accurately captures the primary change of restructuring the repository into a monorepo, aligning directly with the PR’s objectives. It is concise, specific, and easily understood by reviewers scanning the change history.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch justin808/monorepo

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.

- Change all references from v15 to v16 throughout documentation
- Update new-demo.sh with modern Rails options:
  - Add --skip-javascript, --skip-hotwire, --skip-turbolinks
  - Add --skip-action-text
  - Use bundle add with --strict flag
  - Create database as part of setup
- Add scaffold-demo.sh script with advanced options:
  - TypeScript support option
  - CSS framework options (Tailwind, Bootstrap, Material-UI)
  - Colored output for better UX
  - Complete project setup with example controller/view
  - Comprehensive README generation
- Update documentation to mention new scaffolding capabilities

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Copy link

@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: 10

🧹 Nitpick comments (25)
demos/.gitkeep (1)

1-1: OK to keep; consider Rails-style .keep.

The placeholder is fine. If you prefer Rails conventions in demos/*, consider .keep to match the patterns you already whitelist elsewhere.

packages/demo_common/.gitignore (1)

3-3: Tighten patterns and add a few common Ruby/dev artifacts.

  • Use trailing slashes for directories (.bundle/, pkg/) to avoid accidental file matches.
  • Consider ignoring RuboCop cache, RSpec status, and byebug history.

Apply:

-.bundle
+.bundle/
@@
-pkg
+pkg/
@@
 coverage
+.rubocop-https---cache
+.rspec_status
+.byebug_history

Also applies to: 12-12, 9-9, 27-28

packages/demo_common/demo_common.gemspec (2)

13-14: Files glob may package unnecessary dirs.

{app,config,db,lib}/**/* can include empty or app-only dirs. Prefer tracking via git or a tighter glob to avoid bloat.

For example:

-spec.files = Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"]
+spec.files = `git ls-files -z`.split("\x0")

Or restrict to needed paths (e.g., lib/**/*, configs, license, readme).


18-22: Consider moving RuboCop deps to development or document grouping.

Having rubocop* as runtime dependencies pulls them into production installs of consumer apps. If this gem is dev-only, either:

  • Move to development dependencies here, and instruct consumers to add RuboCop in their app’s development/test group, or
  • Explicitly document that apps should include gem 'demo_common', group: %i[development test].

Example:

-spec.add_dependency "rubocop", "~> 1.50"
-spec.add_dependency "rubocop-rails", "~> 2.19"
-spec.add_dependency "rubocop-performance", "~> 1.17"
+spec.add_development_dependency "rubocop", "~> 1.50"
+spec.add_development_dependency "rubocop-rails", "~> 2.19"
+spec.add_development_dependency "rubocop-performance", "~> 1.17"

Also consider adding:

spec.metadata = {
  "homepage_uri" => spec.homepage,
  "source_code_uri" => spec.homepage,
  "changelog_uri" => "#{spec.homepage}/blob/main/CHANGELOG.md"
}
.gitignore (1)

44-47: Add common JS test artifacts (Playwright) and tool caches.

Since Playwright and JS tooling are documented, ignore their outputs/caches.

 # Testing
 /demos/*/coverage/
 /demos/*/test/reports/
+playwright-report/
+test-results/
+.nyc_output/
+.eslintcache
scripts/apply-shared.sh (4)

4-11: Make script location-agnostic (determine repo root).

Currently assumes execution from repo root. Add repo-root discovery to avoid surprises when invoked from other dirs.

 set -euo pipefail
+
+# Ensure we run from the repo root
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
+cd "${REPO_ROOT}"

14-16: Robust demo iteration (spaces, hidden dirs).

Globbing can break on spaces and will include non-dirs. Prefer find with NUL delim.

-if [ -d "demos" ] && [ "$(ls -A demos 2>/dev/null)" ]; then
-  for demo in demos/*; do
-    if [ -d "$demo" ]; then
+if [ -d "demos" ] && [ "$(ls -A demos 2>/dev/null)" ]; then
+  while IFS= read -r -d '' demo; do
+    if [ -d "$demo" ]; then
       demo_name=$(basename "$demo")
       echo "📦 Updating $demo_name..."
@@
-    fi
-  done
+    fi
+  done < <(find demos -mindepth 1 -maxdepth 1 -type d -print0)

39-44: Ensure bin directory exists before linking.

Avoid failures if a demo lacks bin/. Harmless for Rails, but safer generally.

-        if [ -f "$demo/bin/dev" ]; then
+        mkdir -p "$demo/bin"
+        if [ -f "$demo/bin/dev" ] || [ -L "$demo/bin/dev" ]; then
           rm "$demo/bin/dev"
         fi

47-54: Tighten Gemfile detection to avoid commented matches and duplicates.

Current grep may match comments. Use a stricter regex and append only once.

-        if ! grep -q "demo_common" "$demo/Gemfile"; then
+        if ! grep -Eq '^\s*gem\s+["'\'']demo_common["'\'']\b' "$demo/Gemfile"; then
           echo "  Adding demo_common to Gemfile..."
           echo '' >> "$demo/Gemfile"
           echo '# Shared demo configuration and utilities' >> "$demo/Gemfile"
           echo 'gem "demo_common", path: "../../packages/demo_common"' >> "$demo/Gemfile"
         fi
scripts/new-demo.sh (3)

4-11: Pre-flight checks for required tools.

Fail fast with clear messages if rails/bundle/node package manager is missing.

 set -euo pipefail
+
+# Pre-flight
+for cmd in rails bundle; do
+  command -v "$cmd" >/dev/null 2>&1 || { echo "Error: $cmd not found"; exit 1; }
+done
+PKG_MGR="npm"
+if command -v pnpm >/dev/null 2>&1; then PKG_MGR="pnpm"; fi
+command -v "$PKG_MGR" >/dev/null 2>&1 || { echo "Error: $PKG_MGR not found"; exit 1; }

59-67: Align shared config links (.prettierrc) and harden bin/dev linking.

Keep parity with apply-shared.sh and ensure idempotence.

 echo "🔗 Creating configuration symlinks..."
 ln -sf ../../packages/demo_common/config/.rubocop.yml .rubocop.yml
 ln -sf ../../packages/demo_common/config/.eslintrc.js .eslintrc.js
+if [ -f "../../packages/demo_common/config/.prettierrc" ]; then
+  ln -sf ../../packages/demo_common/config/.prettierrc .prettierrc
+fi
 if [ -f "../../packages/demo_common/bin/dev" ]; then
-  rm -f bin/dev
+  mkdir -p bin
+  rm -f bin/dev
   ln -sf ../../../packages/demo_common/bin/dev bin/dev
   chmod +x bin/dev
 fi

116-121: Update “Next steps” to reflect JS install (if not installing automatically).

If you choose not to auto-install, at least instruct users to run the package manager before bin/dev.

-echo "  cd $DEMO_DIR"
-echo "  bin/dev"
+echo "  cd $DEMO_DIR"
+echo "  pnpm install  # or npm install"
+echo "  bin/dev"
CONTRIBUTING.md (2)

90-99: Add language to fenced code block (markdownlint MD040).

The Conventional Commits example lacks a language tag. Use “text” for neutral formatting.

-```
+```text
 feat: add TypeScript support to v16 demo
@@
 Fixes #123

---

`129-137`: **Cross-link shared config automation.**

Mention scripts/apply-shared.sh so contributors know how to propagate updates.


```diff
 When modifying shared configuration in `packages/demo_common/`:
@@
 3. Consider backward compatibility
 4. Update the gem/npm package versions appropriately
+5. Run `./scripts/apply-shared.sh` to re-link configs across demos
scripts/bootstrap-all.sh (3)

4-7: Make script resilient to invocation path (repo root).

Mirror the root detection used in other scripts for consistency.

 set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
+cd "${REPO_ROOT}"

29-31: Use NUL-delimited find for robust demo iteration.

Avoid breakage with spaces/unusual names.

-if [ -d "demos" ] && [ "$(ls -A demos 2>/dev/null)" ]; then
-  for demo in demos/*; do
-    if [ -d "$demo" ]; then
+if [ -d "demos" ] && [ "$(ls -A demos 2>/dev/null)" ]; then
+  while IFS= read -r -d '' demo; do
+    if [ -d "$demo" ]; then
@@
-    fi
-  done
+    fi
+  done < <(find demos -mindepth 1 -maxdepth 1 -type d -print0)

45-52: Prefer pnpm when available (consistency with demo_common block).

Minor nit: matches the earlier branch.

-        if [ -f "package.json" ]; then
+        if [ -f "package.json" ]; then
           echo "  Installing JavaScript dependencies..."
           if command -v pnpm &> /dev/null; then
             pnpm install
           else
             npm install
           fi
         fi
scripts/test-all.sh (2)

59-61: Guard RuboCop invocation.

Only run if RuboCop is available to avoid hard failures on demos that don’t include it.

-          echo "  Running RuboCop..."
-          bundle exec rubocop || FAILED_DEMOS="$FAILED_DEMOS $demo_name-rubocop"
+          echo "  Running RuboCop..."
+          if bundle exec rubocop -v >/dev/null 2>&1; then
+            bundle exec rubocop || FAILED_DEMOS="$FAILED_DEMOS $demo_name-rubocop"
+          fi

63-68: Decide whether JS tests should gate CI and prefer pnpm when available.

Right now JS tests/lint are best-effort and don’t affect FAILED_DEMOS, and npm is hardcoded. If these should gate, capture failures and prefer pnpm like other scripts.

-      echo "  Running JavaScript tests..."
-      npm test 2>/dev/null || true
-      npm run lint 2>/dev/null || true
+      echo "  Running JavaScript tests..."
+      if command -v pnpm &> /dev/null; then PKG=pnpm; else PKG=npm; fi
+      $PKG test 2>/dev/null || FAILED_DEMOS="$FAILED_DEMOS $demo_name-js"
+      $PKG run lint 2>/dev/null || FAILED_DEMOS="$FAILED_DEMOS $demo_name-lint"

Apply the same change in the packages/demo_common section.

-      npm test 2>/dev/null || true
-      npm run lint 2>/dev/null || true
+      if command -v pnpm &> /dev/null; then PKG=pnpm; else PKG=npm; fi
+      $PKG test 2>/dev/null || FAILED_DEMOS="$FAILED_DEMOS demo_common-js"
+      $PKG run lint 2>/dev/null || FAILED_DEMOS="$FAILED_DEMOS demo_common-lint"

Also applies to: 29-34

.github/workflows/ci.yml (1)

32-48: Optional: enable Bundler caching in subdir jobs for speed.

Use ruby/setup-ruby with bundler-cache: true and working-directory: packages/demo_common (already present) and consider caching npm via lockfile. For demo loops, consider an actions/cache on vendor/bundle keyed by each demo’s Gemfile.lock to reduce CI time.

Also applies to: 50-70

README.md (2)

7-19: Specify language on fenced code block for the tree.

Adds syntax hint and satisfies MD040.

Apply this diff:

-```
+```text
 react_on_rails-demos/
 ├─ packages/
 │  └─ demo_common/          # Shared configuration and utilities
 │     ├─ Gemfile           # Shared Ruby dependencies
 │     ├─ package.json       # Shared JavaScript dependencies
 │     ├─ config/            # Shared linting configs
 │     └─ lib/               # Ruby utilities and templates
 └─ demos/
    ├─ react_on_rails-demo-v16-ssr-auto-registration-bundle-splitting/
    ├─ react_on_rails-demo-v16-react-server-components/
    └─ ...                   # Additional demo applications

---

`27-27`: **Replace emphasized placeholder with plain text or a list item.**

Avoid MD036 by not using emphasis as a heading.

Apply this diff:

```diff
-*(Demos will be listed here as they are added)*
+- None yet. Use `./scripts/new-demo.sh` to add one.
scripts/scaffold-demo.sh (3)

98-101: Database creation may fail on machines without Postgres configured.

Consider guarding or making it non-fatal with clear guidance.

Apply this diff:

-if [ "$SKIP_DB" = false ]; then
+if [ "$SKIP_DB" = false ]; then
   echo -e "${YELLOW}📦 Setting up database...${NC}"
-  bin/rails db:create
+  if ! bin/rails db:create; then
+    echo -e "${YELLOW}Skipping DB creation (configure PostgreSQL or pass --skip-db).${NC}"
+  fi
 fi

173-201: Wire Tailwind/Bootstrap CSS into a pack entry.

After adding Tailwind or Bootstrap, import the CSS so it actually loads.

Apply this diff:

   npm install --save-dev tailwindcss postcss autoprefixer
   npx tailwindcss init -p
@@
   mkdir -p app/javascript/styles
   cat > app/javascript/styles/application.css << 'EOF'
 @tailwind base;
 @tailwind components;
 @tailwind utilities;
 EOF
 fi
@@
 if [ "$SKIP_INSTALL" = false ]; then
   echo -e "${YELLOW}📦 Installing npm dependencies...${NC}"
   npm install
 fi
+
+# Ensure CSS is imported by a pack entry
+if [ -f app/javascript/packs/application.js ]; then
+  grep -q "styles/application.css" app/javascript/packs/application.js || \
+    echo "import '../styles/application.css';" >> app/javascript/packs/application.js
+fi
+if [ -f app/javascript/packs/application.ts ]; then
+  grep -q "styles/application.css" app/javascript/packs/application.ts || \
+    echo "import '../styles/application.css';" >> app/javascript/packs/application.ts
+fi

For Bootstrap, also import CSS (e.g., import 'bootstrap/dist/css/bootstrap.min.css';) in the same pack when USE_BOOTSTRAP=true. Want me to add that conditional block?

Also applies to: 215-219


12-26: Optional: preflight checks for required tools.

Fail fast with helpful messages if rails, bundle, node, npm (and optionally psql) aren’t available. I can add a small helper to check commands.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 276f1f0 and b031070.

⛔ Files ignored due to path filters (1)
  • packages/demo_common/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (13)
  • .github/workflows/ci.yml (1 hunks)
  • .gitignore (1 hunks)
  • CONTRIBUTING.md (1 hunks)
  • README.md (1 hunks)
  • demos/.gitkeep (1 hunks)
  • packages/demo_common/.gitignore (1 hunks)
  • packages/demo_common/README.md (1 hunks)
  • packages/demo_common/demo_common.gemspec (1 hunks)
  • scripts/apply-shared.sh (1 hunks)
  • scripts/bootstrap-all.sh (1 hunks)
  • scripts/new-demo.sh (1 hunks)
  • scripts/scaffold-demo.sh (1 hunks)
  • scripts/test-all.sh (1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.18.1)
CONTRIBUTING.md

7-7: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


27-27: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

README.md

7-7: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


27-27: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

🪛 actionlint (1.7.7)
.github/workflows/ci.yml

78-78: "matrix values" section should not be empty

(syntax-check)


84-84: shellcheck reported issue in this script: SC2086:info:4:30: Double quote to prevent globbing and word splitting

(shellcheck)

🔇 Additional comments (4)
.gitignore (1)

29-31: Confirm intent to ignore .ruby-version at repo root.

Ignoring .ruby-version is fine if you manage versions via asdf/Toolfiles, but teams often commit it for onboarding consistency. Confirm this choice.

packages/demo_common/README.md (1)

30-35: Remove generator rename suggestion
The install generator lives in ReactOnRailsDemoCommon::Generators and is invoked via rails generate react_on_rails_demo_common:install, so the README is already correct.

Likely an incorrect or invalid review comment.

README.md (1)

33-37: Align tool recommendation with scripts/CI.

README recommends pnpm, but scripts/CI use npm. Either:

  • Note that scripts default to npm, or
  • Add pnpm support switches in scripts.
    Would you like a small patch to detect and prefer pnpm when available?
scripts/scaffold-demo.sh (1)

81-94: Confirm Rails flags; drop obsolete --skip-turbolinks.

Rails 7+ no longer uses Turbolinks; the flag is obsolete and may be ignored.

Prefer:

rails new "$DEMO_DIR" --database=postgresql --skip-javascript --skip-hotwire \
  --skip-action-mailbox --skip-action-text --skip-active-storage \
  --skip-action-cable --skip-sprockets --skip-system-test

Want me to update the script after confirming your target Rails version?

Comment on lines 76 to 80
strategy:
matrix:
demo: [] # Will be populated as demos are added
fail-fast: false
steps:
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Remove empty matrix; run loop without strategy.

Empty matrix triggers actionlint and adds no value since you already loop over demos.

Apply this diff:

-    strategy:
-      matrix:
-        demo: []  # Will be populated as demos are added
-      fail-fast: false
🧰 Tools
🪛 actionlint (1.7.7)

78-78: "matrix values" section should not be empty

(syntax-check)

🤖 Prompt for AI Agents
.github/workflows/ci.yml around lines 76 to 80 contains an empty strategy.matrix
block which is unnecessary and triggers actionlint; remove the entire strategy
block (both matrix: and fail-fast: entries) so the job runs without a strategy,
leaving the steps section directly under the job definition and ensuring
indentation remains correct.


Gem::Specification.new do |spec|
spec.name = "react_on_rails_demo_common"
spec.name = "demo_common"
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Rename looks correct; verify library namespace and require path.

Ensure lib/demo_common.rb and lib/demo_common/version.rb define module DemoCommon so require 'demo_common' works with the new name.

Run:

Also applies to: 10-10


🏁 Script executed:

#!/bin/bash
fd -a --hidden -t f -E .git 'demo_common.rb|version.rb' | xargs -I{} sh -c 'echo "== {} =="; sed -n "1,120p" "{}"'
rg -nP '^\s*module\s+DemoCommon\b' -C2

Length of output: 694


Update module namespace and file paths to match demo_common gem
Rename lib/react_on_rails_demo_common.rblib/demo_common.rb and lib/react_on_rails_demo_common/version.rblib/demo_common/version.rb, and change both module ReactOnRailsDemoCommon declarations to module DemoCommon.

🤖 Prompt for AI Agents
In packages/demo_common/demo_common.gemspec around line 4, the gem name is set
to "demo_common" but the library files and module namespace still reference the
old react_on_rails_demo_common naming; rename the files
lib/react_on_rails_demo_common.rb → lib/demo_common.rb and
lib/react_on_rails_demo_common/version.rb → lib/demo_common/version.rb, and
update both module declarations from module ReactOnRailsDemoCommon to module
DemoCommon within those files so the gemspec, file paths, and module namespace
are consistent.

Comment on lines 24 to 28
```json
"devDependencies": {
"@shakacode/react-on-rails-demo-common": "github:shakacode/react_on_rails_demo_common"
}
```
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify or update the NPM package name and source.

The README references @shakacode/react-on-rails-demo-common. If the JS package is not (yet) published or is renamed for the monorepo, update this section accordingly or mark as TBD.

Run to check for an actual package manifest:


🏁 Script executed:

#!/bin/bash
fd -a package.json packages/demo_common | xargs -I{} sh -c 'echo "== {} =="; jq -r ".name, .version" "{}" 2>/dev/null || true'
rg -n "@shakacode/react-on-rails-demo-common|@shakacode/demo-common|react-on-rails-demo-common" -C2

Length of output: 5562


Dev package name is correct; fix GitHub install path
The package @shakacode/[email protected] exists in package.json. Update the README devDependencies snippet to use the hyphenated GitHub repo shorthand:

"@shakacode/react-on-rails-demo-common": "github:shakacode/react-on-rails-demo-common"

(packages/demo_common/README.md:26)

🤖 Prompt for AI Agents
In packages/demo_common/README.md around lines 24 to 28, the devDependencies
snippet uses an incorrect GitHub install path for the package; update the
snippet so the key uses the correct hyphenated package name
"@shakacode/react-on-rails-demo-common" and set its value to the hyphenated
GitHub shorthand "github:shakacode/react-on-rails-demo-common" so the README
accurately reflects the package name and repository install path.

Comment on lines 121 to 127
ln -sf ../../packages/demo_common/config/.rubocop.yml .rubocop.yml
ln -sf ../../packages/demo_common/config/.eslintrc.js .eslintrc.js
if [ -f "../../packages/demo_common/bin/dev" ]; then
rm -f bin/dev
ln -sf ../../../packages/demo_common/bin/dev bin/dev
chmod +x bin/dev
fi
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Broken symlink: extra “../” in path to demo_common/bin/dev.

The symlink points outside the repo; bin/dev will be broken.

Apply this diff:

 if [ -f "../../packages/demo_common/bin/dev" ]; then
   rm -f bin/dev
-  ln -sf ../../../packages/demo_common/bin/dev bin/dev
+  ln -sf ../../packages/demo_common/bin/dev bin/dev
   chmod +x bin/dev
 fi
📝 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
ln -sf ../../packages/demo_common/config/.rubocop.yml .rubocop.yml
ln -sf ../../packages/demo_common/config/.eslintrc.js .eslintrc.js
if [ -f "../../packages/demo_common/bin/dev" ]; then
rm -f bin/dev
ln -sf ../../../packages/demo_common/bin/dev bin/dev
chmod +x bin/dev
fi
ln -sf ../../packages/demo_common/config/.rubocop.yml .rubocop.yml
ln -sf ../../packages/demo_common/config/.eslintrc.js .eslintrc.js
if [ -f "../../packages/demo_common/bin/dev" ]; then
rm -f bin/dev
ln -sf ../../packages/demo_common/bin/dev bin/dev
chmod +x bin/dev
fi
🤖 Prompt for AI Agents
In scripts/scaffold-demo.sh around lines 121 to 127 the symlink for bin/dev
points too high (uses ../../../packages/demo_common/bin/dev) which creates a
broken link outside the repo; change the symlink target to
../../packages/demo_common/bin/dev so it points to the correct path, keep the rm
-f, chmod +x and conditional logic unchanged.

Enhanced both new-demo.sh and scaffold-demo.sh scripts with:

Pre-flight checks:
- Verify target directory does not exist
- Check for uncommitted changes (staged or unstaged)
- Confirm running in a git repository
- Provide clear error messages with remediation steps

Dry-run mode:
- Add --dry-run flag to preview commands without execution
- Display all commands that would be run
- Show final command to execute for real

Generator improvements:
- Add --ignore-warnings flag to react_on_rails:install
- Skips git check when running generator from scripts
- Allows clean execution in controlled environments

All commands now use run_cmd() wrapper for consistent handling
of dry-run vs actual execution modes.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
@claude
Copy link

claude bot commented Sep 30, 2025

Code Review: Reorganize into monorepo structure

Great work on reorganizing the repository into a monorepo structure! This is a solid foundation for managing multiple React on Rails demo applications.

Strengths

  • Well-structured monorepo layout: The separation of packages/demo_common/ and demos/ directories provides clear organization
  • Comprehensive documentation: Excellent README updates and the new CONTRIBUTING.md guide
  • Robust helper scripts: The shell scripts for bootstrapping, testing, and creating new demos are well-implemented with proper error handling
  • CI/CD ready: The GitHub Actions workflow is well-configured for the monorepo structure
  • Good DX considerations: Dry-run options and colored output in scripts enhance developer experience

🔍 Issues & Suggestions

1. Shell Script Security (scripts/new-demo.sh:61, scripts/scaffold-demo.sh:97)

The eval command is used with user input, which could be a security risk. Consider using safer alternatives or adding proper input validation.

2. CI Workflow Edge Case (.github/workflows/ci.yml:114)

The test checking for spec directory should use -d flag instead of -f

3. Missing Executable Permissions

Ensure all shell scripts have executable permissions set in git: git update-index --chmod=+x scripts/*.sh

4. Error Handling in bootstrap-all.sh

Database preparation errors are silenced (line 57). Consider logging failures instead of silently continuing.

5. Package.json Path Consistency

The gem is renamed to demo_common but npm package references might still use the old name. Ensure consistency across all package references.

🚀 Performance Considerations

  • Shell scripts run operations sequentially. Consider parallelizing independent operations in bootstrap-all.sh for faster execution with multiple demos
  • CI workflow wisely limits changed demo detection to 5 demos to prevent long-running builds

🔒 Security Considerations

  • Good practice checking for uncommitted changes before creating new demos
  • Consider adding shellcheck linting for the shell scripts
  • Add input sanitization for demo names to prevent path traversal

🧪 Test Coverage

  • CI workflow appropriately handles both RSpec and Rails test frameworks
  • Consider adding shell script tests using bats or similar framework
  • The continue-on-error flags in CI might hide real issues - consider making these fail-fast after initial setup phase

💡 Additional Recommendations

  1. Add a .editorconfig file for consistent formatting across different editors
  2. Consider using a monorepo tool like Turborepo or Nx for better dependency management and build orchestration as the project grows
  3. Add pre-commit hooks using the configured lefthook for automated checks
  4. Version management: Consider how demo versioning will work with the shared demo_common package

Overall Assessment

This is a well-executed monorepo migration that sets up a maintainable structure for multiple demos. The breaking changes are clearly documented, and migration instructions are helpful. With the minor issues addressed above, this will provide an excellent foundation for the React on Rails demo ecosystem.

Recommendation: ✅ Approve with minor changes

Great work on this reorganization! The structure will make it much easier to maintain and expand the demo collection.

🤖 Generated with Claude Code

justin808 and others added 3 commits September 29, 2025 15:59
Each Rails app should use its own bin/dev script that's specific
to that app's configuration. Removed symlink logic from:

- scripts/new-demo.sh
- scripts/scaffold-demo.sh
- scripts/apply-shared.sh

Only configuration files (.rubocop.yml, .eslintrc.js) should be
symlinked, as these are truly shared across demos.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Added version configuration system for demo creation:

New `.demo-versions` config file:
- Defines default versions for Shakapacker and React on Rails
- Simple bash file sourced by scripts
- Easy to update for new releases

Command-line overrides:
- Added --shakapacker-version flag to both scripts
- Added --react-on-rails-version flag to both scripts
- Supports version constraints (~> 8.0) or exact versions (8.0.0)
- Command-line flags override config file defaults

Version display:
- Scripts now show which versions are being used
- Clear feedback during demo creation

Updated documentation:
- Added Version Configuration section to README
- Examples of using version flags
- Explanation of fallback behavior

This allows:
1. Global defaults for most demos
2. Per-demo version customization when needed
3. Easy testing of new React on Rails/Shakapacker versions

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Created complete documentation and tooling for managing gem versions
across demos, including beta testing and bulk updates.

New documentation (docs/VERSION_MANAGEMENT.md):
- Overview of version precedence (demo Gemfile > global defaults)
- Creating demos with stable, custom, or beta versions
- Updating individual demos to new versions
- Beta/RC version testing workflow
- Bulk updates across all demos
- Version tracking patterns
- Troubleshooting and examples

Enhanced demo templates:
- Added "Gem Versions" section to README templates
- Shows which versions were used at creation
- Includes creation/update date
- Links to version management docs

New update-all-demos.sh script:
- Bulk update React on Rails and/or Shakapacker across demos
- Supports selective updates with pattern matching
- Runs tests after updates (optional)
- Dry-run mode for preview
- Updates README dates and versions
- Provides clear summary and next steps

Updated .demo-versions config:
- Added comprehensive comments explaining usage
- Clarified that it only affects NEW demos
- Quick reference for common workflows
- Links to full documentation

Workflows documented:
1. Standard demo creation with global defaults
2. Custom version override per demo
3. Beta/RC version testing in dedicated demos
4. Updating existing demos individually
5. Bulk updates when releasing new versions
6. Edge/development version testing with git branches

This provides a complete system for:
- Consistent versioning across demos
- Flexible per-demo customization
- Safe beta testing without affecting stable demos
- Easy updates when releasing new versions

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Copy link

@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: 2

♻️ Duplicate comments (2)
scripts/new-demo.sh (1)

188-195: Install JS dependencies after Rails generators.

The Shakapacker and React on Rails installers modify package.json, but the script never runs npm install to install those dependencies. Users must manually run npm install before bin/dev will work, which contradicts the "Next steps" output.

Apply this diff to install JS dependencies:

 echo ""
 echo "📦 Installing React on Rails (skipping git check)..."
 run_cmd "cd '$DEMO_DIR' && bundle exec rails generate react_on_rails:install --ignore-warnings"

+echo ""
+echo "📦 Installing JavaScript dependencies..."
+run_cmd "cd '$DEMO_DIR' && npm install"
+
 echo ""
 echo "📝 Creating README..."

Then update the "Next steps" section to reflect this:

   echo "✅ Demo created successfully at $DEMO_DIR"
   echo ""
   echo "Next steps:"
   echo "  cd $DEMO_DIR"
   echo "  bin/dev"
scripts/scaffold-demo.sh (1)

66-67: Validate demo name to prevent path traversal.

The script assigns DEMO_NAME directly from $1 without validation, allowing path traversal attacks like ../../etc/passwd. This is a security issue.

Apply this diff:

 # Parse first argument as demo name
 DEMO_NAME="$1"
 shift
+
+# Validate demo name: alphanumeric, hyphens, underscores only
+if [[ -z "$DEMO_NAME" ]] || [[ ! "$DEMO_NAME" =~ ^[a-zA-Z0-9_-]+$ ]]; then
+  echo -e "${RED}Error: Invalid demo name '$DEMO_NAME'${NC}"
+  echo "Demo name must contain only letters, numbers, hyphens, and underscores"
+  exit 1
+fi
🧹 Nitpick comments (5)
scripts/update-all-demos.sh (3)

80-86: eval usage is acceptable here but consider safer alternative.

The shellcheck warning about eval negating array benefits is valid. However, in this context where commands are constructed internally (not from user input), the security risk is minimal. The pattern is also consistent with other scripts in this PR.

For future hardening, consider using arrays with proper quoting instead of eval:

run_cmd() {
  if [ "$DRY_RUN" = true ]; then
    echo -e "${YELLOW}[DRY-RUN]${NC} $*"
  else
    "$@"  # Execute directly without eval
  fi
}

Then call with: run_cmd bundle add react_on_rails --version "$REACT_ON_RAILS_VERSION" --skip-install


144-164: README update logic assumes specific format.

The sed commands expect exact markdown formatting (## Gem Versions, - **React on Rails**:, etc.). If a demo's README has different formatting or is missing these sections, the updates will silently fail.

Consider adding validation or warning messages:

if [ "$DRY_RUN" = false ]; then
  if [ -f README.md ] && grep -q "## Gem Versions" README.md; then
    echo -e "  Updating README.md with new versions..."
    # ... existing sed commands ...
  else
    echo -e "  ${YELLOW}⚠ README.md missing '## Gem Versions' section, skipping update${NC}"
  fi
fi

167-177: Test failures halt processing but continue summary.

When tests fail (line 174 exit 1), the subshell exits but the loop continues. This is correct behavior, but the error output > /dev/null 2>&1 hides useful diagnostic information.

Consider showing test output on failure:

 if [ -d "spec" ]; then
   echo -e "  Running tests..."
-  if bundle exec rspec --fail-fast > /dev/null 2>&1; then
+  if bundle exec rspec --fail-fast --format progress; then
     echo -e "  ${GREEN}✓ Tests passed${NC}"
   else
     echo -e "  ${RED}✗ Tests failed${NC}"
     exit 1
   fi
 fi
README.md (2)

7-19: Add language identifier to fenced code block.

The directory structure code block should specify text or plaintext as the language identifier for proper rendering and to satisfy markdownlint.

Apply this diff:

-```
+```text
 react_on_rails-demos/
 ├─ packages/
 │  └─ demo_common/          # Shared configuration and utilities

25-27: Use proper heading instead of emphasized text.

Line 27 uses emphasis (*(Demos will be listed here as they are added)*) instead of a proper heading or list item. While functional, using proper markdown structure is better for accessibility and parsing.

Consider using a blockquote or note format:

 ### Available Demos

-*(Demos will be listed here as they are added)*
+> Demos will be listed here as they are added.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b031070 and 9873588.

📒 Files selected for processing (7)
  • .demo-versions (1 hunks)
  • README.md (1 hunks)
  • docs/VERSION_MANAGEMENT.md (1 hunks)
  • scripts/apply-shared.sh (1 hunks)
  • scripts/new-demo.sh (1 hunks)
  • scripts/scaffold-demo.sh (1 hunks)
  • scripts/update-all-demos.sh (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • .demo-versions
🚧 Files skipped from review as they are similar to previous changes (1)
  • scripts/apply-shared.sh
🧰 Additional context used
🧬 Code graph analysis (3)
scripts/new-demo.sh (1)
scripts/scaffold-demo.sh (2)
  • show_usage (26-47)
  • run_cmd (126-133)
scripts/update-all-demos.sh (2)
scripts/new-demo.sh (1)
  • run_cmd (90-97)
scripts/scaffold-demo.sh (1)
  • run_cmd (126-133)
scripts/scaffold-demo.sh (1)
scripts/new-demo.sh (2)
  • show_usage (26-41)
  • run_cmd (90-97)
🪛 markdownlint-cli2 (0.18.1)
README.md

7-7: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


27-27: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

🪛 Shellcheck (0.11.0)
scripts/new-demo.sh

[warning] 95-95: eval negates the benefit of arrays. Drop eval to preserve whitespace/symbols (or eval as string).

(SC2294)

scripts/update-all-demos.sh

[warning] 84-84: eval negates the benefit of arrays. Drop eval to preserve whitespace/symbols (or eval as string).

(SC2294)

scripts/scaffold-demo.sh

[error] 371-371: Couldn't find 'fi' for this 'if'.

(SC1046)


[error] 371-371: Couldn't parse this if expression. Fix to allow more checks.

(SC1073)


[error] 374-374: '(' is invalid here. Did you forget to escape it?

(SC1036)


[error] 374-374: Expected 'fi' matching previously mentioned 'if'.

(SC1047)


[error] 374-374: Expected 'fi'. Fix any mentioned problems and try again.

(SC1072)

🔇 Additional comments (5)
docs/VERSION_MANAGEMENT.md (1)

1-510: Comprehensive version management documentation looks excellent!

This documentation provides clear, detailed workflows for version management across demos. The structure is logical, examples are practical, and it addresses common scenarios (beta testing, bulk updates, edge versions).

scripts/new-demo.sh (1)

90-97: eval usage consistent with other scripts.

The shellcheck warning about eval is noted. This pattern is consistent across all scripts in this PR (update-all-demos.sh, scaffold-demo.sh). While a safer array-based approach would be ideal, the current implementation is acceptable for this context where commands are constructed internally.

README.md (1)

1-113: Clear, well-structured monorepo documentation!

The README provides an excellent overview of the monorepo structure, available scripts, and workflows. The examples are practical and the version configuration section clearly explains the defaults and override mechanisms.

scripts/scaffold-demo.sh (2)

125-133: Consistent eval usage across scripts.

The run_cmd function uses eval, similar to other scripts in this PR. While shellcheck warns about this, it's an acceptable pattern for this use case where commands are constructed internally.


336-341: npm install conditional logic is clear.

The script appropriately skips npm installation when --skip-install is specified, and clearly documents this in the usage and next steps output.

justin808 and others added 4 commits September 29, 2025 23:49
Converted all demo management scripts from Bash to Ruby with a
full test suite using RSpec.

New Ruby Implementation:
- Modular, object-oriented design
- Type-safe with clear interfaces
- Fully testable and tested
- Better error handling
- Consistent API across all scripts

Core Modules (lib/demo_scripts/):
- Config: Loads and manages version configuration
- PreFlightChecks: Validates environment before creating demos
- CommandRunner: Executes commands with dry-run support
- DemoCreator: Creates basic React on Rails demos
- DemoScaffolder: Creates advanced demos with options (TypeScript, Tailwind, etc.)
- DemoUpdater: Bulk updates gem versions across demos

Executable Scripts (bin/):
- bin/new-demo: Create a new basic demo
- bin/scaffold-demo: Create an advanced demo with options
- bin/update-all-demos: Update versions across all demos

All scripts support:
- --dry-run mode for previewing changes
- Custom version overrides
- Clear error messages
- Help documentation

Test Suite (spec/):
- Config loading and version precedence
- Command execution with dry-run
- Pre-flight checks for git state
- Demo creation logic
- README generation
- 100% coverage of core functionality

Benefits over Bash:
- Testable: Full RSpec test suite
- Maintainable: Clear class structure
- Extensible: Easy to add new features
- Reliable: Type checking and error handling
- Cross-platform: Better Windows support

Bash scripts retained for compatibility but Ruby versions
are recommended for production use.

Usage:
  bin/new-demo my-demo --dry-run
  bin/scaffold-demo my-demo --typescript --tailwind
  bin/update-all-demos --react-on-rails-version '~> 16.1'

Testing:
  bundle install
  bundle exec rspec
  bundle exec rubocop

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Fixed CI workflow to properly handle PRs targeting non-main branches
and added proper shell variable quoting for shellcheck compliance.

Changes to .github/workflows/ci.yml:

1. Full history fetch:
   - Added fetch-depth: 0 to checkout step
   - Ensures git diff has full history for comparison

2. Dynamic base ref handling:
   - Added step to fetch PR base ref explicitly
   - Changed git diff from hardcoded origin/main to origin/${{ github.base_ref }}
   - Now works correctly for PRs targeting any branch

3. Shell quoting fixes (SC2086):
   - Quoted all variable expansions: ${var}
   - Fixed: demos=${CHANGED} instead of demos=$CHANGED
   - Fixed: "demos/${demo}" instead of "demos/$demo"
   - Fixed: -n "${CHANGED}" instead of -n "$CHANGED"
   - Fixed: ls -A demos 2>/dev/null in test condition

4. Other improvements:
   - Changed -f "spec" to -d "spec" (spec is a directory)
   - Better error handling with 2>/dev/null redirection

This fixes the issue where:
- Shallow clones broke git diff operations
- PRs targeting feature branches would diff against wrong base
- shellcheck warnings about unquoted expansions

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Added Lefthook git hooks to enforce code quality standards
automatically before commits.

New lefthook.yml configuration:
- Pre-commit trailing newline check (auto-fixes)
- Pre-commit RuboCop linting (auto-fixes safe issues)
- Commit message validation

Hooks automatically:
1. Add trailing newlines to staged files (*.rb, *.js, *.ts, etc.)
2. Run RuboCop on staged Ruby files with auto-fix
3. Validate commit messages are not empty

Added lefthook to Gemfile:
- gem "lefthook", "~> 1.5"

New documentation (docs/CONTRIBUTING_SETUP.md):
- Complete development setup guide
- How to install and use git hooks
- How to skip hooks when needed
- Testing and linting instructions
- Troubleshooting common issues
- Best practices for contributors

Updated README.md:
- Added Initial Setup section
- Instructions for installing hooks
- Link to detailed setup documentation

Usage:
  bundle install
  lefthook install

This ensures all committed files meet quality standards:
- Files end with newlines (required by POSIX)
- Ruby code passes RuboCop checks
- Commit messages are meaningful

Hooks can be skipped with LEFTHOOK=0 if needed, but this
is discouraged except in exceptional circumstances.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Updated RuboCop and related gems to latest versions and fixed all
linting violations across the codebase.

RuboCop Updates:
- rubocop: ~> 1.50 → ~> 1.69 (latest)
- rubocop-performance: ~> 1.17 → ~> 1.23
- rubocop-rspec: ~> 2.20 → ~> 3.2

New .rubocop.yml configuration:
- Enabled all new cops (NewCops: enable)
- Configured plugins for rubocop-performance and rubocop-rspec
- Adjusted metrics for demo creation scripts (complex setup code)
- Disabled Documentation cop for internal tools
- Configured RSpec cops for test files
- Excluded build artifacts and vendor directories

Metrics Adjustments (for demo scripts):
- ClassLength: 150 → 300 (demo creators have lots of setup)
- AbcSize: 17 → 25 (complex orchestration methods)
- CyclomaticComplexity: 7 → 10 (conditional setup flows)
- PerceivedComplexity: 8 → 12 (options handling)
- ParameterLists: 5 → 12 (demo scaffolder has many options)
- BlockLength: Excluded Rakefiles and specs

Fixed Violations:
- Converted all double quotes to single quotes
- Added trailing newlines to all files
- Fixed string interpolation style
- Fixed indentation consistency
- Removed unnecessary File.exist? checks
- Added RuboCop metadata to gemspec

Result:
- 23 files inspected
- 0 offenses detected
- All code now passes latest RuboCop standards

Files auto-formatted by RuboCop:
- All Ruby files in lib/
- All spec files
- Gemfiles and gemspecs
- Rakefiles

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
@@ -0,0 +1,510 @@
# Version Management Workflow

This document describes how to manage React on Rails and Shakapacker versions across demos, including creating demos with beta versions and updating existing demos.
Copy link
Member Author

Choose a reason for hiding this comment

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

--dry-run
```

Then manually edit the demo's `Gemfile` to point to a git branch:
Copy link
Member Author

Choose a reason for hiding this comment

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

this is really a separate type of change.

enable bin/new-demo to support using branches


## Version Precedence

After demo creation, version management follows this pattern:
Copy link
Member Author

Choose a reason for hiding this comment

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

I want a top level file that demos can inherit from:

Like:

# frozen_string_literal: true

source "https://rubygems.org"

eval_gemfile File.expand_path("../../Gemfile.development_dependencies", __dir__)

gem "react_on_rails", path: "../.."

https://github.com/shakacode/react_on_rails/blob/master/Gemfile.development_dependencies

Copy link
Member Author

Choose a reason for hiding this comment

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

How best to have a default and allow overrides?


```bash
# Using beta version
./scripts/scaffold-demo.sh react_on_rails-demo-v16-beta-test \
Copy link
Member Author

Choose a reason for hiding this comment

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

Need updates to reflect new ruby program

…docs updates

## Changes Made

### Package Renaming (Breaking Change)
- Renamed ReactOnRailsDemoCommon module to DemoCommon for consistency
- Renamed lib files: react_on_rails_demo_common → demo_common
- Updated all references throughout the codebase
- Updated packages/demo_common/README.md with correct monorepo installation examples

### Security Fixes
- Added input validation to new-demo.sh to prevent path traversal attacks
- Added input validation to scaffold-demo.sh to prevent path traversal attacks
- Fixed variable expansion syntax error in scaffold-demo.sh (line 381)
- Fixed subshell issue in test-all.sh that prevented FAILED_DEMOS tracking

### Script Improvements
- Added JavaScript dependency installation after Rails generators in new-demo.sh
- Scripts now properly install npm/pnpm dependencies after shakapacker/react_on_rails installation
- test-all.sh now uses pushd/popd instead of subshells for proper variable propagation

### Documentation Updates
- Added Gemfile.development_dependencies for shared development dependencies across demos
- Updated VERSION_MANAGEMENT.md to reference new Ruby programs (bin/*) instead of bash scripts
- Added cypress-playwright-on-rails reference to VERSION_MANAGEMENT.md
- Documented eval_gemfile pattern for demo applications
- Added note about git branch support as a future enhancement

## Migration Guide for demo_common

If you're using the old module name:

**Before:**
```ruby
require 'react_on_rails_demo_common'
ReactOnRailsDemoCommon.root
```

**After:**
```ruby
require 'demo_common'
DemoCommon.root
```

Generator invocation also changes:
```bash
# Before
rails generate react_on_rails_demo_common:install

# After
rails generate demo_common:install
```

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Copy link

@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: 15

♻️ Duplicate comments (2)
.github/workflows/ci.yml (1)

76-79: Empty matrix adds no value; consider removing the strategy block entirely.

The matrix: { demo: [] } is empty and triggers actionlint warnings. The job already loops over demos using shell logic (lines 109-124), making the matrix strategy unnecessary.

Apply this diff to remove the empty strategy:

-    strategy:
-      matrix:
-        demo: []  # Will be populated as demos are added
-      fail-fast: false
scripts/scaffold-demo.sh (1)

389-395: Fix README date interpolation. CURRENT_DATE is computed but the heredoc escapes it, so the generated README shows $CURRENT_DATE literally. Drop the backslash so the actual date is written.

Apply this diff:

-Created: \$CURRENT_DATE
+Created: $CURRENT_DATE
🧹 Nitpick comments (15)
Gemfile (1)

5-5: Consider a more recent Ruby minimum version.

Ruby 3.0 reached end-of-life in March 2024. Consider requiring >= 3.1 or >= 3.2 to ensure security support and access to newer language features.

-ruby '>= 3.0'
+ruby '>= 3.1'
docs/CONTRIBUTING_SETUP.md (1)

47-47: Consider using proper heading levels for subsections.

The bold text "Trailing Newline Check" could be converted to a proper heading (e.g., ##### Trailing Newline Check) to improve document structure and accessibility.

Apply this pattern to similar subsections:

-**Trailing Newline Check**
+##### Trailing Newline Check

Also applies to lines 52 and 59 ("RuboCop Linting" and "Message Validation").

spec/demo_scripts/command_runner_spec.rb (1)

16-21: Make the dir test shell-agnostic.

touch is POSIX-only, so this spec fails on Windows/agents without coreutils. Switch to a Ruby command so the test still exercises dir: handling without relying on platform-specific binaries.

-          runner.run('touch test.txt', dir: tmpdir)
+          runner.run(%(#{RbConfig.ruby} -e "File.write('test.txt', '')"), dir: tmpdir)

If RbConfig isn’t already loaded in your spec helper, add require 'rbconfig'.

spec/demo_scripts/config_spec.rb (1)

11-12: Consider using constants for expected default versions.

The hardcoded version strings '~> 8.0' and '~> 16.0' might drift from the actual defaults if DemoScripts::Config::DEFAULT_SHAKAPACKER_VERSION and DEFAULT_REACT_ON_RAILS_VERSION change. Referencing the implementation constants would make tests more maintainable.

Apply this diff:

-        expect(config.shakapacker_version).to eq('~> 8.0')
-        expect(config.react_on_rails_version).to eq('~> 16.0')
+        expect(config.shakapacker_version).to eq(DemoScripts::Config::DEFAULT_SHAKAPACKER_VERSION)
+        expect(config.react_on_rails_version).to eq(DemoScripts::Config::DEFAULT_REACT_ON_RAILS_VERSION)
spec/demo_scripts/pre_flight_checks_spec.rb (1)

21-30: Stub specific git commands for success case

In the success context, replace

allow(checks).to receive(:system).and_call_original

with stubs for the two Git checks:

allow(checks).to receive(:system).with(/git rev-parse/).and_return(true)
allow(checks).to receive(:system).with(/git diff-index/).and_return(true)

This avoids relying on real repository state and mirrors the failure specs.

scripts/new-demo.sh (1)

97-104: eval negates benefit of argument safety.

The run_cmd function uses eval "$@", which can be unsafe if arguments contain special characters. Shellcheck correctly flags this as negating the benefits of proper argument handling.

Consider this safer alternative that preserves arguments without eval:

 run_cmd() {
   if [ "$DRY_RUN" = true ]; then
     echo "[DRY-RUN] $*"
   else
     echo "▶ $*"
-    eval "$@"
+    "$@"
   fi
 }

Note: If you need shell metacharacter expansion (pipes, redirects, etc.), the current eval approach is necessary, but be aware it requires careful input sanitization.

lib/demo_scripts/config.rb (2)

21-31: Add error handling for file reading.

File.readlines will raise an exception if the file becomes unreadable between the existence check and the read operation (TOCTOU issue), or if there are permission issues.

Apply this diff to add error handling:

 def load_config
-  File.readlines(@config_file).each do |line|
-    next if line.strip.empty? || line.strip.start_with?('#')
-
-    if line =~ /^SHAKAPACKER_VERSION\s*=\s*["'](.+)["']/
-      @shakapacker_version = ::Regexp.last_match(1)
-    elsif line =~ /^REACT_ON_RAILS_VERSION\s*=\s*["'](.+)["']/
-      @react_on_rails_version = ::Regexp.last_match(1)
+  File.readlines(@config_file).each do |line|
+    next if line.strip.empty? || line.strip.start_with?('#')
+
+    if line =~ /^SHAKAPACKER_VERSION\s*=\s*["'](.+)["']/
+      @shakapacker_version = ::Regexp.last_match(1)
+    elsif line =~ /^REACT_ON_RAILS_VERSION\s*=\s*["'](.+)["']/
+      @react_on_rails_version = ::Regexp.last_match(1)
+    end
+  end
+rescue Errno::EACCES, Errno::ENOENT => e
+  # File became inaccessible; silently fall back to defaults
+  warn "Warning: Could not read config file #{@config_file}: #{e.message}" if ENV['DEBUG']
+end
+
+private
+
+def load_config
+  begin
+    File.readlines(@config_file).each do |line|
+      next if line.strip.empty? || line.strip.start_with?('#')
+
+      if line =~ /^SHAKAPACKER_VERSION\s*=\s*["'](.+)["']/
+        @shakapacker_version = ::Regexp.last_match(1)
+      elsif line =~ /^REACT_ON_RAILS_VERSION\s*=\s*["'](.+)["']/
+        @react_on_rails_version = ::Regexp.last_match(1)
+      end
     end
+  rescue Errno::EACCES, Errno::ENOENT => e
+    # Silently fall back to defaults, optionally warn in debug mode
+    warn "Warning: Could not read #{@config_file}: #{e.message}" if ENV['DEBUG']
   end
 end

25-28: Regex patterns could be stricter.

The regex patterns allow any characters within quotes (.+), which could match malformed input or excessively long strings.

Consider using a more restrictive pattern:

-    if line =~ /^SHAKAPACKER_VERSION\s*=\s*["'](.+)["']/
+    if line =~ /^SHAKAPACKER_VERSION\s*=\s*["']([~>= \d.]+)["']/
       @shakapacker_version = ::Regexp.last_match(1)
-    elsif line =~ /^REACT_ON_RAILS_VERSION\s*=\s*["'](.+)["']/
+    elsif line =~ /^REACT_ON_RAILS_VERSION\s*=\s*["']([~>= \d.]+)["']/
       @react_on_rails_version = ::Regexp.last_match(1)
     end

This limits matches to valid gem version specifiers (digits, dots, and version operators).

bin/new-demo (1)

35-41: Comma-separated argument parsing may silently accept malformed input.

The split(',').map(&:strip) approach doesn't validate that arguments are well-formed Rails/React on Rails options. For example, --rails-args="--skip-test,,--api" would create an empty string element.

Consider filtering out empty strings:

 opts.on('--rails-args ARGS', 'Additional arguments to pass to rails new (comma-separated)') do |args|
-  options[:rails_args] = args.split(',').map(&:strip)
+  options[:rails_args] = args.split(',').map(&:strip).reject(&:empty?)
 end
 
 opts.on('--react-on-rails-args ARGS', 'Additional arguments to pass to react_on_rails:install (comma-separated)') do |args|
-  options[:react_on_rails_args] = args.split(',').map(&:strip)
+  options[:react_on_rails_args] = args.split(',').map(&:strip).reject(&:empty?)
 end
lib/demo_scripts/pre_flight_checks.rb (1)

36-41: Handle repos with no initial commit.

git diff-index HEAD fails on repos without HEAD. Prefer “git status --porcelain” to detect a clean tree.

-    def check_uncommitted_changes!
-      if system('git diff-index --quiet HEAD -- 2>/dev/null')
+    def check_uncommitted_changes!
+      if `git status --porcelain 2>/dev/null`.strip.empty?
         puts '✓ No uncommitted changes' if @verbose
         return
       end
lib/demo_scripts/demo_scaffolder.rb (1)

79-91: Prefer argv arrays with CommandRunner to avoid shell quoting issues.

Once CommandRunner supports argv/chdir, pass arguments as arrays for safety. Example changes:

-      @runner.run!(
-        "bundle exec rails generate react_on_rails:install #{all_args}",
-        dir: @demo_dir
-      )
+      @runner.run!(["bundle", "exec", "rails", "generate", "react_on_rails:install", *all_args.split(' ')], dir: @demo_dir)
-      @runner.run!(
-        'npm install --save-dev typescript @types/react @types/react-dom',
-        dir: @demo_dir
-      )
+      @runner.run!(["npm", "install", "--save-dev", "typescript", "@types/react", "@types/react-dom"], dir: @demo_dir)
-      @runner.run!(
-        'npm install --save-dev tailwindcss postcss autoprefixer',
-        dir: @demo_dir
-      )
-      @runner.run!('npx tailwindcss init -p', dir: @demo_dir)
+      @runner.run!(["npm", "install", "--save-dev", "tailwindcss", "postcss", "autoprefixer"], dir: @demo_dir)
+      @runner.run!(["npx", "tailwindcss", "init", "-p"], dir: @demo_dir)
-      @runner.run!('npm install bootstrap react-bootstrap', dir: @demo_dir)
+      @runner.run!(["npm", "install", "bootstrap", "react-bootstrap"], dir: @demo_dir)
-      @runner.run!(
-        'npm install @mui/material @emotion/react @emotion/styled',
-        dir: @demo_dir
-      )
+      @runner.run!(["npm", "install", "@mui/material", "@emotion/react", "@emotion/styled"], dir: @demo_dir)
-      @runner.run!('npm install', dir: @demo_dir)
+      @runner.run!(["npm", "install"], dir: @demo_dir)
-      @runner.run('bundle exec rubocop -a --fail-level error', dir: @demo_dir)
+      @runner.run(["bundle", "exec", "rubocop", "-a", "--fail-level", "error"], dir: @demo_dir)

Also applies to: 93-105, 107-128, 130-134, 136-143, 145-149, 183-187

lib/demo_scripts/demo_updater.rb (3)

86-101: Use argv arrays for bundle add (align with safer CommandRunner).

Preps for the safer CommandRunner.

-      @runner.run!(
-        "bundle add react_on_rails --version '#{@react_on_rails_version}' --skip-install",
-        dir: demo_path
-      )
+      @runner.run!(["bundle", "add", "react_on_rails", "--version", @react_on_rails_version.to_s, "--skip-install"], dir: demo_path)
-      @runner.run!(
-        "bundle add shakapacker --version '#{@shakapacker_version}' --skip-install",
-        dir: demo_path
-      )
+      @runner.run!(["bundle", "add", "shakapacker", "--version", @shakapacker_version.to_s, "--skip-install"], dir: demo_path)

102-105: Also switch bundle/rspec invocations to argv arrays.

-      @runner.run!('bundle install', dir: demo_path)
+      @runner.run!(["bundle", "install"], dir: demo_path)
-      success = @runner.run('bundle exec rspec --fail-fast', dir: demo_path)
+      success = @runner.run(["bundle", "exec", "rspec", "--fail-fast"], dir: demo_path)

Also applies to: 135-148


130-133: Make README date update robust (handle existing “Updated:” lines).

If README already has “Updated: …”, current logic won’t bump the date. Update both cases.

-      content.gsub!(/Created:.*/, "Updated: #{current_date}")
+      if content =~ /Updated: .*/
+        content.gsub!(/Updated:.*/, "Updated: #{current_date}")
+      else
+        content.gsub!(/Created:.*/, "Updated: #{current_date}")
+      end
packages/demo_common/lib/generators/demo_common/install/install_generator.rb (1)

7-140: Consider making the generator idempotent.

The generator will overwrite existing configuration files without checking if they've been customized. For files created with create_file (like .rubocop.yml, .eslintrc.js, etc.), consider using create_file ... unless File.exist?(...) or adding a force/skip prompt to preserve user customizations when re-running the generator.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9873588 and 1c501df.

⛔ Files ignored due to path filters (1)
  • Gemfile.lock is excluded by !**/*.lock
📒 Files selected for processing (38)
  • .github/workflows/ci.yml (1 hunks)
  • .rspec (1 hunks)
  • .rubocop.yml (1 hunks)
  • Gemfile (1 hunks)
  • Gemfile.development_dependencies (1 hunks)
  • README.md (1 hunks)
  • Rakefile (1 hunks)
  • bin/new-demo (1 hunks)
  • bin/scaffold-demo (1 hunks)
  • bin/update-all-demos (1 hunks)
  • docs/CONTRIBUTING_SETUP.md (1 hunks)
  • docs/VERSION_MANAGEMENT.md (1 hunks)
  • lefthook.yml (1 hunks)
  • lib/demo_scripts.rb (1 hunks)
  • lib/demo_scripts/command_runner.rb (1 hunks)
  • lib/demo_scripts/config.rb (1 hunks)
  • lib/demo_scripts/demo_creator.rb (1 hunks)
  • lib/demo_scripts/demo_scaffolder.rb (1 hunks)
  • lib/demo_scripts/demo_updater.rb (1 hunks)
  • lib/demo_scripts/pre_flight_checks.rb (1 hunks)
  • lib/demo_scripts/version.rb (1 hunks)
  • packages/demo_common/Gemfile (1 hunks)
  • packages/demo_common/README.md (1 hunks)
  • packages/demo_common/Rakefile (1 hunks)
  • packages/demo_common/demo_common.gemspec (1 hunks)
  • packages/demo_common/lib/demo_common.rb (1 hunks)
  • packages/demo_common/lib/demo_common/railtie.rb (1 hunks)
  • packages/demo_common/lib/demo_common/version.rb (1 hunks)
  • packages/demo_common/lib/generators/demo_common/install/install_generator.rb (1 hunks)
  • packages/demo_common/lib/tasks/demo_common.rake (2 hunks)
  • scripts/new-demo.sh (1 hunks)
  • scripts/scaffold-demo.sh (1 hunks)
  • scripts/test-all.sh (1 hunks)
  • spec/demo_scripts/command_runner_spec.rb (1 hunks)
  • spec/demo_scripts/config_spec.rb (1 hunks)
  • spec/demo_scripts/demo_creator_spec.rb (1 hunks)
  • spec/demo_scripts/pre_flight_checks_spec.rb (1 hunks)
  • spec/spec_helper.rb (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • packages/demo_common/Gemfile
  • .rubocop.yml
🚧 Files skipped from review as they are similar to previous changes (4)
  • packages/demo_common/README.md
  • scripts/test-all.sh
  • packages/demo_common/demo_common.gemspec
  • docs/VERSION_MANAGEMENT.md
🧰 Additional context used
🧬 Code graph analysis (12)
lib/demo_scripts/command_runner.rb (3)
lib/demo_scripts/demo_creator.rb (2)
  • initialize (5-217)
  • initialize (6-26)
lib/demo_scripts/demo_scaffolder.rb (2)
  • initialize (5-354)
  • initialize (6-37)
lib/demo_scripts/demo_updater.rb (2)
  • initialize (5-184)
  • initialize (6-23)
spec/demo_scripts/pre_flight_checks_spec.rb (1)
lib/demo_scripts/pre_flight_checks.rb (1)
  • run! (11-19)
spec/demo_scripts/demo_creator_spec.rb (1)
lib/demo_scripts/demo_creator.rb (1)
  • create! (28-49)
lib/demo_scripts/pre_flight_checks.rb (2)
lib/demo_scripts/demo_creator.rb (2)
  • initialize (5-217)
  • initialize (6-26)
lib/demo_scripts/demo_scaffolder.rb (2)
  • initialize (5-354)
  • initialize (6-37)
spec/demo_scripts/command_runner_spec.rb (1)
lib/demo_scripts/command_runner.rb (3)
  • run (13-24)
  • run! (26-31)
  • capture (33-42)
lib/demo_scripts/demo_updater.rb (2)
lib/demo_scripts/demo_creator.rb (2)
  • initialize (5-217)
  • initialize (6-26)
lib/demo_scripts/command_runner.rb (3)
  • initialize (8-11)
  • run! (26-31)
  • run (13-24)
lib/demo_scripts/config.rb (1)
lib/demo_scripts/demo_creator.rb (2)
  • initialize (5-217)
  • initialize (6-26)
lib/demo_scripts/demo_creator.rb (4)
lib/demo_scripts/demo_scaffolder.rb (6)
  • initialize (5-354)
  • initialize (6-37)
  • create! (39-67)
  • setup_database (71-77)
  • print_completion_message (332-353)
  • generate_readme_content (232-330)
lib/demo_scripts/command_runner.rb (2)
  • initialize (8-11)
  • run! (26-31)
lib/demo_scripts/config.rb (1)
  • initialize (11-17)
lib/demo_scripts/pre_flight_checks.rb (3)
  • initialize (5-54)
  • initialize (6-9)
  • run! (11-19)
lib/demo_scripts/demo_scaffolder.rb (4)
lib/demo_scripts/demo_creator.rb (13)
  • initialize (5-217)
  • initialize (6-26)
  • create! (28-49)
  • run_pre_flight_checks (53-55)
  • create_rails_app (57-73)
  • setup_database (75-79)
  • add_gems (81-95)
  • add_demo_common (97-114)
  • create_symlinks (116-121)
  • install_shakapacker (123-127)
  • create_readme (140-148)
  • print_completion_message (202-216)
  • generate_readme_content (150-200)
lib/demo_scripts/command_runner.rb (3)
  • initialize (8-11)
  • run! (26-31)
  • run (13-24)
lib/demo_scripts/config.rb (1)
  • initialize (11-17)
lib/demo_scripts/pre_flight_checks.rb (3)
  • initialize (5-54)
  • initialize (6-9)
  • run! (11-19)
packages/demo_common/lib/generators/demo_common/install/install_generator.rb (1)
packages/demo_common/lib/demo_common.rb (2)
  • root (10-12)
  • config_path (14-16)
scripts/scaffold-demo.sh (2)
scripts/new-demo.sh (2)
  • show_usage (26-41)
  • run_cmd (97-104)
scripts/update-all-demos.sh (2)
  • show_usage (12-31)
  • run_cmd (80-86)
scripts/new-demo.sh (1)
scripts/scaffold-demo.sh (2)
  • show_usage (26-47)
  • run_cmd (133-140)
🪛 actionlint (1.7.7)
.github/workflows/ci.yml

78-78: "matrix values" section should not be empty

(syntax-check)

🪛 Brakeman (7.0.2)
lib/demo_scripts/command_runner.rb

[medium] 41-41: Possible command injection
Type: Command Injection
Confidence: Medium
More info: https://brakemanscanner.org/docs/warning_types/command_injection/

(Command Injection)


[medium] 23-23: Possible command injection
Type: Command Injection
Confidence: Medium
More info: https://brakemanscanner.org/docs/warning_types/command_injection/

(Command Injection)

🪛 markdownlint-cli2 (0.18.1)
docs/CONTRIBUTING_SETUP.md

47-47: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


52-52: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


59-59: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

README.md

47-47: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


52-52: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


59-59: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

🪛 Shellcheck (0.11.0)
scripts/scaffold-demo.sh

[warning] 138-138: eval negates the benefit of arrays. Drop eval to preserve whitespace/symbols (or eval as string).

(SC2294)


[warning] 381-381: CURRENT_DATE appears unused. Verify use (or export if used externally).

(SC2034)

scripts/new-demo.sh

[warning] 102-102: eval negates the benefit of arrays. Drop eval to preserve whitespace/symbols (or eval as string).

(SC2294)

🔇 Additional comments (45)
.rspec (1)

1-3: LGTM! Standard RSpec configuration.

The configuration correctly enables spec_helper auto-require, colored output, and documentation formatting, which are standard best practices for RSpec projects.

packages/demo_common/lib/demo_common/version.rb (1)

1-5: LGTM! Standard version constant definition.

The version module follows Ruby conventions with frozen string literal and a semantic version constant, appropriate for the initial 0.1.0 release of the demo_common gem.

Gemfile.development_dependencies (3)

1-24: Well-structured shared dependencies file with clear documentation.

The file provides a clean, centralized manifest for demo development dependencies with helpful usage instructions. The gem selections and version constraints are appropriate for a modern Rails/RSpec development environment.


13-16: Addresses duplication concern with root Gemfile.

These rubocop gems (rubocop, rubocop-performance, rubocop-rspec) also appear in the root Gemfile. Since this file is intended to be loaded by demo applications via eval_gemfile, and the root Gemfile serves the monorepo root, the duplication may be intentional. However, consider whether demos should exclusively use this shared file while the root uses a different set, or if consolidation is possible.


23-23: Retain the listen gem for Rails 7+ evented file watching. Rails 7 still expects and recommends listen for the ActiveSupport::EventedFileUpdateChecker in development.

Likely an incorrect or invalid review comment.

spec/spec_helper.rb (1)

1-27: LGTM! Comprehensive and well-configured RSpec setup.

The spec helper follows RSpec best practices with appropriate configurations for expectations, mocks, example persistence, monkey patching disabled, profiling, and randomized execution. The setup properly supports the new DemoScripts test suite.

Gemfile (1)

7-12: No action required: development dependencies file isn’t evaluated by the root Gemfile.
The root Gemfile doesn’t reference Gemfile.development_dependencies (no eval_gemfile) so overlapping gems live in separate contexts and won’t conflict.

Likely an incorrect or invalid review comment.

docs/CONTRIBUTING_SETUP.md (1)

1-180: Documentation is comprehensive and well-structured.

The setup guide covers all essential development tasks, from initial setup through git hooks, testing, linting, and troubleshooting. The inclusion of Lefthook configuration details and best practices is particularly valuable for new contributors.

packages/demo_common/lib/demo_common.rb (1)

1-22: LGTM! Clean module structure with path helpers.

The module provides a clear public API for accessing package paths. The use of Pathname and conditional Railtie loading follows Ruby best practices.

.github/workflows/ci.yml (1)

10-124: CI workflow is well-structured for the monorepo.

The workflow appropriately separates linting and testing concerns across demo_common and individual demos. The dynamic demo detection for PRs is a good approach for selective testing.

README.md (2)

31-46: Clarify where bundle install should be run.

The prerequisites mention Ruby 3.3+, but the setup instructions show bundle install without specifying the working directory. Should this be run from the repository root or from packages/demo_common/?

Consider adding a working directory comment:

 ```bash
-# Install dependencies
+# Install dependencies (run from repository root)
 bundle install

1-141: README provides clear guidance for monorepo navigation.

The documentation effectively communicates the new monorepo structure, script-driven workflows, and version management. The distinction between Ruby and Bash scripts is helpful for users.

Rakefile (1)

1-14: LGTM! Clean integration of linting into the default test flow.

The Rakefile now provides a unified test-and-lint workflow through the default task, with a convenient test alias. Removing the dynamic task loading simplifies the build configuration.

spec/demo_scripts/config_spec.rb (2)

16-60: LGTM!

The tests properly cover custom version overrides, file-based config loading, and edge cases like comments and empty lines. The use of Dir.mktmpdir ensures proper cleanup and isolation.


62-79: LGTM!

The test correctly validates that constructor-provided versions take precedence over config file values, and demonstrates partial override behavior (only react_on_rails_version is overridden while shakapacker_version is loaded from the file).

bin/scaffold-demo (3)

1-20: LGTM!

The executable setup is clean with proper requires and well-structured default options. All flags are explicitly initialized.


22-82: LGTM!

The option parsing is well-structured with clear help text and examples. The handling of comma-separated arguments with split(',').map(&:strip) is appropriate for CLI input.


84-107: LGTM!

The main execution flow properly validates that a demo name is provided, initializes the scaffolder with the parsed options, and handles both application errors (DemoScripts::Error) and option parsing errors appropriately. Exit status 1 is correctly used for all error conditions.

lib/demo_scripts.rb (2)

1-9: LGTM!

The require structure is clean and well-organized, loading core components (version, config) before higher-level functionality (creators, scaffolders, updater).


11-16: LGTM!

The exception hierarchy is well-designed with a base Error class inheriting from StandardError (Ruby best practice) and specialized error types (PreFlightCheckError, CommandError) for different failure modes, enabling precise error handling in client code.

spec/demo_scripts/pre_flight_checks_spec.rb (4)

1-19: LGTM!

The test setup properly isolates each test with cleanup in both before and after blocks. Using verbose: false prevents output pollution during test runs.


32-43: LGTM!

The test properly validates that an existing directory triggers a PreFlightCheckError. The regex matcher allows for flexible message matching.


45-56: LGTM!

The test correctly stubs the git repository check and validates the expected error. Using a regex for the command allows flexibility in the exact git command format.


58-71: LGTM!

The test correctly simulates being in a git repository with uncommitted changes by stubbing both git rev-parse (returns true) and git diff-index (returns false). This properly validates the uncommitted changes check.

spec/demo_scripts/demo_creator_spec.rb (5)

1-21: LGTM!

The initialization test provides good baseline coverage, verifying that a DemoCreator can be instantiated with the expected options.


23-42: LGTM!

The tests properly validate dry-run mode behavior: output indicates dry-run status and no demo directory is created. Using skip_pre_flight: true appropriately avoids git state dependencies in unit tests.


44-61: LGTM!

The test appropriately uses send to access the private generate_readme_content method for validation. It correctly verifies that gem versions and the version header are included in the generated README.


63-68: LGTM!

The test correctly validates that the creation date in YYYY-MM-DD format is included in the generated README.


70-75: LGTM!

The test validates that the README includes a link to the version management documentation, ensuring discoverability of important documentation for demo users.

scripts/new-demo.sh (3)

83-88: Good validation and pre-flight checks!

The demo name validation prevents path traversal, and the comprehensive pre-flight checks (directory existence, git status, git repository) prevent common errors. This addresses the concerns from previous reviews.

Also applies to: 109-134


204-209: JS dependency installation is now included.

The script correctly installs JavaScript dependencies after running the installers, addressing the previous review concern. The conditional check for pnpm with fallback to npm is a good practice.


147-157: No action needed: multi-line rails new is handled correctly
The backslash-newline escapes collapse at parse time into a single string argument, so run_cmd’s eval "$@" executes the full command as intended.

lib/demo_scripts/config.rb (1)

1-17: LGTM! Clean configuration loader with proper precedence.

The initialization logic correctly implements the precedence order (explicit arguments → file-loaded values → defaults), and the default constants are well-defined.

bin/new-demo (2)

1-14: LGTM! Clean setup with proper dependencies.

The script correctly requires dependencies and initializes the options hash with appropriate defaults.


54-77: Good error handling with specific exception types.

The error handling appropriately catches DemoScripts::Error for domain errors and OptionParser::InvalidOption for CLI parsing errors, providing clear messages and proper exit codes.

lib/demo_scripts/demo_updater.rb (1)

34-38: Confirm error type resolves to DemoScripts::Error.

Ensure DemoScripts::Error exists and is required before DemoUpdater loads; otherwise qualify or require the defining file.

Would you like me to scan the repo to confirm the definition and load order?

packages/demo_common/lib/generators/demo_common/install/install_generator.rb (9)

12-15: Verify error handling for npm command.

The run method doesn't halt the generator on command failure by default. If npm is unavailable or the path is invalid, the installation will continue silently.

Consider whether explicit error checking is needed:

def add_npm_package
  say 'Adding @shakacode/react-on-rails-demo-common to package.json'
  unless run "npm install --save-dev @shakacode/react-on-rails-demo-common@file:#{gem_root_path}", abort_on_failure: true
    say 'Failed to install npm package', :red
    exit(1)
  end
end

17-22: Verify error handling for sequential commands.

Multiple shell commands are executed without checking for failures. If npm or lefthook is unavailable, or if any command fails, subsequent commands may produce unexpected results.


24-32: LGTM!

The RuboCop configuration creation is well-structured with proper YAML formatting and clear guidance for project-specific overrides.


34-44: LGTM!

The ESLint configuration follows a good pattern for extending shared config with clear guidance for project-specific overrides.


46-53: LGTM!

The Prettier configuration setup correctly reuses the shared config and copies the ignore file.


75-82: LGTM!

The Commitlint configuration is correctly structured.


84-87: LGTM!

The GitHub Actions workflow setup uses the standard Rails generator template method appropriately.


89-105: LGTM!

The .gitignore updates are well-organized with clear section comments.


107-119: LGTM!

The post-install instructions are clear, well-formatted, and provide helpful next steps.

Comment on lines 66 to 70
run: npm run lint
continue-on-error: true # Allow to continue if lint script doesn't exist
- name: Check Prettier formatting
run: npm run format:check
continue-on-error: true # Allow to continue if format script doesn't exist
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify that npm scripts exist before using continue-on-error.

Both npm run lint and npm run format:check use continue-on-error: true, which silently ignores failures if the scripts don't exist. This can mask configuration issues.

Run this script to verify the npm scripts are defined in package.json:


🏁 Script executed:

#!/bin/bash
# Description: Check if lint and format:check scripts exist in package.json

if [ -f "packages/demo_common/package.json" ]; then
  echo "Checking for npm scripts in packages/demo_common/package.json:"
  jq -r '.scripts | keys[]' packages/demo_common/package.json | grep -E '^(lint|format:check)$' || echo "Missing lint or format:check scripts"
else
  echo "package.json not found at packages/demo_common/package.json"
fi

Length of output: 176


Lint and formatting CI steps silently ignored
In .github/workflows/ci.yml (lines 66–70), the npm run lint and npm run format:check steps use continue-on-error: true despite the corresponding scripts missing in packages/demo_common/package.json. This masks failures.

  • Add lint and format:check scripts to packages/demo_common/package.json.
  • Remove continue-on-error: true so CI fails on errors.
  • Or switch to npm run lint --if-present and npm run format:check --if-present.
🤖 Prompt for AI Agents
.github/workflows/ci.yml lines 66-70: the workflow currently runs `npm run lint`
and `npm run format:check` with `continue-on-error: true`, which masks failures
because the scripts are missing; fix by either (A) adding `lint` and
`format:check` scripts to packages/demo_common/package.json so the commands
exist and then remove the `continue-on-error: true` lines so CI fails on errors,
or (B) update the workflow commands to `npm run lint --if-present` and `npm run
format:check --if-present` and remove `continue-on-error: true` so the steps are
no-ops when scripts are absent but still fail for real script errors—make the
change consistently for both steps and remove the `continue-on-error` keys.

Comment on lines +62 to +69
demo_name = ARGV[0]

creator = DemoScripts::DemoCreator.new(
demo_name: demo_name,
**options
)

creator.create!
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Validate demo name before passing to DemoCreator.

The script accepts any ARGV[0] as the demo name without validation. While DemoCreator might validate internally, the CLI should provide early feedback to avoid unnecessary work.

Let's verify whether DemoCreator validates the demo name:


🏁 Script executed:

#!/bin/bash
# Check if DemoCreator validates demo names

ast-grep --pattern 'class DemoCreator
  $$$
  def initialize($$$)
    $$$
  end
  $$$
'

# Also search for validation methods
rg -n "def.*validate" lib/demo_scripts/demo_creator.rb

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Locate the DemoCreator source file
fd demo_creator\.rb

# Inspect the first 200 lines of the file to find initialize and any validation logic
sed -n '1,200p' lib/demo_scripts/demo_creator.rb

# Search within for demo_name handling and validation methods
rg -n "demo_name" -C3 lib/demo_scripts/demo_creator.rb
rg -n "validate" -C3 lib/demo_scripts/demo_creator.rb

Length of output: 6682


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show occurrences of PreFlightChecks
rg -n "PreFlightChecks" -S || true

# If the common location exists, print it
if [ -f lib/demo_scripts/pre_flight_checks.rb ]; then
  sed -n '1,240p' lib/demo_scripts/pre_flight_checks.rb
fi

# Print the CLI script to check ARGV handling
sed -n '1,260p' bin/new-demo

Length of output: 3609


Add demo name validation in bin/new-demo
The CLI currently accepts any string as DEMO_NAME and neither DemoCreator nor PreFlightChecks enforce a valid format or sanitize path inputs. Add a validation step in bin/new-demo—for example, enforce something like /\A[a-zA-Z0-9_-]+\z/—and exit early with a clear error if the name is invalid.

🤖 Prompt for AI Agents
In bin/new-demo around lines 62 to 69, the script currently passes any demo_name
from ARGV[0] into DemoCreator; add a validation step before constructing
DemoCreator that checks ARGV[0] exists and matches the allowed pattern (for
example /\A[a-zA-Z0-9_-]+\z/), and if it fails print a clear error to STDERR and
exit with a nonzero status; ensure this validation occurs before any filesystem
or creator actions so invalid names are rejected early (optionally also enforce
a max length and reject values containing path separators to prevent traversal).

Comment on lines 5 to 27
parallel: true
commands:
trailing-newline:
glob: "*.{rb,js,ts,jsx,tsx,yml,yaml,json,md,sh}"
run: |
for file in {staged_files}; do
if [ -f "$file" ]; then
# Check if file ends with newline
if [ -n "$(tail -c 1 "$file")" ]; then
echo "❌ Missing trailing newline: $file"
# Add trailing newline
echo "" >> "$file"
git add "$file"
echo "✅ Added trailing newline to: $file"
fi
fi
done
rubocop:
glob: "*.rb"
run: bundle exec rubocop --force-exclusion {staged_files}
stage_fixed: true

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Run pre-commit commands sequentially when they git add.

With parallel: true, Lefthook can launch trailing-newline and rubocop at the same time. Both commands ultimately execute git add (explicitly in the newline fixer and implicitly via stage_fixed: true), so they’ll race on .git/index and intermittently fail with “index.lock” errors. Flip the block back to sequential execution (or move the newline fixer into a separate serial block) so only one command touches the index at a time.

Apply this diff:

 pre-commit:
-  parallel: true
+  parallel: false
🤖 Prompt for AI Agents
In lefthook.yml around lines 5 to 27, running commands in parallel causes races
on git index because both trailing-newline (which calls git add) and rubocop
(which uses stage_fixed) can touch .git/index concurrently; change execution to
sequential by removing or setting parallel to false for this block (or move the
trailing-newline hook into a separate serial block) so only one command performs
git add at a time, preventing intermittent index.lock errors.

Comment on lines +10 to +21
for file in {staged_files}; do
if [ -f "$file" ]; then
# Check if file ends with newline
if [ -n "$(tail -c 1 "$file")" ]; then
echo "❌ Missing trailing newline: $file"
# Add trailing newline
echo "" >> "$file"
git add "$file"
echo "✅ Added trailing newline to: $file"
fi
fi
done
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Handle staged paths with spaces.

for file in {staged_files}; do splits on whitespace, so a staged file like docs/Release Notes.md is never processed and the hook silently fails to add the newline. Read the staged list with a NUL‑delimited loop (via git diff --cached --name-only -z) to preserve spaces and other special characters.

Apply this diff:

-        for file in {staged_files}; do
-          if [ -f "$file" ]; then
-            # Check if file ends with newline
-            if [ -n "$(tail -c 1 "$file")" ]; then
-              echo "❌ Missing trailing newline: $file"
-              # Add trailing newline
-              echo "" >> "$file"
-              git add "$file"
-              echo "✅ Added trailing newline to: $file"
-            fi
-          fi
-        done
+        git diff --cached --name-only -z -- '*.{rb,js,ts,jsx,tsx,yml,yaml,json,md,sh}' |
+          while IFS= read -r -d '' file; do
+            if [ -f "$file" ] && [ -n "$(tail -c 1 "$file")" ]; then
+              echo "❌ Missing trailing newline: $file"
+              echo "" >> "$file"
+              git add "$file"
+              echo "✅ Added trailing newline to: $file"
+            fi
+          done
📝 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
for file in {staged_files}; do
if [ -f "$file" ]; then
# Check if file ends with newline
if [ -n "$(tail -c 1 "$file")" ]; then
echo "❌ Missing trailing newline: $file"
# Add trailing newline
echo "" >> "$file"
git add "$file"
echo "✅ Added trailing newline to: $file"
fi
fi
done
git diff --cached --name-only -z -- '*.{rb,js,ts,jsx,tsx,yml,yaml,json,md,sh}' |
while IFS= read -r -d '' file; do
if [ -f "$file" ] && [ -n "$(tail -c 1 "$file")" ]; then
echo "❌ Missing trailing newline: $file"
echo "" >> "$file"
git add "$file"
echo "✅ Added trailing newline to: $file"
fi
done
🤖 Prompt for AI Agents
In lefthook.yml around lines 10 to 21, the current for-loop splits staged file
paths on whitespace so files with spaces are skipped; replace the iteration with
a NUL-delimited read loop that obtains staged paths using git diff --cached
--name-only -z and then uses while IFS= read -r -d '' file; do ... done to
handle filenames with spaces and special characters safely, keeping the same
checks (ensure file exists, check trailing newline with tail -c 1, append a
newline when missing, and git add the modified file) and ensure all uses of
$file are properly quoted.

@@ -0,0 +1,44 @@
# frozen_string_literal: true
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Add required dependency.

Needed for Open3 usage above.

 # frozen_string_literal: true
+
+require 'open3'
📝 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
# frozen_string_literal: true
# frozen_string_literal: true
require 'open3'
🤖 Prompt for AI Agents
In lib/demo_scripts/command_runner.rb around lines 1 to 1, the file uses Open3
but doesn't require it; add the missing dependency by inserting require 'open3'
at the top of the file (immediately after the frozen_string_literal comment) so
Open3 methods are available.

Comment on lines +55 to +73
def add_npm_scripts
say 'Adding npm scripts'
package_json_path = Rails.root.join('package.json')

return unless File.exist?(package_json_path)

package_json = JSON.parse(File.read(package_json_path))

package_json['scripts'] ||= {}
package_json['scripts'].merge!({
'lint' => 'eslint . --ext .js,.jsx,.ts,.tsx',
'lint:fix' => 'eslint . --ext .js,.jsx,.ts,.tsx --fix',
'format' => "prettier --write '**/*.{js,jsx,ts,tsx,json,css,scss,md}'",
'format:check' => "prettier --check '**/*.{js,jsx,ts,tsx,json,css,scss,md}'",
'prepare' => 'lefthook install'
})

File.write(package_json_path, JSON.pretty_generate(package_json))
end
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add error handling for JSON parsing and consider merge behavior.

Two concerns:

  1. JSON.parse will raise an exception if package.json is malformed, halting the generator unexpectedly.
  2. merge! will silently overwrite any existing scripts with the same keys. Consider whether this is intentional or if you should preserve existing scripts.

Consider adding error handling:

def add_npm_scripts
  say 'Adding npm scripts'
  package_json_path = Rails.root.join('package.json')

  return unless File.exist?(package_json_path)

  begin
    package_json = JSON.parse(File.read(package_json_path))
  rescue JSON::ParserError => e
    say "Failed to parse package.json: #{e.message}", :red
    return
  end

  package_json['scripts'] ||= {}
  # Consider using reverse_merge! to preserve existing scripts
  package_json['scripts'].merge!({
    # ... scripts
  })

  File.write(package_json_path, JSON.pretty_generate(package_json))
end
🤖 Prompt for AI Agents
In packages/demo_common/lib/generators/demo_common/install/install_generator.rb
around lines 55 to 73, JSON.parse(File.read(package_json_path)) can raise
JSON::ParserError and halt the generator, and merge! will silently overwrite
existing npm scripts; wrap the parse in a begin/rescue that catches
JSON::ParserError, say a clear error (e.g., say "Failed to parse package.json:
#{e.message}", :red) and return early so you don't overwrite the file, and
change the merging behavior to preserve existing scripts (use a non-destructive
approach such as only adding keys that don't exist or using reverse_merge-like
semantics) before writing the updated package.json.


# Validate demo name: alphanumeric, hyphens, underscores only; no path separators
if [[ ! "$DEMO_NAME" =~ ^[a-zA-Z0-9_-]+$ ]]; then
echo -e "${RED}Error: Invalid demo name '$DEMO_NAME'${NC}"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Undefined color variables will cause display issues.

Line 85 references ${RED} and ${NC} but these variables are never defined in the script, resulting in empty strings being output.

Apply this diff to define the color variables:

 set -euo pipefail
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
 
 # Load default versions from config file
📝 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
echo -e "${RED}Error: Invalid demo name '$DEMO_NAME'${NC}"
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Load default versions from config file
# (rest of script continues…)
🤖 Prompt for AI Agents
In scripts/new-demo.sh around line 85, the echo uses undefined color variables
${RED} and ${NC}; define ANSI color variables (e.g., RED and NC/reset) near the
top of the script (or before use) so they are non-empty and safe, e.g., add
assignments like RED='\033[0;31m' and NC='\033[0m' (or wrap in a conditional to
only set when output is a tty) so the error message prints with proper coloring
without producing empty strings.

Comment on lines +69 to +74
# Validate demo name: alphanumeric, hyphens, underscores only; no path separators
if [[ -z "$DEMO_NAME" ]] || [[ ! "$DEMO_NAME" =~ ^[a-zA-Z0-9_-]+$ ]]; then
echo -e "${RED}Error: Invalid demo name '$DEMO_NAME'${NC}"
echo "Demo name must contain only letters, numbers, hyphens, and underscores"
exit 1
fi
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Reject demo names starting with “-”. Allowing a leading hyphen means every subsequent cd '$DEMO_DIR' is interpreted as an option (cd -foo), so the scaffold fails immediately after rails new. Add an explicit guard so names beginning with - are rejected up front.

Apply this diff:

-if [[ -z "$DEMO_NAME" ]] || [[ ! "$DEMO_NAME" =~ ^[a-zA-Z0-9_-]+$ ]]; then
+if [[ -z "$DEMO_NAME" ]] || [[ "$DEMO_NAME" =~ ^- ]] || [[ ! "$DEMO_NAME" =~ ^[a-zA-Z0-9][a-zA-Z0-9_-]*$ ]]; then
📝 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
# Validate demo name: alphanumeric, hyphens, underscores only; no path separators
if [[ -z "$DEMO_NAME" ]] || [[ ! "$DEMO_NAME" =~ ^[a-zA-Z0-9_-]+$ ]]; then
echo -e "${RED}Error: Invalid demo name '$DEMO_NAME'${NC}"
echo "Demo name must contain only letters, numbers, hyphens, and underscores"
exit 1
fi
# Validate demo name: alphanumeric, hyphens, underscores only; no path separators
if [[ -z "$DEMO_NAME" ]] || [[ "$DEMO_NAME" =~ ^- ]] || [[ ! "$DEMO_NAME" =~ ^[a-zA-Z0-9][a-zA-Z0-9_-]*$ ]]; then
echo -e "${RED}Error: Invalid demo name '$DEMO_NAME'${NC}"
echo "Demo name must contain only letters, numbers, hyphens, and underscores"
exit 1
fi
🤖 Prompt for AI Agents
In scripts/scaffold-demo.sh around lines 69 to 74, the demo name validation
currently allows names that begin with a hyphen which causes commands like cd to
treat them as options; add an explicit guard rejecting names that start with '-'
(for example check if DEMO_NAME begins with '-' and fail with the same error
message), ensuring the script exits with status 1 when a leading hyphen is
detected.

@justin808 justin808 requested a review from Copilot October 1, 2025 00:10
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR reorganizes the repository into a monorepo structure to support multiple React on Rails demo applications. The existing code has been moved to packages/demo_common/ subdirectory and infrastructure has been added for managing multiple demos.

Key changes:

  • Reorganized codebase into monorepo structure with packages/ and demos/ directories
  • Renamed Ruby gem from react_on_rails_demo_common to demo_common
  • Added comprehensive tooling for creating, managing, and testing demos with both Ruby and Bash scripts

Reviewed Changes

Copilot reviewed 48 out of 65 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
packages/demo_common/ Contains renamed and relocated shared configuration gem
lib/demo_scripts/ New Ruby classes for demo creation and management
scripts/ Bash scripts for demo operations (bootstrap, scaffold, test, update)
bin/ Ruby executable scripts (recommended over bash versions)
docs/ New documentation for version management and development setup
spec/ Test coverage for new Ruby demo scripts

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.


Example: `rake demo_common:release[1.2.0]` or `rake demo_common:release[1.2.0,true]` for dry run"
task :release, %i[gem_version dry_run] do |_t, args|
require_relative 'lib/react_on_rails_demo_common/version'
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

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

The require path is incorrect. It should require demo_common/version since the gem was renamed from react_on_rails_demo_common to demo_common.

Copilot uses AI. Check for mistakes.
end

# Update version in version.rb
version_file = 'lib/react_on_rails_demo_common/version.rb'
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

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

The version file path is incorrect. It should be lib/demo_common/version.rb to match the renamed gem structure.

Copilot uses AI. Check for mistakes.
Comment on lines 85 to 92
sh 'gem build react_on_rails_demo_common.gemspec'

puts "\nReady to push gem to RubyGems.org"
puts 'You may need to enter your RubyGems OTP'
sh "gem push react_on_rails_demo_common-#{gem_version}.gem"

# Clean up gem file
sh "rm react_on_rails_demo_common-#{gem_version}.gem"
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

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

The gemspec filename is incorrect. It should be demo_common.gemspec to match the renamed gem.

Suggested change
sh 'gem build react_on_rails_demo_common.gemspec'
puts "\nReady to push gem to RubyGems.org"
puts 'You may need to enter your RubyGems OTP'
sh "gem push react_on_rails_demo_common-#{gem_version}.gem"
# Clean up gem file
sh "rm react_on_rails_demo_common-#{gem_version}.gem"
sh 'gem build demo_common.gemspec'
puts "\nReady to push gem to RubyGems.org"
puts 'You may need to enter your RubyGems OTP'
sh "gem push demo_common-#{gem_version}.gem"
# Clean up gem file
sh "rm demo_common-#{gem_version}.gem"

Copilot uses AI. Check for mistakes.
Comment on lines 89 to 92
sh "gem push react_on_rails_demo_common-#{gem_version}.gem"

# Clean up gem file
sh "rm react_on_rails_demo_common-#{gem_version}.gem"
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

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

The gem filename is incorrect. It should be demo_common-#{gem_version}.gem to match the renamed gem.

Suggested change
sh "gem push react_on_rails_demo_common-#{gem_version}.gem"
# Clean up gem file
sh "rm react_on_rails_demo_common-#{gem_version}.gem"
sh "gem push demo_common-#{gem_version}.gem"
# Clean up gem file
sh "rm demo_common-#{gem_version}.gem"

Copilot uses AI. Check for mistakes.
Comment on lines 85 to 92
sh 'gem build react_on_rails_demo_common.gemspec'

puts "\nReady to push gem to RubyGems.org"
puts 'You may need to enter your RubyGems OTP'
sh "gem push react_on_rails_demo_common-#{gem_version}.gem"

# Clean up gem file
sh "rm react_on_rails_demo_common-#{gem_version}.gem"
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

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

The gem filename is incorrect. It should be demo_common-#{gem_version}.gem to match the renamed gem.

Suggested change
sh 'gem build react_on_rails_demo_common.gemspec'
puts "\nReady to push gem to RubyGems.org"
puts 'You may need to enter your RubyGems OTP'
sh "gem push react_on_rails_demo_common-#{gem_version}.gem"
# Clean up gem file
sh "rm react_on_rails_demo_common-#{gem_version}.gem"
sh 'gem build demo_common.gemspec'
puts "\nReady to push gem to RubyGems.org"
puts 'You may need to enter your RubyGems OTP'
sh "gem push demo_common-#{gem_version}.gem"
# Clean up gem file
sh "rm demo_common-#{gem_version}.gem"

Copilot uses AI. Check for mistakes.
Comment on lines 115 to 118
require_relative 'lib/react_on_rails_demo_common/version'
package_json = JSON.parse(File.read('package.json'))

puts "Gem version: #{ReactOnRailsDemoCommon::VERSION}"
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

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

The require path and module reference are incorrect. They should use demo_common/version and DemoCommon::VERSION respectively to match the renamed gem.

Suggested change
require_relative 'lib/react_on_rails_demo_common/version'
package_json = JSON.parse(File.read('package.json'))
puts "Gem version: #{ReactOnRailsDemoCommon::VERSION}"
require 'demo_common/version'
package_json = JSON.parse(File.read('package.json'))
puts "Gem version: #{DemoCommon::VERSION}"

Copilot uses AI. Check for mistakes.
Comment on lines +70 to +71
if [[ -z "$DEMO_NAME" ]] || [[ ! "$DEMO_NAME" =~ ^[a-zA-Z0-9_-]+$ ]]; then
echo -e "${RED}Error: Invalid demo name '$DEMO_NAME'${NC}"
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

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

The variables RED and NC are referenced before they are defined. These color variables are defined later in the script on lines 7-10, but this validation happens before those definitions are reached.

Copilot uses AI. Check for mistakes.
Comment on lines +84 to +85
if [[ ! "$DEMO_NAME" =~ ^[a-zA-Z0-9_-]+$ ]]; then
echo -e "${RED}Error: Invalid demo name '$DEMO_NAME'${NC}"
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

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

The variables RED and NC are referenced before they are defined. The color variables need to be moved before this validation block.

Copilot uses AI. Check for mistakes.
justin808 and others added 2 commits September 30, 2025 14:13
The DemoScaffolder class now has 13 parameters after adding rails_args and
react_on_rails_args to support custom arguments for rails new and
react_on_rails:install commands. This is reasonable given the class's
responsibility for scaffolding demos with many configuration options.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Added Prettier configuration and tooling for consistent formatting across
the monorepo:

- Created package.json with Prettier dependency and npm scripts
- Added .prettierrc configuration with standard settings
- Added .prettierignore to exclude build outputs and dependencies
- Formatted all existing files (markdown, YAML, JSON, JavaScript)
- Updated README.md with Prettier usage instructions

Demos will still use Prettier from @shakacode/react-on-rails-demo-common,
but this allows formatting of root-level files and documentation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@justin808 justin808 requested a review from Copilot October 1, 2025 00:18
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 56 out of 70 changed files in this pull request and generated 1 comment.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

- **React on Rails**: \`$REACT_ON_RAILS_VERSION\`
- **Shakapacker**: \`$SHAKAPACKER_VERSION\`
Created: \$CURRENT_DATE
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

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

The variable expansion syntax is incorrect. It should be Created: $CURRENT_DATE (without the backslash) to properly expand the CURRENT_DATE variable in the generated README.

Suggested change
Created: \$CURRENT_DATE
Created: $CURRENT_DATE

Copilot uses AI. Check for mistakes.
justin808 and others added 4 commits September 30, 2025 14:20
…mands

Improved user experience with detailed command descriptions and examples:

Command Help Messages:
- bin/new-demo: Added description explaining basic demo creation
- bin/scaffold-demo: Added description for advanced demo scaffolding
- bin/update-all-demos: Added description for bulk version updates
- All commands now show clear usage, examples, and available options

Documentation Updates:
- README.md: Reorganized demo management section with detailed examples for all three commands
- CONTRIBUTING.md: Updated with Ruby script recommendations and help flag usage
- CONTRIBUTING_SETUP.md: Added links to main README for detailed examples
- .gitignore: Added pr_description.md and commit_message.txt

Users can now run --help on any command to see:
- What the command does
- Usage syntax
- Example invocations
- All available options

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Renamed the version configuration file for better clarity:
- .demo-versions → .new-demo-versions

This makes it clearer that this file configures defaults for NEW demos,
and doesn't affect existing demos (as documented in the file itself).

Updated all references:
- lib/demo_scripts/config.rb: Updated default config file path
- spec/demo_scripts/config_spec.rb: Updated test file names
- scripts/new-demo.sh: Updated VERSION_FILE path
- scripts/scaffold-demo.sh: Updated VERSION_FILE path
- bin/new-demo: Updated help text references
- README.md: Updated all documentation references
- docs/VERSION_MANAGEMENT.md: Updated all documentation references

Also fixed encoding issue:
- Added UTF-8 encoding specification to File.readlines in config.rb
  to handle Unicode characters in the config file

All tests pass ✓
RuboCop passes ✓

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Changed the gem name to be more specific and avoid conflicts:
- demo_common → shakacode-demo-common

The name 'demo_common' is too generic and could conflict with other gems.
'shakacode-demo-common' clearly indicates it's from ShakaCode and is for demos.

Updated all references:
- packages/demo_common/demo_common.gemspec: Changed spec.name
- lib/demo_scripts/demo_creator.rb: Updated gem installation code
- scripts/new-demo.sh: Updated gem reference
- scripts/scaffold-demo.sh: Updated gem reference
- scripts/apply-shared.sh: Updated gem reference
- packages/demo_common/README.md: Updated all installation examples

Note: The module name (DemoCommon) and directory structure remain unchanged,
only the published gem name is different.

Installation in demos:
  gem 'shakacode-demo-common', path: '../../packages/demo_common'

RuboCop passes ✓

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Rename the entire package to use the full shakacode_demo_common name
consistently across the codebase for better clarity and to avoid generic
naming conflicts.

Changes:
- Rename packages/demo_common/ to packages/shakacode_demo_common/
- Update gem name from 'demo_common' to 'shakacode_demo_common'
- Update module name from DemoCommon to ShakacodeDemoCommon
- Rename all lib/ files and directories to use shakacode_demo_common
- Update all require statements and references in Ruby code
- Update all bash scripts to reference the new package name
- Update documentation (README.md, CONTRIBUTING.md, etc.)
- Update rake tasks namespace from :demo_common to :shakacode_demo_common
- Update .rubocop.yml to reference new directory and remove unused plugins

This ensures the gem name matches the directory name convention where
both use underscores (shakacode_demo_common), following Ruby best practices.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
@justin808 justin808 requested a review from Copilot October 1, 2025 02:35
Remove backslash escape from $CURRENT_DATE to allow proper variable
expansion in generated README files. Without this fix, the literal text
"$CURRENT_DATE" would appear instead of the actual date.

This brings scaffold-demo.sh in line with new-demo.sh which already
uses the correct unescaped syntax.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <[email protected]>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Copilot reviewed 58 out of 72 changed files in this pull request and generated 3 comments.


Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

if line =~ /^SHAKAPACKER_VERSION\s*=\s*["'](.+)["']/
@shakapacker_version = ::Regexp.last_match(1)
elsif line =~ /^REACT_ON_RAILS_VERSION\s*=\s*["'](.+)["']/
@react_on_rails_version = ::Regexp.last_match(1)
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

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

[nitpick] The explicit ::Regexp.last_match(1) is unnecessarily verbose. Use $1 or Regexp.last_match(1) for better readability.

Suggested change
@react_on_rails_version = ::Regexp.last_match(1)
@react_on_rails_version = $1

Copilot uses AI. Check for mistakes.

# Run tests first
Rake::Task["demo_common:all"].invoke
Rake::Task['demo_common:all'].invoke
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

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

This rake task reference is incorrect. It should be 'shakacode_demo_common:all' to match the namespace defined on line 3.

Suggested change
Rake::Task['demo_common:all'].invoke
Rake::Task['shakacode_demo_common:all'].invoke

Copilot uses AI. Check for mistakes.

puts "\nReinstalling..."
Rake::Task["demo_common:setup"].invoke
Rake::Task['demo_common:setup'].invoke
Copy link

Copilot AI Oct 1, 2025

Choose a reason for hiding this comment

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

This rake task reference is incorrect. It should be 'shakacode_demo_common:setup' to match the namespace defined on line 3.

Suggested change
Rake::Task['demo_common:setup'].invoke
Rake::Task['shakacode_demo_common:setup'].invoke

Copilot uses AI. Check for mistakes.
@claude
Copy link

claude bot commented Oct 1, 2025

Pull Request Review: Monorepo Structure Setup

Summary

This is an excellent foundation for a monorepo structure supporting multiple React on Rails demo applications. The PR demonstrates strong engineering practices with comprehensive tooling, testing, documentation, and automation. The conversion from bash to Ruby scripts with full test coverage is particularly impressive.

✅ Strengths

Architecture & Design

  • Well-structured monorepo: Clean separation between shared packages and demos
  • Modular Ruby implementation: Object-oriented design with clear separation of concerns (Config, CommandRunner, PreFlightChecks, DemoCreator, DemoScaffolder, DemoUpdater)
  • Comprehensive version management: Excellent system for managing gem versions across demos with precedence (demo Gemfile > CLI flags > config file > defaults)
  • Developer experience: Dry-run mode, help messages, pre-flight checks, and clear error messages

Code Quality

  • Full test coverage: RSpec tests for all Ruby modules with edge cases covered
  • Linting configured: RuboCop (Ruby), ESLint (JS), and Prettier with appropriate rules
  • Git hooks: Lefthook configured for pre-commit quality checks
  • Passing CI: RuboCop shows 0 offenses, proper metrics adjustments for complex scaffolding code

Documentation

  • Exceptional documentation: VERSION_MANAGEMENT.md, CONTRIBUTING.md, CONTRIBUTING_SETUP.md, and comprehensive README
  • Clear examples: Multiple workflow examples for common tasks
  • Migration guide: Breaking changes well-documented for the module rename
  • Help text: All commands have detailed --help output

Security

  • Input validation: Path traversal protection mentioned in PR description ✅
  • Git state checks: Pre-flight checks prevent creating demos with uncommitted changes
  • No credentials in code: Proper .gitignore for sensitive files

🔍 Areas for Improvement

1. Security - Path Traversal Validation (Priority: HIGH)

Issue: While the PR description mentions path traversal protection, I do not see explicit validation in the code.

Location: lib/demo_scripts/demo_creator.rb:16, lib/demo_scripts/pre_flight_checks.rb:24

Concern: A malicious demo name like ../../sensitive-dir could potentially create directories outside the intended location.

Recommendation: Add explicit validation for demo names to prevent path traversal attacks.

2. Code Quality - Logic Bug in PreFlightChecks (Priority: MEDIUM)

Issue: Incorrect logic in target directory check at lib/demo_scripts/pre_flight_checks.rb:24-28

The method returns early if directory exists, but then tries to print success message which is never executed. The logic should check if directory exists first, raise error, then print success message.

3. Testing - Missing Edge Cases (Priority: MEDIUM)

Gaps identified:

  • No tests for path traversal attack scenarios
  • No integration tests for the full demo creation workflow
  • No tests for the symlink creation functionality
  • Missing test for config file with malformed syntax

🎯 Test Coverage Assessment

Overall: Good (estimated 75-80% coverage of critical paths)

Well-covered: Config loading, command runner dry-run, pre-flight checks for git state
Needs coverage: Path traversal scenarios, symlink creation, README generation, error handling for failed shell commands

🔒 Security Assessment

Overall: Good with one critical gap

Critical Gap: Missing path traversal validation for demo names

📋 Recommendations Summary

Must Fix (before merge)

  1. ⚠️ Add path traversal validation for demo names
  2. 🐛 Fix inverted logic in check_target_directory!

Should Fix (can be follow-up PR)

  1. Add tests for symlink creation and edge cases
  2. Add integration tests for full workflow
  3. Document troubleshooting section in README

Nice to Have

  1. Add comment about why Gemfile.lock is committed
  2. Add more inline comments in CI workflow
  3. Consider atomic directory creation to prevent race conditions

✨ Final Thoughts

This is production-ready code with minor issues to address. The thoughtful architecture, comprehensive testing, excellent documentation, and developer-friendly tooling make this an exemplary monorepo setup.

Recommendation: ✅ Approve with minor changes

Fix the two must-fix items (path validation and logic bug), and this will be an excellent foundation for the React on Rails demos repository.

Great work! 🎉


Review conducted using Claude Code

Copy link

@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: 13

Caution

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

⚠️ Outside diff range comments (2)
spec/examples.txt (1)

1-28: Add spec/examples.txt to .gitignore and remove it from version control
This file is auto-generated by RSpec’s config.example_status_persistence_file_path = 'spec/examples.txt' (in spec/spec_helper.rb) and will constantly change, leading to merge conflicts if tracked.

commit_message.txt (1)

1-57: Don’t track commit_message.txt; remove from repo.

This file is already ignored in .gitignore and its content conflicts with the finalized naming used elsewhere. Delete it and keep canonical details in the PR body/CHANGELOG.

-commit_message.txt
+# Remove this tracked file from the repo:
+#   git rm --cached commit_message.txt
♻️ Duplicate comments (12)
lefthook.yml (2)

5-5: Disable parallel execution to prevent git index races.

With parallel: true, both trailing-newline and rubocop can execute git add concurrently (line 17 and stage_fixed: true on line 26), causing intermittent .git/index.lock errors.

Apply this diff:

 pre-commit:
-  parallel: true
+  parallel: false

10-21: Fix file iteration to handle paths with spaces.

The for file in {staged_files} construct splits on whitespace, so files like docs/Release Notes.md are silently skipped. Use a NUL-delimited loop instead.

Apply this diff:

-      run: |
-        for file in {staged_files}; do
-          if [ -f "$file" ]; then
-            # Check if file ends with newline
-            if [ -n "$(tail -c 1 "$file")" ]; then
-              echo "❌ Missing trailing newline: $file"
-              # Add trailing newline
-              echo "" >> "$file"
-              git add "$file"
-              echo "✅ Added trailing newline to: $file"
-            fi
-          fi
-        done
+      run: |
+        git diff --cached --name-only -z -- '*.{rb,js,ts,jsx,tsx,yml,yaml,json,md,sh}' |
+          while IFS= read -r -d '' file; do
+            if [ -f "$file" ] && [ -n "$(tail -c 1 "$file")" ]; then
+              echo "❌ Missing trailing newline: $file"
+              echo "" >> "$file"
+              git add "$file"
+              echo "✅ Added trailing newline to: $file"
+            fi
+          done
packages/shakacode_demo_common/lib/tasks/shakacode_demo_common.rake (2)

45-45: Fix task reference to match renamed namespace.

Line 45 invokes demo_common:all but the namespace was renamed to shakacode_demo_common on line 3. This will raise a runtime error.

Apply this diff:

-    Rake::Task['demo_common:all'].invoke
+    Rake::Task['shakacode_demo_common:all'].invoke

63-63: Fix task reference to match renamed namespace.

Line 63 invokes demo_common:setup but the namespace was renamed to shakacode_demo_common on line 3. This will raise a runtime error.

Apply this diff:

-    Rake::Task['demo_common:setup'].invoke
+    Rake::Task['shakacode_demo_common:setup'].invoke
scripts/test-all.sh (1)

4-16: Good: robust shell options and subshell fix.

Using set -euo pipefail with pushd/popd addresses prior subshell mutation bug; flow and summary handling look solid.

Also applies to: 35-41, 69-82

bin/new-demo (1)

62-74: CRITICAL: Add demo name validation to prevent path traversal.

The script accepts any string from ARGV[0] as the demo name without validation. This creates a path traversal vulnerability where a malicious or accidental input like ../../../etc/passwd could be passed to DemoCreator, potentially creating files outside the intended demos/ directory.

As noted in the previous review, add validation before line 67:

   if ARGV.empty?
     puts parser
     exit 1
   end
 
+  demo_name = ARGV[0]
+
+  # Validate demo name format
+  unless demo_name.match?(/\A[a-zA-Z0-9_-]+\z/) && demo_name.length <= 100
+    warn "Error: Invalid demo name '#{demo_name}'"
+    warn "Demo name must contain only letters, numbers, hyphens, and underscores (max 100 chars)"
+    exit 1
+  end
+
-  demo_name = ARGV[0]
-
   creator = DemoScripts::DemoCreator.new(

This check should happen before any DemoCreator instantiation to prevent path traversal attacks.

.github/workflows/ci.yml (2)

65-70: Don’t mask ESLint/Prettier failures; use --if-present instead of continue-on-error.
Currently failures are ignored and scripts may not exist in the package. Make failures actionable while remaining optional.

Apply this diff:

-      - name: Run ESLint
-        run: npm run lint
-        continue-on-error: true # Allow to continue if lint script doesn't exist
+      - name: Run ESLint (if present)
+        run: npm run lint --if-present
@@
-      - name: Check Prettier formatting
-        run: npm run format:check
-        continue-on-error: true # Allow to continue if format script doesn't exist
+      - name: Check Prettier formatting (if present)
+        run: npm run format:check --if-present

76-80: Remove empty matrix; it triggers actionlint and adds no value.
You already loop over demos; the matrix is unused and empty.

Apply this diff:

-    strategy:
-      matrix:
-        demo: [] # Will be populated as demos are added
-      fail-fast: false
scripts/new-demo.sh (1)

83-89: Define color variables before use.
${RED} and ${NC} are used but never defined.

Apply this diff near the top (after set -euo pipefail):

 set -euo pipefail
+
+# Colors for output
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+NC='\033[0m' # No Color
packages/shakacode_demo_common/package.json (1)

1-45: Add scripts used by CI (lint, format:check).
Workflow runs npm run lint/format:check; scripts are missing.

 {
   "name": "@shakacode/react-on-rails-demo-common",
   "version": "0.1.0",
   "description": "Common JavaScript/TypeScript configurations for React on Rails demos",
   "main": "index.js",
+  "scripts": {
+    "lint": "eslint --max-warnings=0 .",
+    "format": "prettier --write .",
+    "format:check": "prettier --check ."
+  },
scripts/scaffold-demo.sh (1)

69-74: Validation still allows leading hyphens.

The regex ^[a-zA-Z0-9_-]+$ permits names starting with -, causing cd "$DEMO_DIR" to fail (the hyphen is interpreted as an option). The past review flagged this, but the fix wasn't applied.

Apply this diff:

-if [[ -z "$DEMO_NAME" ]] || [[ ! "$DEMO_NAME" =~ ^[a-zA-Z0-9_-]+$ ]]; then
+if [[ -z "$DEMO_NAME" ]] || [[ "$DEMO_NAME" =~ ^- ]] || [[ ! "$DEMO_NAME" =~ ^[a-zA-Z0-9][a-zA-Z0-9_-]*$ ]]; then
   echo -e "${RED}Error: Invalid demo name '$DEMO_NAME'${NC}"
   echo "Demo name must contain only letters, numbers, hyphens, and underscores"
   exit 1
 fi
packages/shakacode_demo_common/lib/generators/shakacode_demo_common/install/install_generator.rb (1)

55-73: Add error handling for JSON parsing and consider merge behavior.

Two concerns remain from previous reviews:

  1. JSON.parse (line 61) will raise an exception if package.json is malformed, halting the generator.
  2. merge! (line 64) overwrites existing scripts with the same keys. Consider using reverse_merge! or checking for existing keys to preserve user customizations.

Apply this diff:

 def add_npm_scripts
   say 'Adding npm scripts'
   package_json_path = Rails.root.join('package.json')

   return unless File.exist?(package_json_path)

+  begin
     package_json = JSON.parse(File.read(package_json_path))
+  rescue JSON::ParserError => e
+    say "Failed to parse package.json: #{e.message}", :red
+    return
+  end

   package_json['scripts'] ||= {}
-  package_json['scripts'].merge!({
+  # Preserve existing scripts by only adding keys that don't exist
+  new_scripts = {
     'lint' => 'eslint . --ext .js,.jsx,.ts,.tsx',
     'lint:fix' => 'eslint . --ext .js,.jsx,.ts,.tsx --fix',
     'format' => "prettier --write '**/*.{js,jsx,ts,tsx,json,css,scss,md}'",
     'format:check' => "prettier --check '**/*.{js,jsx,ts,tsx,json,css,scss,md}'",
     'prepare' => 'lefthook install'
-  })
+  }
+  package_json['scripts'].reverse_merge!(new_scripts)

   File.write(package_json_path, JSON.pretty_generate(package_json))
 end
🧹 Nitpick comments (21)
packages/shakacode_demo_common/shakacode_demo_common.gemspec (2)

6-13: Add helpful gem metadata URIs.

Improve discoverability on RubyGems by adding source, docs, and changelog links.

   spec.homepage    = 'https://github.com/shakacode/react_on_rails-demos'
+  spec.metadata['source_code_uri'] = 'https://github.com/shakacode/react_on_rails-demos'
+  spec.metadata['changelog_uri']   = 'https://github.com/shakacode/react_on_rails-demos/blob/main/CHANGELOG.md'
+  spec.metadata['documentation_uri']= 'https://github.com/shakacode/react_on_rails-demos/tree/main/packages/shakacode_demo_common'

25-27: Ensure a license file is present
Add a LICENSE (or rename your MIT-LICENSE) in packages/shakacode_demo_common and update the gemspec paths if necessary.

scripts/test-all.sh (3)

48-60: Tighten Rails detection and guard RuboCop.

Use executable check for Rails and only run RuboCop when a Gemfile exists to avoid false failures in non-Rails demos.

-      if [ -f "bin/rails" ]; then
+      if [ -x "bin/rails" ]; then
         if [ -d "spec" ]; then
           echo "  Running RSpec tests..."
           bundle exec rspec || FAILED_DEMOS="$FAILED_DEMOS $demo_name"
         elif [ -d "test" ]; then
           echo "  Running Rails tests..."
           bin/rails test || FAILED_DEMOS="$FAILED_DEMOS $demo_name"
         fi

-        # Run RuboCop
-        echo "  Running RuboCop..."
-        bundle exec rubocop || FAILED_DEMOS="$FAILED_DEMOS $demo_name-rubocop"
+        # Run RuboCop
+        if [ -f "Gemfile" ]; then
+          echo "  Running RuboCop..."
+          bundle exec rubocop || FAILED_DEMOS="$FAILED_DEMOS $demo_name-rubocop"
+        fi
       fi

39-45: Handle demo paths with spaces (optional).

Current glob can split on spaces. Consider a nullglob + loop or find -print0 for robustness.

-if [ -d "demos" ] && [ "$(ls -A demos 2>/dev/null)" ]; then
-  for demo in demos/*; do
+shopt -s nullglob
+if [ -d "demos" ] && compgen -G "demos/*" >/dev/null; then
+  for demo in demos/*; do
     if [ -d "$demo" ]; then

29-33: Detect pnpm when present (keeps PR’s “npm/pnpm support” promise).

Prefer pnpm if pnpm-lock.yaml exists; fall back to npm.

-    echo "  Running JavaScript tests..."
-    if ! npm test; then
+    PM="npm"; [ -f "pnpm-lock.yaml" ] && PM="pnpm"
+    echo "  Running JavaScript tests with $PM..."
+    if ! $PM test; then
       FAILED_DEMOS="$FAILED_DEMOS shakacode_demo_common-js"
     fi
-    if ! npm run lint; then
+    if ! $PM run lint; then
       FAILED_DEMOS="$FAILED_DEMOS shakacode_demo_common-eslint"
     fi
-        echo "  Running JavaScript tests..."
-        if ! npm test; then
+        PM="npm"; [ -f "pnpm-lock.yaml" ] && PM="pnpm"
+        echo "  Running JavaScript tests with $PM..."
+        if ! $PM test; then
           FAILED_DEMOS="$FAILED_DEMOS ${demo_name}-js"
         fi
-        if ! npm run lint; then
+        if ! $PM run lint; then
           FAILED_DEMOS="$FAILED_DEMOS ${demo_name}-eslint"
         fi

Also applies to: 64-67

package.json (1)

6-12: Prettier setup looks fine; consider minor hardening.

Optional:

  • Add a .prettierignore for build/coverage/tmp to avoid accidental walks.
  • Consider bumping Prettier to latest 3.x and adding an engines.node constraint for consistency across contributors.
pr_description.md (1)

209-214: Remove AI tool attribution lines from PR description.

Keep PR text vendor-neutral and focused on changes.

-🤖 Generated with [Claude Code](https://claude.com/claude-code)
-
-Co-Authored-By: Claude <[email protected]>
.new-demo-versions (1)

10-13: Consider documenting the version constraint format.

The pessimistic version constraints (~> 8.0, ~> 16.0) are standard Ruby gem syntax, but the file format mixing shell-style comments with gem version syntax may be confusing. Consider adding a note explaining that these follow Ruby gem version constraint conventions if they're consumed by Ruby code, or converting to a format more appropriate for the actual consumer (e.g., YAML, JSON, or pure shell variables).

bin/update-all-demos (2)

60-64: Consider validating that at least one version is specified.

The script allows execution without specifying --react-on-rails-version or --shakapacker-version, which would result in no updates being performed. Consider adding a check to ensure at least one version option is provided before instantiating the updater, or document this as expected behavior if DemoUpdater handles it gracefully.

 begin
   parser.parse!
+
+  if options[:react_on_rails_version].nil? && options[:shakapacker_version].nil?
+    warn "Error: At least one of --react-on-rails-version or --shakapacker-version must be specified"
+    puts parser
+    exit 1
+  end

   updater = DemoScripts::DemoUpdater.new(**options)
   updater.update!

44-46: Document glob pattern constraints for the --demos option.

The --demos option accepts an arbitrary glob pattern without validation. While glob patterns are powerful, consider adding a usage note in the help text about expected patterns (e.g., '*typescript*', 'demo-v16-*') to guide users and prevent confusion with unexpected pattern matching behavior.

-  opts.on('--demos PATTERN', 'Only update demos matching pattern (glob)') do |pattern|
+  opts.on('--demos PATTERN', 'Only update demos matching pattern (glob, e.g., "*typescript*")') do |pattern|
     options[:demo_pattern] = pattern
   end
bin/scaffold-demo (1)

70-76: Consider alternative parsing for arguments containing commas.

The --rails-args and --react-on-rails-args options split on commas (lines 71, 75), which will break if any argument value contains a comma. Consider using a different delimiter (e.g., space or semicolon) or accepting multiple instances of the flag instead.

-  opts.on('--rails-args ARGS', 'Additional arguments to pass to rails new (comma-separated)') do |args|
-    options[:rails_args] = args.split(',').map(&:strip)
+  opts.on('--rails-args ARGS', 'Additional arguments to pass to rails new (space-separated, quoted if needed)') do |args|
+    options[:rails_args] = args.split(/\s+/)
   end

-  opts.on('--react-on-rails-args ARGS', 'Additional arguments to pass to react_on_rails:install (comma-separated)') do |args|
-    options[:react_on_rails_args] = args.split(',').map(&:strip)
+  opts.on('--react-on-rails-args ARGS', 'Additional arguments to pass to react_on_rails:install (space-separated, quoted if needed)') do |args|
+    options[:react_on_rails_args] = args.split(/\s+/)
   end

Alternatively, allow multiple flag instances:

+  options[:rails_args] = []
+  options[:react_on_rails_args] = []
+
-  opts.on('--rails-args ARGS', 'Additional arguments to pass to rails new (comma-separated)') do |args|
-    options[:rails_args] = args.split(',').map(&:strip)
+  opts.on('--rails-args ARG', 'Additional argument to pass to rails new (can be specified multiple times)') do |arg|
+    options[:rails_args] << arg
   end

-  opts.on('--react-on-rails-args ARGS', 'Additional arguments to pass to react_on_rails:install (comma-separated)') do |args|
-    options[:react_on_rails_args] = args.split(',').map(&:strip)
+  opts.on('--react-on-rails-args ARG', 'Additional argument to pass to react_on_rails:install (can be specified multiple times)') do |arg|
+    options[:react_on_rails_args] << arg
   end
scripts/apply-shared.sh (1)

36-44: Improve Gemfile modification robustness.

Two concerns:

  1. Line 38: grep -q "shakacode_demo_common" matches any occurrence, including comments or strings. This could skip adding the gem if it appears in a comment.
  2. Lines 40-42: Appending without ensuring the file ends with a newline could cause formatting issues.

Apply this diff to make the check more precise and ensure proper formatting:

-      if ! grep -q "shakacode_demo_common" "$demo/Gemfile"; then
+      if ! grep -q "^[[:space:]]*gem[[:space:]]*[\"']shakacode_demo_common[\"']" "$demo/Gemfile"; then
         echo "  Adding shakacode_demo_common to Gemfile..."
-        echo '' >> "$demo/Gemfile"
+        # Ensure file ends with newline before appending
+        [ -n "$(tail -c1 "$demo/Gemfile")" ] && echo '' >> "$demo/Gemfile"
+        echo '' >> "$demo/Gemfile"
         echo '# Shared demo configuration and utilities' >> "$demo/Gemfile"
scripts/new-demo.sh (3)

96-105: Avoid eval in run_cmd; preserve quoting safely.
eval is error‑prone for spaces/special chars. Use bash -lc and show colored prefixes.

-run_cmd() {
-  if [ "$DRY_RUN" = true ]; then
-    echo "[DRY-RUN] $*"
-  else
-    echo "▶ $*"
-    eval "$@"
-  fi
-}
+run_cmd() {
+  if [ "$DRY_RUN" = true ]; then
+    echo -e "${YELLOW}[DRY-RUN]${NC} $*"
+  else
+    echo -e "${GREEN}▶${NC} $*"
+    bash -lc "$*"
+  fi
+}

Based on learnings (consistency with scaffold-demo.sh run_cmd).


116-135: Check “in git repo” before running git diff-index.
Current order can emit misleading errors if not in a repo (stderr hidden). Swap checks.

-# Check 1: No uncommitted changes (staged or unstaged)
-if ! git diff-index --quiet HEAD -- 2>/dev/null; then
-  echo "❌ Error: Repository has uncommitted changes"
-  ...
-  exit 1
-fi
-echo "✓ No uncommitted changes"
-
-# Check if we're in a git repository
-if ! git rev-parse --git-dir > /dev/null 2>&1; then
-  echo "❌ Error: Not in a git repository"
-  exit 1
-fi
-echo "✓ In git repository"
+# Check if we're in a git repository
+if ! git rev-parse --git-dir > /dev/null 2>&1; then
+  echo "❌ Error: Not in a git repository"
+  exit 1
+fi
+echo "✓ In git repository"
+
+# Check 1: No uncommitted changes (staged or unstaged)
+if ! git diff-index --quiet HEAD --; then
+  echo "❌ Error: Repository has uncommitted changes"
+  ...
+  exit 1
+fi
+echo "✓ No uncommitted changes"

203-209: Prefer reproducible JS installs.
Use pnpm if present; otherwise npm ci when lockfile exists, else npm install.

-if command -v pnpm &> /dev/null; then
-  run_cmd "cd '$DEMO_DIR' && pnpm install"
-else
-  run_cmd "cd '$DEMO_DIR' && npm install"
-fi
+if command -v pnpm >/dev/null 2>&1; then
+  run_cmd "cd '$DEMO_DIR' && pnpm install"
+else
+  if [ -f "$DEMO_DIR/package-lock.json" ]; then
+    run_cmd "cd '$DEMO_DIR' && npm ci"
+  else
+    run_cmd "cd '$DEMO_DIR' && npm install"
+  fi
+fi
packages/shakacode_demo_common/package.json (3)

21-25: Ensure first-time publish works for scoped package.
Add publishConfig access: public; otherwise npm publish may fail.

   "files": [
     "configs/",
     "playwright/",
     "index.js"
   ],
+  "publishConfig": {
+    "access": "public"
+  },

37-44: Mark playwright peer as optional (avoid install warnings).
Many consumers won’t use Playwright directly.

   "peerDependencies": {
     "playwright": "^1.35.0"
   },
+  "peerDependenciesMeta": {
+    "playwright": {
+      "optional": true
+    }
+  },

6-9: Repository metadata should point to monorepo and package directory.
Set repository.url to the monorepo and add directory for clarity on npm.

   "repository": {
     "type": "git",
-    "url": "https://github.com/shakacode/react-on-rails-demo-common.git"
+    "url": "https://github.com/shakacode/react_on_rails-demos.git",
+    "directory": "packages/shakacode_demo_common"
   },
packages/shakacode_demo_common/Rakefile (1)

95-101: Make npm publish explicit for scoped public package.
Align with package.json publishConfig and avoid interactive failure.

-      sh 'npm publish'
+      sh 'npm publish --access public'
lib/demo_scripts/demo_creator.rb (1)

57-73: Consider path safety in shell command construction.

Line 72 uses single quotes around @demo_dir in the shell command. While demo names are validated to contain only safe characters, consider using Shellwords.escape for extra safety if validation ever changes.

Optional refactor:

require 'shellwords'

def create_rails_app
  puts '📦 Creating Rails application...'
  base_args = [
    '--database=postgresql',
    '--skip-javascript',
    '--skip-hotwire',
    '--skip-action-mailbox',
    '--skip-action-text',
    '--skip-active-storage',
    '--skip-action-cable',
    '--skip-sprockets',
    '--skip-system-test',
    '--skip-turbolinks'
  ]
  all_args = (base_args + @rails_args).join(' ')
  @runner.run!("rails new #{Shellwords.escape(@demo_dir)} #{all_args}")
end
packages/shakacode_demo_common/lib/generators/shakacode_demo_common/install/install_generator.rb (1)

17-22: Consider adding error handling for npm install commands.

Each public method in a generator is executed sequentially, but the run method for npm install commands (lines 14, 20-21) doesn't check exit status. If npm install fails (network issues, permission problems, etc.), the generator continues silently, potentially leaving the project in a broken state.

Consider checking command success:

def copy_lefthook_config
  say 'Installing Lefthook configuration'
  copy_file lefthook_config_path, 'lefthook.yml'
  
  unless run 'npm install --save-dev lefthook @commitlint/cli @commitlint/config-conventional'
    say 'Warning: npm install failed for Lefthook dependencies', :yellow
  end
  
  unless run 'npx lefthook install'
    say 'Warning: Lefthook installation failed', :yellow
  end
end
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1c501df and a997e94.

⛔ Files ignored due to path filters (2)
  • package-lock.json is excluded by !**/package-lock.json
  • packages/shakacode_demo_common/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (46)
  • .github/workflows/ci.yml (1 hunks)
  • .gitignore (1 hunks)
  • .new-demo-versions (1 hunks)
  • .prettierignore (1 hunks)
  • .prettierrc (1 hunks)
  • .rubocop.yml (1 hunks)
  • CONTRIBUTING.md (1 hunks)
  • README.md (1 hunks)
  • bin/new-demo (1 hunks)
  • bin/scaffold-demo (1 hunks)
  • bin/update-all-demos (1 hunks)
  • commit_message.txt (1 hunks)
  • docs/CONTRIBUTING_SETUP.md (1 hunks)
  • docs/VERSION_MANAGEMENT.md (1 hunks)
  • lefthook.yml (1 hunks)
  • lib/demo_scripts/config.rb (1 hunks)
  • lib/demo_scripts/demo_creator.rb (1 hunks)
  • package.json (1 hunks)
  • packages/shakacode_demo_common/.gitignore (1 hunks)
  • packages/shakacode_demo_common/Gemfile (1 hunks)
  • packages/shakacode_demo_common/README.md (1 hunks)
  • packages/shakacode_demo_common/RELEASING.md (2 hunks)
  • packages/shakacode_demo_common/Rakefile (1 hunks)
  • packages/shakacode_demo_common/config/lefthook.yml (2 hunks)
  • packages/shakacode_demo_common/config/rubocop.yml (3 hunks)
  • packages/shakacode_demo_common/configs/eslint.config.js (1 hunks)
  • packages/shakacode_demo_common/configs/prettier.config.js (1 hunks)
  • packages/shakacode_demo_common/index.js (1 hunks)
  • packages/shakacode_demo_common/lefthook.yml (1 hunks)
  • packages/shakacode_demo_common/lib/generators/shakacode_demo_common/install/install_generator.rb (1 hunks)
  • packages/shakacode_demo_common/lib/generators/shakacode_demo_common/install/templates/github_workflow.yml (2 hunks)
  • packages/shakacode_demo_common/lib/shakacode_demo_common.rb (1 hunks)
  • packages/shakacode_demo_common/lib/shakacode_demo_common/railtie.rb (1 hunks)
  • packages/shakacode_demo_common/lib/shakacode_demo_common/version.rb (1 hunks)
  • packages/shakacode_demo_common/lib/tasks/shakacode_demo_common.rake (2 hunks)
  • packages/shakacode_demo_common/package.json (1 hunks)
  • packages/shakacode_demo_common/playwright/helpers.js (1 hunks)
  • packages/shakacode_demo_common/shakacode_demo_common.gemspec (1 hunks)
  • pr_description.md (1 hunks)
  • scripts/apply-shared.sh (1 hunks)
  • scripts/bootstrap-all.sh (1 hunks)
  • scripts/new-demo.sh (1 hunks)
  • scripts/scaffold-demo.sh (1 hunks)
  • scripts/test-all.sh (1 hunks)
  • spec/demo_scripts/config_spec.rb (1 hunks)
  • spec/examples.txt (1 hunks)
✅ Files skipped from review due to trivial changes (13)
  • packages/shakacode_demo_common/configs/prettier.config.js
  • packages/shakacode_demo_common/.gitignore
  • docs/CONTRIBUTING_SETUP.md
  • packages/shakacode_demo_common/RELEASING.md
  • .prettierignore
  • packages/shakacode_demo_common/Gemfile
  • packages/shakacode_demo_common/playwright/helpers.js
  • packages/shakacode_demo_common/lefthook.yml
  • packages/shakacode_demo_common/index.js
  • .prettierrc
  • packages/shakacode_demo_common/config/lefthook.yml
  • packages/shakacode_demo_common/configs/eslint.config.js
  • packages/shakacode_demo_common/README.md
🚧 Files skipped from review as they are similar to previous changes (4)
  • lib/demo_scripts/config.rb
  • .rubocop.yml
  • spec/demo_scripts/config_spec.rb
  • scripts/bootstrap-all.sh
🧰 Additional context used
🧬 Code graph analysis (4)
scripts/scaffold-demo.sh (2)
scripts/new-demo.sh (2)
  • show_usage (26-41)
  • run_cmd (97-104)
scripts/update-all-demos.sh (2)
  • show_usage (12-31)
  • run_cmd (80-86)
scripts/new-demo.sh (2)
scripts/scaffold-demo.sh (2)
  • show_usage (26-47)
  • run_cmd (133-140)
scripts/update-all-demos.sh (2)
  • show_usage (12-31)
  • run_cmd (80-86)
lib/demo_scripts/demo_creator.rb (4)
lib/demo_scripts/config.rb (1)
  • initialize (11-17)
lib/demo_scripts/command_runner.rb (2)
  • initialize (8-11)
  • run! (26-31)
lib/demo_scripts/pre_flight_checks.rb (3)
  • initialize (5-54)
  • initialize (6-9)
  • run! (11-19)
lib/demo_scripts/demo_scaffolder.rb (5)
  • initialize (5-354)
  • initialize (6-37)
  • create! (39-67)
  • setup_database (71-77)
  • print_completion_message (332-353)
packages/shakacode_demo_common/lib/generators/shakacode_demo_common/install/install_generator.rb (1)
packages/shakacode_demo_common/lib/shakacode_demo_common.rb (2)
  • root (10-12)
  • config_path (14-16)
🪛 actionlint (1.7.7)
.github/workflows/ci.yml

78-78: "matrix values" section should not be empty

(syntax-check)

🪛 Shellcheck (0.11.0)
scripts/scaffold-demo.sh

[warning] 138-138: eval negates the benefit of arrays. Drop eval to preserve whitespace/symbols (or eval as string).

(SC2294)

scripts/new-demo.sh

[warning] 102-102: eval negates the benefit of arrays. Drop eval to preserve whitespace/symbols (or eval as string).

(SC2294)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: claude-review
🔇 Additional comments (34)
packages/shakacode_demo_common/shakacode_demo_common.gemspec (2)

15-17: Retain current spec.files glob
The existing pattern packages only lib/**/*, Rakefile, and README.md; no extraneous files are included, and switching to git ls-files would pull in dotfiles and other tracked files you likely don’t want in the gem.

Likely an incorrect or invalid review comment.


3-3: Verified version file and constant presence. The version.rb exists and defines ShakacodeDemoCommon::VERSION.

packages/shakacode_demo_common/lib/shakacode_demo_common/version.rb (1)

1-5: LGTM!

The version file follows Ruby gem conventions correctly with frozen string literal, proper module nesting, and semantic versioning for the initial release.

lefthook.yml (1)

28-36: LGTM!

The commit-msg validation correctly checks for non-empty commit messages using a robust regex pattern.

packages/shakacode_demo_common/lib/tasks/shakacode_demo_common.rake (2)

3-3: LGTM!

The namespace rename from demo_common to shakacode_demo_common aligns with the broader gem/module renaming in this PR.


6-19: LGTM!

The quote style changes from double to single quotes are consistent and the task logic remains correct.

CONTRIBUTING.md (5)

1-17: LGTM!

Clear and welcoming introduction with a structured getting-started workflow.


18-36: LGTM!

Prerequisites are well-defined and the setup instructions correctly reference the new bootstrap-all.sh script introduced in this PR.


49-61: LGTM!

The documentation correctly references the new Ruby-based demo creation scripts (bin/new-demo, bin/scaffold-demo) and appropriately recommends them as "fully tested and recommended."


96-112: LGTM!

Clear commit message guidelines with helpful examples following conventional commit format.


130-143: LGTM!

Excellent demo standards checklist that establishes clear quality expectations for contributors.

packages/shakacode_demo_common/lib/generators/shakacode_demo_common/install/templates/github_workflow.yml (1)

5-7: LGTM: YAML formatting normalized.

The array formatting changes (removing spaces inside brackets) standardize the YAML style consistently with the monorepo's conventions.

packages/shakacode_demo_common/config/rubocop.yml (1)

12-18: LGTM: Quote style normalized.

The switch from double to single quotes in Exclude lists is a pure formatting change with no functional impact.

Also applies to: 49-49, 53-56, 75-75

bin/update-all-demos (1)

65-72: LGTM: Error handling is appropriate.

The script properly handles both domain errors (DemoScripts::Error) and parsing errors (OptionParser::InvalidOption) with clear messages and appropriate exit codes.

bin/scaffold-demo (1)

105-112: LGTM: Error handling follows established pattern.

The error handling is consistent with bin/update-all-demos and properly handles both domain errors and parsing errors with clear messages and appropriate exit codes.

.new-demo-versions (1)

1-32: .new-demo-versions is sourced by bash scripts and parsed by Ruby code
Both scripts/new-demo.sh and scripts/scaffold-demo.sh use source "$VERSION_FILE" to load defaults, and DemoScripts::Config in lib/demo_scripts/config.rb reads and parses the same file. No changes required.

scripts/apply-shared.sh (1)

1-11: LGTM!

Good use of strict error handling with set -euo pipefail and clear validation of required directories.

packages/shakacode_demo_common/lib/shakacode_demo_common/railtie.rb (2)

1-4: LGTM!

Standard Railtie setup with proper frozen string literal pragma.


7-13: Paths for rake task and generator are correct.

packages/shakacode_demo_common/lib/shakacode_demo_common.rb (2)

1-7: LGTM!

Good practice to conditionally load Rails-specific code and define a custom error class for the gem.


9-22: LGTM!

The path calculations correctly resolve to the gem root and expected subdirectories. The module singleton methods provide clean access to key gem paths.

docs/VERSION_MANAGEMENT.md (1)

1-528: Documentation is comprehensive and addresses past review feedback.

The version management workflow is well-documented with clear examples, troubleshooting guidance, and best practices. Past review comments appear to have been addressed:

  • Line 5 includes the cypress-playwright-on-rails reference
  • Line 47 recommends Ruby scripts in bin/
  • Lines 99-105 document Gemfile.development_dependencies inheritance
  • Line 84 acknowledges git branch support as a future enhancement
bin/new-demo (3)

1-14: LGTM!

Clean setup with proper requires and options initialization.


16-57: LGTM!

Well-structured option parser with clear help text and good handling of comma-separated arguments.


75-82: LGTM!

Appropriate error handling with clear user feedback and proper exit codes.

packages/shakacode_demo_common/package.json (1)

5-5: Entry point verified
packages/shakacode_demo_common/index.js exists, so the main field in package.json is correct.

scripts/scaffold-demo.sh (3)

133-140: eval usage is acceptable here despite shellcheck warning.

Shellcheck warns that eval negates array benefits (SC2294). In this context, eval "$@" is needed to handle complex commands with pipes, redirects, and compound statements that run_cmd receives. The pattern is safe because all input is controlled by the script itself, not user input.


26-47: LGTM! Clear and helpful usage documentation.

The usage function provides comprehensive help with practical examples and follows the same pattern as other scripts in the repo.


377-477: README generation is correct.

The date variable expansion and heredoc syntax are correct. The past comment about escaping issues appears to be a false positive—EOF (unquoted) allows variable expansion, and $CURRENT_DATE will expand properly at line 393.

lib/demo_scripts/demo_creator.rb (3)

6-26: LGTM! Clean initialization with proper dependency injection.

The initializer properly sets up all dependencies (Config, CommandRunner) and instance variables with sensible defaults.


28-49: LGTM! Clear orchestration of demo creation steps.

The create! method follows a logical sequence of steps and properly handles pre-flight checks and dry-run mode.


150-200: LGTM! Well-structured README template.

The generated README includes all essential information: gem versions, setup instructions, and helpful links. The interpolation of config values and current date is handled correctly.

packages/shakacode_demo_common/lib/generators/shakacode_demo_common/install/install_generator.rb (1)

107-119: LGTM! Helpful post-install guidance.

The completion message provides clear next steps and customization guidance for users.

README.md (1)

1-206: All referenced documentation files exist. Verified that every file cited in the README is present—no updates required.

Comment on lines +14 to +15
working-directory: packages/demo_common
steps:
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix paths to renamed package directory (breaking).

Jobs still use packages/demo_common. The repo renamed to packages/shakacode_demo_common, so steps will run in a non-existent path and fail.

Apply this diff:

-        working-directory: packages/demo_common
+        working-directory: packages/shakacode_demo_common
@@
-          working-directory: packages/demo_common
+          working-directory: packages/shakacode_demo_common
@@
-        working-directory: packages/demo_common
+        working-directory: packages/shakacode_demo_common
@@
-          working-directory: packages/demo_common
+          working-directory: packages/shakacode_demo_common
@@
-          cache-dependency-path: packages/demo_common/package-lock.json
+          cache-dependency-path: packages/shakacode_demo_common/package-lock.json
@@
-        working-directory: packages/demo_common
+        working-directory: packages/shakacode_demo_common
@@
-          cache-dependency-path: packages/demo_common/package-lock.json
+          cache-dependency-path: packages/shakacode_demo_common/package-lock.json

Also applies to: 22-22, 30-31, 38-38, 44-45, 54-55, 62-62

🤖 Prompt for AI Agents
.github/workflows/ci.yml around lines 14-15 (and also at 22, 30-31, 38, 44-45,
54-55, 62): CI steps reference the old package path packages/demo_common which
no longer exists; update every occurrence of packages/demo_common in this
workflow to the new directory name packages/shakacode_demo_common so all
working-directory and path entries point to the renamed package; ensure no
remaining references to the old name remain in the listed line ranges.

Comment on lines +52 to +55
# Ignore master key for decrypting credentials and more
/demos/*/config/master.key
/demos/*/config/credentials.yml.encpr_description.md
commit_message.txt
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix malformed ignore entry; potential secret handling confusion.

/demos/*/config/credentials.yml.encpr_description.md is a concatenation typo. It won’t ignore credentials.yml.enc and accidentally references pr_description.md.

-/demos/*/config/credentials.yml.encpr_description.md
+/demos/*/config/credentials.yml.enc
+# keep PR docs tracked
+# pr_description.md should not be ignored here
📝 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
# Ignore master key for decrypting credentials and more
/demos/*/config/master.key
/demos/*/config/credentials.yml.encpr_description.md
commit_message.txt
# Ignore master key for decrypting credentials and more
/demos/*/config/master.key
/demos/*/config/credentials.yml.enc
# keep PR docs tracked
# pr_description.md should not be ignored here
commit_message.txt
🤖 Prompt for AI Agents
In .gitignore around lines 52 to 55, the line
"/demos/*/config/credentials.yml.encpr_description.md" is a concatenation typo
that prevents credentials.yml.enc from being ignored and accidentally references
pr_description.md; fix it by replacing that single malformed entry with the
correct ignore entry "/demos/*/config/credentials.yml.enc" and remove the
accidental "pr_description.md" fragment (or add "pr_description.md" as a
separate ignore line only if you actually intend to ignore that file).

Comment on lines +92 to +97
if ARGV.empty?
puts parser
exit 1
end

demo_name = ARGV[0]
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify demo name validation in DemoScaffolder.

The script extracts demo_name directly from ARGV[0] without validation. The PR objectives mention "input validation to prevent path traversal in demo creation" as a security fix. Ensure that DemoScripts::DemoScaffolder validates the demo_name to reject path separators, special characters, or patterns that could lead to path traversal vulnerabilities.

Run the following script to verify the validation logic:


🏁 Script executed:

#!/bin/bash
# Check if DemoScaffolder or related modules validate demo_name

echo "=== Checking demo_name validation in DemoScaffolder ==="
ast-grep --pattern $'class DemoScaffolder
  $$$
  def initialize($$$)
    $$$
  end
  $$$
end' | head -50

echo "=== Checking for path traversal validation ==="
rg -n 'demo_name.*\.\.|demo_name.*/' lib/demo_scripts/ -A3 -B3

echo "=== Checking validation methods ==="
rg -n 'def validate.*name|valid.*name|sanitize.*name' lib/demo_scripts/ -A5

Length of output: 612


🏁 Script executed:

#!/bin/bash
echo "=== Locate DemoScaffolder class file ==="
rg -n 'class DemoScaffolder' -C2 -g '*.rb'
demo_file=$(rg -l 'class DemoScaffolder' -g '*.rb')
echo "Found: $demo_file"
echo "=== Inspect initialize method ==="
if [[ -f "$demo_file" ]]; then
  awk '/class DemoScaffolder/,/end/' "$demo_file" | sed -n '/def initialize/,/end/p'
else
  echo "DemoScaffolder file not found"
fi

Length of output: 1296


Enforce demo_name validation in DemoScaffolder
In lib/demo_scripts/demo_scaffolder.rb within initialize, validate demo_name before calling super(…) to reject any ../, path separators (/ or \), or disallowed characters (e.g. only allow /\A[\w-]+\z/). Raise an error or exit with a clear message on invalid input to prevent path traversal.

🤖 Prompt for AI Agents
In bin/scaffold-demo around lines 92 to 97, the CLI accepts ARGV[0] without
enforcing a safe demo_name; update lib/demo_scripts/demo_scaffolder.rb's
initialize to validate demo_name before calling super(...) by rejecting any path
traversal or separators (../, '/' or '\') and allow only a safe pattern such as
/\A[\w-]+\z/; if validation fails, raise a descriptive error or exit with a
clear message and non-zero status so invalid input is rejected early and no
unsafe filesystem paths are used.

Comment on lines +324 to +331
grep -l "prerender: true" demos/*/app/views/**/*.erb | \
xargs dirname | xargs dirname | xargs dirname | \
while read demo; do
(
cd "$demo"
bundle add react_on_rails --version '~> 16.1'
)
done
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix bash glob pattern for recursive file matching.

Line 324 uses demos/*/app/views/**/*.erb which requires bash's globstar option to work recursively. Without it, ** is treated as a literal.

Apply this diff to fix the example:

-grep -l "prerender: true" demos/*/app/views/**/*.erb | \
+# Enable globstar for recursive matching
+shopt -s globstar
+grep -l "prerender: true" demos/*/app/views/**/*.erb | \
   xargs dirname | xargs dirname | xargs dirname | \
   while read demo; do

Or use find which doesn't require globstar:

-grep -l "prerender: true" demos/*/app/views/**/*.erb | \
-  xargs dirname | xargs dirname | xargs dirname | \
-  while read demo; do
+find demos/*/app/views -name "*.erb" -exec grep -l "prerender: true" {} + | \
+  while read file; do
+    demo=$(echo "$file" | cut -d/ -f1-2)
🤖 Prompt for AI Agents
In docs/VERSION_MANAGEMENT.md around lines 324 to 331, the shell example uses
the glob pattern demos/*/app/views/**/*.erb which requires bash's globstar to be
enabled or will not match recursively; fix by either enabling globstar before
the loop (add shopt -s globstar) so ** works as intended, or replace the glob
usage with a find-based command that searches recursively (e.g., use find demos
-path '*/app/views/*.erb' or similar) and pipe results through the same
dirname/xargs workflow; update the example to use one of these two approaches
and ensure portability by documenting the chosen option.

Comment on lines +23 to +26
rubocop:
glob: '*.rb'
run: bundle exec rubocop --force-exclusion {staged_files}
stage_fixed: true
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

RuboCop command also vulnerable to filename whitespace issues.

Line 25 uses {staged_files} without quoting, which will break if any Ruby file path contains spaces. Although less common in Ruby projects, this should be quoted for robustness.

Apply this diff:

-      run: bundle exec rubocop --force-exclusion {staged_files}
+      run: bundle exec rubocop --force-exclusion -- {staged_files}

Note: Lefthook expands {staged_files} as space-separated arguments. The -- delimiter helps, but the underlying issue remains. Consider using the files command instead:

     rubocop:
       glob: '*.rb'
-      run: bundle exec rubocop --force-exclusion {staged_files}
+      run: bundle exec rubocop --force-exclusion {files}
       stage_fixed: true
📝 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
rubocop:
glob: '*.rb'
run: bundle exec rubocop --force-exclusion {staged_files}
stage_fixed: true
rubocop:
glob: '*.rb'
run: bundle exec rubocop --force-exclusion -- {staged_files}
stage_fixed: true
🤖 Prompt for AI Agents
In lefthook.yml around lines 23 to 26, the rubocop run command uses
{staged_files} unquoted which breaks on paths with spaces; update the hook so
staged filenames are passed safely — either add the POSIX argument separator and
quote the expansion (e.g., include -- before and quote {staged_files}) or,
preferably, switch to Lefthook's files mechanism so each filename is passed
separately to RuboCop (replace the run line with a files-based invocation that
calls rubocop per file or uses Lefthook's files expansion).

Comment on lines +21 to +34
if [ -f "packages/shakacode_demo_common/config/.rubocop.yml" ]; then
echo " Linking .rubocop.yml..."
ln -sf ../../packages/shakacode_demo_common/config/.rubocop.yml "$demo/.rubocop.yml"
fi

if [ -f "packages/shakacode_demo_common/config/.eslintrc.js" ]; then
echo " Linking .eslintrc.js..."
ln -sf ../../packages/shakacode_demo_common/config/.eslintrc.js "$demo/.eslintrc.js"
fi

if [ -f "packages/shakacode_demo_common/config/.prettierrc" ]; then
echo " Linking .prettierrc..."
ln -sf ../../packages/shakacode_demo_common/config/.prettierrc "$demo/.prettierrc"
fi
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Verify script execution context and symlink target paths.

The symlink paths (../../packages/shakacode_demo_common/config/...) assume the script is executed from the repository root. If run from a different directory, the symlinks will point to incorrect locations.

Consider either:

  1. Adding a check at the top to ensure the script runs from the repo root
  2. Computing the symlink paths dynamically based on the script's location

🏁 Script executed:

#!/bin/bash
# Verify the script execution assumption

# Check if the script is typically run from repo root
rg -n "scripts/apply-shared.sh" -g "*.md" -g "*.sh" -C 2

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Inspect the header of apply-shared.sh for working-directory checks
sed -n '1,50p' scripts/apply-shared.sh

Length of output: 1791


Resolve script paths to repository root before linking.
At the top of scripts/apply-shared.sh, compute the script’s directory and cd into the repo root (e.g.

script_dir=$(cd "$(dirname "$0")" && pwd)
cd "$script_dir/.."

) so that all ln -sf ../../packages/... calls reliably point to the correct targets.

🤖 Prompt for AI Agents
In scripts/apply-shared.sh around lines 21 to 34, the relative ln -sf calls
assume the current working directory is the repo root which can break when the
script is invoked from elsewhere; at the top of the script (before any linking)
determine the script directory and change into the repo root (script_dir=$(cd
"$(dirname "$0")" && pwd) then cd "$script_dir/..") so that subsequent ln -sf
../../packages/... calls resolve correctly; add this CD step immediately after
the shebang and any set -e/-u lines.

Comment on lines +191 to +194
echo "🔗 Creating configuration symlinks..."
run_cmd "cd '$DEMO_DIR' && ln -sf ../../packages/shakacode_demo_common/config/.rubocop.yml .rubocop.yml"
run_cmd "cd '$DEMO_DIR' && ln -sf ../../packages/shakacode_demo_common/config/.eslintrc.js .eslintrc.js"

Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify symlink targets exist (config vs configs).
Targets use config/.eslintrc.js and .rubocop.yml; ensure these paths exist in packages/shakacode_demo_common (Node package lists “configs/”).

Run:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
for f in "config/.rubocop.yml" "config/.eslintrc.js" "configs/.eslintrc.js"; do
  test -f "packages/shakacode_demo_common/$f" && echo "Found: $f" || echo "Missing: $f"
done

Length of output: 87


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate .rubocop.yml and .eslintrc.js anywhere under the package
find packages/shakacode_demo_common -type f \( -name ".rubocop.yml" -o -name ".eslintrc.js" \) -print

Length of output: 0


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# List all package directories
echo "Packages directory listing:"
ls -1 packages

# Find any config directories and relevant files
echo -e "\nConfig directories under packages:"
find packages -maxdepth 2 -type d -name "*config*"

echo -e "\nAny .rubocop.yml or .eslintrc.js in repo:"
find . -type f \( -name ".rubocop.yml" -o -name ".eslintrc.js" \) -print

Length of output: 220


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Config directory files under packages/shakacode_demo_common/config:"
ls -1 packages/shakacode_demo_common/config || echo "  (none)"

echo -e "\nConfigs directory files under packages/shakacode_demo_common/configs:"
ls -1 packages/shakacode_demo_common/configs || echo "  (none)"

Length of output: 199


Fix symlink targets to point at existing files
The demo script currently links to non‐existent paths under packages/shakacode_demo_common. Update as follows:

- ln -sf ../../packages/shakacode_demo_common/config/.rubocop.yml .rubocop.yml
+ ln -sf ../../packages/shakacode_demo_common/config/rubocop.yml    .rubocop.yml

- ln -sf ../../packages/shakacode_demo_common/config/.eslintrc.js .eslintrc.js
+ ln -sf ../../packages/shakacode_demo_common/configs/eslint.config.js .eslintrc.js

This ensures the symlinks reference the actual files (config/rubocop.yml and configs/eslint.config.js).

📝 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
echo "🔗 Creating configuration symlinks..."
run_cmd "cd '$DEMO_DIR' && ln -sf ../../packages/shakacode_demo_common/config/.rubocop.yml .rubocop.yml"
run_cmd "cd '$DEMO_DIR' && ln -sf ../../packages/shakacode_demo_common/config/.eslintrc.js .eslintrc.js"
echo "🔗 Creating configuration symlinks..."
run_cmd "cd '$DEMO_DIR' && ln -sf ../../packages/shakacode_demo_common/config/rubocop.yml .rubocop.yml"
run_cmd "cd '$DEMO_DIR' && ln -sf ../../packages/shakacode_demo_common/configs/eslint.config.js .eslintrc.js"
🤖 Prompt for AI Agents
In scripts/new-demo.sh around lines 191 to 194, the symlink targets point to
non-existent files under packages/shakacode_demo_common; update the ln -sf
commands to link to the actual existing files by changing the RuboCop target to
packages/shakacode_demo_common/config/rubocop.yml and the ESLint target to
packages/shakacode_demo_common/configs/eslint.config.js so the created
.rubocop.yml and .eslintrc.js in the demo dir reference real files.

Comment on lines +28 to +33
# Run JavaScript tests
if [ -f "package.json" ]; then
echo " Running JavaScript tests..."
npm test 2>/dev/null || true
npm run lint 2>/dev/null || true
fi
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

JS test/lint failures are ignored; not added to FAILED_DEMOS.

Current || true with stderr suppression hides failures and never marks the package as failed. Aggregate JS exits into FAILED_DEMOS and surface their output.

Apply this diff:

-  if [ -f "package.json" ]; then
-    echo "  Running JavaScript tests..."
-    npm test 2>/dev/null || true
-    npm run lint 2>/dev/null || true
-  fi
+  if [ -f "package.json" ]; then
+    echo "  Running JavaScript tests..."
+    if ! npm test; then
+      FAILED_DEMOS="$FAILED_DEMOS shakacode_demo_common-js"
+    fi
+    if ! npm run lint; then
+      FAILED_DEMOS="$FAILED_DEMOS shakacode_demo_common-eslint"
+    fi
+  fi
📝 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
# Run JavaScript tests
if [ -f "package.json" ]; then
echo " Running JavaScript tests..."
npm test 2>/dev/null || true
npm run lint 2>/dev/null || true
fi
# Run JavaScript tests
if [ -f "package.json" ]; then
echo " Running JavaScript tests..."
if ! npm test; then
FAILED_DEMOS="$FAILED_DEMOS shakacode_demo_common-js"
fi
if ! npm run lint; then
FAILED_DEMOS="$FAILED_DEMOS shakacode_demo_common-eslint"
fi
fi
🤖 Prompt for AI Agents
In scripts/test-all.sh around lines 28-33, tests and lint commands for JS use
"npm test 2>/dev/null || true" which suppresses stderr and always succeeds so
failures are never added to FAILED_DEMOS; replace those calls by running "npm
test" and "npm run lint" without discarding output, capture each command's exit
code (e.g., run and save $? or redirect output to a temp file and check its exit
status), and if the exit code is non-zero append the current demo path to
FAILED_DEMOS (and optionally echo the captured output to console); ensure you do
this for both test and lint runs so any failure aggregates into FAILED_DEMOS.

Comment on lines +62 to +67
# Run JavaScript tests
if [ -f "package.json" ]; then
echo " Running JavaScript tests..."
npm test 2>/dev/null || true
npm run lint 2>/dev/null || true
fi
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Same issue for demos: JS failures aren’t tracked.

Track JS test/lint failures per demo and avoid hiding stderr.

-      if [ -f "package.json" ]; then
-        echo "  Running JavaScript tests..."
-        npm test 2>/dev/null || true
-        npm run lint 2>/dev/null || true
-      fi
+      if [ -f "package.json" ]; then
+        echo "  Running JavaScript tests..."
+        if ! npm test; then
+          FAILED_DEMOS="$FAILED_DEMOS ${demo_name}-js"
+        fi
+        if ! npm run lint; then
+          FAILED_DEMOS="$FAILED_DEMOS ${demo_name}-eslint"
+        fi
+      fi
📝 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
# Run JavaScript tests
if [ -f "package.json" ]; then
echo " Running JavaScript tests..."
npm test 2>/dev/null || true
npm run lint 2>/dev/null || true
fi
# Run JavaScript tests
if [ -f "package.json" ]; then
echo " Running JavaScript tests..."
if ! npm test; then
FAILED_DEMOS="$FAILED_DEMOS ${demo_name}-js"
fi
if ! npm run lint; then
FAILED_DEMOS="$FAILED_DEMOS ${demo_name}-eslint"
fi
fi
🤖 Prompt for AI Agents
In scripts/test-all.sh around lines 62 to 67, the JS test/lint commands
currently hide stderr and always succeed due to redirection and "|| true";
change them to run per-demo without redirecting stderr, capture each command's
exit code, record failures per demo (e.g., increment a failure counter or append
demo name to a failures list) and continue to the next demo, and at the end exit
non-zero if any demo had test or lint failures so CI sees the errors.

./spec/demo_scripts/demo_creator_spec.rb[1:3:1] | passed | 0.00008 seconds |
./spec/demo_scripts/demo_creator_spec.rb[1:3:2] | passed | 0.00169 seconds |
./spec/demo_scripts/demo_creator_spec.rb[1:3:3] | passed | 0.00011 seconds |
./spec/demo_scripts/pre_flight_checks_spec.rb[1:1:1:1] | failed | 0.04821 seconds |
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Investigate the failing spec.

Line 24 shows a test failure in pre_flight_checks_spec.rb[1:1:1:1] with a runtime of 0.04821 seconds. This failure should be resolved before merging.

Run the following script to get details about the failing test:


🏁 Script executed:

#!/bin/bash
# Description: Retrieve the failing test details and recent runs

# Get the specific test context
rg -A 10 -B 5 "describe.*PreFlightChecks" spec/demo_scripts/pre_flight_checks_spec.rb

# Check if there are any recent test failure logs
fd --type f "rspec" tmp/ log/ --exec cat {} \; 2>/dev/null | head -100

Length of output: 328


🏁 Script executed:

#!/bin/bash
# Display lines 1-50 of spec/examples.txt to locate the failing example and error message
sed -n '1,50p' spec/examples.txt

Length of output: 2267


Resolve failing PreFlightChecks spec
spec/examples.txt line 24 reports failure in spec/demo_scripts/pre_flight_checks_spec.rb[1:1:1:1]; run the spec locally to inspect the error and fix this example before merging.

🤖 Prompt for AI Agents
In spec/examples.txt around line 24, the recorded example for
spec/demo_scripts/pre_flight_checks_spec.rb[1:1:1:1] is failing; run the spec
locally (rspec spec/demo_scripts/pre_flight_checks_spec.rb --format
documentation or the full example id) to capture the current failure output,
inspect the spec and related code to determine whether the test or the demo
script is incorrect, fix the underlying code or adjust the spec so it passes,
then update spec/examples.txt to reflect the corrected example output (including
status and timing) and re-run the full suite to ensure the example remains green
before committing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants