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