From acd187c6233c6c23e10864ada034b18008cb8658 Mon Sep 17 00:00:00 2001 From: Chris Oliver Date: Wed, 21 Oct 2026 12:24:33 -0500 Subject: [PATCH] Install an exact Bundler version with --conservative Reinstalling an exact version that is already installed changes nothing, but `gem install` removes the gem directory before extracting it again. On self-hosted runners that share a tool cache, a concurrent job then fails at `bundle config` with "cannot load such file -- .../bundler-X.Y.Z/lib/bundler/dsl" (#191). With `--conservative`, RubyGems returns before touching anything when the requirement is already met, including by a default gem. Only exact versions pass the flag, so a `~>` constraint still gets the latest matching release rather than the Bundler the Ruby shipped with (see #48). `--conservative` has worked this way for `gem install` since RubyGems 1.8.23. --- bundler.js | 10 +++++++++- dist/index.js | 10 +++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/bundler.js b/bundler.js index 59f969e94..9718d5612 100644 --- a/bundler.js +++ b/bundler.js @@ -144,7 +144,15 @@ export async function installBundler(bundlerVersionInput, rubygemsInputSet, lock const versionParts = [...bundlerVersion.matchAll(/\d+/g)].length const bundlerVersionConstraint = versionParts >= 3 ? bundlerVersion : `~> ${bundlerVersion}.0` - await exec.exec(gem, ['install', 'bundler', '-v', bundlerVersionConstraint]) + const args = ['install', 'bundler', '-v', bundlerVersionConstraint] + // Reinstalling an exact version that is already there changes nothing, and `gem install` + // removes the gem directory before extracting it again, which breaks the other jobs + // sharing a tool cache on self-hosted runners (#191). A `~>` constraint still gets the + // latest matching release rather than whatever Bundler the Ruby shipped with. + if (versionParts >= 3) { + args.push('--conservative') + } + await exec.exec(gem, args) return bundlerVersion } diff --git a/dist/index.js b/dist/index.js index e19adbb0b..7e97918f2 100644 --- a/dist/index.js +++ b/dist/index.js @@ -158,7 +158,15 @@ async function installBundler(bundlerVersionInput, rubygemsInputSet, lockFile, p const versionParts = [...bundlerVersion.matchAll(/\d+/g)].length const bundlerVersionConstraint = versionParts >= 3 ? bundlerVersion : `~> ${bundlerVersion}.0` - await exec.exec(gem, ['install', 'bundler', '-v', bundlerVersionConstraint]) + const args = ['install', 'bundler', '-v', bundlerVersionConstraint] + // Reinstalling an exact version that is already there changes nothing, and `gem install` + // removes the gem directory before extracting it again, which breaks the other jobs + // sharing a tool cache on self-hosted runners (#191). A `~>` constraint still gets the + // latest matching release rather than whatever Bundler the Ruby shipped with. + if (versionParts >= 3) { + args.push('--conservative') + } + await exec.exec(gem, args) return bundlerVersion }