Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support bundler-only runs #548

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ jobs:
'1.9', '2.0', '2.1', '2.2', '2.3', '2.4', '2.5', '2.6', '2.7', '3.0', '3.1', '3.2', '3.3', ruby-head,
jruby, jruby-head,
truffleruby, truffleruby-head,
truffleruby+graalvm, truffleruby+graalvm-head
truffleruby+graalvm, truffleruby+graalvm-head,
system
]
include:
- { os: windows-2019, ruby: mingw }
Expand Down Expand Up @@ -56,13 +57,15 @@ jobs:
- { os: windows-2022, ruby: truffleruby-head }
- { os: windows-2022, ruby: truffleruby+graalvm }
- { os: windows-2022, ruby: truffleruby+graalvm-head }
- { os: windows-2022, ruby: system }

name: ${{ matrix.os }} ${{ matrix.ruby }}
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4

- uses: ./
id: setup-ruby
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
Expand All @@ -72,6 +75,8 @@ jobs:
run: |
# Show PATH with Powershell
$f, $r = $env:PATH.split([IO.Path]::PathSeparator); $r
- name: Ruby prefix output
run: echo "${{ steps.setup-ruby.outputs.ruby-prefix }}"

- name: build compiler
run: |
Expand Down Expand Up @@ -107,6 +112,7 @@ jobs:

- run: gem env
- name: C extension test
if: matrix.ruby != 'system'
run: gem install json -v 2.2.0
- run: bundle --version
# This step is redundant with `bundler-cache: true` but is there to check a redundant `bundle install` still works
Expand All @@ -129,6 +135,7 @@ jobs:
if: startsWith(matrix.os, 'windows')

- name: Test `gem github:` in a Gemfile
if: matrix.ruby != 'system' || !startsWith(matrix.os, 'ubuntu')
run: bundle install
env:
BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/gem_from_github.gemfile
Expand Down
5 changes: 4 additions & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ branding:
icon: download
inputs:
ruby-version:
description: 'Engine and version to use, see the syntax in the README. Reads from .ruby-version or .tool-versions if unset.'
description: |
Engine and version to use. Either 'default' (the default), 'system', or a engine/version syntax described in the README.
For 'default', reads from .ruby-version or .tool-versions.
For 'system', don't install any Ruby and use the first available Ruby on the PATH.
default: 'default'
rubygems:
description: |
Expand Down
67 changes: 61 additions & 6 deletions bundler.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ const fs = require('fs')
const path = require('path')
const core = require('@actions/core')
const exec = require('@actions/exec')
const io = require('@actions/io')
const cache = require('@actions/cache')
const semver = require('semver')
const common = require('./common')

export const DEFAULT_CACHE_VERSION = '0'
Expand Down Expand Up @@ -58,7 +60,7 @@ async function afterLockFile(lockFile, platform, engine, rubyVersion) {
}
}

export async function installBundler(bundlerVersionInput, rubygemsInputSet, lockFile, platform, rubyPrefix, engine, rubyVersion) {
export async function installBundler(bundlerVersionInput, rubygemsInputSet, systemRubyUsed, lockFile, platform, rubyPrefix, engine, rubyVersion) {
let bundlerVersion = bundlerVersionInput

if (rubygemsInputSet && (bundlerVersion === 'default' || bundlerVersion === 'Gemfile.lock')) {
Expand All @@ -79,7 +81,21 @@ export async function installBundler(bundlerVersionInput, rubygemsInputSet, lock
const floatVersion = common.floatVersion(rubyVersion)

if (bundlerVersion === 'default') {
if (common.isBundler2dot2Default(engine, rubyVersion)) {
if (systemRubyUsed) {
Copy link
Member

Choose a reason for hiding this comment

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

I think we shouldn't pass systemRubyUsed to bundler.js, it should just work if we pass the correct rubyVersion.
So we'd ask the RUBY_VERSION of the system ruby early on, and use that for rubyVersion.
We'd just skip downloading, extracting and adding to PATH if the input is system.

Copy link
Author

Choose a reason for hiding this comment

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

Basing it on rubyVersion isn't quite correct as:

  • Ubuntu image doesn't ship with any Bundler
  • macOS 11 image ships with a default Ruby 2.7 but with Bundler 2.4.

The logic used here for system Ruby is generic enough however that we could replace the existing common.isBundler2dot2Default logic here and thus avoid the systemRubyUsed.

Copy link
Member

Choose a reason for hiding this comment

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

Ubuntu image doesn't ship with any Bundler

Which Ubuntu image? IIRC ubuntu-22.04 ships with some Ruby version in PATH by default from the toolcache, and that has Bundler.

macOS 11 image ships with a default Ruby 2.7 but with Bundler 2.4.

That's rather weird, updating to latest Bundler but not latest Ruby. It is what it is though.

Copy link
Author

@Bo98 Bo98 Dec 6, 2023

Choose a reason for hiding this comment

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

Which Ubuntu image? IIRC ubuntu-22.04 ships with some Ruby version in PATH by default from the toolcache, and that has Bundler.

This is what I get with ubuntu-22.04: https://github.com/Bo98/workflow-test/actions/runs/7118996527/job/19383150052

macOS and Ubuntu runners do not use a toolcache Ruby by default. They use ones from system package managers.

Windows does use a toolcache Ruby by default (though badly broken for native gem builds on windows-2022).

if (await io.which('bundle', false)) {
bundlerVersion = await getBundlerVersion()
if (semver.lt(semver.coerce(bundlerVersion), '2.2.0')) {
console.log('Using latest Bundler because the system Bundler is too old')
bundlerVersion = 'latest'
} else {
console.log(`Using system Bundler ${bundlerVersion}`)
return bundlerVersion
}
} else {
console.log('Installing latest Bundler as we could not find a system Bundler')
bundlerVersion = 'latest'
}
} else if (common.isBundler2dot2Default(engine, rubyVersion)) {
if (common.windows && engine === 'ruby' && (common.isStableVersion(engine, rubyVersion) || rubyVersion === 'head')) {
// https://github.com/ruby/setup-ruby/issues/371
console.log(`Installing latest Bundler for ${engine}-${rubyVersion} on Windows because bin/bundle does not work in bash otherwise`)
Expand Down Expand Up @@ -144,12 +160,38 @@ 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', ...force, '-v', bundlerVersionConstraint])
const args = ['install', 'bundler', ...force, '-v', bundlerVersionConstraint]

let stderr = ''
try {
await exec.exec(gem, args, {
listeners: {
stderr: (data) => (stderr += data.toString())
}
})
} catch (error) {
if (systemRubyUsed && stderr.includes('Gem::FilePermissionError')) {
await exec.exec('sudo', [gem, ...args]);
} else {
throw error
}
}

return bundlerVersion
}

export async function bundleInstall(gemfile, lockFile, platform, engine, rubyVersion, bundlerVersion, cacheVersion) {
async function getBundlerVersion() {
let bundlerVersion = ''
await exec.exec('bundle', ['--version'], {
silent: true,
listeners: {
stdout: (data) => (bundlerVersion += data.toString())
}
})
return bundlerVersion.replace(/^Bundler version /, '').trim()
}

export async function bundleInstall(gemfile, lockFile, platform, engine, rubyVersion, bundlerVersion, cacheVersion, systemRubyUsed) {
if (gemfile === null) {
console.log('Could not determine gemfile path, skipping "bundle install" and caching')
return false
Expand Down Expand Up @@ -182,7 +224,7 @@ export async function bundleInstall(gemfile, lockFile, platform, engine, rubyVer

// cache key
const paths = [cachePath]
const baseKey = await computeBaseKey(platform, engine, rubyVersion, lockFile, cacheVersion)
const baseKey = await computeBaseKey(platform, engine, rubyVersion, lockFile, cacheVersion, systemRubyUsed)
const key = `${baseKey}-${await common.hashFile(lockFile)}`
// If only Gemfile.lock changes we can reuse part of the cache, and clean old gem versions below
const restoreKeys = [`${baseKey}-`]
Expand Down Expand Up @@ -232,7 +274,7 @@ export async function bundleInstall(gemfile, lockFile, platform, engine, rubyVer
return true
}

async function computeBaseKey(platform, engine, version, lockFile, cacheVersion) {
async function computeBaseKey(platform, engine, version, lockFile, cacheVersion, systemRubyUsed) {
const cwd = process.cwd()
const bundleWith = process.env['BUNDLE_WITH'] || ''
const bundleWithout = process.env['BUNDLE_WITHOUT'] || ''
Expand All @@ -259,6 +301,19 @@ async function computeBaseKey(platform, engine, version, lockFile, cacheVersion)
}
}

if (systemRubyUsed) {
let platform = ''
await exec.exec('ruby', ['-e', 'print RUBY_PLATFORM'], {
silent: true,
listeners: {
stdout: (data) => {
platform += data.toString();
}
}
});
key += `-platform-${platform}`
}

key += `-${lockFile}`
return key
}
Loading