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.
		
		
		
		
		
			
		
			
				
					310 lines
				
				8.6 KiB
			
		
		
			
		
	
	
					310 lines
				
				8.6 KiB
			| 
								 
											3 years ago
										 
									 | 
							
								'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
							 | 
						||
| 
								 | 
							
								});
							 |