Compare commits

..
13 Commits
Author SHA1 Message Date
Benoit Daloze ad1ebae995 Also include the version in the platform for macos and windows
* Guess the platform based on $ImageOS.
2020-11-12 20:23:53 +01:00
Benoit Daloze f4a8aa6c33 Use ImageOS for the cache key, so it also includes the OS version 2020-11-12 19:59:05 +01:00
Benoit Daloze bc0f274d1c Fix Bundler cache when there is no Gemfile.lock
* Always show the list of installed gems in the log.
2020-11-12 19:34:43 +01:00
Benoit Daloze 1c896c8ea1 Always use Bundler 1 for JRuby 9.1.x 2020-11-12 19:08:42 +01:00
Benoit Daloze ba761ba8ff Use a hardcoded toolcache path since we need to extract there to let Ruby work 2020-11-10 19:58:59 +01:00
Benoit Daloze bb3a0c6e0b Bundler 2 is shipped with truffleruby-head now 2020-11-10 15:33:41 +01:00
Benoit Daloze 8e77d9e0fa Mention RUNNER_TOOL_CACHE requirements for self-hosted runners 2020-11-10 11:44:22 +01:00
Benoit Daloze 339f055c86 Clarify Windows check 2020-11-10 11:35:50 +01:00
Benoit Daloze 0592d01dc3 Ruby 2.4 is EOL
https://www.ruby-lang.org/en/news/2020/04/05/support-of-ruby-2-4-has-ended/
2020-11-09 19:42:02 +01:00
Benoit Daloze d7b76f909a Add Ruby 3.0.0-preview1
* Fixes https://github.com/ruby/setup-ruby/issues/100
2020-11-09 13:07:33 +01:00
Benoit Daloze ebed7f7b66 Invalidate cache key since Rubies are installed in the toolcache now
* Some gems might capture the absolute path of the Ruby prefix.
2020-11-09 13:01:13 +01:00
Benoit Daloze 933dca770c Extract CRuby releases under $RUNNER_TOOL_CACHE 2020-11-09 13:00:42 +01:00
Benoit Daloze 693cf1794d Include the Gemfile path in the cache key
* See https://github.com/ruby/setup-ruby/issues/103#issuecomment-723960352
2020-11-09 13:00:04 +01:00
7 changed files with 250 additions and 152 deletions
+10 -4
View File
@@ -21,13 +21,16 @@ This action currently supports these versions of MRI, JRuby and TruffleRuby:
`ruby-debug` is the same as `ruby-head` but with assertions enabled (`-DRUBY_DEBUG=1`). `ruby-debug` is the same as `ruby-head` but with assertions enabled (`-DRUBY_DEBUG=1`).
On Windows, `mingw` and `mswin` are `ruby-head` builds using the MSYS2/MinGW and the MSVC toolchains respectively. On Windows, `mingw` and `mswin` are `ruby-head` builds using the MSYS2/MinGW and the MSVC toolchains respectively.
Preview and RC versions of Ruby might be available too on Ubuntu and macOS (not on Windows).
However, it is recommended to test against `ruby-head` rather than previews,
as it provides more useful feedback for the Ruby core team and for upcoming changes.
Only versions published by [RubyInstaller](https://rubyinstaller.org/downloads/archives/) are available on Windows. Only versions published by [RubyInstaller](https://rubyinstaller.org/downloads/archives/) are available on Windows.
Due to that, Ruby 2.2 resolves to 2.2.6 on Windows and 2.2.10 on other platforms. Due to that, Ruby 2.2 resolves to 2.2.6 on Windows and 2.2.10 on other platforms.
And Ruby 2.3 on Windows only has builds for 2.3.0, 2.3.1 and 2.3.3. And Ruby 2.3 on Windows only has builds for 2.3.0, 2.3.1 and 2.3.3.
Note that Ruby ≤ 2.3 and the OpenSSL version it needs (1.0.2) are both end-of-life, Note that Ruby ≤ 2.4 and the OpenSSL version it needs (1.0.2) are both end-of-life,
which means Ruby ≤ 2.3 is unmaintained and considered insecure. which means Ruby ≤ 2.4 is unmaintained and considered insecure.
On Windows, Ruby 2.4 uses OpenSSL 1.0.2, which is no longer maintained.
### Supported Platforms ### Supported Platforms
@@ -200,7 +203,10 @@ if the [virtual environment](https://github.com/actions/virtual-environments) is
* Make sure to use the same operating system and version. * Make sure to use the same operating system and version.
* Make sure to use the same version of libssl. * Make sure to use the same version of libssl.
* Make sure that the operating system has `libyaml-0` installed * Make sure that the operating system has `libyaml-0` installed
* The runner software is running as user `runner` with a home directory of `/home/runner`, or you have created a symlink for the home directory of whatever user that the runner is running as to `/home/runner`. * The default tool cache directory (`/opt/hostedtoolcache` on Linux, `/Users/runner/hostedtoolcache` on macOS,
`C:/hostedtoolcache/windows` on Windows) must be writable by the `runner` user.
This is necessary since the Ruby builds embed the install path when built and cannot be moved around.
* `/home/runner` must be writable by the `runner` user.
In other cases, please use a system Ruby or [install Ruby manually](https://github.com/postmodern/chruby/wiki#installing-rubies) instead. In other cases, please use a system Ruby or [install Ruby manually](https://github.com/postmodern/chruby/wiki#installing-rubies) instead.
+44 -16
View File
@@ -36,27 +36,55 @@ export async function hashFile(file) {
return hash.digest('hex') return hash.digest('hex')
} }
function getImageOS() {
const imageOS = process.env['ImageOS']
if (!imageOS) {
throw new Error('The environment variable ImageOS must be set')
}
return imageOS
}
export function getVirtualEnvironmentName() { export function getVirtualEnvironmentName() {
const platform = os.platform() const imageOS = getImageOS()
if (platform === 'linux') {
return `ubuntu-${findUbuntuVersion()}` let match = imageOS.match(/^ubuntu(\d+)/) // e.g. ubuntu18
} else if (platform === 'darwin') { if (match) {
return 'macos-latest' return `ubuntu-${match[1]}.04`
} else if (platform === 'win32') { }
return 'windows-latest'
match = imageOS.match(/^macos(\d{2})(\d+)/) // e.g. macos1015
if (match) {
return `macos-${match[1]}.${match[2]}`
}
match = imageOS.match(/^win(\d+)/) // e.g. win19
if (match) {
return `windows-20${match[1]}`
}
throw new Error(`Unknown ImageOS ${imageOS}`)
}
export function shouldExtractInToolCache(engine, version) {
return engine === 'ruby' && !isHeadVersion(version)
}
function getPlatformToolCache(platform) {
// Hardcode paths rather than using $RUNNER_TOOL_CACHE because the prebuilt Rubies cannot be moved anyway
if (platform.startsWith('ubuntu-')) {
return '/opt/hostedtoolcache'
} else if (platform.startsWith('macos-')) {
return '/Users/runner/hostedtoolcache'
} else if (platform.startsWith('windows-')) {
return 'C:/hostedtoolcache/windows'
} else { } else {
throw new Error(`Unknown platform ${platform}`) throw new Error('Unknown platform')
} }
} }
function findUbuntuVersion() { export function getToolCacheRubyPrefix(platform, version) {
const lsb_release = fs.readFileSync('/etc/lsb-release', 'utf8') const toolCache = getPlatformToolCache(platform)
const match = lsb_release.match(/^DISTRIB_RELEASE=(\d+\.\d+)$/m) return path.join(toolCache, 'Ruby', version, 'x64')
if (match) {
return match[1]
} else {
throw new Error('Could not find Ubuntu version')
}
} }
// convert windows path like C:\Users\runneradmin to /c/Users/runneradmin // convert windows path like C:\Users\runneradmin to /c/Users/runneradmin
Generated Vendored
+121 -74
View File
@@ -27682,6 +27682,7 @@ const path = __webpack_require__(622)
const cp = __webpack_require__(129) const cp = __webpack_require__(129)
const core = __webpack_require__(186) const core = __webpack_require__(186)
const exec = __webpack_require__(514) const exec = __webpack_require__(514)
const io = __webpack_require__(436)
const tc = __webpack_require__(188) const tc = __webpack_require__(188)
const common = __webpack_require__(390) const common = __webpack_require__(390)
const rubyInstallerVersions = __webpack_require__(223).versions const rubyInstallerVersions = __webpack_require__(223).versions
@@ -27711,7 +27712,13 @@ async function install(platform, engine, version) {
} }
const base = url.slice(url.lastIndexOf('/') + 1, url.length - '.7z'.length) const base = url.slice(url.lastIndexOf('/') + 1, url.length - '.7z'.length)
const rubyPrefix = `${drive}:\\${base}` let rubyPrefix
if (common.shouldExtractInToolCache(engine, version)) {
rubyPrefix = common.getToolCacheRubyPrefix(platform, version)
} else {
rubyPrefix = `${drive}:\\${base}`
}
const parentDir = path.dirname(rubyPrefix)
let toolchainPaths = (version === 'mswin') ? await setupMSWin() : await setupMingw(version) let toolchainPaths = (version === 'mswin') ? await setupMSWin() : await setupMingw(version)
@@ -27723,7 +27730,11 @@ async function install(platform, engine, version) {
}) })
await common.measure('Extracting Ruby', async () => await common.measure('Extracting Ruby', async () =>
exec.exec('7z', ['x', downloadPath, `-xr!${base}\\share\\doc`, `-o${drive}:\\`], { silent: true })) exec.exec('7z', ['x', downloadPath, `-xr!${base}\\share\\doc`, `-o${parentDir}`], { silent: true }))
if (base !== path.basename(rubyPrefix)) {
await io.mv(path.join(parentDir, base), rubyPrefix)
}
return rubyPrefix return rubyPrefix
} }
@@ -32087,6 +32098,8 @@ __webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isHeadVersion", function() { return isHeadVersion; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "isHeadVersion", function() { return isHeadVersion; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "hashFile", function() { return hashFile; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "hashFile", function() { return hashFile; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getVirtualEnvironmentName", function() { return getVirtualEnvironmentName; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getVirtualEnvironmentName", function() { return getVirtualEnvironmentName; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "shouldExtractInToolCache", function() { return shouldExtractInToolCache; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "getToolCacheRubyPrefix", function() { return getToolCacheRubyPrefix; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "win2nix", function() { return win2nix; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "win2nix", function() { return win2nix; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "setupPath", function() { return setupPath; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "setupPath", function() { return setupPath; });
const os = __webpack_require__(87) const os = __webpack_require__(87)
@@ -32127,27 +32140,55 @@ async function hashFile(file) {
return hash.digest('hex') return hash.digest('hex')
} }
function getImageOS() {
const imageOS = process.env['ImageOS']
if (!imageOS) {
throw new Error('The environment variable ImageOS must be set')
}
return imageOS
}
function getVirtualEnvironmentName() { function getVirtualEnvironmentName() {
const platform = os.platform() const imageOS = getImageOS()
if (platform === 'linux') {
return `ubuntu-${findUbuntuVersion()}` let match = imageOS.match(/^ubuntu(\d+)/) // e.g. ubuntu18
} else if (platform === 'darwin') { if (match) {
return 'macos-latest' return `ubuntu-${match[1]}.04`
} else if (platform === 'win32') { }
return 'windows-latest'
match = imageOS.match(/^macos(\d{2})(\d+)/) // e.g. macos1015
if (match) {
return `macos-${match[1]}.${match[2]}`
}
match = imageOS.match(/^win(\d+)/) // e.g. win19
if (match) {
return `windows-20${match[1]}`
}
throw new Error(`Unknown ImageOS ${imageOS}`)
}
function shouldExtractInToolCache(engine, version) {
return engine === 'ruby' && !isHeadVersion(version)
}
function getPlatformToolCache(platform) {
// Hardcode paths rather than using $RUNNER_TOOL_CACHE because the prebuilt Rubies cannot be moved anyway
if (platform.startsWith('ubuntu-')) {
return '/opt/hostedtoolcache'
} else if (platform.startsWith('macos-')) {
return '/Users/runner/hostedtoolcache'
} else if (platform.startsWith('windows-')) {
return 'C:/hostedtoolcache/windows'
} else { } else {
throw new Error(`Unknown platform ${platform}`) throw new Error('Unknown platform')
} }
} }
function findUbuntuVersion() { function getToolCacheRubyPrefix(platform, version) {
const lsb_release = fs.readFileSync('/etc/lsb-release', 'utf8') const toolCache = getPlatformToolCache(platform)
const match = lsb_release.match(/^DISTRIB_RELEASE=(\d+\.\d+)$/m) return path.join(toolCache, 'Ruby', version, 'x64')
if (match) {
return match[1]
} else {
throw new Error('Could not find Ubuntu version')
}
} }
// convert windows path like C:\Users\runneradmin to /c/Users/runneradmin // convert windows path like C:\Users\runneradmin to /c/Users/runneradmin
@@ -44485,6 +44526,7 @@ function getVersions(platform) {
"2.5.0", "2.5.1", "2.5.2", "2.5.3", "2.5.4", "2.5.5", "2.5.6", "2.5.7", "2.5.8", "2.5.0", "2.5.1", "2.5.2", "2.5.3", "2.5.4", "2.5.5", "2.5.6", "2.5.7", "2.5.8",
"2.6.0", "2.6.1", "2.6.2", "2.6.3", "2.6.4", "2.6.5", "2.6.6", "2.6.0", "2.6.1", "2.6.2", "2.6.3", "2.6.4", "2.6.5", "2.6.6",
"2.7.0", "2.7.1", "2.7.2", "2.7.0", "2.7.1", "2.7.2",
"3.0.0-preview1",
"head", "debug", "head", "debug",
], ],
"jruby": [ "jruby": [
@@ -51155,7 +51197,7 @@ async function setupRuby(options = {}) {
const [engine, parsedVersion] = parseRubyEngineAndVersion(inputs['ruby-version']) const [engine, parsedVersion] = parseRubyEngineAndVersion(inputs['ruby-version'])
let installer let installer
if (platform === 'windows-latest' && engine !== 'jruby') { if (platform.startsWith('windows-') && engine === 'ruby') {
installer = __webpack_require__(216) installer = __webpack_require__(216)
} else { } else {
installer = __webpack_require__(974) installer = __webpack_require__(974)
@@ -51191,25 +51233,15 @@ async function setupRuby(options = {}) {
core.setOutput('ruby-prefix', rubyPrefix) core.setOutput('ruby-prefix', rubyPrefix)
} }
// The returned gemfile is guaranteed to exist, the lockfile might not exist
function detectGemfiles() { function detectGemfiles() {
const gemfilePath = process.env['BUNDLE_GEMFILE'] || 'Gemfile' const gemfilePath = process.env['BUNDLE_GEMFILE'] || 'Gemfile'
if (fs.existsSync(gemfilePath)) { if (fs.existsSync(gemfilePath)) {
const lockPath = `${gemfilePath}.lock` return [gemfilePath, `${gemfilePath}.lock`]
if (fs.existsSync(lockPath)) {
return [gemfilePath, lockPath]
} else {
return [gemfilePath, null]
}
} }
const gemsRbPath = "gems.rb" if (fs.existsSync("gems.rb")) {
if (fs.existsSync(gemsRbPath)) { return ["gems.rb", "gems.locked"]
const lockPath = "gems.locked"
if (fs.existsSync(lockPath)) {
return [gemsRbPath, lockPath]
} else {
return [gemsRbPath, null]
}
} }
return [null, null] return [null, null]
@@ -51242,7 +51274,7 @@ function parseRubyEngineAndVersion(rubyVersion) {
version = rubyVersion version = rubyVersion
} else if (!rubyVersion.includes('-')) { // myruby -> myruby-stableVersion } else if (!rubyVersion.includes('-')) { // myruby -> myruby-stableVersion
engine = rubyVersion engine = rubyVersion
version = '' // Let the logic below find the version version = '' // Let the logic in validateRubyEngineAndVersion() find the version
} else { // engine-X.Y.Z } else { // engine-X.Y.Z
[engine, version] = rubyVersion.split('-', 2) [engine, version] = rubyVersion.split('-', 2)
} }
@@ -51293,7 +51325,7 @@ function envPreInstall() {
} }
function readBundledWithFromGemfileLock(lockFile) { function readBundledWithFromGemfileLock(lockFile) {
if (lockFile !== null) { if (lockFile !== null && fs.existsSync(lockFile)) {
const contents = fs.readFileSync(lockFile, 'utf8') const contents = fs.readFileSync(lockFile, 'utf8')
const lines = contents.split(/\r?\n/) const lines = contents.split(/\r?\n/)
const bundledWithLine = lines.findIndex(line => /^BUNDLED WITH$/.test(line.trim())) const bundledWithLine = lines.findIndex(line => /^BUNDLED WITH$/.test(line.trim()))
@@ -51311,7 +51343,7 @@ function readBundledWithFromGemfileLock(lockFile) {
} }
async function installBundler(bundlerVersionInput, lockFile, platform, rubyPrefix, engine, rubyVersion) { async function installBundler(bundlerVersionInput, lockFile, platform, rubyPrefix, engine, rubyVersion) {
var bundlerVersion = bundlerVersionInput let bundlerVersion = bundlerVersionInput
if (bundlerVersion === 'default' || bundlerVersion === 'Gemfile.lock') { if (bundlerVersion === 'default' || bundlerVersion === 'Gemfile.lock') {
bundlerVersion = readBundledWithFromGemfileLock(lockFile) bundlerVersion = readBundledWithFromGemfileLock(lockFile)
@@ -51331,17 +51363,20 @@ async function installBundler(bundlerVersionInput, lockFile, platform, rubyPrefi
throw new Error(`Cannot parse bundler input: ${bundlerVersion}`) throw new Error(`Cannot parse bundler input: ${bundlerVersion}`)
} }
if (rubyVersion.match(/^2\.[12]/)) { if (engine === 'ruby' && rubyVersion.match(/^2\.[12]/)) {
console.log('Bundler 2 requires Ruby 2.3+, using Bundler 1 on Ruby <= 2.2') console.log('Bundler 2 requires Ruby 2.3+, using Bundler 1 on Ruby <= 2.2')
bundlerVersion = '1' bundlerVersion = '1'
} else if (rubyVersion.startsWith('2.3')) { } else if (engine === 'ruby' && rubyVersion.startsWith('2.3')) {
console.log('Ruby 2.3 has a bug with Bundler 2 (https://github.com/rubygems/rubygems/issues/3570), using Bundler 1 instead on Ruby 2.3') console.log('Ruby 2.3 has a bug with Bundler 2 (https://github.com/rubygems/rubygems/issues/3570), using Bundler 1 instead on Ruby 2.3')
bundlerVersion = '1' bundlerVersion = '1'
} else if (engine === 'jruby' && rubyVersion.startsWith('9.1.')) { // JRuby 9.1 targets Ruby 2.3, treat it the same
console.log('JRuby 9.1 has a bug with Bundler 2 (https://github.com/ruby/setup-ruby/issues/108), using Bundler 1 instead on JRuby 9.1')
bundlerVersion = '1'
} }
if (engine === 'ruby' && common.isHeadVersion(rubyVersion) && bundlerVersion === '2') { if ((engine === 'ruby' || engine === 'truffleruby') && common.isHeadVersion(rubyVersion) && bundlerVersion === '2') {
console.log(`Using Bundler 2 shipped with ${engine}-${rubyVersion}`) console.log(`Using Bundler 2 shipped with ${engine}-${rubyVersion}`)
} else if (engine === 'truffleruby' && bundlerVersion === '1') { } else if (engine === 'truffleruby' && !common.isHeadVersion(rubyVersion) && bundlerVersion === '1') {
console.log(`Using Bundler 1 shipped with ${engine}`) console.log(`Using Bundler 1 shipped with ${engine}`)
} else { } else {
const gem = path.join(rubyPrefix, 'bin', 'gem') const gem = path.join(rubyPrefix, 'bin', 'gem')
@@ -51358,24 +51393,22 @@ async function bundleInstall(gemfile, lockFile, platform, engine, version) {
// config // config
const path = 'vendor/bundle' const path = 'vendor/bundle'
if (lockFile !== null) {
await exec.exec('bundle', ['config', '--local', 'deployment', 'true'])
}
await exec.exec('bundle', ['config', '--local', 'path', path]) await exec.exec('bundle', ['config', '--local', 'path', path])
if (fs.existsSync(lockFile)) {
await exec.exec('bundle', ['config', '--local', 'deployment', 'true'])
} else {
// Generate the lockfile so we can use it to compute the cache key.
// This will also automatically pick up the latest gem versions compatible with the Gemfile.
await exec.exec('bundle', ['lock'])
}
// cache key // cache key
const paths = [path] const paths = [path]
const baseKey = await computeBaseKey(platform, engine, version) const baseKey = await computeBaseKey(platform, engine, version, lockFile)
let key = baseKey const key = `${baseKey}-${await common.hashFile(lockFile)}`
let restoreKeys // If only Gemfile.lock changes we can reuse part of the cache (but it will keep old gem versions in the cache)
if (lockFile !== null) { const restoreKeys = [`${baseKey}-`]
key += `-${lockFile}-${await common.hashFile(lockFile)}`
// If only Gemfile.lock we can reuse some of the cache (but it will keep old gem versions in the cache)
restoreKeys = [`${baseKey}-${lockFile}-`]
} else {
// Only exact key, to never mix native gems of different platforms or Ruby versions
restoreKeys = []
}
console.log(`Cache key: ${key}`) console.log(`Cache key: ${key}`)
// restore cache & install // restore cache & install
@@ -51394,15 +51427,11 @@ async function bundleInstall(gemfile, lockFile, platform, engine, version) {
console.log(`Found cache for key: ${cachedKey}`) console.log(`Found cache for key: ${cachedKey}`)
} }
let alreadyInstalled = false // Always run 'bundle install' to list the gems
if (cachedKey === key) { await exec.exec('bundle', ['install', '--jobs', '4'])
const exitCode = await exec.exec('bundle', ['check'], { ignoreReturnCode: true })
alreadyInstalled = (exitCode === 0)
}
if (!alreadyInstalled) {
await exec.exec('bundle', ['install', '--jobs', '4'])
// @actions/cache only allows to save for non-existing keys
if (cachedKey !== key) {
// Error handling from https://github.com/actions/cache/blob/master/src/save.ts // Error handling from https://github.com/actions/cache/blob/master/src/save.ts
console.log('Saving cache') console.log('Saving cache')
try { try {
@@ -51421,8 +51450,9 @@ async function bundleInstall(gemfile, lockFile, platform, engine, version) {
return true return true
} }
async function computeBaseKey(platform, engine, version) { async function computeBaseKey(platform, engine, version, lockFile) {
let baseKey = `setup-ruby-bundle-install-${platform}-${engine}-${version}` let key = `setup-ruby-bundler-cache-v2-${platform}-${engine}-${version}`
if (engine !== 'jruby' && common.isHeadVersion(version)) { if (engine !== 'jruby' && common.isHeadVersion(version)) {
let revision = ''; let revision = '';
await exec.exec('ruby', ['-e', 'print RUBY_REVISION'], { await exec.exec('ruby', ['-e', 'print RUBY_REVISION'], {
@@ -51433,9 +51463,11 @@ async function computeBaseKey(platform, engine, version) {
} }
} }
}); });
baseKey += `-revision-${revision}` key += `-revision-${revision}`
} }
return baseKey
key += `-${lockFile}`
return key
} }
if (__filename.endsWith('index.js')) { run() } if (__filename.endsWith('index.js')) { run() }
@@ -52859,7 +52891,7 @@ const tc = __webpack_require__(188)
const common = __webpack_require__(390) const common = __webpack_require__(390)
const rubyBuilderVersions = __webpack_require__(694) const rubyBuilderVersions = __webpack_require__(694)
const builderReleaseTag = 'enable-shared' const builderReleaseTag = 'toolcache'
const releasesURL = 'https://github.com/ruby/ruby-builder/releases' const releasesURL = 'https://github.com/ruby/ruby-builder/releases'
const windows = common.windows const windows = common.windows
@@ -52873,14 +52905,22 @@ async function install(platform, engine, version) {
} }
async function downloadAndExtract(platform, engine, version) { async function downloadAndExtract(platform, engine, version) {
const rubiesDir = windows ? `${common.drive}:` : path.join(os.homedir(), '.rubies') let rubyPrefix
if (common.shouldExtractInToolCache(engine, version)) {
rubyPrefix = common.getToolCacheRubyPrefix(platform, version)
} else if (windows) {
rubyPrefix = path.join(`${common.drive}:`, `${engine}-${version}`)
} else {
rubyPrefix = path.join(os.homedir(), '.rubies', `${engine}-${version}`)
}
const rubyPrefix = path.join(rubiesDir, `${engine}-${version}`) const parentDir = path.dirname(rubyPrefix)
// Set the PATH now, so the MSYS2 'tar' is in Path on Windows // Set the PATH now, so the MSYS2 'tar' is in Path on Windows
common.setupPath([path.join(rubyPrefix, 'bin')]) common.setupPath([path.join(rubyPrefix, 'bin')])
await io.mkdirP(rubiesDir) await io.rmRF(rubyPrefix)
await io.mkdirP(parentDir)
const downloadPath = await common.measure('Downloading Ruby', async () => { const downloadPath = await common.measure('Downloading Ruby', async () => {
const url = getDownloadURL(platform, engine, version) const url = getDownloadURL(platform, engine, version)
@@ -52891,9 +52931,9 @@ async function downloadAndExtract(platform, engine, version) {
await common.measure('Extracting Ruby', async () => { await common.measure('Extracting Ruby', async () => {
if (windows) { if (windows) {
// Windows 2016 doesn't have system tar, use MSYS2's, it needs unix style paths // Windows 2016 doesn't have system tar, use MSYS2's, it needs unix style paths
await exec.exec('tar', [ '-xz', '-C', common.win2nix(rubiesDir), '-f', common.win2nix(downloadPath) ]) await exec.exec('tar', [ '-xz', '-C', common.win2nix(parentDir), '-f', common.win2nix(downloadPath) ])
} else { } else {
await exec.exec('tar', [ '-xz', '-C', rubiesDir, '-f', downloadPath ]) await exec.exec('tar', [ '-xz', '-C', parentDir, '-f', downloadPath ])
} }
}) })
@@ -52901,10 +52941,17 @@ async function downloadAndExtract(platform, engine, version) {
} }
function getDownloadURL(platform, engine, version) { function getDownloadURL(platform, engine, version) {
let builderPlatform = platform
if (platform.startsWith('windows-')) {
builderPlatform = 'windows-latest'
} else if (platform.startsWith('macos-')) {
builderPlatform = 'macos-latest'
}
if (common.isHeadVersion(version)) { if (common.isHeadVersion(version)) {
return getLatestHeadBuildURL(platform, engine, version) return getLatestHeadBuildURL(builderPlatform, engine, version)
} else { } else {
return `${releasesURL}/download/${builderReleaseTag}/${engine}-${version}-${platform}.tar.gz` return `${releasesURL}/download/${builderReleaseTag}/${engine}-${version}-${builderPlatform}.tar.gz`
} }
} }
+38 -48
View File
@@ -39,7 +39,7 @@ export async function setupRuby(options = {}) {
const [engine, parsedVersion] = parseRubyEngineAndVersion(inputs['ruby-version']) const [engine, parsedVersion] = parseRubyEngineAndVersion(inputs['ruby-version'])
let installer let installer
if (platform === 'windows-latest' && engine !== 'jruby') { if (platform.startsWith('windows-') && engine === 'ruby') {
installer = require('./windows') installer = require('./windows')
} else { } else {
installer = require('./ruby-builder') installer = require('./ruby-builder')
@@ -75,25 +75,15 @@ export async function setupRuby(options = {}) {
core.setOutput('ruby-prefix', rubyPrefix) core.setOutput('ruby-prefix', rubyPrefix)
} }
// The returned gemfile is guaranteed to exist, the lockfile might not exist
function detectGemfiles() { function detectGemfiles() {
const gemfilePath = process.env['BUNDLE_GEMFILE'] || 'Gemfile' const gemfilePath = process.env['BUNDLE_GEMFILE'] || 'Gemfile'
if (fs.existsSync(gemfilePath)) { if (fs.existsSync(gemfilePath)) {
const lockPath = `${gemfilePath}.lock` return [gemfilePath, `${gemfilePath}.lock`]
if (fs.existsSync(lockPath)) {
return [gemfilePath, lockPath]
} else {
return [gemfilePath, null]
}
} }
const gemsRbPath = "gems.rb" if (fs.existsSync("gems.rb")) {
if (fs.existsSync(gemsRbPath)) { return ["gems.rb", "gems.locked"]
const lockPath = "gems.locked"
if (fs.existsSync(lockPath)) {
return [gemsRbPath, lockPath]
} else {
return [gemsRbPath, null]
}
} }
return [null, null] return [null, null]
@@ -126,7 +116,7 @@ function parseRubyEngineAndVersion(rubyVersion) {
version = rubyVersion version = rubyVersion
} else if (!rubyVersion.includes('-')) { // myruby -> myruby-stableVersion } else if (!rubyVersion.includes('-')) { // myruby -> myruby-stableVersion
engine = rubyVersion engine = rubyVersion
version = '' // Let the logic below find the version version = '' // Let the logic in validateRubyEngineAndVersion() find the version
} else { // engine-X.Y.Z } else { // engine-X.Y.Z
[engine, version] = rubyVersion.split('-', 2) [engine, version] = rubyVersion.split('-', 2)
} }
@@ -177,7 +167,7 @@ function envPreInstall() {
} }
function readBundledWithFromGemfileLock(lockFile) { function readBundledWithFromGemfileLock(lockFile) {
if (lockFile !== null) { if (lockFile !== null && fs.existsSync(lockFile)) {
const contents = fs.readFileSync(lockFile, 'utf8') const contents = fs.readFileSync(lockFile, 'utf8')
const lines = contents.split(/\r?\n/) const lines = contents.split(/\r?\n/)
const bundledWithLine = lines.findIndex(line => /^BUNDLED WITH$/.test(line.trim())) const bundledWithLine = lines.findIndex(line => /^BUNDLED WITH$/.test(line.trim()))
@@ -195,7 +185,7 @@ function readBundledWithFromGemfileLock(lockFile) {
} }
async function installBundler(bundlerVersionInput, lockFile, platform, rubyPrefix, engine, rubyVersion) { async function installBundler(bundlerVersionInput, lockFile, platform, rubyPrefix, engine, rubyVersion) {
var bundlerVersion = bundlerVersionInput let bundlerVersion = bundlerVersionInput
if (bundlerVersion === 'default' || bundlerVersion === 'Gemfile.lock') { if (bundlerVersion === 'default' || bundlerVersion === 'Gemfile.lock') {
bundlerVersion = readBundledWithFromGemfileLock(lockFile) bundlerVersion = readBundledWithFromGemfileLock(lockFile)
@@ -215,17 +205,20 @@ async function installBundler(bundlerVersionInput, lockFile, platform, rubyPrefi
throw new Error(`Cannot parse bundler input: ${bundlerVersion}`) throw new Error(`Cannot parse bundler input: ${bundlerVersion}`)
} }
if (rubyVersion.match(/^2\.[12]/)) { if (engine === 'ruby' && rubyVersion.match(/^2\.[12]/)) {
console.log('Bundler 2 requires Ruby 2.3+, using Bundler 1 on Ruby <= 2.2') console.log('Bundler 2 requires Ruby 2.3+, using Bundler 1 on Ruby <= 2.2')
bundlerVersion = '1' bundlerVersion = '1'
} else if (rubyVersion.startsWith('2.3')) { } else if (engine === 'ruby' && rubyVersion.startsWith('2.3')) {
console.log('Ruby 2.3 has a bug with Bundler 2 (https://github.com/rubygems/rubygems/issues/3570), using Bundler 1 instead on Ruby 2.3') console.log('Ruby 2.3 has a bug with Bundler 2 (https://github.com/rubygems/rubygems/issues/3570), using Bundler 1 instead on Ruby 2.3')
bundlerVersion = '1' bundlerVersion = '1'
} else if (engine === 'jruby' && rubyVersion.startsWith('9.1.')) { // JRuby 9.1 targets Ruby 2.3, treat it the same
console.log('JRuby 9.1 has a bug with Bundler 2 (https://github.com/ruby/setup-ruby/issues/108), using Bundler 1 instead on JRuby 9.1')
bundlerVersion = '1'
} }
if (engine === 'ruby' && common.isHeadVersion(rubyVersion) && bundlerVersion === '2') { if ((engine === 'ruby' || engine === 'truffleruby') && common.isHeadVersion(rubyVersion) && bundlerVersion === '2') {
console.log(`Using Bundler 2 shipped with ${engine}-${rubyVersion}`) console.log(`Using Bundler 2 shipped with ${engine}-${rubyVersion}`)
} else if (engine === 'truffleruby' && bundlerVersion === '1') { } else if (engine === 'truffleruby' && !common.isHeadVersion(rubyVersion) && bundlerVersion === '1') {
console.log(`Using Bundler 1 shipped with ${engine}`) console.log(`Using Bundler 1 shipped with ${engine}`)
} else { } else {
const gem = path.join(rubyPrefix, 'bin', 'gem') const gem = path.join(rubyPrefix, 'bin', 'gem')
@@ -242,24 +235,22 @@ async function bundleInstall(gemfile, lockFile, platform, engine, version) {
// config // config
const path = 'vendor/bundle' const path = 'vendor/bundle'
if (lockFile !== null) {
await exec.exec('bundle', ['config', '--local', 'deployment', 'true'])
}
await exec.exec('bundle', ['config', '--local', 'path', path]) await exec.exec('bundle', ['config', '--local', 'path', path])
if (fs.existsSync(lockFile)) {
await exec.exec('bundle', ['config', '--local', 'deployment', 'true'])
} else {
// Generate the lockfile so we can use it to compute the cache key.
// This will also automatically pick up the latest gem versions compatible with the Gemfile.
await exec.exec('bundle', ['lock'])
}
// cache key // cache key
const paths = [path] const paths = [path]
const baseKey = await computeBaseKey(platform, engine, version) const baseKey = await computeBaseKey(platform, engine, version, lockFile)
let key = baseKey const key = `${baseKey}-${await common.hashFile(lockFile)}`
let restoreKeys // If only Gemfile.lock changes we can reuse part of the cache (but it will keep old gem versions in the cache)
if (lockFile !== null) { const restoreKeys = [`${baseKey}-`]
key += `-${lockFile}-${await common.hashFile(lockFile)}`
// If only Gemfile.lock we can reuse some of the cache (but it will keep old gem versions in the cache)
restoreKeys = [`${baseKey}-${lockFile}-`]
} else {
// Only exact key, to never mix native gems of different platforms or Ruby versions
restoreKeys = []
}
console.log(`Cache key: ${key}`) console.log(`Cache key: ${key}`)
// restore cache & install // restore cache & install
@@ -278,15 +269,11 @@ async function bundleInstall(gemfile, lockFile, platform, engine, version) {
console.log(`Found cache for key: ${cachedKey}`) console.log(`Found cache for key: ${cachedKey}`)
} }
let alreadyInstalled = false // Always run 'bundle install' to list the gems
if (cachedKey === key) { await exec.exec('bundle', ['install', '--jobs', '4'])
const exitCode = await exec.exec('bundle', ['check'], { ignoreReturnCode: true })
alreadyInstalled = (exitCode === 0)
}
if (!alreadyInstalled) {
await exec.exec('bundle', ['install', '--jobs', '4'])
// @actions/cache only allows to save for non-existing keys
if (cachedKey !== key) {
// Error handling from https://github.com/actions/cache/blob/master/src/save.ts // Error handling from https://github.com/actions/cache/blob/master/src/save.ts
console.log('Saving cache') console.log('Saving cache')
try { try {
@@ -305,8 +292,9 @@ async function bundleInstall(gemfile, lockFile, platform, engine, version) {
return true return true
} }
async function computeBaseKey(platform, engine, version) { async function computeBaseKey(platform, engine, version, lockFile) {
let baseKey = `setup-ruby-bundle-install-${platform}-${engine}-${version}` let key = `setup-ruby-bundler-cache-v2-${platform}-${engine}-${version}`
if (engine !== 'jruby' && common.isHeadVersion(version)) { if (engine !== 'jruby' && common.isHeadVersion(version)) {
let revision = ''; let revision = '';
await exec.exec('ruby', ['-e', 'print RUBY_REVISION'], { await exec.exec('ruby', ['-e', 'print RUBY_REVISION'], {
@@ -317,9 +305,11 @@ async function computeBaseKey(platform, engine, version) {
} }
} }
}); });
baseKey += `-revision-${revision}` key += `-revision-${revision}`
} }
return baseKey
key += `-${lockFile}`
return key
} }
if (__filename.endsWith('index.js')) { run() } if (__filename.endsWith('index.js')) { run() }
+1
View File
@@ -8,6 +8,7 @@ export function getVersions(platform) {
"2.5.0", "2.5.1", "2.5.2", "2.5.3", "2.5.4", "2.5.5", "2.5.6", "2.5.7", "2.5.8", "2.5.0", "2.5.1", "2.5.2", "2.5.3", "2.5.4", "2.5.5", "2.5.6", "2.5.7", "2.5.8",
"2.6.0", "2.6.1", "2.6.2", "2.6.3", "2.6.4", "2.6.5", "2.6.6", "2.6.0", "2.6.1", "2.6.2", "2.6.3", "2.6.4", "2.6.5", "2.6.6",
"2.7.0", "2.7.1", "2.7.2", "2.7.0", "2.7.1", "2.7.2",
"3.0.0-preview1",
"head", "debug", "head", "debug",
], ],
"jruby": [ "jruby": [
+23 -8
View File
@@ -7,7 +7,7 @@ const tc = require('@actions/tool-cache')
const common = require('./common') const common = require('./common')
const rubyBuilderVersions = require('./ruby-builder-versions') const rubyBuilderVersions = require('./ruby-builder-versions')
const builderReleaseTag = 'enable-shared' const builderReleaseTag = 'toolcache'
const releasesURL = 'https://github.com/ruby/ruby-builder/releases' const releasesURL = 'https://github.com/ruby/ruby-builder/releases'
const windows = common.windows const windows = common.windows
@@ -21,14 +21,22 @@ export async function install(platform, engine, version) {
} }
async function downloadAndExtract(platform, engine, version) { async function downloadAndExtract(platform, engine, version) {
const rubiesDir = windows ? `${common.drive}:` : path.join(os.homedir(), '.rubies') let rubyPrefix
if (common.shouldExtractInToolCache(engine, version)) {
rubyPrefix = common.getToolCacheRubyPrefix(platform, version)
} else if (windows) {
rubyPrefix = path.join(`${common.drive}:`, `${engine}-${version}`)
} else {
rubyPrefix = path.join(os.homedir(), '.rubies', `${engine}-${version}`)
}
const rubyPrefix = path.join(rubiesDir, `${engine}-${version}`) const parentDir = path.dirname(rubyPrefix)
// Set the PATH now, so the MSYS2 'tar' is in Path on Windows // Set the PATH now, so the MSYS2 'tar' is in Path on Windows
common.setupPath([path.join(rubyPrefix, 'bin')]) common.setupPath([path.join(rubyPrefix, 'bin')])
await io.mkdirP(rubiesDir) await io.rmRF(rubyPrefix)
await io.mkdirP(parentDir)
const downloadPath = await common.measure('Downloading Ruby', async () => { const downloadPath = await common.measure('Downloading Ruby', async () => {
const url = getDownloadURL(platform, engine, version) const url = getDownloadURL(platform, engine, version)
@@ -39,9 +47,9 @@ async function downloadAndExtract(platform, engine, version) {
await common.measure('Extracting Ruby', async () => { await common.measure('Extracting Ruby', async () => {
if (windows) { if (windows) {
// Windows 2016 doesn't have system tar, use MSYS2's, it needs unix style paths // Windows 2016 doesn't have system tar, use MSYS2's, it needs unix style paths
await exec.exec('tar', [ '-xz', '-C', common.win2nix(rubiesDir), '-f', common.win2nix(downloadPath) ]) await exec.exec('tar', [ '-xz', '-C', common.win2nix(parentDir), '-f', common.win2nix(downloadPath) ])
} else { } else {
await exec.exec('tar', [ '-xz', '-C', rubiesDir, '-f', downloadPath ]) await exec.exec('tar', [ '-xz', '-C', parentDir, '-f', downloadPath ])
} }
}) })
@@ -49,10 +57,17 @@ async function downloadAndExtract(platform, engine, version) {
} }
function getDownloadURL(platform, engine, version) { function getDownloadURL(platform, engine, version) {
let builderPlatform = platform
if (platform.startsWith('windows-')) {
builderPlatform = 'windows-latest'
} else if (platform.startsWith('macos-')) {
builderPlatform = 'macos-latest'
}
if (common.isHeadVersion(version)) { if (common.isHeadVersion(version)) {
return getLatestHeadBuildURL(platform, engine, version) return getLatestHeadBuildURL(builderPlatform, engine, version)
} else { } else {
return `${releasesURL}/download/${builderReleaseTag}/${engine}-${version}-${platform}.tar.gz` return `${releasesURL}/download/${builderReleaseTag}/${engine}-${version}-${builderPlatform}.tar.gz`
} }
} }
+13 -2
View File
@@ -6,6 +6,7 @@ const path = require('path')
const cp = require('child_process') const cp = require('child_process')
const core = require('@actions/core') const core = require('@actions/core')
const exec = require('@actions/exec') const exec = require('@actions/exec')
const io = require('@actions/io')
const tc = require('@actions/tool-cache') const tc = require('@actions/tool-cache')
const common = require('./common') const common = require('./common')
const rubyInstallerVersions = require('./windows-versions').versions const rubyInstallerVersions = require('./windows-versions').versions
@@ -35,7 +36,13 @@ export async function install(platform, engine, version) {
} }
const base = url.slice(url.lastIndexOf('/') + 1, url.length - '.7z'.length) const base = url.slice(url.lastIndexOf('/') + 1, url.length - '.7z'.length)
const rubyPrefix = `${drive}:\\${base}` let rubyPrefix
if (common.shouldExtractInToolCache(engine, version)) {
rubyPrefix = common.getToolCacheRubyPrefix(platform, version)
} else {
rubyPrefix = `${drive}:\\${base}`
}
const parentDir = path.dirname(rubyPrefix)
let toolchainPaths = (version === 'mswin') ? await setupMSWin() : await setupMingw(version) let toolchainPaths = (version === 'mswin') ? await setupMSWin() : await setupMingw(version)
@@ -47,7 +54,11 @@ export async function install(platform, engine, version) {
}) })
await common.measure('Extracting Ruby', async () => await common.measure('Extracting Ruby', async () =>
exec.exec('7z', ['x', downloadPath, `-xr!${base}\\share\\doc`, `-o${drive}:\\`], { silent: true })) exec.exec('7z', ['x', downloadPath, `-xr!${base}\\share\\doc`, `-o${parentDir}`], { silent: true }))
if (base !== path.basename(rubyPrefix)) {
await io.mv(path.join(parentDir, base), rubyPrefix)
}
return rubyPrefix return rubyPrefix
} }