From 101f62de1d2aff7846f52bf23989d934bd75874d Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Sun, 5 Jan 2020 18:07:37 +0100 Subject: [PATCH] Add support for Windows --- .eslintrc.json | 3 +- .github/workflows/test.yml | 14 +++- README.md | 15 ++-- dist/index.js | 137 +++++++++++++++++++++++++++++++++---- index.js | 36 ++++++---- package.json | 2 +- windows.js | 60 ++++++++++++++++ 7 files changed, 231 insertions(+), 36 deletions(-) create mode 100644 windows.js diff --git a/.eslintrc.json b/.eslintrc.json index d097bf7..52aedaf 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -10,7 +10,8 @@ "SharedArrayBuffer": "readonly" }, "parserOptions": { - "ecmaVersion": 2018 + "ecmaVersion": 2018, + "sourceType": "module" }, "rules": { } diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5d3ad94..b54ba08 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,9 +5,16 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-16.04, ubuntu-18.04, macos-latest ] + os: [ ubuntu-16.04, ubuntu-18.04, macos-latest, windows-latest ] # Use various version syntaxes here for testing ruby: [ 2.3, ruby-2.4, 2.5, ruby-2.6.5, 2.7.0, jruby, truffleruby ] + exclude: + - os: windows-latest + ruby: 2.3 + - os: windows-latest + ruby: jruby + - os: windows-latest + ruby: truffleruby runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 @@ -15,4 +22,7 @@ jobs: with: ruby-version: ${{ matrix.ruby }} - run: ruby -v - - run: ruby -ropen-uri -e 'puts open("https://rubygems.org/") { |f| f.read(2014) }' + - run: ridk version + if: matrix.os == 'windows-latest' + - run: ruby -ropen-uri -e 'puts open(%{https://rubygems.org/}) { |f| f.read(2014) }' + - run: gem install json --no-document diff --git a/README.md b/README.md index d1a82bf..62fda62 100644 --- a/README.md +++ b/README.md @@ -27,10 +27,13 @@ which means Ruby 2.3 is unmaintained and considered insecure. ### Supported Platforms -The action works for the `ubuntu-16.04`, `ubuntu-18.04` and `macos-latest` GitHub-hosted runners. -`windows-latest` is not yet supported. +The action works for all GitHub-hosted runners: +`ubuntu-16.04`, `ubuntu-18.04`, `macos-latest` and `windows-latest`. -The prebuilt rubies are generated by https://github.com/eregon/ruby-install-builder. +Ruby 2.3, JRuby and TruffleRuby are not yet supported on `windows-latest`. + +The prebuilt rubies are generated by https://github.com/eregon/ruby-install-builder +and on Windows by https://github.com/oneclick/rubyinstaller2. ## Usage @@ -82,6 +85,8 @@ jobs: ## Limitations -* Currently does not work on Windows since the builder doesn't build on Windows. - https://github.com/MSP-Greg/actions-ruby is an alternative on Windows. * This action currently only works with GitHub-hosted runners, not private runners. + +## Credits + +Most of the Windows logic is from https://github.com/MSP-Greg/actions-ruby by MSP-Greg. diff --git a/dist/index.js b/dist/index.js index 28fc2ac..db8a934 100644 --- a/dist/index.js +++ b/dist/index.js @@ -36,6 +36,8 @@ module.exports = /******/ // Load entry module and return exports /******/ return __webpack_require__(104); /******/ }; +/******/ // initialize runtime +/******/ runtime(__webpack_require__); /******/ /******/ // run startup /******/ return startup(); @@ -1383,33 +1385,43 @@ const core = __webpack_require__(470) const io = __webpack_require__(1) const tc = __webpack_require__(533) const axios = __webpack_require__(53) +const windows = __webpack_require__(664) const releasesURL = 'https://github.com/eregon/ruby-install-builder/releases' const metadataURL = 'https://raw.githubusercontent.com/eregon/ruby-install-builder/metadata' async function run() { try { + const platform = getVirtualEnvironmentName() const ruby = await getRubyEngineAndVersion(core.getInput('ruby-version')) - const rubiesDir = `${process.env.HOME}/.rubies` - await io.mkdirP(rubiesDir) - - const platform = getVirtualEnvironmentName() - const tag = await getLatestReleaseTag() - const url = `${releasesURL}/download/${tag}/${ruby}-${platform}.tar.gz` - console.log(url) - - const downloadPath = await tc.downloadTool(url) - await tc.extractTar(downloadPath, rubiesDir) - - const rubyPrefix = `${rubiesDir}/${ruby}` - core.addPath(`${rubyPrefix}/bin`) + let rubyPrefix + if (platform === 'windows-latest') { + rubyPrefix = await windows.downloadExtractAndSetPATH(ruby) + } else { + rubyPrefix = await downloadAndExtract(platform, ruby) + core.addPath(`${rubyPrefix}/bin`) + } core.setOutput('ruby-prefix', rubyPrefix) } catch (error) { core.setFailed(error.message) } } +async function downloadAndExtract(platform, ruby) { + const rubiesDir = `${process.env.HOME}/.rubies` + await io.mkdirP(rubiesDir) + + const tag = await getLatestReleaseTag() + const url = `${releasesURL}/download/${tag}/${ruby}-${platform}.tar.gz` + console.log(url) + + const downloadPath = await tc.downloadTool(url) + await tc.extractTar(downloadPath, rubiesDir) + + return `${rubiesDir}/${ruby}` +} + async function getLatestReleaseTag() { const response = await axios.get(`${metadataURL}/latest_release.tag`) return response.data.trim() @@ -6440,6 +6452,76 @@ function plural(ms, n, name) { } +/***/ }), + +/***/ 664: +/***/ (function(__unusedmodule, __webpack_exports__, __webpack_require__) { + +"use strict"; +__webpack_require__.r(__webpack_exports__); +/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "downloadExtractAndSetPATH", function() { return downloadExtractAndSetPATH; }); +// Most of this logic is from +// https://github.com/MSP-Greg/actions-ruby/blob/master/lib/main.js + +const core = __webpack_require__(470) +const exec = __webpack_require__(986) +const tc = __webpack_require__(533) + +const releasesURL = 'https://github.com/oneclick/rubyinstaller2/releases' + +async function downloadExtractAndSetPATH(ruby) { + const version = ruby.split('-', 2)[1] + if (!ruby.startsWith('ruby-') || version.startsWith('2.3')) { + throw new Error(`Only ruby >= 2.4 is supported on Windows currently (input: ${ruby})`) + } + const tag = `RubyInstaller-${version}-1` + const base = `${tag.toLowerCase()}-x64` + + const url = `${releasesURL}/download/${tag}/${base}.7z` + console.log(url) + + const downloadPath = await tc.downloadTool(url) + await exec.exec(`7z x ${downloadPath} -xr!${base}\\share\\doc -oC:\\`) + const rubyPrefix = `C:\\${base}` + + const msys2 = await linkMSYS2() + const newPath = setupPath(msys2, rubyPrefix) + core.exportVariable('PATH', newPath) + + return rubyPrefix +} + +async function linkMSYS2() { + const toolCacheVersions = tc.findAllVersions('Ruby') + toolCacheVersions.sort() + const latestVersion = toolCacheVersions.slice(-1)[0] + const latestHostedRuby = tc.find('Ruby', latestVersion) + + const hostedMSYS2 = `${latestHostedRuby}\\msys64` + const msys2 = 'C:\\msys64' + await exec.exec(`cmd /c mklink /D ${msys2} ${hostedMSYS2}`) + return msys2 +} + +function setupPath(msys2, rubyPrefix) { + let path = process.env['PATH'].split(';') + + // Remove conflicting dev tools from PATH + path = path.filter(e => !e.match(/\b(Chocolatey|CMake|mingw64|OpenSSL|Strawberry)\b/)) + + // Remove default Ruby in PATH + path = path.filter(e => !e.match(/\bRuby\b/)) + + // Add MSYS2 in PATH + path.unshift(`${msys2}\\mingw64`, `${msys2}\\usr`) + + // Add the downloaded Ruby in PATH + path.unshift(`${rubyPrefix}\\bin`) + + return path.join(';') +} + + /***/ }), /***/ 669: @@ -8762,4 +8844,31 @@ exports.exec = exec; /***/ }) -/******/ }); \ No newline at end of file +/******/ }, +/******/ function(__webpack_require__) { // webpackRuntimeModules +/******/ "use strict"; +/******/ +/******/ /* webpack/runtime/make namespace object */ +/******/ !function() { +/******/ // define __esModule on exports +/******/ __webpack_require__.r = function(exports) { +/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { +/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ Object.defineProperty(exports, '__esModule', { value: true }); +/******/ }; +/******/ }(); +/******/ +/******/ /* webpack/runtime/define property getter */ +/******/ !function() { +/******/ // define getter function for harmony exports +/******/ var hasOwnProperty = Object.prototype.hasOwnProperty; +/******/ __webpack_require__.d = function(exports, name, getter) { +/******/ if(!hasOwnProperty.call(exports, name)) { +/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); +/******/ } +/******/ }; +/******/ }(); +/******/ +/******/ } +); \ No newline at end of file diff --git a/index.js b/index.js index 1b07674..1ebeb73 100644 --- a/index.js +++ b/index.js @@ -4,33 +4,43 @@ const core = require('@actions/core') const io = require('@actions/io') const tc = require('@actions/tool-cache') const axios = require('axios') +const windows = require('./windows') const releasesURL = 'https://github.com/eregon/ruby-install-builder/releases' const metadataURL = 'https://raw.githubusercontent.com/eregon/ruby-install-builder/metadata' async function run() { try { + const platform = getVirtualEnvironmentName() const ruby = await getRubyEngineAndVersion(core.getInput('ruby-version')) - const rubiesDir = `${process.env.HOME}/.rubies` - await io.mkdirP(rubiesDir) - - const platform = getVirtualEnvironmentName() - const tag = await getLatestReleaseTag() - const url = `${releasesURL}/download/${tag}/${ruby}-${platform}.tar.gz` - console.log(url) - - const downloadPath = await tc.downloadTool(url) - await tc.extractTar(downloadPath, rubiesDir) - - const rubyPrefix = `${rubiesDir}/${ruby}` - core.addPath(`${rubyPrefix}/bin`) + let rubyPrefix + if (platform === 'windows-latest') { + rubyPrefix = await windows.downloadExtractAndSetPATH(ruby) + } else { + rubyPrefix = await downloadAndExtract(platform, ruby) + core.addPath(`${rubyPrefix}/bin`) + } core.setOutput('ruby-prefix', rubyPrefix) } catch (error) { core.setFailed(error.message) } } +async function downloadAndExtract(platform, ruby) { + const rubiesDir = `${process.env.HOME}/.rubies` + await io.mkdirP(rubiesDir) + + const tag = await getLatestReleaseTag() + const url = `${releasesURL}/download/${tag}/${ruby}-${platform}.tar.gz` + console.log(url) + + const downloadPath = await tc.downloadTool(url) + await tc.extractTar(downloadPath, rubiesDir) + + return `${rubiesDir}/${ruby}` +} + async function getLatestReleaseTag() { const response = await axios.get(`${metadataURL}/latest_release.tag`) return response.data.trim() diff --git a/package.json b/package.json index 01f5e4c..1a0351c 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Download a prebuilt ruby and add it to $PATH", "main": "index.js", "scripts": { - "lint": "eslint index.js", + "lint": "eslint *.js", "package": "ncc build index.js -o dist" }, "repository": { diff --git a/windows.js b/windows.js new file mode 100644 index 0000000..202d100 --- /dev/null +++ b/windows.js @@ -0,0 +1,60 @@ +// Most of this logic is from +// https://github.com/MSP-Greg/actions-ruby/blob/master/lib/main.js + +const core = require('@actions/core') +const exec = require('@actions/exec') +const tc = require('@actions/tool-cache') + +const releasesURL = 'https://github.com/oneclick/rubyinstaller2/releases' + +export async function downloadExtractAndSetPATH(ruby) { + const version = ruby.split('-', 2)[1] + if (!ruby.startsWith('ruby-') || version.startsWith('2.3')) { + throw new Error(`Only ruby >= 2.4 is supported on Windows currently (input: ${ruby})`) + } + const tag = `RubyInstaller-${version}-1` + const base = `${tag.toLowerCase()}-x64` + + const url = `${releasesURL}/download/${tag}/${base}.7z` + console.log(url) + + const downloadPath = await tc.downloadTool(url) + await exec.exec(`7z x ${downloadPath} -xr!${base}\\share\\doc -oC:\\`) + const rubyPrefix = `C:\\${base}` + + const msys2 = await linkMSYS2() + const newPath = setupPath(msys2, rubyPrefix) + core.exportVariable('PATH', newPath) + + return rubyPrefix +} + +async function linkMSYS2() { + const toolCacheVersions = tc.findAllVersions('Ruby') + toolCacheVersions.sort() + const latestVersion = toolCacheVersions.slice(-1)[0] + const latestHostedRuby = tc.find('Ruby', latestVersion) + + const hostedMSYS2 = `${latestHostedRuby}\\msys64` + const msys2 = 'C:\\msys64' + await exec.exec(`cmd /c mklink /D ${msys2} ${hostedMSYS2}`) + return msys2 +} + +function setupPath(msys2, rubyPrefix) { + let path = process.env['PATH'].split(';') + + // Remove conflicting dev tools from PATH + path = path.filter(e => !e.match(/\b(Chocolatey|CMake|mingw64|OpenSSL|Strawberry)\b/)) + + // Remove default Ruby in PATH + path = path.filter(e => !e.match(/\bRuby\b/)) + + // Add MSYS2 in PATH + path.unshift(`${msys2}\\mingw64`, `${msys2}\\usr`) + + // Add the downloaded Ruby in PATH + path.unshift(`${rubyPrefix}\\bin`) + + return path.join(';') +}