123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309 |
- '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
- });
|