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