Skip to content

Commit c462704

Browse files
justin808claude
andcommitted
Add CI debugging tools for efficient local failure reproduction
## Changes ### RuboCop Configuration - Exclude bin/* shell scripts from RuboCop checks ### Configuration Improvements - Fix error message wording in configuration.rb (prefer :defer over :sync) - Add explanatory comment in context.ts about TypeScript var requirement - Improve ESLint config formatting (export at top) - Add clarifying comment in helper spec about defer default strategy ### CI Debugging Tools - Add bin/ci-rerun-failures: Smart script that fetches actual CI failures from GitHub and re-runs only failed jobs locally - Add bin/ci-run-failed-specs: Parse and re-run only specific failing RSpec examples - Add bin/ci-switch-config: Switch between latest/minimum dependency configurations 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 28405e3 commit c462704

26 files changed

+36
-190
lines changed

.claude/commands/update-changelog.md

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ You are helping to add an entry to the CHANGELOG.md file for the React on Rails
55
## Critical Requirements
66

77
1. **User-visible changes only**: Only add changelog entries for user-visible changes:
8-
98
- New features
109
- Bug fixes
1110
- Breaking changes
@@ -118,21 +117,18 @@ When a new version is released:
118117
### For Regular Changelog Updates
119118

120119
1. **ALWAYS fetch latest changes first**:
121-
122120
- **CRITICAL**: Run `git fetch origin master` to ensure you have the latest commits
123121
- The workspace may be behind origin/master, causing you to miss recently merged PRs
124122
- After fetching, use `origin/master` for all comparisons, NOT local `master` branch
125123

126124
2. **Determine the correct version tag to compare against**:
127-
128125
- First, check the tag dates: `git log --tags --simplify-by-decoration --pretty="format:%ai %d" | head -10`
129126
- Find the latest version tag and its date
130127
- Compare origin/master branch date to the tag date
131128
- If the tag is NEWER than origin/master, it means the branch needs to be updated to include the tag's commits
132129
- **CRITICAL**: Always use `git log TAG..BRANCH` to find commits that are in the tag but not in the branch, as the tag may be ahead
133130

134131
3. **Check commits and version boundaries**:
135-
136132
- **IMPORTANT**: Use `origin/master` in all commands below, not local `master`
137133
- Run `git log --oneline LAST_TAG..origin/master` to see commits since the last release
138134
- Also check `git log --oneline origin/master..LAST_TAG` to see if the tag is ahead of origin/master
@@ -148,20 +144,17 @@ When a new version is released:
148144
5. **Read the current CHANGELOG.md** to understand the existing structure and formatting.
149145

150146
6. **Determine where entries should go**:
151-
152147
- If the latest version tag is NEWER than origin/master branch, move entries from "Unreleased" to that version section
153148
- If origin/master is ahead of the latest tag, add new entries to "Unreleased"
154149
- Always verify the version date in CHANGELOG.md matches the actual tag date
155150

156151
7. **Add or move entries** to the appropriate section under appropriate category headings.
157-
158152
- **CRITICAL**: When moving entries from "Unreleased" to a version section, merge them with existing entries under the same category heading
159153
- **NEVER create duplicate section headings** (e.g., don't create two "### Fixed" sections)
160154
- If the version section already has a category heading (e.g., "### Fixed"), add the moved entries to that existing section
161155
- Maintain the category order as defined above
162156

163157
8. **Verify formatting**:
164-
165158
- Bold description with period
166159
- Proper PR link (NO hash symbol)
167160
- Proper author link
@@ -181,12 +174,10 @@ When a new version is released:
181174
When releasing from beta to a stable version (e.g., v16.1.0-beta.3 → v16.1.0):
182175

183176
1. **Remove all beta version labels** from the changelog:
184-
185177
- Change `### [v16.1.0-beta.1]`, `### [v16.1.0-beta.2]`, etc. to a single `### [v16.1.0]` section
186178
- Combine all beta entries into the stable release section
187179

188180
2. **Consolidate duplicate entries**:
189-
190181
- If bug fixes or changes were made to features introduced in earlier betas, keep only the final state
191182
- Remove redundant changelog entries for fixes to beta features
192183
- Keep the most recent/accurate description of each change

.rubocop.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ AllCops:
2525
- '**/node_modules/**/*'
2626
- '**/public/**/*'
2727
- '**/tmp/**/*'
28+
- 'bin/*' # Shell scripts
2829
- 'coverage/**/*'
2930
- 'gen-examples/examples/**/*'
3031
- 'node_modules/**/*'

CHANGELOG.md

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ Changes since the last non-beta release.
3838
#### Changed
3939

4040
- **Shakapacker 9.0.0 Upgrade**: Upgraded Shakapacker from 8.2.0 to 9.0.0 with Babel transpiler configuration for compatibility. Key changes include:
41-
4241
- Configured `javascript_transpiler: babel` in shakapacker.yml (Shakapacker 9.0 defaults to SWC which has PropTypes handling issues)
4342
- Added precompile hook support via `bin/shakapacker-precompile-hook` for ReScript builds and pack generation
4443
- Configured CSS Modules to use default exports (`namedExport: false`) for backward compatibility with existing `import styles from` syntax
@@ -100,7 +99,6 @@ To migrate to React on Rails Pro:
10099
**Note:** If you're not using any of the Pro-only methods listed above, no changes are required.
101100

102101
- **Pro-Specific Configurations Moved to Pro Gem**: The following React Server Components (RSC) configurations have been moved from `ReactOnRails.configure` to `ReactOnRailsPro.configure`:
103-
104102
- `rsc_bundle_js_file` - Path to the RSC bundle file
105103
- `react_server_client_manifest_file` - Path to the React server client manifest
106104
- `react_client_manifest_file` - Path to the React client manifest
@@ -126,7 +124,6 @@ To migrate to React on Rails Pro:
126124
See the [React on Rails Pro Configuration docs](https://github.com/shakacode/react_on_rails/blob/master/react_on_rails_pro/docs/configuration.md) for more details.
127125

128126
- **Streaming View Helpers Moved to Pro Gem**: The following view helpers have been removed from the open-source gem and are now only available in React on Rails Pro:
129-
130127
- `stream_react_component` - Progressive SSR using React 18+ streaming
131128
- `rsc_payload_react_component` - RSC payload rendering
132129

@@ -151,12 +148,10 @@ To migrate to React on Rails Pro:
151148
#### New Features
152149

153150
- **Server Bundle Security**: Added new configuration options for enhanced server bundle security and organization:
154-
155151
- `server_bundle_output_path`: Configurable directory (relative to the Rails root) for server bundle output (default: "ssr-generated"). If set to `nil`, the server bundle will be loaded from the same public directory as client bundles. [PR 1798](https://github.com/shakacode/react_on_rails/pull/1798) by [justin808](https://github.com/justin808)
156152
- `enforce_private_server_bundles`: When enabled, ensures server bundles are only loaded from private directories outside the public folder (default: false for backward compatibility) [PR 1798](https://github.com/shakacode/react_on_rails/pull/1798) by [justin808](https://github.com/justin808)
157153

158154
- **Improved Bundle Path Resolution**: Bundle path resolution for server bundles now works as follows:
159-
160155
- If `server_bundle_output_path` is set, the server bundle is loaded from that directory.
161156
- If `server_bundle_output_path` is not set, the server bundle falls back to the client bundle directory (typically the public output path).
162157
- If `enforce_private_server_bundles` is enabled:
@@ -268,7 +263,6 @@ See [Release Notes](docs/release-notes/16.0.0.md) for complete migration guide.
268263

269264
- **`defer_generated_component_packs` deprecated** → use `generated_component_packs_loading_strategy`
270265
- Migration:
271-
272266
- `defer_generated_component_packs: true``generated_component_packs_loading_strategy: :defer`
273267
- `defer_generated_component_packs: false``generated_component_packs_loading_strategy: :sync`
274268
- Recommended: `generated_component_packs_loading_strategy: :async` for best performance
@@ -677,7 +671,6 @@ for details.
677671
- Removal of config.symlink_non_digested_assets_regex as it's no longer needed with rails/webpacker.
678672
If any business needs this, we can move the code to a separate gem.
679673
- Added configuration option `same_bundle_for_client_and_server` with default `false` because
680-
681674
1. Production applications would typically have a server bundle that differs from the client bundle
682675
2. This change only affects trying to use HMR with react_on_rails with rails/webpacker.
683676

@@ -1395,13 +1388,11 @@ No changes.
13951388
- Added automatic compilation of assets at precompile is now done by ReactOnRails. Thus, you don't need to provide your own `assets.rake` file that does the precompilation.
13961389
[#398](https://github.com/shakacode/react_on_rails/pull/398) by [robwise](https://github.com/robwise), [jbhatab](https://github.com/jbhatab), and [justin808](https://github.com/justin808).
13971390
- **Migration to v6**
1398-
13991391
- Do not run the generator again if you've already run it.
14001392

14011393
- See [shakacode/react-webpack-rails-tutorial/pull/287](https://github.com/shakacode/react-webpack-rails-tutorial/pull/287) for an example of upgrading from v5.
14021394

14031395
- To configure the asset compilation you can either
1404-
14051396
1. Specify a `config/react_on_rails` setting for `build_production_command` to be nil to turn this feature off.
14061397
2. Specify the script command you want to run to build your production assets, and remove your `assets.rake` file.
14071398

CODING_AGENTS.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -260,18 +260,15 @@ Use `gh pr create` with:
260260
## Debugging Workflow
261261

262262
1. **Understand the Problem**
263-
264263
- Read the issue carefully
265264
- Reproduce the bug if possible
266265
- Identify root cause
267266

268267
2. **Create Minimal Test Case**
269-
270268
- Write failing test that demonstrates issue
271269
- Keep it focused and minimal
272270

273271
3. **Implement Fix**
274-
275272
- Make smallest change possible
276273
- Ensure fix doesn't break existing functionality
277274
- Follow existing code patterns

CONTRIBUTING.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -607,22 +607,18 @@ This approach:
607607
**Common Issues and Solutions:**
608608

609609
1. **React components not rendering (empty divs)**
610-
611610
- **Cause**: Missing yalc setup for JavaScript package
612611
- **Solution**: Follow yalc setup steps above after running generator
613612

614613
2. **Generator fails with Shakapacker errors**
615-
616614
- **Cause**: Conflicting Shakapacker versions or incomplete installation
617615
- **Solution**: Clean reset and ensure consistent Shakapacker version across tests
618616

619617
3. **Babel configuration conflicts during yalc development**
620-
621618
- **Cause**: Both `babel.config.js` and `package.json` "babel" section defining presets
622619
- **Solution**: Remove "babel" section from `package.json`, keep only `babel.config.js`
623620

624621
4. **"Package.json not found" errors**
625-
626622
- **Cause**: Generator trying to access non-existent package.json files
627623
- **Solution**: Test with commits that fix this specific issue (e.g., bc69dcd0)
628624

TODO.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,11 @@
5959
## Developer Experience
6060
6161
- [ ] **bin/dev help command enhancements**
62-
6362
- [x] Add emojis and colors for better readability
6463
- [ ] Add section about component development patterns
6564
- [ ] Include troubleshooting for client/server split issues
6665
6766
- [ ] **Babel Configuration Conflict Detection**
68-
6967
- [ ] Add validation in generator/initializer to detect conflicting Babel configs
7068
- [ ] Improve error messaging for duplicate preset issues
7169
- [ ] Common conflict: babel.config.js + package.json "babel" section

bin/ci-rerun-failures

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -71,22 +71,6 @@ done
7171
echo -e "${BLUE}=== CI Failure Re-runner ===${NC}"
7272
echo ""
7373

74-
# Check required dependencies
75-
MISSING_DEPS=()
76-
command -v gh >/dev/null 2>&1 || MISSING_DEPS+=("gh (GitHub CLI)")
77-
command -v jq >/dev/null 2>&1 || MISSING_DEPS+=("jq")
78-
79-
if [ ${#MISSING_DEPS[@]} -gt 0 ]; then
80-
echo -e "${RED}Error: Missing required dependencies:${NC}"
81-
for dep in "${MISSING_DEPS[@]}"; do
82-
echo " - $dep"
83-
done
84-
echo ""
85-
echo "Install with:"
86-
echo " brew install gh jq"
87-
exit 1
88-
fi
89-
9074
# Fetch PR info
9175
if [ -z "$PR_NUMBER" ]; then
9276
if ! gh pr view --json number,commits >/dev/null 2>&1; then
@@ -230,9 +214,9 @@ echo " 3. Run: pbpaste | bin/ci-run-failed-specs"
230214
echo ""
231215

232216
# Confirm before running
233-
read -p "Run these tests now? [Y/n] " -n 1 -r REPLY
217+
read -p "Run these tests now? [Y/n] " -n 1 -r
234218
echo
235-
if [[ ! "${REPLY}" =~ ^[Yy]$ ]] && [[ ! -z "${REPLY}" ]]; then
219+
if [[ ! $REPLY =~ ^[Yy]$ ]] && [[ ! -z $REPLY ]]; then
236220
echo "Cancelled."
237221
exit 0
238222
fi
@@ -255,8 +239,6 @@ for cmd in "${!COMMANDS_TO_RUN[@]}"; do
255239
echo -e "${BLUE}Command: $cmd${NC}"
256240
echo ""
257241

258-
# Note: Using eval here is safe because $cmd comes from predefined JOB_MAP,
259-
# not from user input. Commands may contain shell operators like && and ||.
260242
if eval "$cmd"; then
261243
echo -e "${GREEN}$job_name passed${NC}"
262244
echo ""

bin/ci-run-failed-specs

Lines changed: 9 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,6 @@ YELLOW='\033[1;33m'
1818
BLUE='\033[0;34m'
1919
NC='\033[0m' # No Color
2020

21-
# Check required dependencies
22-
MISSING_DEPS=()
23-
command -v bundle >/dev/null 2>&1 || MISSING_DEPS+=("bundle (Ruby bundler)")
24-
25-
if [ ${#MISSING_DEPS[@]} -gt 0 ]; then
26-
echo -e "${RED}Error: Missing required dependencies:${NC}"
27-
for dep in "${MISSING_DEPS[@]}"; do
28-
echo " - $dep"
29-
done
30-
echo ""
31-
echo "Install with:"
32-
echo " gem install bundler"
33-
exit 1
34-
fi
35-
3621
# Show help
3722
show_help() {
3823
cat << EOF
@@ -137,35 +122,30 @@ if [[ "${UNIQUE_SPECS[0]}" == *"spec/system"* ]] || [[ "${UNIQUE_SPECS[0]}" == *
137122
fi
138123
fi
139124

140-
# Build rspec command as array (safer than eval)
141-
RSPEC_CMD=("bundle" "exec" "rspec")
125+
# Build rspec command
126+
RSPEC_CMD="bundle exec rspec"
142127
for spec in "${UNIQUE_SPECS[@]}"; do
143-
RSPEC_CMD+=("$spec")
128+
RSPEC_CMD="$RSPEC_CMD '$spec'"
144129
done
145130

146-
# Display command for user
147-
DISPLAY_CMD="bundle exec rspec"
148-
for spec in "${UNIQUE_SPECS[@]}"; do
149-
DISPLAY_CMD="$DISPLAY_CMD '$spec'"
150-
done
151-
echo -e "${BLUE}Command:${NC} cd $WORKING_DIR && $DISPLAY_CMD"
131+
echo -e "${BLUE}Command:${NC} cd $WORKING_DIR && $RSPEC_CMD"
152132
echo ""
153133

154134
# Confirm (read from /dev/tty to handle piped input)
155135
if [ -t 0 ]; then
156-
read -p "Run these specs now? [Y/n] " -n 1 -r REPLY
136+
read -p "Run these specs now? [Y/n] " -n 1 -r
157137
else
158-
read -p "Run these specs now? [Y/n] " -n 1 -r REPLY < /dev/tty
138+
read -p "Run these specs now? [Y/n] " -n 1 -r < /dev/tty
159139
fi
160140
echo
161-
if [[ ! "${REPLY}" =~ ^[Yy]$ ]] && [[ ! -z "${REPLY}" ]]; then
141+
if [[ ! $REPLY =~ ^[Yy]$ ]] && [[ ! -z $REPLY ]]; then
162142
echo "Cancelled."
163143
exit 0
164144
fi
165145

166-
# Run the specs directly (no eval - safer)
146+
# Run the specs
167147
cd "$WORKING_DIR"
168-
"${RSPEC_CMD[@]}"
148+
eval "$RSPEC_CMD"
169149
RESULT=$?
170150

171151
echo ""

bin/ci-switch-config

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
# bin/ci-switch-config latest # Ruby 3.4, Node 22, latest deps
77
# bin/ci-switch-config status # Show current configuration
88

9-
set -euo pipefail
9+
set -e
1010

1111
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
1212
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
@@ -67,34 +67,30 @@ show_status() {
6767
echo ""
6868
echo "Dependency versions:"
6969

70-
SHAKAPACKER_GEM=""
7170
if [ -f "$PROJECT_ROOT/Gemfile.development_dependencies" ]; then
72-
SHAKAPACKER_GEM=$(grep 'gem "shakapacker"' "$PROJECT_ROOT/Gemfile.development_dependencies" | sed -E 's/.*"([0-9.]+)".*/\1/' || echo "")
73-
echo " Shakapacker (gem): ${SHAKAPACKER_GEM:-not found}"
71+
SHAKAPACKER_GEM=$(grep 'gem "shakapacker"' "$PROJECT_ROOT/Gemfile.development_dependencies" | sed -E 's/.*"([0-9.]+)".*/\1/')
72+
echo " Shakapacker (gem): $SHAKAPACKER_GEM"
7473
fi
7574

76-
SHAKAPACKER_NPM=""
7775
if [ -f "$PROJECT_ROOT/spec/dummy/package.json" ]; then
78-
SHAKAPACKER_NPM=$(grep '"shakapacker"' "$PROJECT_ROOT/spec/dummy/package.json" | sed -E 's/.*"([0-9.]+)".*/\1/' || echo "")
79-
echo " Shakapacker (npm): ${SHAKAPACKER_NPM:-not found}"
76+
SHAKAPACKER_NPM=$(grep '"shakapacker"' "$PROJECT_ROOT/spec/dummy/package.json" | sed -E 's/.*"([0-9.]+)".*/\1/')
77+
echo " Shakapacker (npm): $SHAKAPACKER_NPM"
8078
fi
8179

82-
REACT_ROOT=""
8380
if [ -f "$PROJECT_ROOT/package.json" ]; then
84-
REACT_ROOT=$(grep '"react":' "$PROJECT_ROOT/package.json" | head -1 | sed -E 's/.*"([^"]+)".*/\1/' || echo "")
85-
echo " React (root): ${REACT_ROOT:-not found}"
81+
REACT_ROOT=$(grep '"react":' "$PROJECT_ROOT/package.json" | head -1 | sed -E 's/.*"([^"]+)".*/\1/')
82+
echo " React (root): $REACT_ROOT"
8683
fi
8784

88-
REACT_DUMMY=""
8985
if [ -f "$PROJECT_ROOT/spec/dummy/package.json" ]; then
90-
REACT_DUMMY=$(grep '"react":' "$PROJECT_ROOT/spec/dummy/package.json" | sed -E 's/.*"([^"]+)".*/\1/' || echo "")
91-
echo " React (dummy): ${REACT_DUMMY:-not found}"
86+
REACT_DUMMY=$(grep '"react":' "$PROJECT_ROOT/spec/dummy/package.json" | sed -E 's/.*"([^"]+)".*/\1/')
87+
echo " React (dummy): $REACT_DUMMY"
9288
fi
9389

9490
echo ""
95-
if [[ "${SHAKAPACKER_GEM}" == "8.2.0" ]] && [[ "${REACT_ROOT}" == "18.0.0" ]]; then
91+
if [[ "$SHAKAPACKER_GEM" == "8.2.0" ]] && [[ "$REACT_ROOT" == "18.0.0" ]]; then
9692
echo -e "Current config: ${GREEN}minimum${NC} (matches CI: Ruby 3.2, Node 20, minimum deps)"
97-
elif [[ "${SHAKAPACKER_GEM}" == "9.3.0" ]] && [[ "${REACT_ROOT}" =~ 19 ]]; then
93+
elif [[ "$SHAKAPACKER_GEM" == "9.3.0" ]] && [[ "$REACT_ROOT" =~ 19 ]]; then
9894
echo -e "Current config: ${GREEN}latest${NC} (matches CI: Ruby 3.4, Node 22, latest deps)"
9995
else
10096
echo -e "Current config: ${YELLOW}unknown/mixed${NC}"
@@ -112,9 +108,9 @@ switch_to_minimum() {
112108
# Check if we have git changes
113109
if ! git diff --quiet || ! git diff --cached --quiet; then
114110
print_warning "You have uncommitted changes. This script will modify files."
115-
read -p "Continue? (y/N) " -n 1 -r REPLY
111+
read -p "Continue? (y/N) " -n 1 -r
116112
echo
117-
if [[ ! "${REPLY}" =~ ^[Yy]$ ]]; then
113+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
118114
echo "Aborted."
119115
exit 1
120116
fi
@@ -192,9 +188,9 @@ restore_to_latest() {
192188
# Check if we have git changes
193189
if ! git diff --quiet || ! git diff --cached --quiet; then
194190
print_warning "You have uncommitted changes. This script will restore from git."
195-
read -p "Continue? This will discard changes to package.json, Gemfile, etc. (y/N) " -n 1 -r REPLY
191+
read -p "Continue? This will discard changes to package.json, Gemfile, etc. (y/N) " -n 1 -r
196192
echo
197-
if [[ ! "${REPLY}" =~ ^[Yy]$ ]]; then
193+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
198194
echo "Aborted."
199195
exit 1
200196
fi

0 commit comments

Comments
 (0)