Files
setup-ruby/windows.js
T

194 lines
6.6 KiB
JavaScript

// Most of this logic is from
// https://github.com/MSP-Greg/actions-ruby/blob/master/lib/main.js
const fs = require('fs')
const path = require('path')
const cp = require('child_process')
const core = require('@actions/core')
const exec = require('@actions/exec')
const io = require('@actions/io')
const tc = require('@actions/tool-cache')
const common = require('./common')
const rubyInstallerVersions = require('./windows-versions').versions
const drive = common.drive
// needed for 2.0-2.3, and mswin, cert file used by Git for Windows
const certFile = 'C:\\Program Files\\Git\\mingw64\\ssl\\cert.pem'
// location & path for old RubyInstaller DevKit (MSYS), Ruby 2.0-2.3
const msys = `${drive}:\\DevKit64`
const msysPathEntries = [`${msys}\\mingw\\x86_64-w64-mingw32\\bin`, `${msys}\\mingw\\bin`, `${msys}\\bin`]
export function getAvailableVersions(platform, engine) {
if (engine === 'ruby') {
return Object.keys(rubyInstallerVersions)
} else {
return undefined
}
}
export async function install(platform, engine, version) {
const url = rubyInstallerVersions[version]
if (!url.endsWith('.7z')) {
throw new Error(`URL should end in .7z: ${url}`)
}
const base = url.slice(url.lastIndexOf('/') + 1, url.length - '.7z'.length)
let rubyPrefix, inToolCache
if (common.shouldUseToolCache(engine, version)) {
inToolCache = tc.find('Ruby', version)
if (inToolCache) {
rubyPrefix = inToolCache
} else {
rubyPrefix = common.getToolCacheRubyPrefix(platform, version)
}
} else {
rubyPrefix = `${drive}:\\${base}`
}
let toolchainPaths = (version === 'mswin') ? await setupMSWin() : await setupMingw(version)
if (!inToolCache) {
await downloadAndExtract(engine, version, url, base, rubyPrefix);
}
const winMSYS2Type = common.setupPath([`${rubyPrefix}\\bin`, ...toolchainPaths])
const virtualEnv = common.getVirtualEnvironmentName()
if (( winMSYS2Type === 'ucrt64') || !['windows-2019', 'windows-2016'].includes(virtualEnv)) {
await installGCCTools(winMSYS2Type)
if (!['windows-2019', 'windows-2016'].includes(virtualEnv)) {
await installMSY2Tools()
}
}
return rubyPrefix
}
// Actions windows-2022 image does not contain any mingw or ucrt build tools. Install tools for it,
// and also install ucrt tools on earlier versions, which have msys2 and mingw tools preinstalled.
async function installGCCTools(type) {
const downloadPath = await common.measure(`Download ${type} build tools`, async () => {
let url = `https://github.com/MSP-Greg/setup-msys2-gcc/releases/download/msys2-gcc-pkgs/${type}.7z`
console.log(url)
return await tc.downloadTool(url)
})
await common.measure(`Extracting ${type} build tools`, async () =>
// -aoa overwrite existing, -bd disable progress indicator
exec.exec('7z', ['x', downloadPath, '-aoa', '-bd', '-oC:\\msys64'], { silent: true }))
}
// Actions windows-2022 image does not contain any MSYS2 build tools. Install tools for it.
// A subset of the MSYS2 base-devel group
async function installMSY2Tools() {
const downloadPath = await common.measure(`Download msys2 build tools`, async () => {
let url = `https://github.com/MSP-Greg/setup-msys2-gcc/releases/download/msys2-gcc-pkgs/msys2.7z`
console.log(url)
return await tc.downloadTool(url)
})
await common.measure(`Extracting msys2 build tools`, async () =>
// -aoa overwrite existing, -bd disable progress indicator
exec.exec('7z', ['x', downloadPath, '-aoa', '-bd', '-oC:\\msys64'], { silent: true }))
}
async function downloadAndExtract(engine, version, url, base, rubyPrefix) {
const parentDir = path.dirname(rubyPrefix)
const downloadPath = await common.measure('Downloading Ruby', async () => {
console.log(url)
return await tc.downloadTool(url)
})
await common.measure('Extracting Ruby', async () =>
exec.exec('7z', ['x', downloadPath, '-bd', `-xr!${base}\\share\\doc`, `-o${parentDir}`], { silent: true }))
if (base !== path.basename(rubyPrefix)) {
await io.mv(path.join(parentDir, base), rubyPrefix)
}
if (common.shouldUseToolCache(engine, version)) {
common.createToolCacheCompleteFile(rubyPrefix)
}
}
async function setupMingw(version) {
core.exportVariable('MAKE', 'make.exe')
if (common.floatVersion(version) <= 2.3) {
core.exportVariable('SSL_CERT_FILE', certFile)
await common.measure('Installing MSYS', async () => installMSYS(version))
return msysPathEntries
} else {
return []
}
}
// Ruby 2.0, 2.1, 2.2 and 2.3
async function installMSYS(version) {
const url = 'https://github.com/oneclick/rubyinstaller/releases/download/devkit-4.7.2/DevKit-mingw64-64-4.7.2-20130224-1432-sfx.exe'
const downloadPath = await tc.downloadTool(url)
await exec.exec('7z', ['x', downloadPath, `-o${msys}`], { silent: true })
// below are set in the old devkit.rb file ?
core.exportVariable('RI_DEVKIT', msys)
core.exportVariable('CC' , 'gcc')
core.exportVariable('CXX', 'g++')
core.exportVariable('CPP', 'cpp')
core.info(`Installed RubyInstaller DevKit for Ruby ${version}`)
}
async function setupMSWin() {
core.exportVariable('MAKE', 'nmake.exe')
// All standard MSVC OpenSSL builds use C:\Program Files\Common Files\SSL
const certsDir = 'C:\\Program Files\\Common Files\\SSL\\certs'
if (!fs.existsSync(certsDir)) {
fs.mkdirSync(certsDir)
}
// cert.pem location is hard-coded by OpenSSL msvc builds
const cert = 'C:\\Program Files\\Common Files\\SSL\\cert.pem'
if (!fs.existsSync(cert)) {
fs.copyFileSync(certFile, cert)
}
return await common.measure('Setting up MSVC environment', async () => addVCVARSEnv())
}
/* Sets MSVC environment for use in Actions
* allows steps to run without running vcvars*.bat, also for PowerShell
* adds a convenience VCVARS environment variable
* this assumes a single Visual Studio version being available in the windows-latest image */
export function addVCVARSEnv() {
const vcVars = '"C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\VC\\Auxiliary\\Build\\vcvars64.bat"'
core.exportVariable('VCVARS', vcVars)
let newEnv = new Map()
let cmd = `cmd.exe /c "${vcVars} && set"`
let newSet = cp.execSync(cmd).toString().trim().split(/\r?\n/)
newSet = newSet.filter(line => /\S=\S/.test(line))
newSet.forEach(s => {
let [k,v] = common.partition(s, '=')
newEnv.set(k,v)
})
let newPathEntries = undefined
for (let [k, v] of newEnv) {
if (process.env[k] !== v) {
if (/^Path$/i.test(k)) {
const newPathStr = v.replace(`${path.delimiter}${process.env['Path']}`, '')
newPathEntries = newPathStr.split(path.delimiter)
} else {
core.exportVariable(k, v)
}
}
}
return newPathEntries
}