diff --git a/README.md b/README.md index 6daad61..4252a0e 100644 --- a/README.md +++ b/README.md @@ -105,9 +105,11 @@ jobs: ### Bundler -Currently, Bundler is guaranteed to be installed for all versions. -If the Ruby ships with Bundler (Ruby >= 2.6), that version is used. -Otherwise (Ruby < 2.6), Bundler 1 is installed when that Ruby was built. +By default, if there is a `Gemfile.lock` file with a `BUNDLED WITH` section, +the latest version of Bundler with the same major version will be installed. +Otherwise, the latest Bundler version is installed (except for Ruby 2.2 where only Bundler 1 is supported). + +This behavior can be customized, see [action.yml](action.yml) for details about the `bundler` input. ### Caching `bundle install` diff --git a/action.yml b/action.yml index 1ae6ed2..a783692 100644 --- a/action.yml +++ b/action.yml @@ -9,6 +9,10 @@ inputs: description: 'Engine and version to use, see the syntax in the README. Reads from .ruby-version if unset.' required: false default: 'default' + bundler: + description: 'The version of Bundler to install. Either none, 1, 2, latest or Gemfile.lock. The default tries Gemfile.lock and otherwise uses latest.' + required: false + default: 'default' outputs: ruby-prefix: description: 'The prefix of the installed ruby' diff --git a/dist/index.js b/dist/index.js index 4c0458b..0e1c852 100644 --- a/dist/index.js +++ b/dist/index.js @@ -983,7 +983,7 @@ async function run() { setupPath(ruby, newPathEntries) - await installBundler(platform, rubyPrefix) + await installBundler(platform, rubyPrefix, engine, version) core.setOutput('ruby-prefix', rubyPrefix) } catch (error) { @@ -1069,11 +1069,55 @@ function setupPath(ruby, newPathEntries) { core.exportVariable('PATH', [...newPathEntries, ...cleanPath].join(path.delimiter)) } -async function installBundler(platform, rubyPrefix) { - const bundle_exe = platform === 'windows-latest' ? 'bundle.cmd' : 'bundle' - // Install Bundler if not already part of the stdlib - if (!fs.existsSync(path.join(rubyPrefix, 'bin', bundle_exe))) { - await exec.exec(path.join(rubyPrefix, 'bin', 'gem'), ['install', 'bundler', '-v', '~> 1', '--no-document']) +function readBundledWithFromGemfileLock() { + if (fs.existsSync('Gemfile.lock')) { + const contents = fs.readFileSync('Gemfile.lock', 'utf8') + const lines = contents.split(/\r?\n/) + const bundledWithLine = lines.findIndex(line => /^BUNDLED WITH$/.test(line.trim())) + if (bundledWithLine !== -1) { + const nextLine = lines[bundledWithLine+1] + if (nextLine && /^\d+/.test(nextLine.trim())) { + const bundlerVersion = nextLine.trim() + const majorVersion = bundlerVersion.match(/^\d+/)[0] + console.log(`Using Bundler ${majorVersion} from Gemfile.lock BUNDLED WITH ${bundlerVersion}`) + return majorVersion + } + } + } + return null +} + +async function installBundler(platform, rubyPrefix, engine, rubyVersion) { + var bundlerVersion = core.getInput('bundler') + if (bundlerVersion === 'none') { + return + } + + if (bundlerVersion === 'default' || bundlerVersion === 'Gemfile.lock') { + bundlerVersion = readBundledWithFromGemfileLock() + if (!bundlerVersion) { + bundlerVersion = 'latest' + } + } + + let versionArray + if (rubyVersion.startsWith('2.2')) { + console.log('Bundler 2 requires Ruby 2.3+, using Bundler 1 on Ruby 2.2') + versionArray = ['-v', '~> 1'] + } else if (/^\d+/.test(bundlerVersion)) { + versionArray = ['-v', `~> ${bundlerVersion}`] + } else if (bundlerVersion === 'latest') { + versionArray = [] + } else { + throw new Error(`Cannot parse bundler input: ${bundlerVersion}`) + } + + if (engine === 'rubinius') { + console.log(`Rubinius only supports the version of Bundler shipped with it`) + } else if (bundlerVersion === '1' && engine === 'truffleruby') { + console.log(`Using the Bundler version shipped with ${engine}`) + } else { + await exec.exec(path.join(rubyPrefix, 'bin', 'gem'), ['install', 'bundler', ...versionArray, '--no-document']) } } diff --git a/index.js b/index.js index 814e6f3..ac8cb7f 100644 --- a/index.js +++ b/index.js @@ -25,7 +25,7 @@ export async function run() { setupPath(ruby, newPathEntries) - await installBundler(platform, rubyPrefix) + await installBundler(platform, rubyPrefix, engine, version) core.setOutput('ruby-prefix', rubyPrefix) } catch (error) { @@ -111,11 +111,55 @@ function setupPath(ruby, newPathEntries) { core.exportVariable('PATH', [...newPathEntries, ...cleanPath].join(path.delimiter)) } -async function installBundler(platform, rubyPrefix) { - const bundle_exe = platform === 'windows-latest' ? 'bundle.cmd' : 'bundle' - // Install Bundler if not already part of the stdlib - if (!fs.existsSync(path.join(rubyPrefix, 'bin', bundle_exe))) { - await exec.exec(path.join(rubyPrefix, 'bin', 'gem'), ['install', 'bundler', '-v', '~> 1', '--no-document']) +function readBundledWithFromGemfileLock() { + if (fs.existsSync('Gemfile.lock')) { + const contents = fs.readFileSync('Gemfile.lock', 'utf8') + const lines = contents.split(/\r?\n/) + const bundledWithLine = lines.findIndex(line => /^BUNDLED WITH$/.test(line.trim())) + if (bundledWithLine !== -1) { + const nextLine = lines[bundledWithLine+1] + if (nextLine && /^\d+/.test(nextLine.trim())) { + const bundlerVersion = nextLine.trim() + const majorVersion = bundlerVersion.match(/^\d+/)[0] + console.log(`Using Bundler ${majorVersion} from Gemfile.lock BUNDLED WITH ${bundlerVersion}`) + return majorVersion + } + } + } + return null +} + +async function installBundler(platform, rubyPrefix, engine, rubyVersion) { + var bundlerVersion = core.getInput('bundler') + if (bundlerVersion === 'none') { + return + } + + if (bundlerVersion === 'default' || bundlerVersion === 'Gemfile.lock') { + bundlerVersion = readBundledWithFromGemfileLock() + if (!bundlerVersion) { + bundlerVersion = 'latest' + } + } + + let versionArray + if (rubyVersion.startsWith('2.2')) { + console.log('Bundler 2 requires Ruby 2.3+, using Bundler 1 on Ruby 2.2') + versionArray = ['-v', '~> 1'] + } else if (/^\d+/.test(bundlerVersion)) { + versionArray = ['-v', `~> ${bundlerVersion}`] + } else if (bundlerVersion === 'latest') { + versionArray = [] + } else { + throw new Error(`Cannot parse bundler input: ${bundlerVersion}`) + } + + if (engine === 'rubinius') { + console.log(`Rubinius only supports the version of Bundler shipped with it`) + } else if (bundlerVersion === '1' && engine === 'truffleruby') { + console.log(`Using the Bundler version shipped with ${engine}`) + } else { + await exec.exec(path.join(rubyPrefix, 'bin', 'gem'), ['install', 'bundler', ...versionArray, '--no-document']) } }