Skip to content

[build] Reconcile Gemfile.lock in rb:pin with bundle lock, not bundle update#17772

Merged
titusfortner merged 1 commit into
trunkfrom
c/bundle-update-versions-dcf812
Jul 13, 2026
Merged

[build] Reconcile Gemfile.lock in rb:pin with bundle lock, not bundle update#17772
titusfortner merged 1 commit into
trunkfrom
c/bundle-update-versions-dcf812

Conversation

@titusfortner

Copy link
Copy Markdown
Member

🔗 Related Issues

Supersedes #17768

💥 What does this PR do?

Makes rb:pin regenerate rb/Gemfile.lock before syncing checksums, so it is self-sufficient like every other language's :pin task.

rb:pin previously only copied gem checksums out of the existing lockfile; it never re-ran bundler. After a release version bump, Gemfile.lock's self-referencing selenium-webdriver path-gem version drifted from the gemspec, and bundler's frozen mode then rejected the mismatch, breaking bazel run //rb:rubocop on trunk.

It reconciles with bundle lock (not bundle update), so :pin stays deterministic — it only updates what actually changed and does not opportunistically upgrade other gems. This preserves the :pin (reconcile) vs :update (intentional upgrade) split, which matters because :pin runs automatically in the release reset and in renovate.

🔧 Implementation Notes

The bundle subcommand is now a caller-supplied argument, so one rb/support/bundle.rb backs both //rb:bundle-lock and //rb:bundle-update instead of duplicating the bundle-env setup across two scripts.

🤖 AI assistance

  • No substantial AI assistance used
  • AI assisted (complete below)
    • Tool(s): Claude Code
    • What was generated: The fix and the pin/update analysis.
    • I reviewed all AI output and can explain the change

💡 Additional Considerations

Supersedes #17768, which fixed the same drift but reconciled with bundle update inside :pin — that turns every automated repin into a full re-resolution. Close that PR in favor of this one.

🔄 Types of changes

  • Bug fix (backwards compatible)

@selenium-ci selenium-ci added C-rb Ruby Bindings B-build Includes scripting, bazel and CI integrations B-support Issue or PR related to support classes labels Jul 13, 2026
@selenium-ci

Copy link
Copy Markdown
Member

Thank you, @titusfortner for this code suggestion.

The support packages contain example code that many users find helpful, but they do not necessarily represent
the best practices for using Selenium, and the Selenium team is not currently merging changes to them.

After reviewing the change, unless it is a critical fix or a feature that is needed for Selenium
to work, we will likely close the PR.

We actively encourage people to add the wrapper and helper code that makes sense for them to their own frameworks.
If you have any questions, please contact us

@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Make rb:pin regenerate Gemfile.lock via bundle lock before syncing checksums

🐞 Bug fix ⚙️ Configuration changes 🕐 10-20 Minutes

Grey Divider

AI Description

• Run bundle lock during rb:pin to reconcile rb/Gemfile.lock deterministically.
• Add a Bazel //rb:bundle-lock binary and reuse a single bundler wrapper script.
• Keep :pin (reconcile) separate from :update (intentional upgrades) to avoid churn.
Diagram

graph TD
  A["rake rb:pin"] --> B["//rb:bundle-lock"] --> C["rb/support/bundle.rb"] --> D["bundle lock"] --> E("rb/Gemfile.lock") --> P["sync gem checksums"] --> F("MODULE.bazel")
  U["//rb:bundle-update"] --> C --> DU["bundle update"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use `bundle update` inside `rb:pin`
  • ➕ Would also fix path-gem version drift by fully re-resolving dependencies
  • ➖ Non-deterministic: opportunistically upgrades unrelated gems during automated repins
  • ➖ Conflates reconcile (:pin) with upgrade (:update), increasing churn and review noise
2. Keep separate scripts (bundle_lock.rb + bundle_update.rb) instead of argv-driven wrapper
  • ➕ Slightly simpler entrypoints per command
  • ➕ Less reliance on argv forwarding correctness
  • ➖ Duplicates bundler environment setup and workspace root detection
  • ➖ Higher maintenance when bundler env logic changes
3. Patch Gemfile.lock directly to fix path-gem version drift
  • ➕ Fast and avoids invoking bundler
  • ➖ Brittle against lockfile format changes
  • ➖ Easy to miss other reconcile needs bundler would handle safely

Recommendation: Current approach (run bundle lock during rb:pin, and reuse a single wrapper script with caller-supplied subcommand) best preserves determinism and the :pin vs :update contract, while minimizing duplicated Bazel/Ruby glue.

Files changed (4) +25 / -6

Bug fix (1) +3 / -1
ruby.rakeRun //rb:bundle-lock before syncing gem checksums in rb:pin +3/-1

Run //rb:bundle-lock before syncing gem checksums in rb:pin

• Updates the 'rb:pin' task to first execute '//rb:bundle-lock', ensuring 'rb/Gemfile.lock' is regenerated/reconciled before extracting gem versions and syncing checksums into 'MODULE.bazel'. Also adjusts the task description to reflect the reconcile behavior.

rake_tasks/ruby.rake

Refactor (1) +3 / -1
bundle.rbGeneralize bundler wrapper to accept subcommand via argv +3/-1

Generalize bundler wrapper to accept subcommand via argv

• Changes the wrapper to execute 'bundle *ARGV' instead of hardcoding 'bundle update', allowing callers to supply 'lock' or 'update' (and flags) while sharing the same bundler environment setup.

rb/support/bundle.rb

Other (2) +19 / -4
BUILD.bazelAdd //rb:bundle-lock target and route bundle-update through shared wrapper +17/-2

Add //rb:bundle-lock target and route bundle-update through shared wrapper

• Introduces a new 'rb_binary(name = "bundle-lock")' that runs 'rb/support/bundle.rb lock'. Updates the existing 'bundle-update' target to use the same wrapper with the 'update' subcommand and depends on '//rb/support:bundle'.

rb/BUILD.bazel

BUILD.bazelRename bundle wrapper library target to //rb/support:bundle +2/-2

Rename bundle wrapper library target to //rb/support:bundle

• Replaces the previous bundle-update-specific library target with a generalized 'bundle' library that packages 'bundle.rb' for reuse by both lock and update Bazel binaries.

rb/support/BUILD.bazel

@qodo-code-review

qodo-code-review Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (1) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 19 rules

Grey Divider


Action required

1. No tests for rb:pin change 📘 Rule violation ▣ Testability
Description
The PR changes the rb:pin task to run //rb:bundle-lock and introduces a new bundle.rb wrapper
behavior, but no corresponding automated tests were added/updated to cover this bug fix. This
increases regression risk for release reset/renovate repinning flows.
Code

rake_tasks/ruby.rake[R190-193]

+desc 'Reconcile Gemfile.lock and sync gem checksums to MODULE.bazel (use force to re-download all)'
task :pin, [:force] do |_task, arguments|
+  Bazel.execute('run', [], '//rb:bundle-lock')
+
Evidence
PR Compliance ID 389273 requires tests for new functionality/bug fixes. The diff shows rb:pin now
executes //rb:bundle-lock, adds a new Bazel bundle-lock binary, and changes the bundler wrapper
to run bundle *ARGV, but there are no accompanying test changes in this PR to validate these
behaviors.

Rule 389273: Require tests for all new functionality and bug fixes
rake_tasks/ruby.rake[190-193]
rb/BUILD.bazel[180-206]
rb/support/bundle.rb[38-41]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR introduces new behavior in `rb:pin` (running `//rb:bundle-lock`) and changes the bundler wrapper to accept caller-supplied subcommands, but no tests were added/updated to assert the behavior.

## Issue Context
This is a bug fix to prevent lockfile drift from breaking frozen bundler mode; without tests, future changes may reintroduce drift or accidentally switch `rb:pin` back to `bundle update` semantics.

## Fix Focus Areas
- rake_tasks/ruby.rake[190-193]
- rb/BUILD.bazel[180-206]
- rb/support/bundle.rb[38-41]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Bundler runs twice in update 🐞 Bug ➹ Performance ⭐ New
Description
rb:update runs //rb:bundle-update and then invokes rb:pin, which now always runs
//rb:bundle-lock, causing an extra Bundler invocation on every update and potentially rewriting
rb/Gemfile.lock a second time. This increases runtime and can introduce non-essential lockfile
churn in the upgrade workflow.
Code

rake_tasks/ruby.rake[R190-193]

+desc 'Reconcile Gemfile.lock and sync gem checksums to MODULE.bazel (use force to re-download all)'
task :pin, [:force] do |_task, arguments|
+  Bazel.execute('run', [], '//rb:bundle-lock')
+
Evidence
The PR adds a Bundler run to rb:pin, and rb:update already runs Bundler before invoking
rb:pin, so updates now execute two Bundler commands back-to-back.

rake_tasks/ruby.rake[190-193]
rake_tasks/ruby.rake[248-254]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`rb:update` already runs Bundler (`//rb:bundle-update`) and then calls `rb:pin`, which now runs Bundler again (`//rb:bundle-lock`). This adds unnecessary work and may produce extra lockfile churn.

### Issue Context
- `rb:pin` now executes `//rb:bundle-lock` unconditionally.
- `rb:update` executes `//rb:bundle-update` and then invokes `rb:pin`.

### Fix Focus Areas
- rake_tasks/ruby.rake[190-193]
- rake_tasks/ruby.rake[248-254]

### Suggested fix
Introduce a way for `rb:update` to reuse the already-updated lockfile without re-running `bundle lock` (e.g., split checksum syncing into its own task, or add an optional `skip_lock` argument to `rb:pin` and use it from `rb:update`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

3. No bundle subcommand check 🐞 Bug ☼ Reliability ⭐ New
Description
rb/support/bundle.rb now forwards caller-supplied ARGV directly to bundle without validating a
subcommand is present, so a misconfigured Bazel target or direct invocation can trigger Bundler’s
default/help behavior with unclear diagnostics. Adding a small guard makes failures deterministic
and easier to debug.
Code

rb/support/bundle.rb[R38-41]

+# The bundle subcommand (e.g. "lock" or "update") and any flags are supplied by
+# the caller so a single script backs both //rb:bundle-lock and //rb:bundle-update.
ruby = RbConfig.ruby
-exec ruby, "-S", "bundle", "update", *ARGV
+exec ruby, "-S", "bundle", *ARGV
Evidence
The new wrapper intentionally delegates the subcommand to ARGV, but there is no check to ensure
ARGV is present/valid before executing Bundler.

rb/support/bundle.rb[38-41]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`rb/support/bundle.rb` now executes `bundle` with `*ARGV` and does not enforce that `ARGV[0]` is a supported subcommand (e.g., `lock` or `update`). If invoked without args (or with a typo), behavior/exit codes are delegated to Bundler and can be confusing.

### Issue Context
This wrapper is intended to be shared by multiple Bazel entrypoints (`//rb:bundle-lock`, `//rb:bundle-update`). Those currently supply arguments correctly, but adding validation prevents future misconfiguration.

### Fix Focus Areas
- rb/support/bundle.rb[38-41]

### Suggested fix
Add a guard like:
- if `ARGV.empty?`, print usage and exit non-zero
- optionally validate `ARGV.first` is in an allowlist (`lock`, `update`) before calling `exec`

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Previous review results

Review updated until commit 8f97969

Results up to commit 376e106


🐞 Bugs (0) 📘 Rule violations (1) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)


Action required
1. No tests for rb:pin change 📘 Rule violation ▣ Testability
Description
The PR changes the rb:pin task to run //rb:bundle-lock and introduces a new bundle.rb wrapper
behavior, but no corresponding automated tests were added/updated to cover this bug fix. This
increases regression risk for release reset/renovate repinning flows.
Code

rake_tasks/ruby.rake[R190-193]

+desc 'Reconcile Gemfile.lock and sync gem checksums to MODULE.bazel (use force to re-download all)'
task :pin, [:force] do |_task, arguments|
+  Bazel.execute('run', [], '//rb:bundle-lock')
+
Evidence
PR Compliance ID 389273 requires tests for new functionality/bug fixes. The diff shows rb:pin now
executes //rb:bundle-lock, adds a new Bazel bundle-lock binary, and changes the bundler wrapper
to run bundle *ARGV, but there are no accompanying test changes in this PR to validate these
behaviors.

Rule 389273: Require tests for all new functionality and bug fixes
rake_tasks/ruby.rake[190-193]
rb/BUILD.bazel[180-206]
rb/support/bundle.rb[38-41]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PR introduces new behavior in `rb:pin` (running `//rb:bundle-lock`) and changes the bundler wrapper to accept caller-supplied subcommands, but no tests were added/updated to assert the behavior.

## Issue Context
This is a bug fix to prevent lockfile drift from breaking frozen bundler mode; without tests, future changes may reintroduce drift or accidentally switch `rb:pin` back to `bundle update` semantics.

## Fix Focus Areas
- rake_tasks/ruby.rake[190-193]
- rb/BUILD.bazel[180-206]
- rb/support/bundle.rb[38-41]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Qodo Logo

Comment thread rake_tasks/ruby.rake
@titusfortner titusfortner force-pushed the c/bundle-update-versions-dcf812 branch from 376e106 to 8f97969 Compare July 13, 2026 15:29
@titusfortner titusfortner merged commit e39c882 into trunk Jul 13, 2026
13 checks passed
@titusfortner titusfortner deleted the c/bundle-update-versions-dcf812 branch July 13, 2026 15:29
Comment thread rake_tasks/ruby.rake
Comment on lines +190 to +193
desc 'Reconcile Gemfile.lock and sync gem checksums to MODULE.bazel (use force to re-download all)'
task :pin, [:force] do |_task, arguments|
Bazel.execute('run', [], '//rb:bundle-lock')

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.

Remediation recommended

1. Bundler runs twice in update 🐞 Bug ➹ Performance

rb:update runs //rb:bundle-update and then invokes rb:pin, which now always runs
//rb:bundle-lock, causing an extra Bundler invocation on every update and potentially rewriting
rb/Gemfile.lock a second time. This increases runtime and can introduce non-essential lockfile
churn in the upgrade workflow.
Agent Prompt
### Issue description
`rb:update` already runs Bundler (`//rb:bundle-update`) and then calls `rb:pin`, which now runs Bundler again (`//rb:bundle-lock`). This adds unnecessary work and may produce extra lockfile churn.

### Issue Context
- `rb:pin` now executes `//rb:bundle-lock` unconditionally.
- `rb:update` executes `//rb:bundle-update` and then invokes `rb:pin`.

### Fix Focus Areas
- rake_tasks/ruby.rake[190-193]
- rake_tasks/ruby.rake[248-254]

### Suggested fix
Introduce a way for `rb:update` to reuse the already-updated lockfile without re-running `bundle lock` (e.g., split checksum syncing into its own task, or add an optional `skip_lock` argument to `rb:pin` and use it from `rb:update`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread rb/support/bundle.rb
Comment on lines +38 to +41
# The bundle subcommand (e.g. "lock" or "update") and any flags are supplied by
# the caller so a single script backs both //rb:bundle-lock and //rb:bundle-update.
ruby = RbConfig.ruby
exec ruby, "-S", "bundle", "update", *ARGV
exec ruby, "-S", "bundle", *ARGV

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.

Informational

2. No bundle subcommand check 🐞 Bug ☼ Reliability

rb/support/bundle.rb now forwards caller-supplied ARGV directly to bundle without validating a
subcommand is present, so a misconfigured Bazel target or direct invocation can trigger Bundler’s
default/help behavior with unclear diagnostics. Adding a small guard makes failures deterministic
and easier to debug.
Agent Prompt
### Issue description
`rb/support/bundle.rb` now executes `bundle` with `*ARGV` and does not enforce that `ARGV[0]` is a supported subcommand (e.g., `lock` or `update`). If invoked without args (or with a typo), behavior/exit codes are delegated to Bundler and can be confusing.

### Issue Context
This wrapper is intended to be shared by multiple Bazel entrypoints (`//rb:bundle-lock`, `//rb:bundle-update`). Those currently supply arguments correctly, but adding validation prevents future misconfiguration.

### Fix Focus Areas
- rb/support/bundle.rb[38-41]

### Suggested fix
Add a guard like:
- if `ARGV.empty?`, print usage and exit non-zero
- optionally validate `ARGV.first` is in an allowlist (`lock`, `update`) before calling `exec`

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code review by qodo was updated up to the latest commit 8f97969

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-build Includes scripting, bazel and CI integrations B-support Issue or PR related to support classes C-rb Ruby Bindings

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants