mirror of
https://github.com/electron/node-gyp.git
synced 2025-08-15 12:58:19 +02:00
lib: migrate requests to fetch (#2220)
PR-URL: https://github.com/nodejs/node-gyp/pull/2220 Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
This commit is contained in:
parent
a78b584236
commit
e81602ef55
5 changed files with 394 additions and 610 deletions
551
lib/install.js
551
lib/install.js
|
@ -4,55 +4,42 @@ const fs = require('graceful-fs')
|
|||
const os = require('os')
|
||||
const tar = require('tar')
|
||||
const path = require('path')
|
||||
const util = require('util')
|
||||
const stream = require('stream')
|
||||
const crypto = require('crypto')
|
||||
const log = require('npmlog')
|
||||
const semver = require('semver')
|
||||
const request = require('request')
|
||||
const fetch = require('make-fetch-happen')
|
||||
const processRelease = require('./process-release')
|
||||
const win = process.platform === 'win32'
|
||||
const getProxyFromURI = require('./proxy')
|
||||
const streamPipeline = util.promisify(stream.pipeline)
|
||||
|
||||
function install (fs, gyp, argv, callback) {
|
||||
var release = processRelease(argv, gyp, process.version, process.release)
|
||||
/**
|
||||
* @param {typeof import('graceful-fs')} fs
|
||||
*/
|
||||
|
||||
// ensure no double-callbacks happen
|
||||
function cb (err) {
|
||||
if (cb.done) {
|
||||
return
|
||||
}
|
||||
cb.done = true
|
||||
if (err) {
|
||||
log.warn('install', 'got an error, rolling back install')
|
||||
// roll-back the install if anything went wrong
|
||||
gyp.commands.remove([release.versionDir], function () {
|
||||
callback(err)
|
||||
})
|
||||
} else {
|
||||
callback(null, release.version)
|
||||
}
|
||||
}
|
||||
async function install (fs, gyp, argv) {
|
||||
const release = processRelease(argv, gyp, process.version, process.release)
|
||||
|
||||
// Determine which node dev files version we are installing
|
||||
log.verbose('install', 'input version string %j', release.version)
|
||||
|
||||
if (!release.semver) {
|
||||
// could not parse the version string with semver
|
||||
return callback(new Error('Invalid version number: ' + release.version))
|
||||
throw new Error('Invalid version number: ' + release.version)
|
||||
}
|
||||
|
||||
if (semver.lt(release.version, '0.8.0')) {
|
||||
return callback(new Error('Minimum target version is `0.8.0` or greater. Got: ' + release.version))
|
||||
throw new Error('Minimum target version is `0.8.0` or greater. Got: ' + release.version)
|
||||
}
|
||||
|
||||
// 0.x.y-pre versions are not published yet and cannot be installed. Bail.
|
||||
if (release.semver.prerelease[0] === 'pre') {
|
||||
log.verbose('detected "pre" node version', release.version)
|
||||
if (gyp.opts.nodedir) {
|
||||
log.verbose('--nodedir flag was passed; skipping install', gyp.opts.nodedir)
|
||||
callback()
|
||||
} else {
|
||||
callback(new Error('"pre" versions of node cannot be installed, use the --nodedir flag instead'))
|
||||
if (!gyp.opts.nodedir) {
|
||||
throw new Error('"pre" versions of node cannot be installed, use the --nodedir flag instead')
|
||||
}
|
||||
log.verbose('--nodedir flag was passed; skipping install', gyp.opts.nodedir)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -60,296 +47,225 @@ function install (fs, gyp, argv, callback) {
|
|||
log.verbose('install', 'installing version: %s', release.versionDir)
|
||||
|
||||
// the directory where the dev files will be installed
|
||||
var devDir = path.resolve(gyp.devDir, release.versionDir)
|
||||
const devDir = path.resolve(gyp.devDir, release.versionDir)
|
||||
|
||||
// If '--ensure' was passed, then don't *always* install the version;
|
||||
// check if it is already installed, and only install when needed
|
||||
if (gyp.opts.ensure) {
|
||||
log.verbose('install', '--ensure was passed, so won\'t reinstall if already installed')
|
||||
fs.stat(devDir, function (err) {
|
||||
if (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
log.verbose('install', 'version not already installed, continuing with install', release.version)
|
||||
go()
|
||||
} else if (err.code === 'EACCES') {
|
||||
eaccesFallback(err)
|
||||
} else {
|
||||
cb(err)
|
||||
try {
|
||||
await fs.promises.stat(devDir)
|
||||
} catch (err) {
|
||||
if (err.code === 'ENOENT') {
|
||||
log.verbose('install', 'version not already installed, continuing with install', release.version)
|
||||
try {
|
||||
return await go()
|
||||
} catch (err) {
|
||||
return rollback(err)
|
||||
}
|
||||
return
|
||||
} else if (err.code === 'EACCES') {
|
||||
return eaccesFallback(err)
|
||||
}
|
||||
log.verbose('install', 'version is already installed, need to check "installVersion"')
|
||||
var installVersionFile = path.resolve(devDir, 'installVersion')
|
||||
fs.readFile(installVersionFile, 'ascii', function (err, ver) {
|
||||
if (err && err.code !== 'ENOENT') {
|
||||
return cb(err)
|
||||
}
|
||||
var installVersion = parseInt(ver, 10) || 0
|
||||
log.verbose('got "installVersion"', installVersion)
|
||||
log.verbose('needs "installVersion"', gyp.package.installVersion)
|
||||
if (installVersion < gyp.package.installVersion) {
|
||||
log.verbose('install', 'version is no good; reinstalling')
|
||||
go()
|
||||
} else {
|
||||
log.verbose('install', 'version is good')
|
||||
cb()
|
||||
}
|
||||
})
|
||||
})
|
||||
throw err
|
||||
}
|
||||
log.verbose('install', 'version is already installed, need to check "installVersion"')
|
||||
const installVersionFile = path.resolve(devDir, 'installVersion')
|
||||
let installVersion = 0
|
||||
try {
|
||||
const ver = await fs.promises.readFile(installVersionFile, 'ascii')
|
||||
installVersion = parseInt(ver, 10) || 0
|
||||
} catch (err) {
|
||||
if (err.code !== 'ENOENT') {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
log.verbose('got "installVersion"', installVersion)
|
||||
log.verbose('needs "installVersion"', gyp.package.installVersion)
|
||||
if (installVersion < gyp.package.installVersion) {
|
||||
log.verbose('install', 'version is no good; reinstalling')
|
||||
try {
|
||||
return await go()
|
||||
} catch (err) {
|
||||
return rollback(err)
|
||||
}
|
||||
}
|
||||
log.verbose('install', 'version is good')
|
||||
} else {
|
||||
go()
|
||||
try {
|
||||
return await go()
|
||||
} catch (err) {
|
||||
return rollback(err)
|
||||
}
|
||||
}
|
||||
|
||||
function getContentSha (res, callback) {
|
||||
var shasum = crypto.createHash('sha256')
|
||||
res.on('data', function (chunk) {
|
||||
shasum.update(chunk)
|
||||
}).on('end', function () {
|
||||
callback(null, shasum.digest('hex'))
|
||||
})
|
||||
}
|
||||
|
||||
function go () {
|
||||
async function go () {
|
||||
log.verbose('ensuring nodedir is created', devDir)
|
||||
|
||||
// first create the dir for the node dev files
|
||||
fs.mkdir(devDir, { recursive: true }, function (err, created) {
|
||||
if (err) {
|
||||
if (err.code === 'EACCES') {
|
||||
eaccesFallback(err)
|
||||
} else {
|
||||
cb(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
try {
|
||||
const created = await fs.promises.mkdir(devDir, { recursive: true })
|
||||
|
||||
if (created) {
|
||||
log.verbose('created nodedir', created)
|
||||
}
|
||||
|
||||
// now download the node tarball
|
||||
var tarPath = gyp.opts.tarball
|
||||
var badDownload = false
|
||||
var extractCount = 0
|
||||
var contentShasums = {}
|
||||
var expectShasums = {}
|
||||
|
||||
// checks if a file to be extracted from the tarball is valid.
|
||||
// only .h header files and the gyp files get extracted
|
||||
function isValid (path) {
|
||||
var isValid = valid(path)
|
||||
if (isValid) {
|
||||
log.verbose('extracted file from tarball', path)
|
||||
extractCount++
|
||||
} else {
|
||||
// invalid
|
||||
log.silly('ignoring from tarball', path)
|
||||
}
|
||||
return isValid
|
||||
} catch (err) {
|
||||
if (err.code === 'EACCES') {
|
||||
return eaccesFallback(err)
|
||||
}
|
||||
|
||||
// download the tarball and extract!
|
||||
if (tarPath) {
|
||||
return tar.extract({
|
||||
file: tarPath,
|
||||
strip: 1,
|
||||
filter: isValid,
|
||||
cwd: devDir
|
||||
}).then(afterTarball, cb)
|
||||
}
|
||||
throw err
|
||||
}
|
||||
|
||||
// now download the node tarball
|
||||
const tarPath = gyp.opts.tarball
|
||||
let extractCount = 0
|
||||
const contentShasums = {}
|
||||
const expectShasums = {}
|
||||
|
||||
// checks if a file to be extracted from the tarball is valid.
|
||||
// only .h header files and the gyp files get extracted
|
||||
function isValid (path) {
|
||||
const isValid = valid(path)
|
||||
if (isValid) {
|
||||
log.verbose('extracted file from tarball', path)
|
||||
extractCount++
|
||||
} else {
|
||||
// invalid
|
||||
log.silly('ignoring from tarball', path)
|
||||
}
|
||||
return isValid
|
||||
}
|
||||
|
||||
// download the tarball and extract!
|
||||
|
||||
if (tarPath) {
|
||||
await tar.extract({
|
||||
file: tarPath,
|
||||
strip: 1,
|
||||
filter: isValid,
|
||||
cwd: devDir
|
||||
})
|
||||
} else {
|
||||
try {
|
||||
var req = download(gyp, process.env, release.tarballUrl)
|
||||
} catch (e) {
|
||||
return cb(e)
|
||||
}
|
||||
const res = await download(gyp, release.tarballUrl)
|
||||
|
||||
// something went wrong downloading the tarball?
|
||||
req.on('error', function (err) {
|
||||
if (res.status !== 200) {
|
||||
throw new Error(`${res.status} response downloading ${release.tarballUrl}`)
|
||||
}
|
||||
|
||||
await streamPipeline(
|
||||
res.body,
|
||||
// content checksum
|
||||
new ShaSum((_, checksum) => {
|
||||
const filename = path.basename(release.tarballUrl).trim()
|
||||
contentShasums[filename] = checksum
|
||||
log.verbose('content checksum', filename, checksum)
|
||||
}),
|
||||
tar.extract({
|
||||
strip: 1,
|
||||
cwd: devDir,
|
||||
filter: isValid
|
||||
})
|
||||
)
|
||||
} catch (err) {
|
||||
// something went wrong downloading the tarball?
|
||||
if (err.code === 'ENOTFOUND') {
|
||||
return cb(new Error('This is most likely not a problem with node-gyp or the package itself and\n' +
|
||||
throw new Error('This is most likely not a problem with node-gyp or the package itself and\n' +
|
||||
'is related to network connectivity. In most cases you are behind a proxy or have bad \n' +
|
||||
'network settings.'))
|
||||
'network settings.')
|
||||
}
|
||||
badDownload = true
|
||||
cb(err)
|
||||
})
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
req.on('close', function () {
|
||||
if (extractCount === 0) {
|
||||
cb(new Error('Connection closed while downloading tarball file'))
|
||||
}
|
||||
})
|
||||
// invoked after the tarball has finished being extracted
|
||||
if (extractCount === 0) {
|
||||
throw new Error('There was a fatal problem while downloading/extracting the tarball')
|
||||
}
|
||||
|
||||
req.on('response', function (res) {
|
||||
if (res.statusCode !== 200) {
|
||||
badDownload = true
|
||||
cb(new Error(res.statusCode + ' response downloading ' + release.tarballUrl))
|
||||
return
|
||||
}
|
||||
// content checksum
|
||||
getContentSha(res, function (_, checksum) {
|
||||
var filename = path.basename(release.tarballUrl).trim()
|
||||
contentShasums[filename] = checksum
|
||||
log.verbose('content checksum', filename, checksum)
|
||||
})
|
||||
log.verbose('tarball', 'done parsing tarball')
|
||||
|
||||
// start unzipping and untaring
|
||||
res.pipe(tar.extract({
|
||||
strip: 1,
|
||||
cwd: devDir,
|
||||
filter: isValid
|
||||
}).on('close', afterTarball).on('error', cb))
|
||||
})
|
||||
const installVersionPath = path.resolve(devDir, 'installVersion')
|
||||
await Promise.all([
|
||||
// need to download node.lib
|
||||
...(win ? downloadNodeLib() : []),
|
||||
// write the "installVersion" file
|
||||
fs.promises.writeFile(installVersionPath, gyp.package.installVersion + '\n'),
|
||||
// Only download SHASUMS.txt if we downloaded something in need of SHA verification
|
||||
...(!tarPath || win ? [downloadShasums()] : [])
|
||||
])
|
||||
|
||||
// invoked after the tarball has finished being extracted
|
||||
function afterTarball () {
|
||||
if (badDownload) {
|
||||
return
|
||||
}
|
||||
if (extractCount === 0) {
|
||||
return cb(new Error('There was a fatal problem while downloading/extracting the tarball'))
|
||||
}
|
||||
log.verbose('tarball', 'done parsing tarball')
|
||||
var async = 0
|
||||
log.verbose('download contents checksum', JSON.stringify(contentShasums))
|
||||
// check content shasums
|
||||
for (const k in contentShasums) {
|
||||
log.verbose('validating download checksum for ' + k, '(%s == %s)', contentShasums[k], expectShasums[k])
|
||||
if (contentShasums[k] !== expectShasums[k]) {
|
||||
throw new Error(k + ' local checksum ' + contentShasums[k] + ' not match remote ' + expectShasums[k])
|
||||
}
|
||||
}
|
||||
|
||||
if (win) {
|
||||
// need to download node.lib
|
||||
async++
|
||||
downloadNodeLib(deref)
|
||||
}
|
||||
async function downloadShasums () {
|
||||
log.verbose('check download content checksum, need to download `SHASUMS256.txt`...')
|
||||
log.verbose('checksum url', release.shasumsUrl)
|
||||
|
||||
// write the "installVersion" file
|
||||
async++
|
||||
var installVersionPath = path.resolve(devDir, 'installVersion')
|
||||
fs.writeFile(installVersionPath, gyp.package.installVersion + '\n', deref)
|
||||
const res = await download(gyp, release.shasumsUrl)
|
||||
|
||||
// Only download SHASUMS.txt if we downloaded something in need of SHA verification
|
||||
if (!tarPath || win) {
|
||||
// download SHASUMS.txt
|
||||
async++
|
||||
downloadShasums(deref)
|
||||
}
|
||||
|
||||
if (async === 0) {
|
||||
// no async tasks required
|
||||
cb()
|
||||
}
|
||||
|
||||
function deref (err) {
|
||||
if (err) {
|
||||
return cb(err)
|
||||
}
|
||||
|
||||
async--
|
||||
if (!async) {
|
||||
log.verbose('download contents checksum', JSON.stringify(contentShasums))
|
||||
// check content shasums
|
||||
for (var k in contentShasums) {
|
||||
log.verbose('validating download checksum for ' + k, '(%s == %s)', contentShasums[k], expectShasums[k])
|
||||
if (contentShasums[k] !== expectShasums[k]) {
|
||||
cb(new Error(k + ' local checksum ' + contentShasums[k] + ' not match remote ' + expectShasums[k]))
|
||||
return
|
||||
}
|
||||
}
|
||||
cb()
|
||||
}
|
||||
}
|
||||
if (res.status !== 200) {
|
||||
throw new Error(`${res.status} status code downloading checksum`)
|
||||
}
|
||||
|
||||
function downloadShasums (done) {
|
||||
log.verbose('check download content checksum, need to download `SHASUMS256.txt`...')
|
||||
log.verbose('checksum url', release.shasumsUrl)
|
||||
try {
|
||||
var req = download(gyp, process.env, release.shasumsUrl)
|
||||
} catch (e) {
|
||||
return cb(e)
|
||||
for (const line of (await res.text()).trim().split('\n')) {
|
||||
const items = line.trim().split(/\s+/)
|
||||
if (items.length !== 2) {
|
||||
return
|
||||
}
|
||||
|
||||
req.on('error', done)
|
||||
req.on('response', function (res) {
|
||||
if (res.statusCode !== 200) {
|
||||
done(new Error(res.statusCode + ' status code downloading checksum'))
|
||||
return
|
||||
}
|
||||
|
||||
var chunks = []
|
||||
res.on('data', function (chunk) {
|
||||
chunks.push(chunk)
|
||||
})
|
||||
res.on('end', function () {
|
||||
var lines = Buffer.concat(chunks).toString().trim().split('\n')
|
||||
lines.forEach(function (line) {
|
||||
var items = line.trim().split(/\s+/)
|
||||
if (items.length !== 2) {
|
||||
return
|
||||
}
|
||||
|
||||
// 0035d18e2dcf9aad669b1c7c07319e17abfe3762 ./node-v0.11.4.tar.gz
|
||||
var name = items[1].replace(/^\.\//, '')
|
||||
expectShasums[name] = items[0]
|
||||
})
|
||||
|
||||
log.verbose('checksum data', JSON.stringify(expectShasums))
|
||||
done()
|
||||
})
|
||||
})
|
||||
// 0035d18e2dcf9aad669b1c7c07319e17abfe3762 ./node-v0.11.4.tar.gz
|
||||
const name = items[1].replace(/^\.\//, '')
|
||||
expectShasums[name] = items[0]
|
||||
}
|
||||
|
||||
function downloadNodeLib (done) {
|
||||
log.verbose('on Windows; need to download `' + release.name + '.lib`...')
|
||||
var archs = ['ia32', 'x64', 'arm64']
|
||||
var async = archs.length
|
||||
archs.forEach(function (arch) {
|
||||
var dir = path.resolve(devDir, arch)
|
||||
var targetLibPath = path.resolve(dir, release.name + '.lib')
|
||||
var libUrl = release[arch].libUrl
|
||||
var libPath = release[arch].libPath
|
||||
var name = arch + ' ' + release.name + '.lib'
|
||||
log.verbose(name, 'dir', dir)
|
||||
log.verbose(name, 'url', libUrl)
|
||||
log.verbose('checksum data', JSON.stringify(expectShasums))
|
||||
}
|
||||
|
||||
fs.mkdir(dir, { recursive: true }, function (err) {
|
||||
if (err) {
|
||||
return done(err)
|
||||
}
|
||||
log.verbose('streaming', name, 'to:', targetLibPath)
|
||||
function downloadNodeLib () {
|
||||
log.verbose('on Windows; need to download `' + release.name + '.lib`...')
|
||||
const archs = ['ia32', 'x64', 'arm64']
|
||||
return archs.map(async (arch) => {
|
||||
const dir = path.resolve(devDir, arch)
|
||||
const targetLibPath = path.resolve(dir, release.name + '.lib')
|
||||
const { libUrl, libPath } = release[arch]
|
||||
const name = `${arch} ${release.name}.lib`
|
||||
log.verbose(name, 'dir', dir)
|
||||
log.verbose(name, 'url', libUrl)
|
||||
|
||||
try {
|
||||
var req = download(gyp, process.env, libUrl, cb)
|
||||
} catch (e) {
|
||||
return cb(e)
|
||||
}
|
||||
await fs.promises.mkdir(dir, { recursive: true })
|
||||
log.verbose('streaming', name, 'to:', targetLibPath)
|
||||
|
||||
req.on('error', done)
|
||||
req.on('response', function (res) {
|
||||
if (res.statusCode === 403 || res.statusCode === 404) {
|
||||
if (arch === 'arm64') {
|
||||
// Arm64 is a newer platform on Windows and not all node distributions provide it.
|
||||
log.verbose(`${name} was not found in ${libUrl}`)
|
||||
} else {
|
||||
log.warn(`${name} was not found in ${libUrl}`)
|
||||
}
|
||||
return
|
||||
} else if (res.statusCode !== 200) {
|
||||
done(new Error(res.statusCode + ' status code downloading ' + name))
|
||||
return
|
||||
}
|
||||
const res = await download(gyp, libUrl)
|
||||
|
||||
getContentSha(res, function (_, checksum) {
|
||||
contentShasums[libPath] = checksum
|
||||
log.verbose('content checksum', libPath, checksum)
|
||||
})
|
||||
if (res.status === 403 || res.status === 404) {
|
||||
if (arch === 'arm64') {
|
||||
// Arm64 is a newer platform on Windows and not all node distributions provide it.
|
||||
log.verbose(`${name} was not found in ${libUrl}`)
|
||||
} else {
|
||||
log.warn(`${name} was not found in ${libUrl}`)
|
||||
}
|
||||
return
|
||||
} else if (res.status !== 200) {
|
||||
throw new Error(`${res.status} status code downloading ${name}`)
|
||||
}
|
||||
|
||||
var ws = fs.createWriteStream(targetLibPath)
|
||||
ws.on('error', cb)
|
||||
req.pipe(ws)
|
||||
})
|
||||
req.on('end', function () { --async || done() })
|
||||
})
|
||||
})
|
||||
} // downloadNodeLib()
|
||||
}) // mkdir()
|
||||
return streamPipeline(
|
||||
res.body,
|
||||
new ShaSum((_, checksum) => {
|
||||
contentShasums[libPath] = checksum
|
||||
log.verbose('content checksum', libPath, checksum)
|
||||
}),
|
||||
fs.createWriteStream(targetLibPath)
|
||||
)
|
||||
})
|
||||
} // downloadNodeLib()
|
||||
} // go()
|
||||
|
||||
/**
|
||||
|
@ -358,10 +274,17 @@ function install (fs, gyp, argv, callback) {
|
|||
|
||||
function valid (file) {
|
||||
// header files
|
||||
var extname = path.extname(file)
|
||||
const extname = path.extname(file)
|
||||
return extname === '.h' || extname === '.gypi'
|
||||
}
|
||||
|
||||
async function rollback (err) {
|
||||
log.warn('install', 'got an error, rolling back install')
|
||||
// roll-back the install if anything went wrong
|
||||
await util.promisify(gyp.commands.remove)([release.versionDir])
|
||||
throw err
|
||||
}
|
||||
|
||||
/**
|
||||
* The EACCES fallback is a workaround for npm's `sudo` behavior, where
|
||||
* it drops the permissions before invoking any child processes (like
|
||||
|
@ -371,14 +294,14 @@ function install (fs, gyp, argv, callback) {
|
|||
* the compilation will succeed...
|
||||
*/
|
||||
|
||||
function eaccesFallback (err) {
|
||||
var noretry = '--node_gyp_internal_noretry'
|
||||
async function eaccesFallback (err) {
|
||||
const noretry = '--node_gyp_internal_noretry'
|
||||
if (argv.indexOf(noretry) !== -1) {
|
||||
return cb(err)
|
||||
throw err
|
||||
}
|
||||
var tmpdir = os.tmpdir()
|
||||
const tmpdir = os.tmpdir()
|
||||
gyp.devDir = path.resolve(tmpdir, '.node-gyp')
|
||||
var userString = ''
|
||||
let userString = ''
|
||||
try {
|
||||
// os.userInfo can fail on some systems, it's not critical here
|
||||
userString = ` ("${os.userInfo().username}")`
|
||||
|
@ -389,59 +312,65 @@ function install (fs, gyp, argv, callback) {
|
|||
log.verbose('tmpdir == cwd', 'automatically will remove dev files after to save disk space')
|
||||
gyp.todo.push({ name: 'remove', args: argv })
|
||||
}
|
||||
gyp.commands.install([noretry].concat(argv), cb)
|
||||
return util.promisify(gyp.commands.install)([noretry].concat(argv))
|
||||
}
|
||||
}
|
||||
|
||||
function download (gyp, env, url) {
|
||||
class ShaSum extends stream.Transform {
|
||||
constructor (callback) {
|
||||
super()
|
||||
this._callback = callback
|
||||
this._digester = crypto.createHash('sha256')
|
||||
}
|
||||
|
||||
_transform (chunk, _, callback) {
|
||||
this._digester.update(chunk)
|
||||
callback(null, chunk)
|
||||
}
|
||||
|
||||
_flush (callback) {
|
||||
this._callback(null, this._digester.digest('hex'))
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
async function download (gyp, url) {
|
||||
log.http('GET', url)
|
||||
|
||||
var requestOpts = {
|
||||
uri: url,
|
||||
const requestOpts = {
|
||||
headers: {
|
||||
'User-Agent': 'node-gyp v' + gyp.version + ' (node ' + process.version + ')',
|
||||
'User-Agent': `node-gyp v${gyp.version} (node ${process.version})`,
|
||||
Connection: 'keep-alive'
|
||||
}
|
||||
},
|
||||
proxy: gyp.opts.proxy,
|
||||
noProxy: gyp.opts.noproxy
|
||||
}
|
||||
|
||||
var cafile = gyp.opts.cafile
|
||||
const cafile = gyp.opts.cafile
|
||||
if (cafile) {
|
||||
requestOpts.ca = readCAFile(cafile)
|
||||
requestOpts.ca = await readCAFile(cafile)
|
||||
}
|
||||
|
||||
// basic support for a proxy server
|
||||
var proxyUrl = getProxyFromURI(gyp, env, url)
|
||||
if (proxyUrl) {
|
||||
if (/^https?:\/\//i.test(proxyUrl)) {
|
||||
log.verbose('download', 'using proxy url: "%s"', proxyUrl)
|
||||
requestOpts.proxy = proxyUrl
|
||||
} else {
|
||||
log.warn('download', 'ignoring invalid "proxy" config setting: "%s"', proxyUrl)
|
||||
}
|
||||
}
|
||||
const res = await fetch(url, requestOpts)
|
||||
log.http(res.status, res.url)
|
||||
|
||||
var req = request(requestOpts)
|
||||
req.on('response', function (res) {
|
||||
log.http(res.statusCode, url)
|
||||
})
|
||||
|
||||
return req
|
||||
return res
|
||||
}
|
||||
|
||||
function readCAFile (filename) {
|
||||
async function readCAFile (filename) {
|
||||
// The CA file can contain multiple certificates so split on certificate
|
||||
// boundaries. [\S\s]*? is used to match everything including newlines.
|
||||
var ca = fs.readFileSync(filename, 'utf8')
|
||||
var re = /(-----BEGIN CERTIFICATE-----[\S\s]*?-----END CERTIFICATE-----)/g
|
||||
const ca = await fs.promises.readFile(filename, 'utf8')
|
||||
const re = /(-----BEGIN CERTIFICATE-----[\S\s]*?-----END CERTIFICATE-----)/g
|
||||
return ca.match(re)
|
||||
}
|
||||
|
||||
module.exports = function (gyp, argv, callback) {
|
||||
return install(fs, gyp, argv, callback)
|
||||
install(fs, gyp, argv).then(callback.bind(undefined, null), callback)
|
||||
}
|
||||
module.exports.test = {
|
||||
download: download,
|
||||
install: install,
|
||||
readCAFile: readCAFile
|
||||
download,
|
||||
install,
|
||||
readCAFile
|
||||
}
|
||||
module.exports.usage = 'Install node development files for the specified node version.'
|
||||
|
|
92
lib/proxy.js
92
lib/proxy.js
|
@ -1,92 +0,0 @@
|
|||
'use strict'
|
||||
// Taken from https://github.com/request/request/blob/212570b/lib/getProxyFromURI.js
|
||||
|
||||
const url = require('url')
|
||||
|
||||
function formatHostname (hostname) {
|
||||
// canonicalize the hostname, so that 'oogle.com' won't match 'google.com'
|
||||
return hostname.replace(/^\.*/, '.').toLowerCase()
|
||||
}
|
||||
|
||||
function parseNoProxyZone (zone) {
|
||||
zone = zone.trim().toLowerCase()
|
||||
|
||||
var zoneParts = zone.split(':', 2)
|
||||
var zoneHost = formatHostname(zoneParts[0])
|
||||
var zonePort = zoneParts[1]
|
||||
var hasPort = zone.indexOf(':') > -1
|
||||
|
||||
return { hostname: zoneHost, port: zonePort, hasPort: hasPort }
|
||||
}
|
||||
|
||||
function uriInNoProxy (uri, noProxy) {
|
||||
var port = uri.port || (uri.protocol === 'https:' ? '443' : '80')
|
||||
var hostname = formatHostname(uri.hostname)
|
||||
var noProxyList = noProxy.split(',')
|
||||
|
||||
// iterate through the noProxyList until it finds a match.
|
||||
return noProxyList.map(parseNoProxyZone).some(function (noProxyZone) {
|
||||
var isMatchedAt = hostname.indexOf(noProxyZone.hostname)
|
||||
var hostnameMatched = (
|
||||
isMatchedAt > -1 &&
|
||||
(isMatchedAt === hostname.length - noProxyZone.hostname.length)
|
||||
)
|
||||
|
||||
if (noProxyZone.hasPort) {
|
||||
return (port === noProxyZone.port) && hostnameMatched
|
||||
}
|
||||
|
||||
return hostnameMatched
|
||||
})
|
||||
}
|
||||
|
||||
function getProxyFromURI (gyp, env, uri) {
|
||||
// If a string URI/URL was given, parse it into a URL object
|
||||
if (typeof uri === 'string') {
|
||||
// eslint-disable-next-line
|
||||
uri = url.parse(uri)
|
||||
}
|
||||
|
||||
// Decide the proper request proxy to use based on the request URI object and the
|
||||
// environmental variables (NO_PROXY, HTTP_PROXY, etc.)
|
||||
// respect NO_PROXY environment variables (see: https://lynx.invisible-island.net/lynx2.8.7/breakout/lynx_help/keystrokes/environments.html)
|
||||
|
||||
var noProxy = gyp.opts.noproxy || env.NO_PROXY || env.no_proxy || env.npm_config_noproxy || ''
|
||||
|
||||
// if the noProxy is a wildcard then return null
|
||||
|
||||
if (noProxy === '*') {
|
||||
return null
|
||||
}
|
||||
|
||||
// if the noProxy is not empty and the uri is found return null
|
||||
|
||||
if (noProxy !== '' && uriInNoProxy(uri, noProxy)) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Check for HTTP or HTTPS Proxy in environment Else default to null
|
||||
|
||||
if (uri.protocol === 'http:') {
|
||||
return gyp.opts.proxy ||
|
||||
env.HTTP_PROXY ||
|
||||
env.http_proxy ||
|
||||
env.npm_config_proxy || null
|
||||
}
|
||||
|
||||
if (uri.protocol === 'https:') {
|
||||
return gyp.opts.proxy ||
|
||||
env.HTTPS_PROXY ||
|
||||
env.https_proxy ||
|
||||
env.HTTP_PROXY ||
|
||||
env.http_proxy ||
|
||||
env.npm_config_proxy || null
|
||||
}
|
||||
|
||||
// if none of that works, return null
|
||||
// (What uri protocol are you using then?)
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
module.exports = getProxyFromURI
|
|
@ -25,9 +25,9 @@
|
|||
"env-paths": "^2.2.0",
|
||||
"glob": "^7.1.4",
|
||||
"graceful-fs": "^4.2.3",
|
||||
"make-fetch-happen": "^8.0.9",
|
||||
"nopt": "^5.0.0",
|
||||
"npmlog": "^4.1.2",
|
||||
"request": "^2.88.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"semver": "^7.3.2",
|
||||
"tar": "^6.0.2",
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
'use strict'
|
||||
|
||||
const test = require('tap').test
|
||||
const { test } = require('tap')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const util = require('util')
|
||||
const http = require('http')
|
||||
const https = require('https')
|
||||
const install = require('../lib/install')
|
||||
|
@ -14,191 +15,142 @@ const log = require('npmlog')
|
|||
|
||||
log.level = 'warn'
|
||||
|
||||
test('download over http', function (t) {
|
||||
test('download over http', async (t) => {
|
||||
t.plan(2)
|
||||
|
||||
var server = http.createServer(function (req, res) {
|
||||
t.strictEqual(req.headers['user-agent'],
|
||||
'node-gyp v42 (node ' + process.version + ')')
|
||||
const server = http.createServer((req, res) => {
|
||||
t.strictEqual(req.headers['user-agent'], `node-gyp v42 (node ${process.version})`)
|
||||
res.end('ok')
|
||||
server.close()
|
||||
})
|
||||
|
||||
var host = 'localhost'
|
||||
server.listen(0, host, function () {
|
||||
var port = this.address().port
|
||||
var gyp = {
|
||||
opts: {},
|
||||
version: '42'
|
||||
}
|
||||
var url = 'http://' + host + ':' + port
|
||||
var req = install.test.download(gyp, {}, url)
|
||||
req.on('response', function (res) {
|
||||
var body = ''
|
||||
res.setEncoding('utf8')
|
||||
res.on('data', function (data) {
|
||||
body += data
|
||||
})
|
||||
res.on('end', function () {
|
||||
t.strictEqual(body, 'ok')
|
||||
})
|
||||
})
|
||||
})
|
||||
t.tearDown(() => new Promise((resolve) => server.close(resolve)))
|
||||
|
||||
const host = 'localhost'
|
||||
await new Promise((resolve) => server.listen(0, host, resolve))
|
||||
const { port } = server.address()
|
||||
const gyp = {
|
||||
opts: {},
|
||||
version: '42'
|
||||
}
|
||||
const url = `http://${host}:${port}`
|
||||
const res = await install.test.download(gyp, url)
|
||||
t.strictEqual(await res.text(), 'ok')
|
||||
})
|
||||
|
||||
test('download over https with custom ca', function (t) {
|
||||
test('download over https with custom ca', async (t) => {
|
||||
t.plan(3)
|
||||
|
||||
var cert = fs.readFileSync(path.join(__dirname, 'fixtures/server.crt'), 'utf8')
|
||||
var key = fs.readFileSync(path.join(__dirname, 'fixtures/server.key'), 'utf8')
|
||||
const cafile = path.join(__dirname, '/fixtures/ca.crt')
|
||||
const [cert, key, ca] = await Promise.all([
|
||||
fs.promises.readFile(path.join(__dirname, 'fixtures/server.crt'), 'utf8'),
|
||||
fs.promises.readFile(path.join(__dirname, 'fixtures/server.key'), 'utf8'),
|
||||
install.test.readCAFile(cafile)
|
||||
])
|
||||
|
||||
var cafile = path.join(__dirname, '/fixtures/ca.crt')
|
||||
var ca = install.test.readCAFile(cafile)
|
||||
t.strictEqual(ca.length, 1)
|
||||
|
||||
var options = { ca: ca, cert: cert, key: key }
|
||||
var server = https.createServer(options, function (req, res) {
|
||||
t.strictEqual(req.headers['user-agent'],
|
||||
'node-gyp v42 (node ' + process.version + ')')
|
||||
const options = { ca: ca, cert: cert, key: key }
|
||||
const server = https.createServer(options, (req, res) => {
|
||||
t.strictEqual(req.headers['user-agent'], `node-gyp v42 (node ${process.version})`)
|
||||
res.end('ok')
|
||||
server.close()
|
||||
})
|
||||
|
||||
server.on('clientError', function (err) {
|
||||
throw err
|
||||
})
|
||||
t.tearDown(() => new Promise((resolve) => server.close(resolve)))
|
||||
|
||||
var host = 'localhost'
|
||||
server.listen(8000, host, function () {
|
||||
var port = this.address().port
|
||||
var gyp = {
|
||||
opts: { cafile: cafile },
|
||||
version: '42'
|
||||
}
|
||||
var url = 'https://' + host + ':' + port
|
||||
var req = install.test.download(gyp, {}, url)
|
||||
req.on('response', function (res) {
|
||||
var body = ''
|
||||
res.setEncoding('utf8')
|
||||
res.on('data', function (data) {
|
||||
body += data
|
||||
})
|
||||
res.on('end', function () {
|
||||
t.strictEqual(body, 'ok')
|
||||
})
|
||||
})
|
||||
})
|
||||
server.on('clientError', (err) => { throw err })
|
||||
|
||||
const host = 'localhost'
|
||||
await new Promise((resolve) => server.listen(0, host, resolve))
|
||||
const { port } = server.address()
|
||||
const gyp = {
|
||||
opts: { cafile },
|
||||
version: '42'
|
||||
}
|
||||
const url = `https://${host}:${port}`
|
||||
const res = await install.test.download(gyp, url)
|
||||
t.strictEqual(await res.text(), 'ok')
|
||||
})
|
||||
|
||||
test('download over http with proxy', function (t) {
|
||||
test('download over http with proxy', async (t) => {
|
||||
t.plan(2)
|
||||
|
||||
var server = http.createServer(function (req, res) {
|
||||
t.strictEqual(req.headers['user-agent'],
|
||||
'node-gyp v42 (node ' + process.version + ')')
|
||||
const server = http.createServer((_, res) => {
|
||||
res.end('ok')
|
||||
pserver.close(function () {
|
||||
server.close()
|
||||
})
|
||||
})
|
||||
|
||||
var pserver = http.createServer(function (req, res) {
|
||||
t.strictEqual(req.headers['user-agent'],
|
||||
'node-gyp v42 (node ' + process.version + ')')
|
||||
const pserver = http.createServer((req, res) => {
|
||||
t.strictEqual(req.headers['user-agent'], `node-gyp v42 (node ${process.version})`)
|
||||
res.end('proxy ok')
|
||||
server.close(function () {
|
||||
pserver.close()
|
||||
})
|
||||
})
|
||||
|
||||
var host = 'localhost'
|
||||
server.listen(0, host, function () {
|
||||
var port = this.address().port
|
||||
pserver.listen(port + 1, host, function () {
|
||||
var gyp = {
|
||||
opts: {
|
||||
proxy: 'http://' + host + ':' + (port + 1)
|
||||
},
|
||||
version: '42'
|
||||
}
|
||||
var url = 'http://' + host + ':' + port
|
||||
var req = install.test.download(gyp, {}, url)
|
||||
req.on('response', function (res) {
|
||||
var body = ''
|
||||
res.setEncoding('utf8')
|
||||
res.on('data', function (data) {
|
||||
body += data
|
||||
})
|
||||
res.on('end', function () {
|
||||
t.strictEqual(body, 'proxy ok')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
t.tearDown(() => Promise.all([
|
||||
new Promise((resolve) => server.close(resolve)),
|
||||
new Promise((resolve) => pserver.close(resolve))
|
||||
]))
|
||||
|
||||
const host = 'localhost'
|
||||
await new Promise((resolve) => server.listen(0, host, resolve))
|
||||
const { port } = server.address()
|
||||
await new Promise((resolve) => pserver.listen(port + 1, host, resolve))
|
||||
const gyp = {
|
||||
opts: {
|
||||
proxy: `http://${host}:${port + 1}`,
|
||||
noproxy: 'bad'
|
||||
},
|
||||
version: '42'
|
||||
}
|
||||
const url = `http://${host}:${port}`
|
||||
const res = await install.test.download(gyp, url)
|
||||
t.strictEqual(await res.text(), 'proxy ok')
|
||||
})
|
||||
|
||||
test('download over http with noproxy', function (t) {
|
||||
test('download over http with noproxy', async (t) => {
|
||||
t.plan(2)
|
||||
|
||||
var server = http.createServer(function (req, res) {
|
||||
t.strictEqual(req.headers['user-agent'],
|
||||
'node-gyp v42 (node ' + process.version + ')')
|
||||
const server = http.createServer((req, res) => {
|
||||
t.strictEqual(req.headers['user-agent'], `node-gyp v42 (node ${process.version})`)
|
||||
res.end('ok')
|
||||
pserver.close(function () {
|
||||
server.close()
|
||||
})
|
||||
})
|
||||
|
||||
var pserver = http.createServer(function (req, res) {
|
||||
t.strictEqual(req.headers['user-agent'],
|
||||
'node-gyp v42 (node ' + process.version + ')')
|
||||
const pserver = http.createServer((_, res) => {
|
||||
res.end('proxy ok')
|
||||
server.close(function () {
|
||||
pserver.close()
|
||||
})
|
||||
})
|
||||
|
||||
var host = 'localhost'
|
||||
server.listen(0, host, function () {
|
||||
var port = this.address().port
|
||||
pserver.listen(port + 1, host, function () {
|
||||
var gyp = {
|
||||
opts: {
|
||||
proxy: 'http://' + host + ':' + (port + 1),
|
||||
noproxy: 'localhost'
|
||||
},
|
||||
version: '42'
|
||||
}
|
||||
var url = 'http://' + host + ':' + port
|
||||
var req = install.test.download(gyp, {}, url)
|
||||
req.on('response', function (res) {
|
||||
var body = ''
|
||||
res.setEncoding('utf8')
|
||||
res.on('data', function (data) {
|
||||
body += data
|
||||
})
|
||||
res.on('end', function () {
|
||||
t.strictEqual(body, 'ok')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
t.tearDown(() => Promise.all([
|
||||
new Promise((resolve) => server.close(resolve)),
|
||||
new Promise((resolve) => pserver.close(resolve))
|
||||
]))
|
||||
|
||||
const host = 'localhost'
|
||||
await new Promise((resolve) => server.listen(0, host, resolve))
|
||||
const { port } = server.address()
|
||||
await new Promise((resolve) => pserver.listen(port + 1, host, resolve))
|
||||
const gyp = {
|
||||
opts: {
|
||||
proxy: `http://${host}:${port + 1}`,
|
||||
noproxy: host
|
||||
},
|
||||
version: '42'
|
||||
}
|
||||
const url = `http://${host}:${port}`
|
||||
const res = await install.test.download(gyp, url)
|
||||
t.strictEqual(await res.text(), 'ok')
|
||||
})
|
||||
|
||||
test('download with missing cafile', function (t) {
|
||||
test('download with missing cafile', async (t) => {
|
||||
t.plan(1)
|
||||
var gyp = {
|
||||
const gyp = {
|
||||
opts: { cafile: 'no.such.file' }
|
||||
}
|
||||
try {
|
||||
install.test.download(gyp, {}, 'http://bad/')
|
||||
await install.test.download(gyp, {}, 'http://bad/')
|
||||
} catch (e) {
|
||||
t.ok(/no.such.file/.test(e.message))
|
||||
}
|
||||
})
|
||||
|
||||
test('check certificate splitting', function (t) {
|
||||
var cas = install.test.readCAFile(path.join(__dirname, 'fixtures/ca-bundle.crt'))
|
||||
test('check certificate splitting', async (t) => {
|
||||
const cas = await install.test.readCAFile(path.join(__dirname, 'fixtures/ca-bundle.crt'))
|
||||
t.plan(2)
|
||||
t.strictEqual(cas.length, 2)
|
||||
t.notStrictEqual(cas[0], cas[1])
|
||||
|
@ -206,7 +158,7 @@ test('check certificate splitting', function (t) {
|
|||
|
||||
// only run this test if we are running a version of Node with predictable version path behavior
|
||||
|
||||
test('download headers (actual)', function (t) {
|
||||
test('download headers (actual)', async (t) => {
|
||||
if (process.env.FAST_TEST ||
|
||||
process.release.name !== 'node' ||
|
||||
semver.prerelease(process.version) !== null ||
|
||||
|
@ -214,55 +166,42 @@ test('download headers (actual)', function (t) {
|
|||
return t.skip('Skipping actual download of headers due to test environment configuration')
|
||||
}
|
||||
|
||||
t.plan(17)
|
||||
t.plan(12)
|
||||
|
||||
const expectedDir = path.join(devDir, process.version.replace(/^v/, ''))
|
||||
rimraf(expectedDir, (err) => {
|
||||
t.ifError(err)
|
||||
await util.promisify(rimraf)(expectedDir)
|
||||
|
||||
const prog = gyp()
|
||||
prog.parseArgv([])
|
||||
prog.devDir = devDir
|
||||
log.level = 'warn'
|
||||
install(prog, [], (err) => {
|
||||
t.ifError(err)
|
||||
const prog = gyp()
|
||||
prog.parseArgv([])
|
||||
prog.devDir = devDir
|
||||
log.level = 'warn'
|
||||
await util.promisify(install)(prog, [])
|
||||
|
||||
fs.readFile(path.join(expectedDir, 'installVersion'), 'utf8', (err, data) => {
|
||||
t.ifError(err)
|
||||
t.strictEqual(data, '9\n', 'correct installVersion')
|
||||
})
|
||||
const data = await fs.promises.readFile(path.join(expectedDir, 'installVersion'), 'utf8')
|
||||
t.strictEqual(data, '9\n', 'correct installVersion')
|
||||
|
||||
fs.readdir(path.join(expectedDir, 'include/node'), (err, list) => {
|
||||
t.ifError(err)
|
||||
const list = await fs.promises.readdir(path.join(expectedDir, 'include/node'))
|
||||
t.ok(list.includes('common.gypi'))
|
||||
t.ok(list.includes('config.gypi'))
|
||||
t.ok(list.includes('node.h'))
|
||||
t.ok(list.includes('node_version.h'))
|
||||
t.ok(list.includes('openssl'))
|
||||
t.ok(list.includes('uv'))
|
||||
t.ok(list.includes('uv.h'))
|
||||
t.ok(list.includes('v8-platform.h'))
|
||||
t.ok(list.includes('v8.h'))
|
||||
t.ok(list.includes('zlib.h'))
|
||||
|
||||
t.ok(list.includes('common.gypi'))
|
||||
t.ok(list.includes('config.gypi'))
|
||||
t.ok(list.includes('node.h'))
|
||||
t.ok(list.includes('node_version.h'))
|
||||
t.ok(list.includes('openssl'))
|
||||
t.ok(list.includes('uv'))
|
||||
t.ok(list.includes('uv.h'))
|
||||
t.ok(list.includes('v8-platform.h'))
|
||||
t.ok(list.includes('v8.h'))
|
||||
t.ok(list.includes('zlib.h'))
|
||||
})
|
||||
const lines = (await fs.promises.readFile(path.join(expectedDir, 'include/node/node_version.h'), 'utf8')).split('\n')
|
||||
|
||||
fs.readFile(path.join(expectedDir, 'include/node/node_version.h'), 'utf8', (err, contents) => {
|
||||
t.ifError(err)
|
||||
// extract the 3 version parts from the defines to build a valid version string and
|
||||
// and check them against our current env version
|
||||
const version = ['major', 'minor', 'patch'].reduce((version, type) => {
|
||||
const re = new RegExp(`^#define\\sNODE_${type.toUpperCase()}_VERSION`)
|
||||
const line = lines.find((l) => re.test(l))
|
||||
const i = line ? parseInt(line.replace(/^[^0-9]+([0-9]+).*$/, '$1'), 10) : 'ERROR'
|
||||
return `${version}${type !== 'major' ? '.' : 'v'}${i}`
|
||||
}, '')
|
||||
|
||||
const lines = contents.split('\n')
|
||||
|
||||
// extract the 3 version parts from the defines to build a valid version string and
|
||||
// and check them against our current env version
|
||||
const version = ['major', 'minor', 'patch'].reduce((version, type) => {
|
||||
const re = new RegExp(`^#define\\sNODE_${type.toUpperCase()}_VERSION`)
|
||||
const line = lines.find((l) => re.test(l))
|
||||
const i = line ? parseInt(line.replace(/^[^0-9]+([0-9]+).*$/, '$1'), 10) : 'ERROR'
|
||||
return `${version}${type !== 'major' ? '.' : 'v'}${i}`
|
||||
}, '')
|
||||
|
||||
t.strictEqual(version, process.version)
|
||||
})
|
||||
})
|
||||
})
|
||||
t.strictEqual(version, process.version)
|
||||
})
|
||||
|
|
|
@ -1,38 +1,46 @@
|
|||
'use strict'
|
||||
|
||||
const test = require('tap').test
|
||||
const install = require('../lib/install').test.install
|
||||
const { test } = require('tap')
|
||||
const { test: { install } } = require('../lib/install')
|
||||
const log = require('npmlog')
|
||||
|
||||
require('npmlog').level = 'error' // we expect a warning
|
||||
log.level = 'error' // we expect a warning
|
||||
|
||||
test('EACCES retry once', function (t) {
|
||||
test('EACCES retry once', async (t) => {
|
||||
t.plan(3)
|
||||
|
||||
var fs = {}
|
||||
fs.stat = function (path, cb) {
|
||||
var err = new Error()
|
||||
err.code = 'EACCES'
|
||||
cb(err)
|
||||
t.ok(true)
|
||||
const fs = {
|
||||
promises: {
|
||||
stat (_) {
|
||||
const err = new Error()
|
||||
err.code = 'EACCES'
|
||||
t.ok(true)
|
||||
throw err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var gyp = {}
|
||||
gyp.devDir = __dirname
|
||||
gyp.opts = {}
|
||||
gyp.opts.ensure = true
|
||||
gyp.commands = {}
|
||||
gyp.commands.install = function (argv, cb) {
|
||||
install(fs, gyp, argv, cb)
|
||||
}
|
||||
gyp.commands.remove = function (argv, cb) {
|
||||
cb()
|
||||
const Gyp = {
|
||||
devDir: __dirname,
|
||||
opts: {
|
||||
ensure: true
|
||||
},
|
||||
commands: {
|
||||
install (argv, cb) {
|
||||
install(fs, Gyp, argv).then(cb, cb)
|
||||
},
|
||||
remove (_, cb) {
|
||||
cb()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gyp.commands.install([], function (err) {
|
||||
try {
|
||||
await install(fs, Gyp, [])
|
||||
} catch (err) {
|
||||
t.ok(true)
|
||||
if (/"pre" versions of node cannot be installed/.test(err.message)) {
|
||||
t.ok(true)
|
||||
t.ok(true)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue