You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
309 lines
8.6 KiB
309 lines
8.6 KiB
'use strict'; |
|
|
|
/** |
|
* Module exports. |
|
*/ |
|
|
|
module.exports = exports; |
|
|
|
/** |
|
* Module dependencies. |
|
*/ |
|
|
|
// load mocking control function for accessing s3 via https. the function is a noop always returning |
|
// false if not mocking. |
|
exports.mockS3Http = require('./util/s3_setup').get_mockS3Http(); |
|
exports.mockS3Http('on'); |
|
const mocking = exports.mockS3Http('get'); |
|
|
|
|
|
const fs = require('fs'); |
|
const path = require('path'); |
|
const nopt = require('nopt'); |
|
const log = require('npmlog'); |
|
log.disableProgress(); |
|
const napi = require('./util/napi.js'); |
|
|
|
const EE = require('events').EventEmitter; |
|
const inherits = require('util').inherits; |
|
const cli_commands = [ |
|
'clean', |
|
'install', |
|
'reinstall', |
|
'build', |
|
'rebuild', |
|
'package', |
|
'testpackage', |
|
'publish', |
|
'unpublish', |
|
'info', |
|
'testbinary', |
|
'reveal', |
|
'configure' |
|
]; |
|
const aliases = {}; |
|
|
|
// differentiate node-pre-gyp's logs from npm's |
|
log.heading = 'node-pre-gyp'; |
|
|
|
if (mocking) { |
|
log.warn(`mocking s3 to ${process.env.node_pre_gyp_mock_s3}`); |
|
} |
|
|
|
// this is a getter to avoid circular reference warnings with node v14. |
|
Object.defineProperty(exports, 'find', { |
|
get: function() { |
|
return require('./pre-binding').find; |
|
}, |
|
enumerable: true |
|
}); |
|
|
|
// in the following, "my_module" is using node-pre-gyp to |
|
// prebuild and install pre-built binaries. "main_module" |
|
// is using "my_module". |
|
// |
|
// "bin/node-pre-gyp" invokes Run() without a path. the |
|
// expectation is that the working directory is the package |
|
// root "my_module". this is true because in all cases npm is |
|
// executing a script in the context of "my_module". |
|
// |
|
// "pre-binding.find()" is executed by "my_module" but in the |
|
// context of "main_module". this is because "main_module" is |
|
// executing and requires "my_module" which is then executing |
|
// "pre-binding.find()" via "node-pre-gyp.find()", so the working |
|
// directory is that of "main_module". |
|
// |
|
// that's why "find()" must pass the path to package.json. |
|
// |
|
function Run({ package_json_path = './package.json', argv }) { |
|
this.package_json_path = package_json_path; |
|
this.commands = {}; |
|
|
|
const self = this; |
|
cli_commands.forEach((command) => { |
|
self.commands[command] = function(argvx, callback) { |
|
log.verbose('command', command, argvx); |
|
return require('./' + command)(self, argvx, callback); |
|
}; |
|
}); |
|
|
|
this.parseArgv(argv); |
|
|
|
// this is set to true after the binary.host property was set to |
|
// either staging_host or production_host. |
|
this.binaryHostSet = false; |
|
} |
|
inherits(Run, EE); |
|
exports.Run = Run; |
|
const proto = Run.prototype; |
|
|
|
/** |
|
* Export the contents of the package.json. |
|
*/ |
|
|
|
proto.package = require('../package.json'); |
|
|
|
/** |
|
* nopt configuration definitions |
|
*/ |
|
|
|
proto.configDefs = { |
|
help: Boolean, // everywhere |
|
arch: String, // 'configure' |
|
debug: Boolean, // 'build' |
|
directory: String, // bin |
|
proxy: String, // 'install' |
|
loglevel: String // everywhere |
|
}; |
|
|
|
/** |
|
* nopt shorthands |
|
*/ |
|
|
|
proto.shorthands = { |
|
release: '--no-debug', |
|
C: '--directory', |
|
debug: '--debug', |
|
j: '--jobs', |
|
silent: '--loglevel=silent', |
|
silly: '--loglevel=silly', |
|
verbose: '--loglevel=verbose' |
|
}; |
|
|
|
/** |
|
* expose the command aliases for the bin file to use. |
|
*/ |
|
|
|
proto.aliases = aliases; |
|
|
|
/** |
|
* Parses the given argv array and sets the 'opts', 'argv', |
|
* 'command', and 'package_json' properties. |
|
*/ |
|
|
|
proto.parseArgv = function parseOpts(argv) { |
|
this.opts = nopt(this.configDefs, this.shorthands, argv); |
|
this.argv = this.opts.argv.remain.slice(); |
|
const commands = this.todo = []; |
|
|
|
// create a copy of the argv array with aliases mapped |
|
argv = this.argv.map((arg) => { |
|
// is this an alias? |
|
if (arg in this.aliases) { |
|
arg = this.aliases[arg]; |
|
} |
|
return arg; |
|
}); |
|
|
|
// process the mapped args into "command" objects ("name" and "args" props) |
|
argv.slice().forEach((arg) => { |
|
if (arg in this.commands) { |
|
const args = argv.splice(0, argv.indexOf(arg)); |
|
argv.shift(); |
|
if (commands.length > 0) { |
|
commands[commands.length - 1].args = args; |
|
} |
|
commands.push({ name: arg, args: [] }); |
|
} |
|
}); |
|
if (commands.length > 0) { |
|
commands[commands.length - 1].args = argv.splice(0); |
|
} |
|
|
|
|
|
// if a directory was specified package.json is assumed to be relative |
|
// to it. |
|
let package_json_path = this.package_json_path; |
|
if (this.opts.directory) { |
|
package_json_path = path.join(this.opts.directory, package_json_path); |
|
} |
|
|
|
this.package_json = JSON.parse(fs.readFileSync(package_json_path)); |
|
|
|
// expand commands entries for multiple napi builds |
|
this.todo = napi.expand_commands(this.package_json, this.opts, commands); |
|
|
|
// support for inheriting config env variables from npm |
|
const npm_config_prefix = 'npm_config_'; |
|
Object.keys(process.env).forEach((name) => { |
|
if (name.indexOf(npm_config_prefix) !== 0) return; |
|
const val = process.env[name]; |
|
if (name === npm_config_prefix + 'loglevel') { |
|
log.level = val; |
|
} else { |
|
// add the user-defined options to the config |
|
name = name.substring(npm_config_prefix.length); |
|
// avoid npm argv clobber already present args |
|
// which avoids problem of 'npm test' calling |
|
// script that runs unique npm install commands |
|
if (name === 'argv') { |
|
if (this.opts.argv && |
|
this.opts.argv.remain && |
|
this.opts.argv.remain.length) { |
|
// do nothing |
|
} else { |
|
this.opts[name] = val; |
|
} |
|
} else { |
|
this.opts[name] = val; |
|
} |
|
} |
|
}); |
|
|
|
if (this.opts.loglevel) { |
|
log.level = this.opts.loglevel; |
|
} |
|
log.resume(); |
|
}; |
|
|
|
/** |
|
* allow the binary.host property to be set at execution time. |
|
* |
|
* for this to take effect requires all the following to be true. |
|
* - binary is a property in package.json |
|
* - binary.host is falsey |
|
* - binary.staging_host is not empty |
|
* - binary.production_host is not empty |
|
* |
|
* if any of the previous checks fail then the function returns an empty string |
|
* and makes no changes to package.json's binary property. |
|
* |
|
* |
|
* if command is "publish" then the default is set to "binary.staging_host" |
|
* if command is not "publish" the the default is set to "binary.production_host" |
|
* |
|
* if the command-line option '--s3_host' is set to "staging" or "production" then |
|
* "binary.host" is set to the specified "staging_host" or "production_host". if |
|
* '--s3_host' is any other value an exception is thrown. |
|
* |
|
* if '--s3_host' is not present then "binary.host" is set to the default as above. |
|
* |
|
* this strategy was chosen so that any command other than "publish" or "unpublish" uses "production" |
|
* as the default without requiring any command-line options but that "publish" and "unpublish" require |
|
* '--s3_host production_host' to be specified in order to *really* publish (or unpublish). publishing |
|
* to staging can be done freely without worrying about disturbing any production releases. |
|
*/ |
|
proto.setBinaryHostProperty = function(command) { |
|
if (this.binaryHostSet) { |
|
return this.package_json.binary.host; |
|
} |
|
const p = this.package_json; |
|
// don't set anything if host is present. it must be left blank to trigger this. |
|
if (!p || !p.binary || p.binary.host) { |
|
return ''; |
|
} |
|
// and both staging and production must be present. errors will be reported later. |
|
if (!p.binary.staging_host || !p.binary.production_host) { |
|
return ''; |
|
} |
|
let target = 'production_host'; |
|
if (command === 'publish' || command === 'unpublish') { |
|
target = 'staging_host'; |
|
} |
|
// the environment variable has priority over the default or the command line. if |
|
// either the env var or the command line option are invalid throw an error. |
|
const npg_s3_host = process.env.node_pre_gyp_s3_host; |
|
if (npg_s3_host === 'staging' || npg_s3_host === 'production') { |
|
target = `${npg_s3_host}_host`; |
|
} else if (this.opts['s3_host'] === 'staging' || this.opts['s3_host'] === 'production') { |
|
target = `${this.opts['s3_host']}_host`; |
|
} else if (this.opts['s3_host'] || npg_s3_host) { |
|
throw new Error(`invalid s3_host ${this.opts['s3_host'] || npg_s3_host}`); |
|
} |
|
|
|
p.binary.host = p.binary[target]; |
|
this.binaryHostSet = true; |
|
|
|
return p.binary.host; |
|
}; |
|
|
|
/** |
|
* Returns the usage instructions for node-pre-gyp. |
|
*/ |
|
|
|
proto.usage = function usage() { |
|
const str = [ |
|
'', |
|
' Usage: node-pre-gyp <command> [options]', |
|
'', |
|
' where <command> is one of:', |
|
cli_commands.map((c) => { |
|
return ' - ' + c + ' - ' + require('./' + c).usage; |
|
}).join('\n'), |
|
'', |
|
'node-pre-gyp@' + this.version + ' ' + path.resolve(__dirname, '..'), |
|
'node@' + process.versions.node |
|
].join('\n'); |
|
return str; |
|
}; |
|
|
|
/** |
|
* Version number getter. |
|
*/ |
|
|
|
Object.defineProperty(proto, 'version', { |
|
get: function() { |
|
return this.package.version; |
|
}, |
|
enumerable: true |
|
});
|
|
|