Skip to content

Commit

Permalink
Merge pull request #128 from prebuild/npm7
Browse files Browse the repository at this point in the history
Support npm 7
  • Loading branch information
vweevers authored Oct 23, 2020
2 parents e5c9a5a + ddb8f69 commit f9ed67e
Show file tree
Hide file tree
Showing 9 changed files with 246 additions and 64 deletions.
9 changes: 8 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,11 @@ node_js:
- 8
- 10
- 12
- 14
- 14

jobs:
include:
- name: npm 7
os: linux
node_js: 14
before_install: npm i npm@7 -g
6 changes: 2 additions & 4 deletions bin.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,14 @@ if (napi.isNapiRuntime(rc.runtime)) napi.logUnsupportedVersion(rc.target, log)

var pm = whichPmRuns()
var isNpm = !pm || pm.name === 'npm'
var origin = util.packageOrigin(process.env, pkg)

if (!isNpm && /node_modules/.test(process.cwd())) {
// From yarn repository
} else if (opts.force) {
log.warn('install', 'prebuilt binaries enforced with --force!')
log.warn('install', 'prebuilt binaries may be out of date!')
} else if (!(typeof pkg._from === 'string')) {
log.info('install', 'installing standalone, skipping download.')
process.exit(1)
} else if (pkg._from.length > 4 && pkg._from.substr(0, 4) === 'git+') {
} else if (origin && origin.length > 4 && origin.substr(0, 4) === 'git+') {
log.info('install', 'installing from git repository, skipping download.')
process.exit(1)
} else if (opts.compile === true || opts.prebuild === false) {
Expand Down
12 changes: 12 additions & 0 deletions log.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
var log = require('npmlog')
var fs = require('fs')
var path = require('path')

module.exports = function (rc, env) {
log.heading = 'prebuild-install'
Expand All @@ -9,5 +11,15 @@ module.exports = function (rc, env) {
log.level = env.npm_config_loglevel || 'notice'
}

// Temporary workaround for npm 7 which swallows our output
if (process.env.npm_config_prebuild_install_logfile) {
var fp = path.resolve(process.env.npm_config_prebuild_install_logfile)

log.on('log', function (msg) {
// Only for tests, don't care about performance
fs.appendFileSync(fp, [log.heading, msg.level, msg.prefix, msg.message].join(' ') + '\n')
})
}

return log
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@
"nock": "^10.0.6",
"rimraf": "^2.5.2",
"standard": "^13.0.2",
"tape": "^4.5.1"
"tape": "^4.5.1",
"tempy": "0.2.1"
},
"bin": "./bin.js",
"repository": {
Expand Down
37 changes: 10 additions & 27 deletions rc.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,32 @@ var detectLibc = require('detect-libc')
var napi = require('napi-build-utils')

var env = process.env

var libc = env.LIBC || (detectLibc.isNonGlibcLinux && detectLibc.family) || ''

// Get `prebuild-install` arguments that were passed to the `npm` command
if (env.npm_config_argv) {
var npmargs = ['prebuild', 'compile', 'build-from-source', 'debug', 'verbose']
try {
var npmArgv = JSON.parse(env.npm_config_argv).cooked
for (var i = 0; i < npmargs.length; ++i) {
if (npmArgv.indexOf('--' + npmargs[i]) !== -1) {
process.argv.push('--' + npmargs[i])
}
if (npmArgv.indexOf('--no-' + npmargs[i]) !== -1) {
process.argv.push('--no-' + npmargs[i])
}
}
if ((i = npmArgv.indexOf('--download')) !== -1) {
process.argv.push(npmArgv[i], npmArgv[i + 1])
}
} catch (e) { }
}

// Get the configuration
module.exports = function (pkg) {
var pkgConf = pkg.config || {}
var sourceBuild = env.npm_config_build_from_source
var buildFromSource = sourceBuild === pkg.name || sourceBuild === 'true'

// TODO: remove compile and prebuild aliases?
var buildFromSource = env.npm_config_build_from_source || env.npm_config_compile

var rc = require('rc')('prebuild-install', {
target: pkgConf.target || env.npm_config_target || process.versions.node,
runtime: pkgConf.runtime || env.npm_config_runtime || 'node',
arch: pkgConf.arch || env.npm_config_arch || process.arch,
libc: libc,
platform: env.npm_config_platform || process.platform,
debug: false,
debug: env.npm_config_debug === 'true',
force: false,
verbose: false,
prebuild: true,
compile: buildFromSource,
verbose: env.npm_config_verbose === 'true',
prebuild: env.npm_config_prebuild !== '',
compile: buildFromSource === pkg.name || buildFromSource === 'true',
path: '.',
proxy: env.npm_config_proxy || env['http_proxy'] || env['HTTP_PROXY'],
'https-proxy': env.npm_config_https_proxy || env['https_proxy'] || env['HTTPS_PROXY'],
'local-address': env.npm_config_local_address,
'tag-prefix': 'v'
'tag-prefix': 'v',
download: env.npm_config_download
}, minimist(process.argv, {
alias: {
target: 't',
Expand Down
89 changes: 58 additions & 31 deletions test/rc-test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
var test = require('tape')
var path = require('path')
var exec = require('child_process').exec
var fs = require('fs')
var tempy = require('tempy') // Locked to 0.2.1 for node 6 support
var cleanEnv = require('./util/clean-env')

test('custom config and aliases', function (t) {
var args = [
Expand Down Expand Up @@ -41,42 +44,48 @@ test('custom config and aliases', function (t) {
})
})

// TODO: merge into above test
test('npm args are passed on from npm environment into rc', function (t) {
var env = {
npm_config_argv: JSON.stringify({
cooked: [
'--build-from-source',
'--download',
'https://foo.bar',
'--debug',
'--verbose'
]
})
}
runRc(t, '', env, function (rc) {
t.equal(rc['build-from-source'], true, '--build-from-source works')
var args = [
'--build-from-source',
'--download',
'https://foo.bar',
'--debug',
'--verbose'
].join(' ')

runRc(t, args, {}, function (rc) {
t.equal(rc.compile, true, 'compile should be true')
t.equal(rc.debug, true, 'debug should be true')
t.equal(rc.verbose, true, 'verbose should be true')
t.equal(rc.download, 'https://foo.bar', 'download is set')
t.equal(rc.prebuild, true, 'prebuild is true')
t.end()
})
})

test('negative npm args are passed on from npm environment into rc', function (t) {
runRc(t, '--no-prebuild', {}, function (rc) {
t.equal(rc.prebuild, false, 'prebuild is false')
t.end()
})
})

test('npm_config_* are passed on from environment into rc', function (t) {
var env = {
npm_config_proxy: 'PROXY',
npm_config_https_proxy: 'HTTPS_PROXY',
npm_config_local_address: 'LOCAL_ADDRESS',
// Note that these are validated by npm
npm_config_proxy: 'http://localhost/',
npm_config_https_proxy: 'https://localhost/',
npm_config_local_address: '127.0.0.1',
npm_config_target: '1.4.0',
npm_config_runtime: 'electron',
npm_config_platform: 'PLATFORM',
npm_config_build_from_source: 'true'
}
runRc(t, '', env, function (rc) {
t.equal(rc.proxy, 'PROXY', 'proxy is set')
t.equal(rc['https-proxy'], 'HTTPS_PROXY', 'https-proxy is set')
t.equal(rc['local-address'], 'LOCAL_ADDRESS', 'local-address is set')
t.equal(rc.proxy, 'http://localhost/', 'proxy is set')
t.equal(rc['https-proxy'], 'https://localhost/', 'https-proxy is set')
t.equal(rc['local-address'], '127.0.0.1', 'local-address is set')
t.equal(rc.target, '1.4.0', 'target is set')
t.equal(rc.runtime, 'electron', 'runtime is set')
t.equal(rc.platform, 'PLATFORM', 'platform is set')
Expand Down Expand Up @@ -116,18 +125,36 @@ test('using --tag-prefix will set the tag prefix', function (t) {
})

function runRc (t, args, env, cb) {
var cmd = 'node ' + path.resolve(__dirname, '..', 'rc.js') + ' ' + args
env = Object.assign({}, process.env, env)
exec(cmd, { env: env }, function (err, stdout, stderr) {
t.error(err, 'no error')
t.equal(stderr.length, 0, 'no stderr')
var result
try {
result = JSON.parse(stdout.toString())
t.pass('json parsed correctly')
} catch (e) {
t.fail(e.message)
var pkg = {
name: 'test',
private: true,
scripts: {
install: 'node ' + path.resolve(__dirname, '..', 'rc.js') + ' ' + args
}
cb(result)
}

var tmp = tempy.directory()
var json = JSON.stringify(pkg)

fs.writeFile(path.join(tmp, 'package.json'), json, function (err) {
if (err) throw err

var npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'
var cmd = npm + ' run install'

env = Object.assign(cleanEnv(process.env), env)

exec(cmd, { env, cwd: tmp }, function (err, stdout, stderr) {
t.error(err, 'no error')
t.equal(stderr.trim(), '', 'no stderr')

try {
var result = JSON.parse(stdout.slice(stdout.indexOf('{')))
} catch (e) {
return t.fail(e)
}

cb(result)
})
})
}
121 changes: 121 additions & 0 deletions test/skip-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
var test = require('tape')
var path = require('path')
var exec = require('child_process').exec
var execFileSync = require('child_process').execFileSync
var fs = require('fs')
var tempy = require('tempy') // Locked to 0.2.1 for node 6 support
var cleanEnv = require('./util/clean-env')
var npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'

test('skips download in git dependency', function (t) {
// We're not testing this flag. Just that we do hit the code paths before it
run(t, 'git', '--build-from-source', function (logs) {
t.is(logs.pop(), 'prebuild-install info install installing from git repository, skipping download.')
t.end()
})
})

test('does not skip download in normal dependency', function (t) {
// We're not testing this flag. Just that we don't hit the code paths before it
run(t, 'tarball', '--build-from-source', function (logs) {
t.is(logs.pop(), 'prebuild-install info install --build-from-source specified, not attempting download.')
t.end()
})
})

test('does not skip download in standalone package', function (t) {
// We're not testing this flag. Just that we don't hit the code paths before it
run(t, 'standalone', '--build-from-source', function (logs) {
t.is(logs.pop(), 'prebuild-install info install --build-from-source specified, not attempting download.')
t.end()
})
})

function run (t, mode, args, cb) {
var addon = tempy.directory()
var logfile = path.join(addon, 'prebuild-install.log')
var cwd = addon

writePackage(addon, {
name: 'addon',
version: '1.0.0',
scripts: {
install: 'node ' + path.resolve(__dirname, '..', 'bin.js') + ' ' + args + ' || exit 0'
}
})

if (mode !== 'standalone') {
// Install as dependency of an app
cwd = tempy.directory()

writePackage(cwd, {
name: 'app',
dependencies: {
addon: mode === 'git' ? prepareGit(addon) : prepareTarball(addon)
}
})
}

var env = Object.assign(cleanEnv(process.env), {
// We shouldn't hit npm or github
npm_config_registry: 'http://localhost:1234',
npm_config_addon_binary_host: 'http://localhost:1234',
npm_config_prefer_offline: 'true',
npm_config_audit: 'false',

// Temporary workaround for npm 7 which swallows our output
npm_config_prebuild_install_logfile: logfile,
npm_config_loglevel: 'info'
})

exec(npm + ' install', { cwd, env }, function (err) {
t.ifError(err, 'no install error')

fs.readFile(logfile, 'utf8', function (err, data) {
t.ifError(err, 'no read error')
cb(logs(data))
})
})
}

function writePackage (cwd, pkg) {
fs.writeFileSync(path.join(cwd, 'package.json'), JSON.stringify(pkg))
}

function prepareGit (cwd) {
execFileSync('git', ['init', '.'], { cwd, stdio: 'ignore' })
execFileSync('git', ['config', 'user.name', 'test'], { cwd, stdio: 'ignore' })
execFileSync('git', ['config', 'user.email', 'test@localhost'], { cwd, stdio: 'ignore' })
execFileSync('git', ['add', 'package.json'], { cwd, stdio: 'ignore' })
execFileSync('git', ['commit', '-m', 'test'], { cwd, stdio: 'ignore' })

if (process.platform === 'win32' && npmVersion() >= 7) {
// Otherwise results in invalid url error
return 'git+file:///' + cwd
}

return 'git+file://' + cwd
}

function npmVersion () {
return parseInt(execFileSync(npm, ['-v']).toString())
}

function prepareTarball (cwd) {
// Packs to <name>-<version>.tgz
execFileSync(npm, ['pack'], { cwd, stdio: 'ignore' })

return 'file:' + path.join(cwd, 'addon-1.0.0.tgz')
}

function logs (stderr) {
return (stderr || '').split(/\r?\n/).filter(isOurs).map(stripPrefix)
}

function isOurs (line) {
return /^(npm ERR! )?prebuild-install /.test(line)
}

function stripPrefix (line) {
return line.replace(/^npm ERR! /, '')
}
14 changes: 14 additions & 0 deletions test/util/clean-env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
var hasOwnProperty = Object.prototype.hasOwnProperty

module.exports = function (env) {
var clean = {}

for (var k in env) {
if (!hasOwnProperty.call(env, k)) continue
if (/^npm_/i.test(k)) continue

clean[k] = env[k]
}

return clean
}
Loading

0 comments on commit f9ed67e

Please sign in to comment.