Skip to content

ci: consolidate Ruby setup and Bundler caching#3354

Closed
justin808 wants to merge 23 commits into
mainfrom
jg/2224-bundler-cache-ci
Closed

ci: consolidate Ruby setup and Bundler caching#3354
justin808 wants to merge 23 commits into
mainfrom
jg/2224-bundler-cache-ci

Conversation

@justin808

@justin808 justin808 commented May 21, 2026

Copy link
Copy Markdown
Member

Summary

  • Consolidates Ruby and Bundler setup around .github/actions/setup-bundle, which now calls ruby/setup-ruby@v1 with bundler-cache: true instead of maintaining custom actions/cache restore/save logic in each workflow.
  • Adds .github/actions/setup-ruby for jobs that need Ruby runtime setup before a separate Bundler install step, including the shared libyaml-dev prerequisite.
  • Applies the reusable setup pattern across the OSS, Pro, benchmark, precompile, integration, Playwright, and lint workflows while preserving explicit Gemfile selection where later bundle exec calls must run against a package or dummy app.
  • Keeps the CI reliability fixes from Improve bundler caching in CI #2224: frozen installs use committed lockfiles, turbolinks stays unconditional so Bundler lock resolution is stable, Pro checksum validation remains enabled, and benchmark runs use the configured BENCHMARK_PORT consistently.

Review and CI updates

  • Rebased onto origin/main and force-pushed the branch at bbf71aa3cc429dae277783105a599218f494b126.
  • Addressed the remaining must-fix review item by pinning BUNDLE_GEMFILE to react_on_rails_pro/spec/dummy/Gemfile for the Pro benchmark step, matching the Core benchmark behavior because bench.rb shells out through bundle exec.
  • Addressed the dummy integration review item by removing the BUNDLE_GEMFILE override from run_rspec:all_dummy, so nested dummy app bundle exec calls can use the dummy Gemfile and lockfile.
  • Addressed optional review items: explicit Bencher retry guard for an empty start-point hash, Ruby 3.4 pin in the lint bootstrap step, BUNDLE_JOBS: '4' preservation in setup-bundle, explicit bundler-cache: false in setup-ruby, Bencher retry indentation cleanup, and a setup-bundle description note for the Linux libyaml-dev prerequisite.
  • No active discuss-only review items were found in the unresolved thread scan. If a later reviewer wants broader changes, the practical next step is to keep them as follow-up design cleanup unless they identify a concrete CI regression in this PR.
  • GitHub checks for the latest push are queued/in progress; inspect_pr_checks.py reports no failing checks at the time this description was updated.

Test plan

  • git rebase origin/main
  • Pre-change assertions for the open review items failed, then passed after the fixes
  • git diff --check origin/main...HEAD && git diff --check
  • SHELLCHECK_OPTS='-S warning' actionlint -color
  • ruby -e 'require "yaml"; ARGV.each { |f| YAML.load_file(f); puts "parsed #{f}" }' .github/actions/setup-bundle/action.yml .github/actions/setup-ruby/action.yml .github/workflows/benchmark.yml .github/workflows/examples.yml .github/workflows/gem-tests.yml .github/workflows/integration-tests.yml .github/workflows/lint-js-and-ruby.yml .github/workflows/pro-integration-tests.yml .github/workflows/pro-test-package-and-gem.yml
  • pnpm start format.listDifferent
  • (cd react_on_rails && bundle exec rubocop)
  • Pre-commit hook: trailing newline check and Prettier on changed workflow/action files
  • Pre-push hook: branch RuboCop on react_on_rails/rakelib/run_rspec.rake and Markdown link check on AGENTS.md
  • Latest GitHub Actions run passes for bbf71aa3cc429dae277783105a599218f494b126

Local note

  • pnpm run lint currently fails in existing packages/react-on-rails-pro-node-renderer/src/** TypeScript files that are outside this PR diff. I did not modify Pro package source files as part of this workflow-focused PR.

Fixes #2224


Note

Cursor Bugbot is generating a summary for commit bbf71aa. Configure here.

@coderabbitai

coderabbitai Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Across eight CI workflow files and one Gemfile, this PR replaces manual Ruby gem caching (via actions/cache and explicit bundle install commands) with the recommended ruby/setup-ruby@v1 action's built-in bundler-cache feature. The change consolidates dependency management across single-app, multi-app, Pro, and benchmark workflows while adding system dependencies and environment variables for Bundler checksum validation where needed.

Changes

Ruby bundler-cache migration

Layer / File(s) Summary
Simple workflow bundler-cache migration
.github/workflows/examples.yml, .github/workflows/gem-tests.yml
Examples and gem-tests workflows switch from manual caching to ruby/setup-ruby@v1 with working-directory: react_on_rails and bundler-cache: true; vendor/bundle caches and manual bundle-install blocks are removed.
Core app dual-directory bundler-cache (lint, precompile, integration)
.github/workflows/lint-js-and-ruby.yml, .github/workflows/precompile-check.yml, .github/workflows/integration-tests.yml
Lint, precompile, and integration workflows add libyaml-dev and use separate ruby/setup-ruby@v1 steps for react_on_rails and react_on_rails/spec/dummy directories with bundler-cache; all vendor/bundle caches and manual gem install blocks are removed. Lint workflow adds a standalone pnpm install --frozen-lockfile step.
Benchmark workflow Ruby setup consolidation
.github/workflows/benchmark.yml
Benchmark adds BUNDLE_DISABLE_CHECKSUM_VALIDATION and installs libyaml-dev early, then consolidates Ruby setup with ruby/setup-ruby@v1 and bundler-cache for both Core and Pro dummy apps; duplicated libyaml step and all vendor/bundle caching/install blocks are removed.
Pro workflows bundler-cache migration (lint, integration, test)
.github/workflows/pro-lint.yml, .github/workflows/pro-integration-tests.yml, .github/workflows/pro-test-package-and-gem.yml
Pro-lint, pro-integration-tests (webpack, RSpec, Playwright E2E jobs), and pro-test-package-and-gem workflows add BUNDLE_DISABLE_CHECKSUM_VALIDATION environment variables and libyaml-dev dependencies; replace manual caching with ruby/setup-ruby@v1 scoped to Pro package and Pro dummy app directories with bundler-cache: true. All vendor/bundle caches and manual Bundler/gem-install scripts are removed. RSpec and Playwright jobs update Rails server startup to use bundle exec.
Gemfile.development_dependencies turbolinks declaration
react_on_rails/Gemfile.development_dependencies
Change turbolinks gem from a conditional declaration (checking DISABLE_TURBOLINKS environment variable) to unconditional; add comments documenting frozen Bundler lock compatibility and that DISABLE_TURBOLINKS only affects the dummy app's asset loading, not the gem being present.

🎯 3 (Moderate) | ⏱️ ~25 minutes

🐰 Eight workflows now rely on bundler-cache true,
No more vendor-bundle caches to manage and review,
The Ruby setup does the work, neat and clean,
Pro and Core both share the bundler machine,
Turbolinks stays put, no conditionals left—
Gemfiles frozen and bundled, of CI theft bereft! 🎉

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The PR successfully implements all key objectives from #2224: replaces manual gem caching with ruby/setup-ruby bundler-cache across nine workflows, supports multiple Gemfiles via ruby/setup-ruby's multiple-uses pattern, adds x86_64-linux platform to Gemfile.lock for frozen installs, improves cache segregation by including matrix.dependency-level, and manages BUNDLE_DISABLE_CHECKSUM_VALIDATION in proper scope.
Out of Scope Changes check ✅ Passed All changes directly support the bundler-cache migration objective. The addition of turbolinks to Gemfile.development_dependencies unconditionally is necessary to support frozen Bundler mode in CI, which is directly in scope for the bundler-cache implementation.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly and accurately summarizes the main change: consolidating Ruby setup and Bundler caching across GitHub Actions workflows by replacing manual gem caching with ruby/setup-ruby's bundler-cache feature.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jg/2224-bundler-cache-ci

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.

@claude

claude Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

Code Review

Overview

This PR is a solid, well-motivated refactor. Replacing ad-hoc actions/cache + manual bundle install blocks with ruby/setup-ruby's built-in bundler-cache: true is the right call — the upstream docs explicitly warn against DIY gem caching, and the PR correctly diagnoses two pre-existing bugs (wrong vendor/bundle path in lint/integration-tests, platform-less Gemfile.lock). The net -145 line reduction is a meaningful simplification.

One potential regression and a couple of minor gaps to address before merge:


1. Ordering bug in benchmark.yml: libyaml-dev installed after bundler-cache runs bundle install

The two new ruby/setup-ruby + bundler-cache: true steps ("Install Core dummy app Ruby gems", "Install Pro dummy app Ruby gems") run bundle install eagerly as part of the action. The libyaml-dev system package — needed to compile psych v5 native extensions — is still installed later in the job at the "Fix dependency for libyaml-dev" step.

This is exactly the ordering fix applied to precompile-check.yml (libyaml-dev moved before Setup Ruby) and integration-tests.yml in this same PR, but it was missed for benchmark.yml. On a cold cache the install will likely fail with a native extension build error.

Fix: Move the Fix dependency for libyaml-dev step to before the first ruby/setup-ruby call (~line 200), mirroring the approach taken in precompile-check.yml and integration-tests.yml.


2. precompile-check.yml is missing cache-version

Every other workflow in this PR sets an explicit cache-version on its bundler-cache: true step (lint, benchmark, ${{ matrix.dependency-level }}). The precompile-check.yml step omits it entirely, relying on the action default. Minor inconsistency but easy to fix.

Suggestion: Add cache-version: precompile for consistency.


3. pro-integration-tests.yml jobs missing BUNDLE_DISABLE_CHECKSUM_VALIDATION

pro-lint.yml and the run-gem-tests job in pro-test-package-and-gem.yml correctly set BUNDLE_DISABLE_CHECKSUM_VALIDATION: 'true' at the job env: level. The three jobs in pro-integration-tests.yml (build-dummy-app-webpack-test-bundles, rspec-dummy-app-node-renderer, third execjs job) that now use bundler-cache: true do not.

The old dummy app install steps also omitted this flag, so it may be benign if the Pro dummy app's Gemfile only references the Pro gem via a local path (bypassing private-source checksum validation). But if any transitive dependency pulls from the private source, the automatic bundle install --frozen will fail on cold cache. Worth explicitly confirming or adding the env var defensively.


Positive highlights

  • The vendor/bundle cache-path bug in lint-js-and-ruby.yml and integration-tests.yml (caching repo-root path while installing to a subdirectory) is correctly identified and fixed.
  • Including ${{ matrix.dependency-level }} in cache-version for matrix jobs prevents minimum/latest gem sets from sharing a cache entry.
  • Adding x86_64-linux to react_on_rails/Gemfile.lock is the right fix for bundle install --frozen on Linux; the dummy app lock already had this platform.

Comment thread .github/workflows/benchmark.yml Outdated
Comment thread .github/workflows/precompile-check.yml Outdated
Comment thread .github/workflows/pro-integration-tests.yml Outdated
@greptile-apps

greptile-apps Bot commented May 21, 2026

Copy link
Copy Markdown

Greptile Summary

This PR replaces ad-hoc actions/cache + manual bundle install blocks across 9 workflows with ruby/setup-ruby@v1's built-in bundler-cache: true, and adds the x86_64-linux platform to react_on_rails/Gemfile.lock to allow frozen installs on Linux CI without a prior bundle lock --add-platform step.

  • Caching fix: The old lint-js-and-ruby.yml and integration-tests.yml cached vendor/bundle at the repo root but installed into react_on_rails/vendor/bundle, so caches were never populated; the new per-directory working-directory setup corrects this.
  • Cache key improvement: cache-version now incorporates matrix.dependency-level in gem-tests, examples, and integration-tests, ensuring latest and minimum dependency matrix legs get separate caches.
  • BUNDLE_DISABLE_CHECKSUM_VALIDATION ordering: Moved to job-level env: in Pro workflows so it is visible to bundler-cache: true's automatic bundle install.

Confidence Score: 4/5

The migration is correct and beneficial across 8 of the 9 workflow files; only benchmark.yml has a step-ordering issue that will cause cold-cache runs to fail.

In benchmark.yml the two bundler-cache: true steps execute before libyaml-dev is installed on the runner. On a cache miss, bundler tries to compile psych 5.x (present in the dummy app Gemfile.lock) without the required native library, crashing the build. All other workflows handle this ordering correctly.

.github/workflows/benchmark.yml — the libyaml-dev install step needs to move above the bundler-cache steps.

Important Files Changed

Filename Overview
.github/workflows/benchmark.yml Migrated to bundler-cache: true for Core/Pro dummy apps, but libyaml-dev is still installed after the bundler-cache steps — will fail on cold cache runs when psych needs to compile its native extension.
.github/workflows/examples.yml Replaced manual cache + bundle install with bundler-cache: true on react_on_rails working-directory; matrix.dependency-level added to cache-version for proper cache separation.
.github/workflows/gem-tests.yml Replaced manual cache + bundle install with bundler-cache: true; matrix.dependency-level added to cache-version for correct minimum/latest cache isolation.
.github/workflows/integration-tests.yml Correctly moves libyaml-dev install before setup-ruby; uses two bundler-cache steps for the full integration job; fixes the previously-broken root vendor/bundle cache path.
.github/workflows/lint-js-and-ruby.yml Adds two bundler-cache steps for react_on_rails and react_on_rails/spec/dummy; fixes the previous mismatch where gems were cached at the repo root but installed into a subdirectory.
.github/workflows/precompile-check.yml Correctly reorders libyaml-dev install to precede ruby/setup-ruby with bundler-cache: true; clean migration with no remaining manual cache steps.
.github/workflows/pro-integration-tests.yml Migrated three Pro integration jobs to bundler-cache: true for spec/dummy; old package-level caches were never populated so dropping them is correct.
.github/workflows/pro-lint.yml BUNDLE_DISABLE_CHECKSUM_VALIDATION moved to job env before bundler-cache: true; two setup-ruby steps handle pro package and dummy app separately.
.github/workflows/pro-test-package-and-gem.yml BUNDLE_DISABLE_CHECKSUM_VALIDATION moved to env for rspec-gem-specs job; bundler-cache: true used correctly for react_on_rails_pro working-directory.
react_on_rails/Gemfile.lock Adds x86_64-linux platform and nokogiri-1.19.2-x86_64-linux-gnu so bundle install --frozen works on Linux CI without a prior bundle lock --add-platform step.

Comments Outside Diff (1)

  1. .github/workflows/benchmark.yml, line 201-242 (link)

    P1 The libyaml-dev install step runs after the two bundler-cache: true steps. Because bundler-cache: true triggers bundle install automatically inside the action, a cold-cache run will try to compile psych (5.3.1) (which is present in react_on_rails/spec/dummy/Gemfile.lock via the sdoc → rdoc → psych chain) before libyaml-dev is present on the runner. This matches exactly the pattern already fixed in integration-tests.yml and precompile-check.yml where the libyaml-dev step was moved above the Ruby setup. Benchmark will fail on any run that misses the gem cache.

Reviews (1): Last reviewed commit: "ci: replace manual gem caching with ruby..." | Re-trigger Greptile

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (2)
.github/workflows/pro-test-package-and-gem.yml (1)

291-294: ⚡ Quick win

Narrow BUNDLE_DISABLE_CHECKSUM_VALIDATION scope.

Line 293 sets checksum validation bypass for the full job. Prefer step-level env on the Ruby setup step so this exception does not leak to unrelated commands.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/pro-test-package-and-gem.yml around lines 291 - 294, The
job-level env currently sets BUNDLE_DISABLE_CHECKSUM_VALIDATION: 'true' which
broadens checksum bypass; remove that key from the job env block and instead add
BUNDLE_DISABLE_CHECKSUM_VALIDATION: 'true' to only the Ruby/bundler setup step
(the step that installs Ruby/gems or runs bundle install) so the exception is
limited to bundler setup; ensure other steps no longer inherit the variable and
confirm the Ruby setup step still uses the same string value.
.github/workflows/pro-lint.yml (1)

90-93: ⚡ Quick win

Scope checksum bypass to the Ruby setup step instead of the whole job.

Line 92 currently applies BUNDLE_DISABLE_CHECKSUM_VALIDATION to every step in the job. Keep this override narrowly scoped to the ruby/setup-ruby step(s) to reduce blast radius.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/pro-lint.yml around lines 90 - 93, The job-level env
currently sets BUNDLE_DISABLE_CHECKSUM_VALIDATION which affects every step;
instead remove that env from the top-level env block and add
BUNDLE_DISABLE_CHECKSUM_VALIDATION: 'true' to only the ruby/setup-ruby step
definition (the step using uses: ruby/setup-ruby), leaving
REACT_ON_RAILS_PRO_LICENSE at job level unchanged; ensure the ruby/setup-ruby
step's env contains the BUNDLE_DISABLE_CHECKSUM_VALIDATION entry so only Ruby
setup is scoped to bypass checksum validation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/benchmark.yml:
- Around line 209-215: The workflow currently uses the tag-pinned action
reference ruby/setup-ruby@v1 in the "Setup Ruby", "Install Core dummy app Ruby
gems", and "Install Pro dummy app Ruby gems" steps; replace each occurrence of
ruby/setup-ruby@v1 with ruby/setup-ruby@<full_commit_sha> (the full immutable
commit SHA for the action) to pin the action to a specific commit and improve
supply-chain integrity, preserving the existing with: keys (ruby-version,
bundler, working-directory, bundler-cache, cache-version) exactly as they are.

In @.github/workflows/integration-tests.yml:
- Around line 124-131: Replace the mutable action ref ruby/setup-ruby@v1 with
immutable full commit SHAs for every uses: entry in the workflow (the "Setup
Ruby and install dummy app gems" step and the two other ruby/setup-ruby uses
mentioned in the review); locate each uses: line that references ruby/setup-ruby
and update it to ruby/setup-ruby@<full-commit-sha> (use the exact commit SHA
from the ruby/setup-ruby repository at the desired release), keeping the
existing with: keys (ruby-version, bundler, working-directory, bundler-cache,
cache-version) unchanged so the steps behavior is identical but pinned to an
immutable SHA.

In @.github/workflows/lint-js-and-ruby.yml:
- Around line 97-113: Replace the mutable actions in the two Ruby setup steps so
they are pinned to immutable commit SHAs: locate the steps named "Setup Ruby and
install package gems" and "Install dummy app gems" that currently use
`ruby/setup-ruby@v1` and change each `uses: ruby/setup-ruby@v1` to the
corresponding `uses: ruby/setup-ruby@<commit-sha>` (use the latest stable commit
SHA from the ruby/setup-ruby repository) to ensure reproducible CI runs.

In @.github/workflows/precompile-check.yml:
- Around line 87-93: The workflow step "Setup Ruby" currently uses the mutable
action reference "ruby/setup-ruby@v1"; replace that with a specific immutable
commit SHA for ruby/setup-ruby to avoid unexpected changes (i.e., pin the action
instead of using `@v1`). Locate the step by its name "Setup Ruby" or the uses
string "ruby/setup-ruby@v1" and update the uses field to the
repo@{full-commit-sha} value from the ruby/setup-ruby releases/tags, then commit
the updated workflow.

In @.github/workflows/pro-integration-tests.yml:
- Around line 99-105: Replace the floating tag ruby/setup-ruby@v1 with an
immutable commit SHA: locate each occurrence of the action usage (the step
titled "Setup Ruby and install Pro dummy app gems" which currently uses
ruby/setup-ruby@v1, and the other two occurrences that match that same uses
string) and change the reference to ruby/setup-ruby@<full-commit-sha> (obtain
the latest stable commit SHA from the ruby/setup-ruby repository and use that
exact 40-char SHA) so the workflow uses a pinned immutable version; update all
three occurrences consistently.

In @.github/workflows/pro-lint.yml:
- Around line 98-112: Replace the floating ruby/setup-ruby@v1 references in the
workflow steps named "Setup Ruby and install Pro package gems" and "Install Pro
dummy app gems" with a specific commit SHA of the ruby/setup-ruby action; locate
the two uses: the with-blocks that include ruby-version, bundler and
working-directory, fetch the canonical commit SHA from the ruby/setup-ruby repo
(or your org's approved mirror) and update both uses to
ruby/setup-ruby@<commit-sha> so both steps are pinned to the same immutable
revision.

---

Nitpick comments:
In @.github/workflows/pro-lint.yml:
- Around line 90-93: The job-level env currently sets
BUNDLE_DISABLE_CHECKSUM_VALIDATION which affects every step; instead remove that
env from the top-level env block and add BUNDLE_DISABLE_CHECKSUM_VALIDATION:
'true' to only the ruby/setup-ruby step definition (the step using uses:
ruby/setup-ruby), leaving REACT_ON_RAILS_PRO_LICENSE at job level unchanged;
ensure the ruby/setup-ruby step's env contains the
BUNDLE_DISABLE_CHECKSUM_VALIDATION entry so only Ruby setup is scoped to bypass
checksum validation.

In @.github/workflows/pro-test-package-and-gem.yml:
- Around line 291-294: The job-level env currently sets
BUNDLE_DISABLE_CHECKSUM_VALIDATION: 'true' which broadens checksum bypass;
remove that key from the job env block and instead add
BUNDLE_DISABLE_CHECKSUM_VALIDATION: 'true' to only the Ruby/bundler setup step
(the step that installs Ruby/gems or runs bundle install) so the exception is
limited to bundler setup; ensure other steps no longer inherit the variable and
confirm the Ruby setup step still uses the same string value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4dc4b01e-1c5e-4d1b-8fe0-584aa1a57bfd

📥 Commits

Reviewing files that changed from the base of the PR and between 5a94d40 and e0b664b.

⛔ Files ignored due to path filters (1)
  • react_on_rails/Gemfile.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • .github/workflows/benchmark.yml
  • .github/workflows/examples.yml
  • .github/workflows/gem-tests.yml
  • .github/workflows/integration-tests.yml
  • .github/workflows/lint-js-and-ruby.yml
  • .github/workflows/precompile-check.yml
  • .github/workflows/pro-integration-tests.yml
  • .github/workflows/pro-lint.yml
  • .github/workflows/pro-test-package-and-gem.yml

Comment thread .github/workflows/benchmark.yml Outdated
Comment thread .github/workflows/integration-tests.yml Outdated
Comment thread .github/workflows/lint-js-and-ruby.yml Outdated
Comment thread .github/workflows/precompile-check.yml Outdated
Comment thread .github/workflows/pro-integration-tests.yml Outdated
Comment thread .github/workflows/pro-lint.yml Outdated
@claude

claude Bot commented May 21, 2026

Copy link
Copy Markdown
Contributor

Code Review: ci: replace manual gem caching with ruby/setup-ruby bundler-cache

Overview

This is a well-motivated cleanup that replaces ad-hoc actions/cache + manual bundle install blocks with ruby/setup-ruby's bundler-cache: true. The net removal of ~145 lines is real simplification, and the PR uncovers and fixes genuine bugs (the lint/integration-tests cache never actually hit because the cached path didn't match the install path).

Positives

  • Root bug fixed: lint-js-and-ruby.yml and integration-tests.yml were caching vendor/bundle at the repo root but installing into react_on_rails/vendor/bundle — the cache was always cold. The new per-working-directory setup correctly aligns cache and install paths.
  • libyaml-dev ordering fix in precompile-check.yml: Moving the apt install libyaml-dev step before ruby/setup-ruby is necessary — with bundler-cache: true, gem installation now happens during the Setup Ruby action, so the native extension dependency must be present before that step runs.
  • bundle exec rails server: Correctly replaces bare rails server in Pro integration tests so the Bundler-managed Rails binary is used.
  • Platform addition to react_on_rails/Gemfile.lock: Eliminates the fragile bundle lock --add-platform 'x86_64-linux' pre-step that was present in multiple jobs, enabling bundle install --frozen on Linux CI.
  • cache-version now includes matrix.dependency-level: Properly isolates latest and minimum gem caches so a warm cache for one dependency level does not bleed into the other.

Issues

1. Missing BUNDLE_DISABLE_CHECKSUM_VALIDATION in all three Pro integration test jobs (medium risk)

The PR correctly adds BUNDLE_DISABLE_CHECKSUM_VALIDATION: 'true' to the job-level env: in pro-lint.yml and in the gem-tests job of pro-test-package-and-gem.yml. However, all three jobs in pro-integration-tests.yml now use bundler-cache: true for react_on_rails_pro/spec/dummy but their env: blocks only contain REACT_ON_RAILS_PRO_LICENSE.

The key difference is that bundler-cache: true calls bundle install --frozen automatically inside the ruby/setup-ruby action — before any subsequent run-step can set bundle config. If the Pro dummy app's Gemfile.lock contains gems sourced from a private or path-based source that lacks bundler checksums, the frozen install will fail with a checksum mismatch error that the original bundle install (non-frozen, run in a shell step) was tolerant of.

The PR description explicitly acknowledges this timing issue: "BUNDLE_DISABLE_CHECKSUM_VALIDATION moves to job env: so it's in scope before bundler-cache: true's automatic bundle install" — but this fix was not applied consistently to all Pro workflows.

See inline comments for the specific locations.

2. Missing cache-version in precompile-check.yml (minor)

Every other bundler-cache: true step in the non-Pro workflows includes a cache-version string (e.g., lint, benchmark, ${{ matrix.dependency-level }}). The precompile-check.yml step omits it entirely. The cache will still function correctly (the Gemfile.lock hash is in the key), but there is no lever to force a cache bust without editing Gemfile.lock.

Suggestion: add cache-version: precompile for consistency and operability.

Summary

The PR is a clean improvement that fixes real bugs and significantly simplifies CI configuration. The main risk before merging is the missing BUNDLE_DISABLE_CHECKSUM_VALIDATION in pro-integration-tests.yml — if any Pro dummy app Gemfile.lock entry lacks a bundler checksum, those three jobs will fail on the first cold cache run. I'd recommend verifying against CI results or adding the env var defensively to all three Pro integration test jobs.

Comment thread .github/workflows/pro-integration-tests.yml Outdated
Comment thread .github/workflows/pro-integration-tests.yml Outdated
Comment thread .github/workflows/pro-integration-tests.yml Outdated
@justin808 justin808 force-pushed the jg/2224-bundler-cache-ci branch from 7eacce3 to 8ff6cd2 Compare May 22, 2026 01:43
@justin808

Copy link
Copy Markdown
Member Author

Review Round 2 — Address & Fix CI

Pushed 8ff6cd24f (plus rebase onto main).

CI failures fixed

  • rspec-package-tests (generators) (both Ruby 3.4 latest + 3.2 minimum): rebased to pick up test: stub stable VERSION in --pro install spec to unblock CI #3363, which stubs a stable ReactOnRails::VERSION in the --pro install spec. The failure was triggered by the current 16.7.0.rc.0 prerelease, not by anything in this PR.
  • dummy-app-integration-tests (3.2 minimum): the dummy_no_turbolinks rake task removes turbolinks from the Gemfile via DISABLE_TURBOLINKS=TRUE, which diverges the lockfile. With bundler-cache: true enabling frozen+deployment mode globally, bundler refused to load. Added BUNDLE_FROZEN=false/BUNDLE_DEPLOYMENT=false overrides to that specific rake task in react_on_rails/rakelib/run_rspec.rake.

Review comments addressed

  • benchmark.yml: moved Fix dependency for libyaml-dev before the bundler-cache Setup Ruby steps.
  • precompile-check.yml: added explicit cache-version: precompile.
  • pro-integration-tests.yml: added BUNDLE_DISABLE_CHECKSUM_VALIDATION: 'true' to the env: block of all three jobs (matches pro-lint.yml / pro-test-package-and-gem.yml).

Deferred

  • coderabbitai's SHA-pinning suggestions (6 files): the repo doesn't currently pin any actions to commit SHAs (actions/checkout@v4, actions/setup-node@v4, pnpm/action-setup@v4, etc. all use tag refs throughout .github/workflows/). Adopting SHA pinning here would be out of scope for a CI caching PR — it's a repo-wide policy change that deserves its own PR.

Watching CI now.

@coderabbitai coderabbitai Bot added the P3 Parked priority label May 22, 2026

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Autofix Details

Bugbot Autofix prepared a fix for the issue found in the latest run.

  • ✅ Fixed: Missing BUNDLE_DISABLE_CHECKSUM_VALIDATION for Pro dummy app gems
    • Added the checksum validation opt-out to the affected Pro dummy app workflow job environments before bundler-cache runs.

Create PR

Or push these changes by commenting:

@cursor push 20a5e74604
Preview (20a5e74604)
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
--- a/.github/workflows/benchmark.yml
+++ b/.github/workflows/benchmark.yml
@@ -144,6 +144,7 @@
     env:
       SECRET_KEY_BASE: 'dummy-secret-key-for-ci-testing-not-used-in-production'
       REACT_ON_RAILS_PRO_LICENSE: ${{ secrets.REACT_ON_RAILS_PRO_LICENSE_V2 }}
+      BUNDLE_DISABLE_CHECKSUM_VALIDATION: 'true'
 
     steps:
       # ============================================

diff --git a/.github/workflows/pro-test-package-and-gem.yml b/.github/workflows/pro-test-package-and-gem.yml
--- a/.github/workflows/pro-test-package-and-gem.yml
+++ b/.github/workflows/pro-test-package-and-gem.yml
@@ -91,6 +91,7 @@
     runs-on: ubuntu-22.04
     env:
       REACT_ON_RAILS_PRO_LICENSE: ${{ secrets.REACT_ON_RAILS_PRO_LICENSE_V2 }}
+      BUNDLE_DISABLE_CHECKSUM_VALIDATION: 'true'
     steps:
       - uses: actions/checkout@v4
         with:

You can send follow-ups to the cloud agent here.

Comment thread .github/workflows/pro-test-package-and-gem.yml Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/benchmark.yml:
- Around line 203-204: The workflow step named "Fix dependency for libyaml-dev"
installs libyaml-dev without refreshing apt metadata; modify that step so it
runs an apt update (e.g., run "sudo apt-get update" or "sudo apt update")
immediately before "sudo apt install libyaml-dev -y" to ensure the package index
is current and prevent intermittent install failures.

In @.github/workflows/integration-tests.yml:
- Line 133: The workflow currently references actions/setup-node@v4 in the two
“Setup Node” steps; update both occurrences to pin the action to a full commit
SHA (e.g., actions/setup-node@<full-commit-sha>) so the CI is reproducible and
supply-chain hardened, ensuring you use the same SHA for both steps and replace
the two instances of actions/setup-node@v4 accordingly.

In @.github/workflows/lint-js-and-ruby.yml:
- Line 114: Replace the mutable reference "uses: actions/setup-node@v4" with an
immutable commit SHA (e.g., "uses: actions/setup-node@<commit-sha>") wherever it
appears; obtain the latest stable commit SHA from the actions/setup-node
repository tags or releases, update the workflow step to use that exact SHA, and
apply the same change to every workflow that currently references
"actions/setup-node@v4" so all usages are pinned to the chosen immutable commit.

In @.github/workflows/precompile-check.yml:
- Around line 85-86: The CI step named "Fix dependency for libyaml-dev" runs a
blocking interactive apt command; change the step to perform a non-interactive
install (e.g., use apt-get install -y and/or set DEBIAN_FRONTEND=noninteractive)
so the workflow cannot hang on prompts—update that run command accordingly to
use a non-interactive install invocation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2b7c395e-a14a-47c9-a2a0-6edcf78c2b72

📥 Commits

Reviewing files that changed from the base of the PR and between 7eacce3 and 8ff6cd2.

⛔ Files ignored due to path filters (1)
  • react_on_rails/Gemfile.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • .github/workflows/benchmark.yml
  • .github/workflows/examples.yml
  • .github/workflows/gem-tests.yml
  • .github/workflows/integration-tests.yml
  • .github/workflows/lint-js-and-ruby.yml
  • .github/workflows/precompile-check.yml
  • .github/workflows/pro-integration-tests.yml
  • .github/workflows/pro-lint.yml
  • .github/workflows/pro-test-package-and-gem.yml
  • react_on_rails/rakelib/run_rspec.rake

Comment thread .github/workflows/benchmark.yml Outdated
Comment thread .github/workflows/integration-tests.yml
Comment thread .github/workflows/lint-js-and-ruby.yml
Comment thread .github/workflows/precompile-check.yml Outdated
@coderabbitai coderabbitai Bot added the P2 Backlog priority label May 22, 2026
@claude

claude Bot commented May 22, 2026

Copy link
Copy Markdown
Contributor

Code Review: CI Ruby Caching Migration (#3354)

This is a well-motivated refactoring that fixes real bugs (cache path mismatches in lint-js-and-ruby.yml and integration-tests.yml) while significantly simplifying CI configuration. The approach of delegating to ruby/setup-ruby's built-in bundler-cache: true is the right pattern. A few issues worth addressing before merging:


Bugs / Issues

1. All Pro workflows are missing cache-version (Medium)

Every open-source workflow that uses bundler-cache: true specifies a cache-version (${{ matrix.dependency-level }}, lint, precompile, benchmark). All three Pro workflows — pro-integration-tests.yml, pro-lint.yml, pro-test-package-and-gem.yml — omit it entirely. This means if the Pro gem cache ever needs to be busted (e.g., a transitive dependency change not reflected in Gemfile.lock, or a corrupted cache), there's no mechanism to do so short of modifying Gemfile.lock or bumping the action version. At minimum, add a static cache-version: pro-v1 to all Pro ruby/setup-ruby calls to allow future cache-busting.

2. pro-lint.yml is missing libyaml-dev installation before bundler-cache (Low-Medium)

integration-tests.yml and precompile-check.yml both correctly install libyaml-dev before their bundler-cache: true step because psych v5 native extensions need it at compile time. The Pro lint workflow adds bundler-cache: true without this precaution. If the Pro gem or any transitive dependency pulls in psych v5 native extensions during cache population, the first (cold cache) CI run will fail. Subsequent runs would succeed from cache, creating a confusing intermittent failure pattern.

3. run-integration job installs only dummy app gems but runs bundle exec rake in react_on_rails/ (Low, documentation gap)

The job calls ruby/setup-ruby with working-directory: react_on_rails/spec/dummy, then later runs cd react_on_rails && bundle exec rake run_rspec:all_dummy. This works because the dummy's Gemfile uses eval_gemfile to pull in all package development dependencies, making its bundle a superset of react_on_rails/Gemfile. With ruby/setup-ruby's globally-set BUNDLE_PATH, the bundle exec in react_on_rails/ finds all required gems in the shared bundle path. This is correct but the implicit dependency is fragile — worth a brief comment on the Setup Ruby and install dummy app gems step.


Improvements

4. BUNDLE_FROZEN=false rake workaround is correct but symptom-treats a design issue

The comment is good. The root cause is that Gemfile.development_dependencies gates turbolinks on ENV["DISABLE_TURBOLINKS"], which makes the Gemfile's effective requirements differ from Gemfile.lock in frozen mode. The fix is pragmatic and acceptable for now, but a follow-up to eliminate the conditional gem call (e.g., always declare turbolinks in the lockfile but skip loading it via Bundler groups) would be cleaner.

5. Static cache-version: benchmark has no busting mechanism

Minor: the benchmark workflow uses a hardcoded "benchmark" string. Since the benchmark job doesn't have a matrix, there's no dependency-level to incorporate. Consider documenting in a comment that bumping this string is the cache-busting mechanism (e.g., cache-version: benchmark-v1).


What's done well

  • Correctly identifies and fixes the pre-existing cache path bug (root vendor/bundle vs. react_on_rails/vendor/bundle)
  • BUNDLE_DISABLE_CHECKSUM_VALIDATION correctly moved to job env: so it applies before bundler-cache's automatic bundle install
  • rails serverbundle exec rails server is a good hygiene fix
  • libyaml-dev correctly repositioned before bundler-cache steps in benchmark.yml, integration-tests.yml, and precompile-check.yml
  • Adding x86_64-linux to react_on_rails/Gemfile.lock removes the runtime bundle lock --add-platform anti-pattern
  • cache-version correctly incorporates matrix.dependency-level for all matrix jobs, ensuring proper cache separation between latest and minimum dependency levels

@justin808

Copy link
Copy Markdown
Member Author

Review address summary (latest batch)

Triaged 4 new coderabbit threads since the last reply batch.

Fixed (1)

  • precompile-check.yml: added -y to sudo apt install libyaml-dev for non-interactive CI install — 913e962 — matches the benchmark.yml pattern.

Deferred (3)

  • benchmark.yml apt-get update before install — pre-existing pattern on main, not introduced by this PR. Better as a separate workflow-hardening pass.
  • actions/setup-node@v4 SHA pinning in integration-tests.yml and lint-js-and-ruby.yml — same reasoning as the prior ruby/setup-ruby SHA-pinning threads: the repo doesn't currently pin any GitHub Actions to commit SHAs, so a repo-wide pass is the right home.

All addressed threads have been marked resolved.

Comment thread .github/workflows/pro-lint.yml Outdated
Comment thread .github/workflows/pro-integration-tests.yml Outdated
Comment thread .github/workflows/pro-integration-tests.yml Outdated
Comment thread .github/workflows/pro-integration-tests.yml Outdated
Comment thread .github/workflows/pro-lint.yml Outdated
Comment thread .github/workflows/pro-lint.yml Outdated
Comment thread .github/workflows/pro-test-package-and-gem.yml Outdated
justin808 and others added 14 commits May 27, 2026 22:10
Address review feedback on the bundler-cache CI migration (#3354).

- benchmark.yml: When both RUN_CORE and RUN_PRO are true, the Pro
  setup-ruby step's bundler-cache wrote BUNDLE_GEMFILE to $GITHUB_ENV,
  leaking the Pro dummy Gemfile into subsequent Core bundle-exec calls
  (bin/prod-assets, bin/prod, benchmarks/bench.rb). Pin BUNDLE_GEMFILE
  to the Core dummy Gemfile via absolute path on the affected Core
  steps. Fixes a real correctness bug flagged by cursor[bot].
- pro-lint.yml: Switch BUNDLE_GEMFILE overrides on Lint Ruby and
  Validate RBS type signatures from relative `Gemfile` to absolute
  `${{ github.workspace }}/react_on_rails_pro/Gemfile` so they're robust
  to changes in defaults.run.working-directory; document why.
- pro-test-package-and-gem.yml: Embed matrix.ruby-version in
  cache-version (belt-and-suspenders for future matrix expansion).
- Gemfile.development_dependencies: Warn future readers not to
  reintroduce the `if ENV["DISABLE_TURBOLINKS"]` conditional, which
  would re-break `bundle install --frozen`.
- benchmark.yml: Minor — document the START_POINT_HASH_ARG empty-string
  edge case for the bencher retry substitution; revert printf format
  string from double to single quotes to drop backtick escapes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After the second ruby/setup-ruby call in lint-js-and-ruby.yml (build job)
and integration-tests.yml (dummy-app-integration-tests job), $GITHUB_ENV's
BUNDLE_GEMFILE points at the dummy app Gemfile. The dummy Gemfile shares
dev dependencies via eval_gemfile, so today these steps still resolve
rubocop/rbs/rspec — but pin the package Gemfile explicitly for parity
with pro-lint.yml and benchmark.yml, and to stay robust if the Gemfiles
ever diverge.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Apply the remaining optional polish items from PR #3354 review.

- Rename "Fix dependency for libyaml-dev" to
  "Install libyaml-dev (required for psych v5 native extensions)" across
  all 13 occurrences (8 workflows). The previous name read like a fix
  for a broken dependency rather than a prerequisite install.
- Align pro-integration-tests.yml job-level checksum comment with the
  more accurate wording already used in benchmark.yml and
  pro-test-package-and-gem.yml ("Pro gem server does not publish
  checksums…"), so contributors understand why job-level scope is
  required.
- Add an inline comment in lint-js-and-ruby.yml above the
  `generate file system-based packs` step explaining the deliberate
  absence of a BUNDLE_GEMFILE pin (the dummy app Gemfile leaked into
  $GITHUB_ENV by the previous setup-ruby step is intentionally reused).
- Switch the `gh issue list --jq` filter at benchmark.yml:787 from
  double to single quotes to match the other jq filters in this file.
- Document in AGENTS.md "Never" that conditional gem declarations like
  `gem "turbolinks" if ENV["DISABLE_TURBOLINKS"].nil?` must not be
  reintroduced in react_on_rails/Gemfile.development_dependencies; the
  conditional inclusion breaks `bundle install --frozen` in CI.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@justin808 justin808 force-pushed the jg/2224-bundler-cache-ci branch from 32a1141 to bbf71aa Compare May 28, 2026 08:26
@justin808

Copy link
Copy Markdown
Member Author

Address-review checkpoint at 2026-05-28T08:28:50Z.

Scan window: feedback after the prior checkpoint at 2026-05-28T05:04:26Z.

Mattered

  • No new must-fix or discuss-only items were found.

Optional

  • Addressed the duplicate Bencher retry indentation comments in .github/workflows/benchmark.yml; the retry branch now visually nests the inner if blocks and retry command inside the outer guard.
  • Addressed the setup-bundle Linux prerequisite note by documenting that Linux callers should ensure libyaml-dev is present before using the action. Runtime behavior is unchanged because current GitHub-hosted runners already satisfy this; self-hosted runner setups should install libyaml-dev first or call the companion setup-ruby action before setup-bundle.

Skipped

  • Skipped the Bencher report-size PR comment as status-only; it points to the job summary and does not request a code change.

The branch was rebased onto origin/main and pushed at bbf71aa3cc429dae277783105a599218f494b126. GitHub checks for that push are queued/in progress, and no failing checks were detected at this checkpoint.

Future full-PR /address-review scans should start after this comment unless check all reviews is requested.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 4 total unresolved issues (including 3 from previous reviews).

Fix All in Cursor

Bugbot Autofix is ON. A cloud agent has been kicked off to fix the reported issue.

Reviewed by Cursor Bugbot for commit bbf71aa. Configure here.

echo "🚀 Starting Pro production server..."
cd react_on_rails_pro/spec/dummy

$SERVER_CMD &

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pro benchmark Prepare/Start steps missing BUNDLE_GEMFILE pinning

Medium Severity

The Core benchmark steps all pin BUNDLE_GEMFILE for "Prepare Core production assets", "Start Core production server", and "Execute Core benchmark suite", with a comment explaining that bin/prod-assets, bin/prod, and bench.rb shell out through bundle exec. The Pro equivalents only pin BUNDLE_GEMFILE for "Execute Pro benchmark suite" but omit it for "Prepare Pro production assets" and "Start Pro production server", even though those steps also invoke bin/prod-assets and bin/prod which shell out via bundle exec. Since setup-bundle now internally calls ruby/setup-ruby@v1 (which may persist BUNDLE_GEMFILE across steps), the Pro Prepare and Start steps could resolve against the wrong bundle if the environment retains a stale value from the preceding Core setup-bundle invocation.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit bbf71aa. Configure here.

@claude

claude Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

test

@claude

claude Bot commented May 28, 2026

Copy link
Copy Markdown
Contributor

Code Review: CI Ruby Setup Consolidation

Overview

This PR replaces manual actions/cache restore/save logic in setup-bundle with ruby/setup-ruby@v1 built-in bundler-cache: true, adds a lightweight setup-ruby action for jobs needing only the Ruby runtime, and applies both consistently across all workflows. The turbolinks unconditional fix correctly resolves the root cause of frozen-install failures.


Strengths

  • Correct root-cause fix: Making gem "turbolinks" unconditional removes the Gemfile dependency-set variance that broke bundle install --frozen. Removing BUNDLE_FROZEN=false from the dummy_no_turbolinks rake task follows naturally — DISABLE_TURBOLINKS now only affects application behaviour, not gem resolution.
  • Shellcheck hygiene: Quoting $GITHUB_ENV, $GITHUB_PATH, and variable-interpolated URLs throughout is a solid defensive improvement.
  • Grouped output redirects: The brace-grouped echo/GITHUB_OUTPUT refactor eliminates duplicated redirects cleanly.
  • Bencher retry refactor: Replacing the fragile sed regex with a guarded bash parameter expansion is more robust when the hash is empty.
  • BUNDLE_GEMFILE scoping: Explicitly pinning BUNDLE_GEMFILE for every step that shells out to bundle exec via scripts (bench.rb, bin/prod, bin/prod-assets) prevents accidental resolution against the wrong lockfile.
  • AGENTS.md guard: Documenting the turbolinks conditional rule makes the constraint discoverable before a contributor re-introduces it.

Issues / Concerns

1. Behavioral change: non-frozen runs now save the gem cache

Old setup-bundle had an explicit guard that skipped cache saving when frozen == 'false' and on cache hits. New setup-bundle delegates entirely to ruby/setup-ruby with bundler-cache: true, which always saves the cache. The minimum-dependency legs (frozen: 'false') run bundle install against a converted Gemfile and may produce a different resolved lockfile. Since cache keys are Gemfile.lock-hash-based, cross-contamination of frozen-mode jobs is unlikely in practice — but the cache grows with extra entries on every minimum-dependency run, and the old explicit guard made contamination structurally impossible.

Suggestion: Suppress the cache write when frozen: 'false' (e.g., conditional bundler-cache value, or a restore-only step for non-frozen legs) to preserve the original safety property.

2. libyaml-dev is a documentation-only contract in setup-bundle

The setup-bundle description warns callers to install libyaml-dev beforehand, but this is not enforced. Any future workflow calling setup-bundle directly — as the Playwright workflow does — silently skips the prerequisite. The Playwright workflow did not need libyaml-dev before and presumably does not now, so there is no current regression, but the pattern is a footgun for new workflow authors.

Suggestion: Add an install-libyaml input to setup-bundle (default 'true') that installs libyaml-dev when runner.os == 'Linux', mirroring setup-ruby. Workflows that already call setup-ruby first can pass install-libyaml: 'false' to avoid the double install.

3. Double ruby/setup-ruby invocation in most jobs

Jobs that call setup-ruby (Ruby + libyaml-dev, no gems) then setup-bundle (which calls ruby/setup-ruby again with bundler-cache: true) run the action twice. The second call skips the binary download because Ruby is already in PATH, but re-runs bootstrap logic. Minor cost, reasonable trade-off for keeping the two actions independent.

4. BUNDLE_FROZEN env interaction with ruby/setup-ruby

setup-bundle passes BUNDLE_FROZEN as an env var to the ruby/setup-ruby step. Bundler respects this as a per-process config override today. However, ruby/setup-ruby also writes Bundler config via bundle config. If a future upstream version calls bundle config set frozen ... it would silently override the env var. The approach works now but is fragile across upstream upgrades.


Minor Points

  • Lint workflow Ruby version: Pinning '3.4' (was 3) is more explicit and stable for CI, but will need a manual bump when Ruby 3.5 ships.
  • Playwright Ruby removal: Correct — setup-bundle now handles Ruby setup internally via ruby/setup-ruby. No regression.
  • cache-version input default: A short comment like "bump to bust cache" would make the input purpose clearer to future editors.

Summary

The consolidation is correct and the root-cause fix is sound. The main concern worth addressing before merge is the non-frozen cache write change (issue 1) — the old behaviour was an explicit safety guard and losing it silently widens the risk surface for minimum-dependency legs. Everything else is minor or an improvement.

description: Cache and install Ruby gems for a Bundler project.
description: >
Setup Ruby, cache, and install gems for a Bundler project. On Linux,
ensure libyaml-dev is already present or install it before calling this action.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The libyaml-dev prerequisite is stated in the description but is not enforced — setup-bundle itself never installs it. Callers using setup-ruby first get it implicitly; callers that use setup-bundle directly (e.g. the Playwright workflow) do not.

Consider adding an install-libyaml input (default 'true') that mirrors the one in setup-ruby, so any direct caller of setup-bundle is protected automatically:

Suggested change
ensure libyaml-dev is already present or install it before calling this action.
Setup Ruby, cache, and install gems for a Bundler project. On Linux,
libyaml-dev is installed automatically (set install-libyaml: 'false' to skip
if the caller already ran setup-ruby).

ruby-version: ${{ inputs.ruby-version }}
working-directory: ${{ inputs.working-directory }}
bundler: ${{ inputs.bundler-version }}
bundler-cache: true

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Behavioral change from old implementation: the previous setup-bundle explicitly skipped the cache-save step when frozen == 'false':

if: inputs.frozen == 'true' && steps.restore-ruby-gems-cache.outputs.cache-hit != 'true'

ruby/setup-ruby with bundler-cache: true always writes the cache regardless of BUNDLE_FROZEN. The minimum-dependency legs that set frozen: 'false' will now write gem caches after every non-frozen bundle install. The cache keys are Gemfile.lock-hash-based so cross-contamination is unlikely, but the old guard made it structurally impossible.

Consider making bundler-cache conditional on the frozen input:

Suggested change
bundler-cache: true
bundler-cache: ${{ inputs.frozen }}

(ruby/setup-ruby accepts 'true'/'false' strings for bundler-cache, so this maps directly.)

with:
ruby-version: ${{ inputs.ruby-version }}
bundler: ${{ inputs.bundler-version }}
bundler-cache: false

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Explicit bundler-cache: false here is correct and important — this action is intentionally for Ruby-only setup (used before setup-bundle). Without this explicit false, ruby/setup-ruby defaults to no caching, but being explicit prevents confusion if the default ever changes upstream.

with:
ruby-version: 3
bundler: 2.5.9
ruby-version: '3.4'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pinning to '3.4' (was 3, which resolved to the latest Ruby 3.x patch) is more stable for CI but will silently stay on 3.4 after Ruby 3.5 ships. Worth tracking this alongside the next Ruby minor upgrade.

@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Bencher report chunks were too large to post as PR comments.

View the full report in the job summary.

@justin808

Copy link
Copy Markdown
Member Author

Closing this as superseded and splitting the still-relevant work into two focused PRs.

While rebasing onto main, I (Claude Code) found that most of this PR has already landed independently:

  • Extract reusable setup-bundle action #3429 "Extract reusable setup-bundle action" added the shared .github/actions/setup-bundle action and rolled it out across every workflow this PR targeted (examples, gem-tests, integration-tests, lint, playwright, precompile-check, pro-integration-tests, pro-test-package-and-gem). The "apply the reusable setup pattern across all workflows" goal is already done on main.
  • Run benchmark suites independently #3448 "Run benchmark suites independently" rewrote benchmark.yml from this PR's single env-gated benchmark job into three independent jobs (benchmark-core, benchmark-pro, benchmark-pro-node-renderer) plus a setup-benchmark-runner action — so this branch's 14 benchmark.yml commits no longer apply mechanically.

What main does not yet have, split out as clean PRs off current main:

  1. ci: declare turbolinks unconditionally to stabilize frozen installs #3482 — declare turbolinks unconditionally so the gemset matches Gemfile.lock, removing the root cause of the frozen-install breakage (replaces main's BUNDLE_FROZEN=false workaround) and documenting the footgun in AGENTS.md.
  2. ci: cache gems via ruby/setup-ruby bundler-cache in setup-bundle #3483 — the caching-mechanism change this PR was really about: make setup-bundle use ruby/setup-ruby@v1 with bundler-cache: true instead of the manual actions/cache logic Extract reusable setup-bundle action #3429 extracted, plus a companion setup-ruby action. Offered as a follow-on improvement for a maintainer call.

The original branch jg/2224-bundler-cache-ci is left intact for reference.

@justin808 justin808 closed this May 29, 2026
justin808 added a commit that referenced this pull request May 29, 2026
…3482)

## Summary

Declares the `turbolinks` development dependency **unconditionally** in
`react_on_rails/Gemfile.development_dependencies`, replacing the `gem
"turbolinks" if ENV["DISABLE_TURBOLINKS"]...` conditional.

The conditional resolved to a dependency set that differs from
`Gemfile.lock` whenever `DISABLE_TURBOLINKS` is set — the lock file is
generated without the env var, but `bundle install` runs with it — which
breaks `bundle install --frozen` in CI. `main` currently works around
this by forcing `BUNDLE_FROZEN=false` in the `dummy_no_turbolinks` rake
task. This PR removes the root cause so the workaround is no longer
needed.

## Why this is safe

`DISABLE_TURBOLINKS` already controls Turbolinks at the **application
layer**, not via gem inclusion:

```erb
<%# react_on_rails/spec/dummy/app/assets/javascripts/application_non_webpack.js.erb %>
<% if ENV["DISABLE_TURBOLINKS"].blank? %>
  <% require_asset "turbolinks" %>
<% end %>
```

So with the gem always present, `DISABLE_TURBOLINKS=TRUE` still disables
Turbolinks (the asset is not required). The `rake
run_rspec:dummy_no_turbolinks` coverage is unchanged — it still
exercises the no-Turbolinks code path.

`Gemfile.lock` is unaffected: it already pins `turbolinks (5.2.1)`
because lock resolution runs without `DISABLE_TURBOLINKS`.

## Changes

- `react_on_rails/Gemfile.development_dependencies` — declare `gem
"turbolinks"` unconditionally (+ explanatory comment).
- `react_on_rails/rakelib/run_rspec.rake` — drop the now-unnecessary
`BUNDLE_FROZEN=false` workaround from `dummy_no_turbolinks`.
- `AGENTS.md` — document the footgun so the conditional declaration is
not reintroduced.

## Context

Split out of #3354 as a focused, standalone fix. The broader
Bundler-cache consolidation from that branch was superseded by #3429
(extract `setup-bundle`) and #3448 (independent benchmark suites)
already on `main`; this is the one root-cause reliability fix from #3354
that `main` does not yet have.

## Test plan

- [x] `bundle exec rubocop rakelib/run_rspec.rake
Gemfile.development_dependencies` — no offenses
- [x] `npx prettier --check AGENTS.md` — passes
- [x] Confirmed `Gemfile.lock` already contains `turbolinks (5.2.1)`; no
lock change
- [x] Verified the app-layer `require_asset` guard handles
`DISABLE_TURBOLINKS` independently of gem presence
- [ ] CI `rake run_rspec:dummy_no_turbolinks` (and frozen installs) pass
on the branch

Related to #2224

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Dev/test dependency and CI wiring only; no production gem behavior
change, and no-Turbolinks coverage stays via the existing asset guard.
> 
> **Overview**
> Fixes **CI `bundle install --frozen` failures** when
`DISABLE_TURBOLINKS` is set by always declaring **`turbolinks`** in
`react_on_rails/Gemfile.development_dependencies`, instead of omitting
it based on that env var.
> 
> **`DISABLE_TURBOLINKS`** still turns Turbolinks off at runtime via the
dummy app’s **`require_asset`** guard; only gem resolution changes. The
**`dummy_no_turbolinks`** rake task no longer sets
**`BUNDLE_FROZEN=false`**.
> 
> **AGENTS.md** and **CONTRIBUTING.md** document the rule so conditional
turbolinks declarations are not reintroduced.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
b8a214e. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

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

* **Documentation**
* Clarified that certain dev-only assets remain bundled while runtime
toggles disable them, to keep dependency sets consistent with the
lockfile.
* **Chores**
* Made development/test workflows more reliable by aligning gem
declarations with the lockfile and adjusting test-run behavior to avoid
frozen-bundle failures in CI.

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/shakacode/react_on_rails/pull/3482?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
justin808 added a commit that referenced this pull request May 30, 2026
## Summary

Reworks the shared `setup-bundle` composite action to delegate Ruby
setup **and** gem caching to `ruby/setup-ruby@v1` with `bundler-cache:
true`, replacing the manual `actions/cache` restore/save logic plus the
`bundle check || bundle install` step that #3429 extracted. Adds a
companion `setup-ruby` action and rolls both out across the OSS, Pro,
integration, examples, precompile, Playwright, and lint workflows.

This is the caching-mechanism half of the original #3354 work,
re-derived cleanly on top of current `main`. (The other salvageable
piece — the unconditional-turbolinks frozen-install fix — is split into
#3482. The rest of #3354 was superseded by #3429 and #3448 and is
dropped.)

## What changes

- **`.github/actions/setup-bundle`** — instead of
`actions/cache/restore` → `bundle check || bundle install` →
`actions/cache/save`, it now calls `ruby/setup-ruby@v1` with
`bundler-cache: true`, which installs Ruby, restores/saves the gem
cache, and runs `bundle install` in one step. Inputs simplified
accordingly (`bundle-path`/`install-args` dropped; `cache-version`
passthrough added; `bundler-version` uses ruby/setup-ruby's selector
values — `Gemfile.lock`/`default`/`latest`/`none`/`x.y.z`).
- **`.github/actions/setup-ruby`** (new) — Ruby runtime + Linux
`libyaml-dev`, no gems. Used by jobs that need Ruby before their first
bundle install (e.g. `script/convert`,
`script/check-gemfile-lock-platforms`).
- **Workflows** — `examples`, `gem-tests`, `integration-tests`,
`playwright`, `precompile-check`, `pro-integration-tests`,
`pro-test-package-and-gem`, and `lint-js-and-ruby` migrated to the
`setup-ruby` + `setup-bundle` pattern.

## Why

`ruby/setup-ruby`'s built-in `bundler-cache` is the upstream-recommended
caching path and removes the bespoke cache key / restore-keys /
save-condition logic the action currently maintains by hand. Less custom
CI code to keep correct.

## Tradeoff / discussion

This **reverses the caching mechanism** that #3429 extracted (manual
`actions/cache`). #3429 was a refactor of pre-existing logic, not
necessarily a deliberate rejection of `bundler-cache: true`, so this is
offered as the follow-on improvement #3354 originally proposed — but
it's worth a maintainer call.

`benchmark.yml` is intentionally **not** restructured: its
`setup-benchmark-runner` (added by #3448) still installs Ruby for
`foreman`, and the subsequent `setup-bundle` call now handles each dummy
app's gems via bundler-cache. That means `ruby/setup-ruby` runs twice in
benchmark jobs (once for the runner tools, once for the dummy bundle) —
functional and matches the approved #3354 pattern, but flagging it in
case reviewers prefer a deeper benchmark refactor.

## Test plan

- [x] `actionlint` (with `SHELLCHECK_OPTS='-S warning'`) — clean
- [x] Ruby `YAML.load_file` parses all changed action/workflow files
- [x] Verified every `setup-bundle` call site passes only supported
inputs (`working-directory`/`ruby-version`/`frozen`); no
`bundle-path`/`install-args`/`lockfile` anywhere
- [x] Verified every `setup-ruby` call site passes only supported inputs
(`ruby-version`/`bundler-version`)
- [x] Trailing-newline check on all changed files
- [ ] Full GitHub Actions run is green (the double `ruby/setup-ruby`
invocation in benchmark + bundler-cache behavior should be confirmed in
CI)

Related to #2224

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Touches how every major workflow installs and caches gems; behavior
shifts from custom cache keys to upstream bundler-cache, though
frozen/minimum-dependency semantics are preserved intentionally.
> 
> **Overview**
> **Centralizes Ruby/Bundler setup in two composite actions** and rolls
them through OSS, Pro, integration, examples, lint, precompile, and
Playwright workflows.
> 
> `setup-bundle` no longer uses hand-rolled `actions/cache` restore/save
plus `bundle check || bundle install`. It now calls `ruby/setup-ruby@v1`
with **`bundler-cache: true`**, passing `BUNDLE_FROZEN`, jobs/retry env
vars, and optional **`cache-version`**. Inputs **`bundle-path`** and
**`install-args`** are removed; **`bundler-version`** aligns with
upstream selectors (default **`Gemfile.lock`**).
> 
> A new **`setup-ruby`** action installs Linux **`libyaml-dev`** when
needed and runs `ruby/setup-ruby` with **`bundler-cache: false`** for
jobs that need Ruby/Bundler before the first Gemfile-scoped install
(e.g. **`script/convert`**, platform checks). Workflows switch from
inline `ruby/setup-ruby` to **`setup-ruby`** then **`setup-bundle`**,
with **`bundler-version`** instead of **`bundler`**.
> 
> Minor workflow hardening: quoted **`$GITHUB_OUTPUT`** /
**`$GITHUB_ENV`**, grouped detect-changes output writes, explicit
**`BUNDLE_GEMFILE`** for package lint/RBS steps, and comments
documenting **`frozen: false`** on minimum-dependency matrix legs.
Per-job **`libyaml-dev`** apt steps are dropped where **`setup-ruby`**
covers them.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
037489a. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

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

* **Chores**
* Adopted a unified, repo-local Ruby setup for consistent Ruby/bundler
across CI.
* Simplified bundler inputs and caching behaviour, adding a
cache-version toggle and clarifying frozen/bundler defaults.
* Improved CI output and environment variable writing for more reliable
workflow execution.
* Added optional libyaml installation and standardized Ruby versions
(including 3.4 in some jobs).

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/shakacode/react_on_rails/pull/3483?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

benchmark codex-wip Codex is actively working this PR enhancement full-ci P2 Backlog priority P3 Parked priority review-needed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve bundler caching in CI

2 participants