From b8d30b2b911b92d1851c12e4f671030658b733d5 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Mon, 18 Nov 2019 01:21:31 -0700 Subject: [PATCH] wip: simpler config and defaults --- greenlock.js | 29 +++---- lib/init.js | 167 +++++++++++++++++++++++++++++++++++++++++ lib/manager-wrapper.js | 7 +- lib/rc.js | 69 +++++++++++++++++ 4 files changed, 257 insertions(+), 15 deletions(-) create mode 100644 lib/init.js create mode 100644 lib/rc.js diff --git a/greenlock.js b/greenlock.js index b8664ca..3fb19d2 100644 --- a/greenlock.js +++ b/greenlock.js @@ -18,7 +18,7 @@ var ChWrapper = require('./lib/challenges-wrapper.js'); var MngWrapper = require('./lib/manager-wrapper.js'); var UserEvents = require('./user-events.js'); -var GreenlockRc = require('./greenlockrc.js'); +var Init = require('./lib/init.js'); var caches = {}; @@ -48,27 +48,30 @@ G.create = function(gconf) { }); } - if (!gconf.packageRoot) { - gconf.packageRoot = process.cwd(); - console.warn( - '`packageRoot` not defined, trying ' + gconf.packageRoot - ); - } - if ('function' === typeof gconf.notify) { gdefaults.notify = gconf.notify; } else { gdefaults.notify = _notify; } - var rc = GreenlockRc.resolve(gconf); - gconf = Object.assign(rc, gconf); + /* + if (!gconf.packageRoot) { + gconf.packageRoot = process.cwd(); + console.warn( + '`packageRoot` not defined, trying ' + gconf.packageRoot + ); + } + */ + + gconf = Init._init(gconf); // OK: /path/to/blah // OK: npm-name-blah // NOT OK: ./rel/path/to/blah - if ('.' === (gconf.manager || '')[0]) { - gconf.manager = gconf.packageRoot + '/' + gconf.manager; + // Error: .blah + if ('.' === (gconf.manager.module || '')[0]) { + gconf.manager.module = + gconf.packageRoot + '/' + gconf.manager.module.slice(2); } // Wraps each of the following with appropriate error checking @@ -573,7 +576,7 @@ function mergeDefaults(MCONF, gconf) { if (false !== MCONF.subscriberEmail) { MCONF.subscriberEmail = gconf.subscriberEmail || gconf.maintainerEmail || undefined; - MCONF.subscriberEmail = gconf.agreeToTerms || undefined; + MCONF.agreeToTerms = gconf.agreeToTerms || undefined; console.info(''); console.info('[default] subscriberEmail: ' + MCONF.subscriberEmail); console.info( diff --git a/lib/init.js b/lib/init.js new file mode 100644 index 0000000..dbdd963 --- /dev/null +++ b/lib/init.js @@ -0,0 +1,167 @@ +"use strict"; + +var Init = module.exports; + +var fs = require("fs"); +var path = require("path"); +//var promisify = require("util").promisify; + +Init._init = function(opts) { + //var Rc = require("@root/greenlock/rc"); + var Rc = require("./rc.js"); + var pkgText; + var pkgErr; + var msgErr; + //var emailErr; + var realPkg; + var userPkg; + var myPkg = {}; + // we want to be SUPER transparent that we're reading from package.json + // we don't want anything unexpected + var implicitConfig = []; + var rc; + + if (opts.packageRoot) { + try { + pkgText = fs.readFileSync(path.resolve(opts.packageRoot, "package.json"), "utf8"); + } catch (e) { + pkgErr = e; + console.warn("`packageRoot` should be the root of the package (probably `__dirname`)"); + } + } + + if (pkgText) { + try { + realPkg = JSON.parse(pkgText); + } catch (e) { + pkgErr = e; + } + } + + userPkg = opts.package; + + if (realPkg || userPkg) { + userPkg = userPkg || {}; + realPkg = realPkg || {}; + + // build package agent + if (!opts.packageAgent) { + // name + myPkg.name = userPkg.name; + if (!myPkg.name) { + myPkg.name = realPkg.name; + implicitConfig.push("name"); + } + + // version + myPkg.version = userPkg.version; + if (!myPkg.version) { + myPkg.version = realPkg.version; + implicitConfig.push("version"); + } + if (myPkg.name && myPkg.version) { + opts.packageAgent = myPkg.name + "/" + myPkg.version; + } + } + + // build author + myPkg.author = opts.maintainerEmail; + if (!myPkg.author) { + myPkg.author = (userPkg.author && userPkg.author.email) || userPkg.author; + } + if (!myPkg.author) { + implicitConfig.push("author"); + myPkg.author = (realPkg.author && realPkg.author.email) || realPkg.author; + } + opts.maintainerEmail = myPkg.author; + } + + if (!opts.packageAgent) { + msgErr = "missing `packageAgent` and also failed to read `name` and/or `version` from `package.json`"; + if (pkgErr) { + msgErr += ": " + pkgErr.message; + } + throw new Error(msgErr); + } + + opts.maintainerEmail = parseMaintainer(opts.maintainerEmail); + if (!opts.maintainerEmail) { + msgErr = + "missing or malformed `maintainerEmail` (or `author` from `package.json`), which is used as the contact for support notices"; + throw new Error(msgErr); + } + + if (opts.packageRoot) { + // Place the rc file in the packageroot + rc = Rc._initSync(opts.packageRoot, opts.manager, opts.configDir); + opts.configDir = rc.configDir; + opts.manager = rc.manager; + } + + if (!opts.configDir && !opts.manager) { + throw new Error("missing `packageRoot` and `configDir`, but no `manager` was supplied"); + } + + //var mkdirp = promisify(require("@root/mkdirp")); + var configFile = path.join(opts.configDir, "config.json"); + var config; + try { + config = JSON.parse(fs.readFileSync(configFile)); + } catch (e) { + if ("ENOENT" !== e.code) { + throw e; + } + config = { defaults: {} }; + } + + opts.manager = rc.manager || (config.defaults && config.defaults.manager) || config.manager; + if (!opts.manager) { + opts.manager = "@greenlock/manager"; + } + if ("string" === typeof opts.manager) { + opts.manager = { + module: opts.manager + }; + } + opts.manager = JSON.parse(JSON.stringify(opts.manager)); + + var confconf = ["configDir", "configFile", "staging", "directoryUrl"]; + Object.keys(opts).forEach(function(k) { + if (!confconf.includes(k)) { + return; + } + if ("undefined" !== typeof opts.manager[k]) { + return; + } + opts.manager[k] = opts[k]; + }); + + /* + var ignore = ["packageRoot", "maintainerEmail", "packageAgent", "staging", "directoryUrl", "manager"]; + Object.keys(opts).forEach(function(k) { + if (ignore.includes(k)) { + return; + } + opts.manager[k] = opts[k]; + }); + */ + + // Place the rc file in the configDir itself + //Rc._initSync(opts.configDir, opts.configDir); + return opts; +}; + +// ex: "John Doe (https://john.doe)" +// ex: "John Doe " +// ex: "" +// ex: "john@example.com" +var looseEmailRe = /(^|[\s<])([^'" <>:;`]+@[^'" <>:;`]+\.[^'" <>:;`]+)/; +function parseMaintainer(maintainerEmail) { + try { + maintainerEmail = maintainerEmail.match(looseEmailRe)[2]; + } catch (e) { + maintainerEmail = null; + } + + return maintainerEmail; +} diff --git a/lib/manager-wrapper.js b/lib/manager-wrapper.js index a5e3e94..24f421c 100644 --- a/lib/manager-wrapper.js +++ b/lib/manager-wrapper.js @@ -398,6 +398,7 @@ function loadManager(gconf) { // 1. Get the manager // 2. Figure out if we need to wrap it + /* if (!gconf.manager) { gconf.manager = '@greenlock/manager'; } @@ -407,9 +408,10 @@ function loadManager(gconf) { '`manager` should be a string representing the npm name or file path of the module' ); } + */ try { // wrap this to be safe for @greenlock/manager - m = require(gconf.manager).create(gconf); + m = require(gconf.manager.module).create(gconf.manager); } catch (e) { console.error('Error loading manager:'); console.error(e.code); @@ -506,7 +508,8 @@ function mergeManager(gconf) { }; } else if (mini.set) { throw new Error( - gconf.manager + ' implements `set()`, but not `get()` or `find()`' + gconf.manager.module + + ' implements `set()`, but not `get()` or `find()`' ); } else { mega.find = m().find; diff --git a/lib/rc.js b/lib/rc.js new file mode 100644 index 0000000..54adceb --- /dev/null +++ b/lib/rc.js @@ -0,0 +1,69 @@ +'use strict'; + +var Rc = module.exports; +var fs = require('fs'); +var path = require('path'); + +// This is only called if packageRoot is specified +// (which it should be most of the time) +Rc._initSync = function(dirname, manager, configDir) { + if (!dirname) { + return {}; + } + + // dirname / opts.packageRoot + var rcpath = path.resolve(dirname, '.greenlockrc'); + var rc; + + try { + rc = JSON.parse(fs.readFileSync(rcpath)); + } catch (e) { + if ('ENOENT' !== e.code) { + throw e; + } + rc = {}; + } + + // In the general case the manager should be specified in the + // config file, which is in the config dir, but for the specific + // case in which all custom plugins are being used and no config + // dir is needed, we allow the manager to be read from the rc. + // ex: manager: { module: 'name', xxxx: 'xxxx' } + if (manager) { + if (rc.manager) { + if ( + rc.manager !== manager || + rc.manager.module !== manager.module + ) { + console.info( + "changing `manager` from '%s' to '%s'", + rc.manager, + manager + ); + } + } + rc.manager = manager; + } + + if (!configDir) { + configDir = rc.configDir; + } + + if (configDir && configDir !== rc.configDir) { + if (rc.configDir) { + console.info( + "changing `configDir` from '%s' to '%s'", + rc.configDir, + configDir + ); + } + rc.configDir = configDir; + fs.writeFileSync(rcpath, JSON.stringify(rc)); + } else if (!rc.configDir) { + configDir = path.resolve(dirname, 'greenlock.d'); + rc.configDir = configDir; + fs.writeFileSync(rcpath, JSON.stringify(rc)); + } + + return rc; +};