diff --git a/bin/lando.js b/bin/lando.js index e3f8a8754..0208f6249 100755 --- a/bin/lando.js +++ b/bin/lando.js @@ -48,45 +48,45 @@ if (!hconf[binPath] || (landoConfig.version !== hconf[binPath].version)) { ]; const plugins = pluginDirs - .filter(dir => dir.type === 'core') - .map(dir => ([dir.dir, fs.readdirSync(path.resolve(__dirname, '..', dir.dir))])) - .map(dir => dir[1].map(plugin => path.join(dir[0], plugin))) - .flat(Number.POSITIVE_INFINITY) - .map(dir => ({ - location: `lando://${dir}`, - manifest: path.join(__dirname, '..', dir, 'plugin.yml'), - pjson: path.join(__dirname, '..', dir, 'package.json'), - })) - .filter(plugin => fs.existsSync(plugin.manifest)) + .filter(dir => dir.type === 'core') + .map(dir => ([dir.dir, fs.readdirSync(path.resolve(__dirname, '..', dir.dir))])) + .map(dir => dir[1].map(plugin => path.join(dir[0], plugin))) + .flat(Number.POSITIVE_INFINITY) + .map(dir => ({ + location: `lando://${dir}`, + manifest: path.join(__dirname, '..', dir, 'plugin.yml'), + pjson: path.join(__dirname, '..', dir, 'package.json'), + })) + .filter(plugin => fs.existsSync(plugin.manifest)) .map(plugin => Object.assign(plugin, { // eslint-disable-line - manifest: yaml.load(fs.readFileSync(plugin.manifest)), - pjson: fs.existsSync(plugin.pjson) ? require(plugin.pjson) : {}, - })) - .map(plugin => ({ - name: plugin.manifest.name || plugin.pjson.name, - package: plugin.manifest.name || plugin.pjson.name, - deprecated: plugin.deprecated === true, - hidden: plugin.hidden === true, - location: plugin.location, - type: 'core', - version: plugin.manifest.version || plugin.pjson.version || version, - isValid: true, - isInstalled: true, - })); + manifest: yaml.load(fs.readFileSync(plugin.manifest)), + pjson: fs.existsSync(plugin.pjson) ? require(plugin.pjson) : {}, + })) + .map(plugin => ({ + name: plugin.manifest.name || plugin.pjson.name, + package: plugin.manifest.name || plugin.pjson.name, + deprecated: plugin.deprecated === true, + hidden: plugin.hidden === true, + location: plugin.location, + type: 'core', + version: plugin.manifest.version || plugin.pjson.version || version, + isValid: true, + isInstalled: true, + })); // get app config const extension = `.${landoFile.split('.')[landoFile.split('.').length -1]}`; const namespace = path.basename(landoFile, extension); const landofiles = preLandoFiles.concat([namespace]).concat(postLandoFiles) - .map(file => file.replace(namespace, '')) - .map(file => file.replace(extension, '')) - .map(file => file.replace('.', '')); + .map(file => file.replace(namespace, '')) + .map(file => file.replace(extension, '')) + .map(file => file.replace('.', '')); // assemble hconf[binPath] = { lando: {globalDir, plugins, pluginDirs, version, envPrefix, product}, app: {landofile: namespace, landofiles, - }}; + }}; // dump fs.mkdirSync(path.dirname(landoConfig.hconf), {recursive: true}); fs.writeFileSync(landoConfig.hconf, JSON.stringify(hconf, null, 2)); diff --git a/lib/app.js b/lib/app.js index 7ec57db2b..492217e72 100644 --- a/lib/app.js +++ b/lib/app.js @@ -20,14 +20,14 @@ const initAndReport = (app, method = 'start') => { * Helper to load in all app plugins */ const loadPlugins = (app, lando) => Promise.resolve(app.plugins.registry) - // Filter out - .filter(plugin => _.has(plugin, 'app')) - // LOADEM! - .map(plugin => app.plugins.load(plugin, plugin.app, app, lando)) - // Remove any naughty shit - .map(plugin => _.pick(plugin.data, ['config', 'composeData', 'env', 'labels'])) - // Merge minotaur - .each(result => _.merge(app, result)); +// Filter out + .filter(plugin => _.has(plugin, 'app')) +// LOADEM! + .map(plugin => app.plugins.load(plugin, plugin.app, app, lando)) +// Remove any naughty shit + .map(plugin => _.pick(plugin.data, ['config', 'composeData', 'env', 'labels'])) +// Merge minotaur + .each(result => _.merge(app, result)); /** * The class to instantiate a new App @@ -92,12 +92,12 @@ module.exports = class App { this.log = new Log(_.merge({}, lando.config, {logName: this.name})); this.shell = new Shell(this.log); this.engine = bootstrap.setupEngine( - lando.config, - lando.cache, - lando.events, - this.log, - this.shell, - lando.config.instance, + lando.config, + lando.cache, + lando.events, + this.log, + this.shell, + lando.config.instance, ); this.Promise = lando.Promise; this.events = new AsyncEvents(this.log); @@ -205,12 +205,12 @@ module.exports = class App { * @alias app.events:pre-destroy * @event pre_destroy */ - .then(() => this.events.emit('pre-destroy')) + .then(() => this.events.emit('pre-destroy')) // Make sure app is stopped. - .then(() => this.stop()) + .then(() => this.stop()) // Uninstall app. - .then(() => this.uninstall(true)) + .then(() => this.uninstall(true)) /** * Event that runs after an app is destroyed. @@ -219,8 +219,8 @@ module.exports = class App { * @alias app.events:post-destroy * @event post_destroy */ - .then(() => this.events.emit('post-destroy')) - .then(() => this.log.info('destroyed app.')); + .then(() => this.events.emit('post-destroy')) + .then(() => this.log.info('destroyed app.')); }; /** @@ -263,12 +263,12 @@ module.exports = class App { return loadPlugins(this, this._lando).then(() => this.events.emit('pre-init', this)) // Actually assemble this thing so its ready for that engine - .then(() => { - // Get all the services - this.services = utils.getServices(this.composeData); - // Merge whatever we have thus far together - this.info = utils.getInfoDefaults(this); - }) + .then(() => { + // Get all the services + this.services = utils.getServices(this.composeData); + // Merge whatever we have thus far together + this.info = utils.getInfoDefaults(this); + }) /** * Event that allows altering of the app object right after it has been * full initialized and all its plugins have been loaded. @@ -278,21 +278,21 @@ module.exports = class App { * @event post_init * @property {App} app The app instance. */ - .then(() => this.events.emit('post-init', this)) + .then(() => this.events.emit('post-init', this)) // Finish up - .then(() => { - // Front load our app mounts - this.add(new this.ComposeService('mounts', {}, {services: utils.getAppMounts(this)}), true); - // Then front load our globals - this.add(new this.ComposeService('globals', {}, {services: utils.getGlobals(this)}), true); - // Take the big dump of all our compose stuff - this.compose = utils.dumpComposeData(this.composeData, this._dir); - // Log - this.initialized = true; - this.log.verbose('app is ready!'); - this.log.silly('app has compose files', this.compose); - this.log.silly('app has config', this.config); - }) + .then(() => { + // Front load our app mounts + this.add(new this.ComposeService('mounts', {}, {services: utils.getAppMounts(this)}), true); + // Then front load our globals + this.add(new this.ComposeService('globals', {}, {services: utils.getGlobals(this)}), true); + // Take the big dump of all our compose stuff + this.compose = utils.dumpComposeData(this.composeData, this._dir); + // Log + this.initialized = true; + this.log.verbose('app is ready!'); + this.log.silly('app has compose files', this.compose); + this.log.silly('app has config', this.config); + }) /** * Event that runs when the app is ready for action * @@ -301,7 +301,7 @@ module.exports = class App { * @event ready * @property {App} app The app instance. */ - .then(() => this.events.emit('ready', this)); + .then(() => this.events.emit('ready', this)); }; /** @@ -334,15 +334,15 @@ module.exports = class App { * @alias app.events:pre-rebuild * @event pre_rebuild */ - .then(() => this.events.emit('pre-rebuild')) + .then(() => this.events.emit('pre-rebuild')) // Stop app. - .then(() => this.stop()) + .then(() => this.stop()) // Uninstall app - .then(() => this.uninstall()) + .then(() => this.uninstall()) // Repull/build components. - .then(() => this.engine.build(this)) + .then(() => this.engine.build(this)) // Install app. - .then(() => this.start()) + .then(() => this.start()) /** * Event that runs after an app is rebuilt. * @@ -350,18 +350,18 @@ module.exports = class App { * @alias app.events:post-rebuild * @event post_rebuild */ - .then(() => this.events.emit('post-rebuild')) - .then(() => this.log.info('rebuilt app.')); + .then(() => this.events.emit('post-rebuild')) + .then(() => this.log.info('rebuilt app.')); }; - /* + /* * @TODO */ reset() { this.initialized = false; }; - /** + /** * Stops and then starts an app. * * This just runs `app.stop` and `app.start` in succession. @@ -380,8 +380,8 @@ module.exports = class App { this.log.info('restarting app...'); // stop/start return this.stop() - .then(() => this.start()) - .then(() => this.log.info('restarted app.')); + .then(() => this.start()) + .then(() => this.log.info('restarted app.')); }; /** @@ -411,10 +411,10 @@ module.exports = class App { * @alias app.events:pre-start * @event pre_start */ - .then(() => this.events.emit('pre-start')) + .then(() => this.events.emit('pre-start')) // Start core containers - .then(() => this.engine.start(this)) + .then(() => this.engine.start(this)) /** * Event that runs after an app is started. @@ -426,8 +426,8 @@ module.exports = class App { * @alias app.events:post-start * @event post_start */ - .then(() => this.events.emit('post-start')) - .then(() => this.log.info('started app.')); + .then(() => this.events.emit('post-start')) + .then(() => this.log.info('started app.')); }; /** @@ -453,10 +453,10 @@ module.exports = class App { * @alias app.events:pre-stop * @event pre_stop */ - .then(() => this.events.emit('pre-stop')) + .then(() => this.events.emit('pre-stop')) // Stop components. - .then(() => this.engine.stop(this)) + .then(() => this.engine.stop(this)) /** * Event that runs after an app stop. @@ -465,8 +465,8 @@ module.exports = class App { * @alias app.events:post-stop * @event post_stop */ - .then(() => this.events.emit('post-stop')) - .then(() => this.log.info('stopped app.')); + .then(() => this.events.emit('post-stop')) + .then(() => this.log.info('stopped app.')); }; /** @@ -498,10 +498,10 @@ module.exports = class App { * @alias app.events:pre-uninstall * @event pre_uninstall */ - .then(() => this.events.emit('pre-uninstall')) + .then(() => this.events.emit('pre-uninstall')) // Kill components. - .then(() => this.engine.destroy(_.merge({}, this, {opts: {purge}}))) + .then(() => this.engine.destroy(_.merge({}, this, {opts: {purge}}))) /** * Event that runs after an app is uninstalled. @@ -513,8 +513,8 @@ module.exports = class App { * @alias app.events:post-uninstall * @event post_uninstall */ - .then(() => this.events.emit('post-uninstall')) - .then(() => this.log.info('uninstalled app.')); + .then(() => this.events.emit('post-uninstall')) + .then(() => this.log.info('uninstalled app.')); }; getServiceContainerId(service) { diff --git a/lib/bootstrap.js b/lib/bootstrap.js index fd4c8b50d..588bc5603 100644 --- a/lib/bootstrap.js +++ b/lib/bootstrap.js @@ -43,8 +43,8 @@ const loadLandoFile = file => { const appRunner = command => (argv, lando) => { const app = lando.getApp(argv._app.root); return lando.events.emit('pre-app-runner', app) - .then(() => lando.events.emit('pre-command-runner', app)) - .then(() => app.init().then(() => _.find(app.tasks, {command}).run(argv))); + .then(() => lando.events.emit('pre-command-runner', app)) + .then(() => app.init().then(() => _.find(app.tasks, {command}).run(argv))); }; /* @@ -65,27 +65,27 @@ const engineRunner = (config, command) => (argv, lando) => { app.config.tooling = utils.getToolingTasks(app.config.tooling, app); // Final event to modify and then load and run return lando.events.emit('pre-engine-runner', app) - .then(() => lando.events.emit('pre-command-runner', app)) - .then(() => buildTask(_.find(app.config.tooling, task => task.name === command), lando).run(argv)); + .then(() => lando.events.emit('pre-command-runner', app)) + .then(() => buildTask(_.find(app.config.tooling, task => task.name === command), lando).run(argv)); }; /* * Helper to traverse up directories from a start point */ const traverseUp = file => _(_.range(path.dirname(file).split(path.sep).length)) - .map(end => _.dropRight(path.dirname(file).split(path.sep), end).join(path.sep)) - .map(dir => path.join(dir, path.basename(file))) - .value(); + .map(end => _.dropRight(path.dirname(file).split(path.sep), end).join(path.sep)) + .map(dir => path.join(dir, path.basename(file))) + .value(); /* * Paths to / */ const pathsToRoot = (startFrom = process.cwd()) => { return _(_.range(path.dirname(startFrom).split(path.sep).length)) - .map(end => _.dropRight(path.dirname(startFrom).split(path.sep), end).join(path.sep)) - .unshift(startFrom) - .dropRight() - .value(); + .map(end => _.dropRight(path.dirname(startFrom).split(path.sep), end).join(path.sep)) + .unshift(startFrom) + .dropRight() + .value(); }; /* @@ -177,13 +177,13 @@ exports.getApp = (files, userConfRoot) => { * Helper to find lando files we can use */ exports.getLandoFiles = (files = [], startFrom = process.cwd()) => _(files) - .flatMap(file => traverseUp(path.resolve(startFrom, file))) - .sortBy().reverse() - .filter(file => fs.existsSync(file) && path.isAbsolute(file)) - .thru(files => _.isEmpty(files) ? [] : [_.first(files)]) - .flatMap(dirFile => _.map(files, file => path.join(path.dirname(dirFile), file))) - .filter(file => fs.existsSync(file)) - .value(); + .flatMap(file => traverseUp(path.resolve(startFrom, file))) + .sortBy().reverse() + .filter(file => fs.existsSync(file) && path.isAbsolute(file)) + .thru(files => _.isEmpty(files) ? [] : [_.first(files)]) + .flatMap(dirFile => _.map(files, file => path.join(path.dirname(dirFile), file))) + .filter(file => fs.existsSync(file)) + .value(); /* * Helper to parse tasks @@ -196,13 +196,13 @@ exports.getTasks = (config = {}, argv = {}, tasks = []) => { if (fs.existsSync(config.toolingRouter)) { // Get the closest route const closestRoute = _(loadCacheFile(config.toolingRouter)) - .map(route => _.merge({}, route, { - closeness: _.indexOf(pathsToRoot(), route.route), - })) - .filter(route => route.closeness !== -1) - .orderBy('closeness') - .thru(routes => routes[0]) - .value(); + .map(route => _.merge({}, route, { + closeness: _.indexOf(pathsToRoot(), route.route), + })) + .filter(route => route.closeness !== -1) + .orderBy('closeness') + .thru(routes => routes[0]) + .value(); // If we have a closest route lets mod config.tooling if (_.has(closestRoute, 'tooling')) { diff --git a/lib/cli.js b/lib/cli.js index 812992ef1..d41f9c4b0 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -189,23 +189,23 @@ module.exports = class Cli { // Yargs! yargs.usage(usage.join(' ')) - .demandCommand(1, 'You need at least one command before moving on') - .example('lando start', 'Run lando start') - .example('lando rebuild --help', 'Get help about using the lando rebuild command') - .example('lando destroy -y -vvv', 'Run lando destroy non-interactively and with maximum verbosity') - .example('lando --clear', 'Clear the lando tasks cache') - .middleware([(argv => { - argv._app = config; - })]) - .recommendCommands() - .wrap(yargs.terminalWidth() * 0.70) - .option('channel', globalOptions.channel) - .option('clear', globalOptions.clear) - .help(false) - .option('lando', globalOptions.lando) - .option('help', globalOptions.help) - .option('verbose', globalOptions.verbose) - .version(false); + .demandCommand(1, 'You need at least one command before moving on') + .example('lando start', 'Run lando start') + .example('lando rebuild --help', 'Get help about using the lando rebuild command') + .example('lando destroy -y -vvv', 'Run lando destroy non-interactively and with maximum verbosity') + .example('lando --clear', 'Clear the lando tasks cache') + .middleware([(argv => { + argv._app = config; + })]) + .recommendCommands() + .wrap(yargs.terminalWidth() * 0.70) + .option('channel', globalOptions.channel) + .option('clear', globalOptions.clear) + .help(false) + .option('lando', globalOptions.lando) + .option('help', globalOptions.help) + .option('verbose', globalOptions.verbose) + .version(false); // Loop through the tasks and add them to the CLI _.forEach(_.sortBy(tasks, 'command'), task => { @@ -303,12 +303,12 @@ module.exports = class Cli { * @event task_CMD_answers * @property {Object} answers argv and inquirer questions */ - .then(() => lando.events.emit('cli-answers', data, argv._[0])) - .then(() => lando.events.emit(`cli-${argv._[0]}-answers`, data, argv._[0])) + .then(() => lando.events.emit('cli-answers', data, argv._[0])) + .then(() => lando.events.emit(`cli-${argv._[0]}-answers`, data, argv._[0])) // Attempt to filter out questions that have already been answered // Prompt the use for feedback if needed and sort by weight - .then(() => formatters.handleInteractive(data.inquiry, data.options, command, lando)) + .then(() => formatters.handleInteractive(data.inquiry, data.options, command, lando)) /** * Event that allows final altering of answers before the task runs @@ -319,23 +319,23 @@ module.exports = class Cli { * @event task_CMD_run * @property {Object} answers object */ - .then(answers => lando.events.emit('cli-run', _.merge(data.options, answers), argv._[0])) - .then(() => lando.events.emit(`cli-${argv._[0]}-run`, data, argv._[0])) + .then(answers => lando.events.emit('cli-run', _.merge(data.options, answers), argv._[0])) + .then(() => lando.events.emit(`cli-${argv._[0]}-run`, data, argv._[0])) // Find and run the task, unless we already have one // @TODO: somehow handle when commands break eg change task name, malformed tasks - .then(() => { - if (_.isFunction(run)) return run(data.options, lando); - else return _.find(lando.tasks, {command}).run(data.options); - }) + .then(() => { + if (_.isFunction(run)) return run(data.options, lando); + else return _.find(lando.tasks, {command}).run(data.options); + }) // Add a final event for other stuff - .then(() => lando.events.emit('before-end')) + .then(() => lando.events.emit('before-end')) // Handle all other errors eg likely things that happen pre bootstrap - .catch(error => this.handleError(error, lando.error, this.argv().verbose, lando)) + .catch(error => this.handleError(error, lando.error, this.argv().verbose, lando)) // If we caught an error that resulted in an error code lets make sure we exit non0 - .finally(() => process.exit(_.get(lando, 'exitCode', 0))); + .finally(() => process.exit(_.get(lando, 'exitCode', 0))); }); }; diff --git a/lib/compose.js b/lib/compose.js index 07a4edfc3..b081b9423 100644 --- a/lib/compose.js +++ b/lib/compose.js @@ -128,7 +128,7 @@ exports.logs = (compose, project, opts = {}) => buildShell('logs', project, comp exports.pull = (compose, project, opts = {}) => { /** @type {string[]} */ const pull = (opts.pullable || []) - .filter(service => !opts.services || opts.services.includes(service)); + .filter(service => !opts.services || opts.services.includes(service)); if (pull.length) { return buildShell('pull', project, compose, {services: pull}); } diff --git a/lib/config.js b/lib/config.js index 0c80425fd..d4efcdb77 100644 --- a/lib/config.js +++ b/lib/config.js @@ -45,43 +45,43 @@ const setDockerHost = (hostname, port = 2376) => url.format({ * @TODO */ const normalizePluginDirs = (dirs = [], baseDir = __dirname, isLandoFile = false) => _(dirs) - .map(data => { - if (_.isString(data)) { - return { - path: data, - subdir: isLandoFile ? '.' : 'plugins', - }; - } - // or just return - return data; - }) - .map(data => { - if (path.isAbsolute(data.path)) return data; - else { - data.path = path.resolve(baseDir, data.path); + .map(data => { + if (_.isString(data)) { + return { + path: data, + subdir: isLandoFile ? '.' : 'plugins', + }; + } + // or just return return data; - } - }) - .value(); + }) + .map(data => { + if (path.isAbsolute(data.path)) return data; + else { + data.path = path.resolve(baseDir, data.path); + return data; + } + }) + .value(); /* * @TODO */ const normalizePlugins = (plugins = [], baseDir = __dirname) => _(plugins) - // @NOTE: right now this is very "dumb", if the plugin is a path that exist then we set to local - // otherwise we assume it needs to be grabbed, although we don't have a way to grab it yet - // @TODO: we need to figure out what the supported API for plugins should be, right now we ASSUME - // it is a key/value pair where value is ONLY a string but we should probably support passing in objects as well - .map((value, key) => { +// @NOTE: right now this is very "dumb", if the plugin is a path that exist then we set to local +// otherwise we assume it needs to be grabbed, although we don't have a way to grab it yet +// @TODO: we need to figure out what the supported API for plugins should be, right now we ASSUME +// it is a key/value pair where value is ONLY a string but we should probably support passing in objects as well + .map((value, key) => { // Try to figure out what the local path would be - const pluginPath = path.isAbsolute(value) ? value : path.join(baseDir, value); - // If SOMETHING exists at that path then assume its a local plugin - if (fs.existsSync(pluginPath)) return {name: key, type: 'local', path: pluginPath}; - // Otherwise assume its an external one - // @TODO: Should we also set a path here for where the plugin should be installed? - else return {name: key, type: 'remote', version: value}; - }) - .value(); + const pluginPath = path.isAbsolute(value) ? value : path.join(baseDir, value); + // If SOMETHING exists at that path then assume its a local plugin + if (fs.existsSync(pluginPath)) return {name: key, type: 'local', path: pluginPath}; + // Otherwise assume its an external one + // @TODO: Should we also set a path here for where the plugin should be installed? + else return {name: key, type: 'remote', version: value}; + }) + .value(); /** * Attempt to parse a JSON string to an objects @@ -192,30 +192,30 @@ exports.getOclifCacheDir = (product = 'hyperdrive') => env.getOclifCacheDir(prod * @return {Object} An object of config merged from file sources */ exports.loadFiles = files => _(files) - // Filter the source out if it doesn't exist - .filter(source => fs.existsSync(source) || fs.existsSync(source.file)) - // If the file is just a string lets map it to an object - .map(source => { - return _.isString(source) ? {file: source, data: yaml.load(fs.readFileSync(source)) || {}} : source; - }) - // Add on the root directory for mapping purposes - .map(source => _.merge({}, source, {root: path.dirname(source.file)})) - // Handle plugins/pluginDirs if they are relative paths - // @TODO: is this the right place to do this? probably not but lets vibe it until we redo it all in v4 - .map(source => { +// Filter the source out if it doesn't exist + .filter(source => fs.existsSync(source) || fs.existsSync(source.file)) +// If the file is just a string lets map it to an object + .map(source => { + return _.isString(source) ? {file: source, data: yaml.load(fs.readFileSync(source)) || {}} : source; + }) +// Add on the root directory for mapping purposes + .map(source => _.merge({}, source, {root: path.dirname(source.file)})) +// Handle plugins/pluginDirs if they are relative paths +// @TODO: is this the right place to do this? probably not but lets vibe it until we redo it all in v4 + .map(source => { // Normlize pluginDirs data - if (!_.isEmpty(source.data.pluginDirs)) { - source.data.pluginDirs = normalizePluginDirs(source.data.pluginDirs, source.root, source.landoFile); - } - // Ditto for plugins - if (!_.isEmpty(source.data.plugins)) { - source.data.plugins = normalizePlugins(source.data.plugins, source.root); - } - // Return the source back - return source; - }) - // Start collecting - .reduce((a, source) => exports.merge(a, source.data), {}); + if (!_.isEmpty(source.data.pluginDirs)) { + source.data.pluginDirs = normalizePluginDirs(source.data.pluginDirs, source.root, source.landoFile); + } + // Ditto for plugins + if (!_.isEmpty(source.data.plugins)) { + source.data.plugins = normalizePlugins(source.data.plugins, source.root); + } + // Return the source back + return source; + }) +// Start collecting + .reduce((a, source) => exports.merge(a, source.data), {}); /** * Filter process.env by a given prefix @@ -226,11 +226,11 @@ exports.loadFiles = files => _(files) * @return {Object} Object of things with camelCased keys */ exports.loadEnvs = prefix => _(process.env) - // Only muck with prefix_ variables - .pickBy((value, key) => _.includes(key, prefix)) - // Prep the keys for consumption - .mapKeys((value, key) => _.camelCase(_.trimStart(key, prefix))) - // If we have a JSON string as a value, parse that and assign its sub-keys - .mapValues(exports.tryConvertJson) - // Resolve the lodash wrapper - .value(); +// Only muck with prefix_ variables + .pickBy((value, key) => _.includes(key, prefix)) +// Prep the keys for consumption + .mapKeys((value, key) => _.camelCase(_.trimStart(key, prefix))) +// If we have a JSON string as a value, parse that and assign its sub-keys + .mapValues(exports.tryConvertJson) +// Resolve the lodash wrapper + .value(); diff --git a/lib/daemon.js b/lib/daemon.js index 9bed2b77f..708caef95 100644 --- a/lib/daemon.js +++ b/lib/daemon.js @@ -52,7 +52,7 @@ module.exports = class LandoDaemon { * @since 3.0.0 * @event post_engine_up */ - .then(() => this.events.emit('post-engine-up')); + .then(() => this.events.emit('post-engine-up')); }; down() { @@ -71,7 +71,7 @@ module.exports = class LandoDaemon { * @since 3.0.0 * @event post_engine_down */ - .then(() => this.events.emit('post-engine-down')); + .then(() => this.events.emit('post-engine-down')); } /* @@ -92,19 +92,19 @@ module.exports = class LandoDaemon { Shell.exec([`"${this.docker}"`, 'info', '--format', 'json'], {silent: true}), Shell.exec([`"${this.compose}"`, 'version', '--short'], {silent: true}), ]) - .then(data => { - let composePluginVersion = ''; - const dockerData = JSON.parse(data[0].stdout); - const plugins = dockerData.ClientInfo?.Plugins; - if (Array.isArray(plugins)) { - const composePlugin = plugins.find(plugin => plugin.Name === 'compose'); - composePluginVersion = composePlugin?.Version ?? ''; - } - return { - engine: dockerData.ServerVersion ?? '', - composePlugin: composePluginVersion, - compose: data[1].stdout.trim(), - }; - }); + .then(data => { + let composePluginVersion = ''; + const dockerData = JSON.parse(data[0].stdout); + const plugins = dockerData.ClientInfo?.Plugins; + if (Array.isArray(plugins)) { + const composePlugin = plugins.find(plugin => plugin.Name === 'compose'); + composePluginVersion = composePlugin?.Version ?? ''; + } + return { + engine: dockerData.ServerVersion ?? '', + composePlugin: composePluginVersion, + compose: data[1].stdout.trim(), + }; + }); }; }; diff --git a/lib/docker.js b/lib/docker.js index 5d4c9fc20..f760b6a36 100644 --- a/lib/docker.js +++ b/lib/docker.js @@ -35,9 +35,9 @@ module.exports = class Landerode extends Dockerode { createNet(name, opts = {}) { return this.createNetwork(_.merge({}, opts, {Name: name, Attachable: true, Internal: true})) // Wrap errors. - .catch(err => { - throw new Error(err, 'Error creating network.'); - }); + .catch(err => { + throw new Error(err, 'Error creating network.'); + }); }; /* @@ -53,18 +53,18 @@ module.exports = class Landerode extends Dockerode { isRunning(cid) { return this.scan(cid) // Get the running state - .then(data => _.get(data, 'State.Running', false)) + .then(data => _.get(data, 'State.Running', false)) // If the container no longer exists, return false since it isn't running. // This will prevent a race condition from happening. // Wrap errors. - .catch(err => { - // This was true for docker composer 1.26.x and below - if (_.includes(err.message, `No such container: ${cid}`)) return false; - // This is what it looks like for 1.27 and above - else if (_.includes(err.message, `no such container -`)) return false; - // Otherwise throw - else throw err; - }); + .catch(err => { + // This was true for docker composer 1.26.x and below + if (_.includes(err.message, `No such container: ${cid}`)) return false; + // This is what it looks like for 1.27 and above + else if (_.includes(err.message, `no such container -`)) return false; + // Otherwise throw + else throw err; + }); }; /* @@ -73,37 +73,37 @@ module.exports = class Landerode extends Dockerode { list(options = {}) { return this.listContainers(options) // Filter out nulls and undefineds. - .filter(_.identity) + .filter(_.identity) // Filter out containers with invalid status - .filter(data => data.Status !== 'Removal In Progress') + .filter(data => data.Status !== 'Removal In Progress') // Map docker containers to lando containers. - .map(container => utils.toLandoContainer(container)) + .map(container => utils.toLandoContainer(container)) // Filter out all non-lando containers - .filter(data => data.lando === true) + .filter(data => data.lando === true) // Filter out other instances - .filter(data => data.instance === this.id) + .filter(data => data.instance === this.id) // Remove orphaned app containers - .filter(container => { - if (!srcExists(container.src) && container.kind === 'app') { - return this.remove(container.id, {force: true}).then(() => false); - } else { - return true; - } - }) + .filter(container => { + if (!srcExists(container.src) && container.kind === 'app') { + return this.remove(container.id, {force: true}).then(() => false); + } else { + return true; + } + }) // Filter by app name if an app name was given. - .then(containers => { - if (options.project) return _.filter(containers, c => c.app === options.project); - else if (options.app) return _.filter(containers, c => c.app === utils.dockerComposify(options.app)); - return containers; - }) + .then(containers => { + if (options.project) return _.filter(containers, c => c.app === options.project); + else if (options.app) return _.filter(containers, c => c.app === utils.dockerComposify(options.app)); + return containers; + }) // And finally filter by everything else - .then(containers => { - if (!_.isEmpty(options.filter)) { - return _.filter(containers, _.fromPairs(_.map(options.filter, filter => filter.split('=')))); - } else { - return containers; - } - }); + .then(containers => { + if (!_.isEmpty(options.filter)) { + return _.filter(containers, _.fromPairs(_.map(options.filter, filter => filter.split('=')))); + } else { + return containers; + } + }); }; /* diff --git a/lib/engine.js b/lib/engine.js index 4f63fb32b..76e492376 100644 --- a/lib/engine.js +++ b/lib/engine.js @@ -15,11 +15,11 @@ module.exports = class Engine { this.daemon = daemon; this.compose = compose; this.engineCmd = (name, data, run = () => router[name](data, this.compose, this.docker)) => router.eventWrapper( - name, - daemon, - daemon.events, - data, - run, + name, + daemon, + daemon.events, + data, + run, ); // Determine install status this.composeInstalled = config.composeBin !== false; diff --git a/lib/env.js b/lib/env.js index 07007d75f..f9212f841 100644 --- a/lib/env.js +++ b/lib/env.js @@ -68,8 +68,8 @@ exports.getComposeExecutable = (fresh = false) => { binary = 'docker-compose.exe'; const programFiles = process.env.ProgramW6432 || process.env.ProgramFiles; possiblePaths.push( - programFiles + '\\Docker\\Docker\\resources\\bin', - process.env.SystemRoot + '\\System32', + programFiles + '\\Docker\\Docker\\resources\\bin', + process.env.SystemRoot + '\\System32', ); if (process.env.ChocolateyInstall) { @@ -109,8 +109,8 @@ exports.getDockerExecutable = (fresh = false) => { } possiblePaths.push( - programFiles + '\\Docker\\Docker\\resources\\bin', - process.env.SystemRoot + '\\System32', + programFiles + '\\Docker\\Docker\\resources\\bin', + process.env.SystemRoot + '\\System32', ); if (process.env.ChocolateyInstall) { @@ -132,11 +132,11 @@ const getOClifHome = () => { case 'linux': return process.env.HOME || os.homedir() || os.tmpdir(); case 'win32': - return process.env.HOME - || (process.env.HOMEDRIVE && process.env.HOMEPATH && path.join(process.env.HOMEDRIVE, process.env.HOMEPATH)) - || process.env.USERPROFILE - || os.homedir() - || os.tmpdir(); + return process.env.HOME || + (process.env.HOMEDRIVE && process.env.HOMEPATH && path.join(process.env.HOMEDRIVE, process.env.HOMEPATH)) || + process.env.USERPROFILE || + os.homedir() || + os.tmpdir(); } }; @@ -144,9 +144,9 @@ const getOClifHome = () => { * Get oclif base dir based on platform */ const getOClifBase= product => { - const base = process.env['XDG_CACHE_HOME'] - || (process.platform === 'win32' && process.env.LOCALAPPDATA) - || path.join(getOClifHome(), '.cache'); + const base = process.env['XDG_CACHE_HOME'] || + (process.platform === 'win32' && process.env.LOCALAPPDATA) || + path.join(getOClifHome(), '.cache'); return path.join(base, product); }; @@ -158,7 +158,7 @@ const macosCacheDir = product => { * This should only be needed for linux */ exports.getOclifCacheDir = product => { - return process.env[`${product.toUpperCase()}_CACHE_DIR`] - || macosCacheDir(product) - || getOClifBase(product); + return process.env[`${product.toUpperCase()}_CACHE_DIR`] || + macosCacheDir(product) || + getOClifBase(product); }; diff --git a/lib/events.js b/lib/events.js index f6aa32be0..9e9e16865 100644 --- a/lib/events.js +++ b/lib/events.js @@ -83,9 +83,9 @@ class AsyncEvents extends EventEmitter { */ emit(name, ...args) { const fns = this._listeners - .filter(listener => listener.name === name) - .sort((a, b) => a.priority - b.priority) - .map(evnt => evnt.fn); + .filter(listener => listener.name === name) + .sort((a, b) => a.priority - b.priority) + .map(evnt => evnt.fn); // Log non engine events so we can keep things quiet if (!name.includes('-engine-')) { @@ -100,10 +100,10 @@ class AsyncEvents extends EventEmitter { }) // Make sure to wait for all mappings. - .all() + .all() // Return true if event had listeners just like the original emit function. - .return(!!fns.length); + .return(!!fns.length); }; }; diff --git a/lib/formatters.js b/lib/formatters.js index 0ca6de628..93cdc0e99 100644 --- a/lib/formatters.js +++ b/lib/formatters.js @@ -48,10 +48,10 @@ exports.formatData = (data, {path = '', format = 'default', filter = []} = {}, o return table.toString(); } return _(data) - .map((value, index) => new Table(value, opts)) - .map(table => table.toString()) - .thru(data => data.join(os.EOL)) - .value(); + .map((value, index) => new Table(value, opts)) + .map(table => table.toString()) + .thru(data => data.join(os.EOL)) + .value(); break; default: return util.inspect(data, { @@ -72,19 +72,19 @@ exports.formatOptions = (omit = []) => _.omit(formatOpts, omit); * Helper to get interactive options */ exports.getInteractive = (options, argv) => _(options) - .map((option, name) => _.merge({}, {name}, {option})) - .filter(option => !_.isEmpty(_.get(option, 'option.interactive', {}))) - .map(option => _.merge({}, {name: option.name, weight: 0}, option.option.interactive)) - .map(option => { - if (_.isNil(argv[option.name]) || argv[option.name] === false) return option; - else { - return _.merge({}, option, {when: answers => { - answers[option.name] = argv[option.name]; - return false; - }}); - } - }) - .value(); + .map((option, name) => _.merge({}, {name}, {option})) + .filter(option => !_.isEmpty(_.get(option, 'option.interactive', {}))) + .map(option => _.merge({}, {name: option.name, weight: 0}, option.option.interactive)) + .map(option => { + if (_.isNil(argv[option.name]) || argv[option.name] === false) return option; + else { + return _.merge({}, option, {when: answers => { + answers[option.name] = argv[option.name]; + return false; + }}); + } + }) + .value(); /* * Helper to prompt the user if needed @@ -115,8 +115,8 @@ exports.handleInteractive = (inquiry, argv, command, lando) => lando.Promise.try * Helper to sort options */ exports.sortOptions = options => _(options) - .keys() - .sortBy() - .map(key => [key, options[key]]) - .fromPairs() - .value(); + .keys() + .sortBy() + .map(key => [key, options[key]]) + .fromPairs() + .value(); diff --git a/lib/lando.js b/lib/lando.js index 345a5f5ce..896ef794e 100644 --- a/lib/lando.js +++ b/lib/lando.js @@ -19,18 +19,18 @@ const DEFAULT_VERSIONS = {networking: 1}; // Helper to get init config const getInitConfig = dirs => _(dirs) - .filter(dir => fs.existsSync(dir)) - .flatMap(dir => glob.sync(path.join(dir, '*', 'init.js'))) - .map(file => require(file)) - .value(); + .filter(dir => fs.existsSync(dir)) + .flatMap(dir => glob.sync(path.join(dir, '*', 'init.js'))) + .map(file => require(file)) + .value(); // Helper to get init source config const getInitSourceConfig = dirs => _(dirs) - .filter(dir => fs.existsSync(dir)) - .flatMap(dir => glob.sync(path.join(dir, '*.js'))) - .map(file => require(file)) - .flatMap(source => source.sources) - .value(); + .filter(dir => fs.existsSync(dir)) + .flatMap(dir => glob.sync(path.join(dir, '*.js'))) + .map(file => require(file)) + .flatMap(source => source.sources) + .value(); /* * Helper to bootstrap plugins @@ -42,15 +42,15 @@ const bootstrapConfig = lando => { // Find the plugins return lando.plugins.find(lando.config.pluginDirs, lando.config) // Init the plugins - .map(plugin => lando.plugins.load(plugin, plugin.path, lando)) - .map(plugin => { - // Merge in config - if (_.has(plugin, 'data.config')) lando.config = _.merge(plugin.data.config, lando.config); - // Add plugins to config - // @NOTE: we remove plugin.data here because circular ref error and because presumably that - // data is now expessed directly in the lando object somewhere - lando.config.plugins.push(_.omit(plugin, 'data')); - }); + .map(plugin => lando.plugins.load(plugin, plugin.path, lando)) + .map(plugin => { + // Merge in config + if (_.has(plugin, 'data.config')) lando.config = _.merge(plugin.data.config, lando.config); + // Add plugins to config + // @NOTE: we remove plugin.data here because circular ref error and because presumably that + // data is now expessed directly in the lando object somewhere + lando.config.plugins.push(_.omit(plugin, 'data')); + }); }; /* @@ -68,24 +68,24 @@ const bootstrapTasks = lando => { // Load in all our tasks return lando.Promise.resolve(lando.config.plugins) - // Make sure the tasks dir exists - .filter(plugin => fs.existsSync(plugin.tasks)) - // Get a list off full js files that exist in that dir - .map(plugin => _(fs.readdirSync(plugin.tasks)) - .map(file => path.join(plugin.tasks, file)) - .filter(path => _.endsWith(path, '.js')) - .value(), - ) - // Loadem and loggem - .then(tasks => _.flatten(tasks)) - .each(task => { - lando.tasks.push(require(task)(lando)); - lando.log.debug('autoloaded task %s', path.basename(task, '.js')); - }) - // Reset the task cache - .then(() => { - lando.cache.set('_.tasks.cache', JSON.stringify(lando.tasks), {persist: true}); - }); + // Make sure the tasks dir exists + .filter(plugin => fs.existsSync(plugin.tasks)) + // Get a list off full js files that exist in that dir + .map(plugin => _(fs.readdirSync(plugin.tasks)) + .map(file => path.join(plugin.tasks, file)) + .filter(path => _.endsWith(path, '.js')) + .value(), + ) + // Loadem and loggem + .then(tasks => _.flatten(tasks)) + .each(task => { + lando.tasks.push(require(task)(lando)); + lando.log.debug('autoloaded task %s', path.basename(task, '.js')); + }) + // Reset the task cache + .then(() => { + lando.cache.set('_.tasks.cache', JSON.stringify(lando.tasks), {persist: true}); + }); }; /* @@ -96,12 +96,12 @@ const bootstrapEngine = lando => { lando.shell = new Shell(lando.log); lando.scanUrls = require('./scan')(lando.log); lando.engine = bootstrap.setupEngine( - lando.config, - lando.cache, - lando.events, - lando.log, - lando.shell, - lando.config.instance, + lando.config, + lando.cache, + lando.events, + lando.log, + lando.shell, + lando.config.instance, ); lando.utils = _.merge({}, require('./utils'), require('./config')); @@ -117,7 +117,7 @@ const bootstrapEngine = lando => { }); const versionsPromise = new Promise(resolve => { - let versions = lando.cache.get('docker-version'); + const versions = lando.cache.get('docker-version'); if (!versions) { lando.engine.daemon.getVersions().then(versions => { lando.config.versions = versions; @@ -143,11 +143,11 @@ const bootstrapApp = lando => { lando.yaml = new Yaml(lando.log); // Load in all our builders in the correct order const builders = _(['compose', 'types', 'services', 'recipes']) - .flatMap(type => _.map(lando.config.plugins, plugin => plugin[type])) - .filter(dir => fs.existsSync(dir)) - .flatMap(dir => glob.sync(path.join(dir, '*', 'builder.js'))) - .map(file => lando.factory.add(require(file)).name) - .value(); + .flatMap(type => _.map(lando.config.plugins, plugin => plugin[type])) + .filter(dir => fs.existsSync(dir)) + .flatMap(dir => glob.sync(path.join(dir, '*', 'builder.js'))) + .map(file => lando.factory.add(require(file)).name) + .value(); // Log _.forEach(builders, builder => lando.log.debug('autoloaded builder %s', builder)); }; @@ -314,7 +314,7 @@ module.exports = class Lando { return this.events.emit(`pre-bootstrap-${level}`, this) // Call the things that should happen at each level - .then(() => bootstrapRouter(level, this)) + .then(() => bootstrapRouter(level, this)) /** * Event that runs after we bootstrap config @@ -364,13 +364,13 @@ module.exports = class Lando { * // My codes * }); */ - .then(() => this.events.emit(`post-bootstrap-${level}`, this)) + .then(() => this.events.emit(`post-bootstrap-${level}`, this)) // Log the doneness - .then(() => this.log.verbose('%s bootstrap completed.', level)); + .then(() => this.log.verbose('%s bootstrap completed.', level)); }) - .then(() => this.log.verbose('bootstrap completed.')) - .then(() => this.events.emit(`post-bootstrap`, this)) - .then(() => this); + .then(() => this.log.verbose('bootstrap completed.')) + .then(() => this.events.emit(`post-bootstrap`, this)) + .then(() => this); }; /** diff --git a/lib/plugins.js b/lib/plugins.js index ff1c54dab..ba7632962 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -30,10 +30,10 @@ const buildPlugin = (file, namespace)=> ({ // Helper to discover things in the plugin that can be autoloaded const discoverPlugin = plugin => _(autoLoaders) - .map(thing => path.join(plugin.dir, thing)) - .filter(path => fs.existsSync(path)) - .keyBy(file => path.basename(_.last(file.split(path.sep)), '.js')) - .value(); + .map(thing => path.join(plugin.dir, thing)) + .filter(path => fs.existsSync(path)) + .keyBy(file => path.basename(_.last(file.split(path.sep)), '.js')) + .value(); /* * @TODO @@ -57,45 +57,45 @@ module.exports = class Plugins { */ find(dirs, {disablePlugins = [], plugins = []} = {}) { return _(dirs) - // Map string usage to object and set path - .map(data => { + // Map string usage to object and set path + .map(data => { // Map string to object - if (_.isString(data)) data = {path: path.join(data)}; - // Assemble the dir to scan - data.dir = path.join(data.path, _.get(data, 'subdir', 'plugins')); - return data; - }) - // Start by scanning for plugins - .filter(data => fs.existsSync(data.dir)) - .flatMap(data => _.merge({}, data, {plugins: glob.sync(path.join(data.dir, '*', 'index.js'))})) - .flatMap(data => _.map(data.plugins, plugin => buildPlugin(plugin, data.namespace))) - // This is a dumb filter to check that external "@lando" plugins have a plugin.yml - // We do this to prevent things like @lando/vuepress-theme-default-plus from being from being loaded as plugins - // @NOTE: in Lando 4 we we will explicitly look for a manifest file, that may be plugin.yml or something else. - .filter(data => { - if (_.includes(data.dir, path.join('node_modules', '@lando'))) { - return fs.existsSync(path.join(data.dir, 'plugin.yml')); - } else if (_.includes(data.dir, path.join('plugins', 'lando-'))) { - return fs.existsSync(path.join(data.dir, 'plugin.yml')); - } else return true; - }) - // Then mix in any local ones that are passed in - .thru(candidates => candidates.concat(_(plugins) + if (_.isString(data)) data = {path: path.join(data)}; + // Assemble the dir to scan + data.dir = path.join(data.path, _.get(data, 'subdir', 'plugins')); + return data; + }) + // Start by scanning for plugins + .filter(data => fs.existsSync(data.dir)) + .flatMap(data => _.merge({}, data, {plugins: glob.sync(path.join(data.dir, '*', 'index.js'))})) + .flatMap(data => _.map(data.plugins, plugin => buildPlugin(plugin, data.namespace))) + // This is a dumb filter to check that external "@lando" plugins have a plugin.yml + // We do this to prevent things like @lando/vuepress-theme-default-plus from being from being loaded as plugins + // @NOTE: in Lando 4 we we will explicitly look for a manifest file, that may be plugin.yml or something else. + .filter(data => { + if (_.includes(data.dir, path.join('node_modules', '@lando'))) { + return fs.existsSync(path.join(data.dir, 'plugin.yml')); + } else if (_.includes(data.dir, path.join('plugins', 'lando-'))) { + return fs.existsSync(path.join(data.dir, 'plugin.yml')); + } else return true; + }) + // Then mix in any local ones that are passed in + .thru(candidates => candidates.concat(_(plugins) // Start by filtering out non-local ones - .filter(plugin => plugin.type === 'local') + .filter(plugin => plugin.type === 'local') // Manually map into plugin object - .map(plugin => ({name: plugin.name, path: path.join(plugin.path, 'index.js'), dir: plugin.path})) + .map(plugin => ({name: plugin.name, path: path.join(plugin.path, 'index.js'), dir: plugin.path})) // Filter again to make sure we have an index.js - .filter(plugin => fs.existsSync(plugin.path)) - .value(), - )) - // Then remove any that are flagged as disabled - .filter(plugin => !_.includes(disablePlugins, plugin.name)) - // Then load the correct one based on the ordering - .groupBy('name') - .map(plugins => _.last(plugins)) - .map(plugin => _.merge({}, plugin, discoverPlugin(plugin))) - .value(); + .filter(plugin => fs.existsSync(plugin.path)) + .value(), + )) + // Then remove any that are flagged as disabled + .filter(plugin => !_.includes(disablePlugins, plugin.name)) + // Then load the correct one based on the ordering + .groupBy('name') + .map(plugins => _.last(plugins)) + .map(plugin => _.merge({}, plugin, discoverPlugin(plugin))) + .value(); }; /** diff --git a/lib/router.js b/lib/router.js index 570371db3..52d57d84e 100644 --- a/lib/router.js +++ b/lib/router.js @@ -22,9 +22,9 @@ const retryEach = (data, run) => Promise.each(utils.normalizer(data), datum => r // Helper to run engine event commands exports.eventWrapper = (name, daemon, events, data, run) => daemon.up() - .then(() => events.emit(`pre-engine-${name}`, data)) - .then(() => run(data)) - .tap(() => events.emit(`post-engine-${name}`, data)); + .then(() => events.emit(`pre-engine-${name}`, data)) + .then(() => run(data)) + .tap(() => events.emit(`post-engine-${name}`, data)); /* * Helper to route to build command @@ -33,7 +33,7 @@ exports.build = (data, compose) => { // Pull return retryEach(data, datum => compose('pull', datum)) // then build - .then(() => retryEach(data, datum => compose('build', datum))); + .then(() => retryEach(data, datum => compose('build', datum))); }; /* @@ -50,11 +50,11 @@ exports.exists = (data, compose, docker, ids = []) => { if (data.compose) return compose('getId', data).then(id => !_.isEmpty(id)); else { return docker.list() - .each(container => { - ids.push(container.id); - ids.push(container.name); - }) - .then(() => _.includes(ids, utils.getId(data))); + .each(container => { + ids.push(container.id); + ids.push(container.name); + }) + .then(() => _.includes(ids, utils.getId(data))); } }; @@ -82,18 +82,20 @@ exports.run = (data, compose, docker, started = true) => Promise.mapSeries(utils } }) // Why were we still using dockerode for this on non-win? - .then(() => compose('run', _.merge({}, datum, {opts: {cmd: datum.cmd, id: datum.id}}))) + .then(() => compose('run', _.merge({}, datum, {opts: {cmd: datum.cmd, id: datum.id}}))) // Stop if we have to - .tap(() => { - // If this is the last step of a build we need to make sure all the containers are stopped - if (_.get(datum, 'opts.prestart', false) && _.get(datum, 'opts.last', false)) delete datum.opts.services; - // Stop if we have to and remove build flags so lando doesn't get tripped up downstream - if (!started || _.get(datum, 'opts.last', false)) return exports.stop(stripRun(datum), compose, docker); - }) + .tap(() => { + // If this is the last step of a build we need to make sure all the containers are stopped + if (_.get(datum, 'opts.prestart', false) && _.get(datum, 'opts.last', false)) delete datum.opts.services; + // Stop if we have to and remove build flags so lando doesn't get tripped up downstream + if (!started || _.get(datum, 'opts.last', false)) return exports.stop(stripRun(datum), compose, docker); + }) // Destroy if we have to - .tap(() => { - if (!started && _.get(datum, 'opts.autoRemove', false)) return exports.destroy(stripRun(datum), compose, docker); - }); + .tap(() => { + if (!started && _.get(datum, 'opts.autoRemove', false)) { + return exports.destroy(stripRun(datum), compose, docker); + } + }); }); /* diff --git a/lib/scan.js b/lib/scan.js index 7fcb11116..c53fbb9d8 100644 --- a/lib/scan.js +++ b/lib/scan.js @@ -66,54 +66,54 @@ module.exports = (log = new Log()) => { // Send REST request. return requestClient().get(url) // Return good responses - .then(response => { - log.debug('scan response %s received', url, { - status: response && response.status, - headers: response && response.headers, - }); - return setGood(url); - }) + .then(response => { + log.debug('scan response %s received', url, { + status: response && response.status, + headers: response && response.headers, + }); + return setGood(url); + }) // Retry waitcodes or fail right away if we have a network issue - .catch(error => { - const extraInformation = { - code: error.code, - message: error.message, - }; - if (error.response) { - extraInformation.status = error.response.status; - extraInformation.headers = error.response.headers; - } - log.debug('scan failed for %s', url, extraInformation); + .catch(error => { + const extraInformation = { + code: error.code, + message: error.message, + }; + if (error.response) { + extraInformation.status = error.response.status; + extraInformation.headers = error.response.headers; + } + log.debug('scan failed for %s', url, extraInformation); - if (error.code === 'ENOTFOUND') { - log.debug('ENOTFOUND for %s, setting to bad', url); - return Promise.resolve(setBad(url)); - } + if (error.code === 'ENOTFOUND') { + log.debug('ENOTFOUND for %s, setting to bad', url); + return Promise.resolve(setBad(url)); + } - if (!error.response) { - log.debug('No response for %s. Setting to bad', url); - return Promise.reject(setBad(url)); - } + if (!error.response) { + log.debug('No response for %s. Setting to bad', url); + return Promise.reject(setBad(url)); + } - if (_.includes(waitCodes, error.response.status)) { - log.debug('Response for %s, returned http code we should retry for. Setting to bad', url); - return Promise.reject(setBad(url)); - } + if (_.includes(waitCodes, error.response.status)) { + log.debug('Response for %s, returned http code we should retry for. Setting to bad', url); + return Promise.reject(setBad(url)); + } - log.debug('Unkown failure for %s. Setting to good', url); - return setGood(url); - }); + log.debug('Unkown failure for %s. Setting to good', url); + return setGood(url); + }); }, {max}) // Catch any error and return an inaccessible url - .catch(err => setBad(url))) + .catch(err => setBad(url))) // Log and then return scan results - .then(results => { - log.verbose('scan completed.'); - log.debug('scan results.', results); - return results; - }); + .then(results => { + log.verbose('scan completed.'); + log.debug('scan results.', results); + return results; + }); }; // Return diff --git a/lib/shell.js b/lib/shell.js index 9be313e9d..7dea60149 100644 --- a/lib/shell.js +++ b/lib/shell.js @@ -24,7 +24,7 @@ const parseCmd = meta => _.merge({}, meta, { * Helper to parse and add a command to our running process log */ const addCommand = ({cmd, id, mode = 'exec', process = {}} = {}) => _.merge({}, - parseCmd(parse(cmd)), {id, mode, process}, + parseCmd(parse(cmd)), {id, mode, process}, ); /* @@ -81,9 +81,9 @@ module.exports = class Shell { * @alias lando.shell.get * @return {Array} An array of the currently running processes */ - get() { + get() { return this.running; - }; + }; /** * Runs a command. @@ -146,19 +146,19 @@ module.exports = class Shell { }) // Assess the results - .then(({code, stdout, stderr}) => { - // if this is an error and stderr is empty use the last few lines of STDOUT - if (code !== 0 && _.isEmpty(stderr)) { - stderr = stdout.trim(); - } - // Log - this.log.debug('process %s finished with exit code %s', id, code); - this.log.silly('process %s had output', id, {stdout, stderr}); - // Return - _.remove(this.running, proc => proc.id === id); - const msg = `${cmd} failed with exit code ${code}\n:${stderr}`; - return (code !== 0) ? Promise.reject(new Error(msg)) : Promise.resolve(stdout); - }); + .then(({code, stdout, stderr}) => { + // if this is an error and stderr is empty use the last few lines of STDOUT + if (code !== 0 && _.isEmpty(stderr)) { + stderr = stdout.trim(); + } + // Log + this.log.debug('process %s finished with exit code %s', id, code); + this.log.silly('process %s had output', id, {stdout, stderr}); + // Return + _.remove(this.running, proc => proc.id === id); + const msg = `${cmd} failed with exit code ${code}\n:${stderr}`; + return (code !== 0) ? Promise.reject(new Error(msg)) : Promise.resolve(stdout); + }); }; /** diff --git a/lib/utils.js b/lib/utils.js index 8489b1a35..e798fcfca 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -14,16 +14,16 @@ const yaml = new Yaml(); * Helper to get app mounts */ exports.getAppMounts = app => _(app.services) - // Objectify - .map(service => _.merge({name: service}, _.get(app, `config.services.${service}`, {}))) - // Set the default - .map(config => _.merge({}, config, {app_mount: _.get(config, 'app_mount', 'cached')})) - // Filter out disabled mountes - .filter(config => config.app_mount !== false && config.app_mount !== 'disabled') - // Combine together - .map(config => ([config.name, {volumes: [`${app.root}:/app:${config.app_mount}`]}])) - .fromPairs() - .value(); +// Objectify + .map(service => _.merge({name: service}, _.get(app, `config.services.${service}`, {}))) +// Set the default + .map(config => _.merge({}, config, {app_mount: _.get(config, 'app_mount', 'cached')})) +// Filter out disabled mountes + .filter(config => config.app_mount !== false && config.app_mount !== 'disabled') +// Combine together + .map(config => ([config.name, {volumes: [`${app.root}:/app:${config.app_mount}`]}])) + .fromPairs() + .value(); /* * Translate a name for use by docker-compose eg strip `-` and `.` and @@ -45,16 +45,16 @@ exports.appMachineName = data => require('transliteration').slugify(data); * Helper to dump all our compose data to files */ exports.dumpComposeData = (data, dir) => _(_.flatten([data])) - .flatMap(group => _.map(group.data, (compose, index) => ({data: compose, file: `${group.id}-${index}.yml`}))) - .map(compose => yaml.dump(path.join(dir, compose.file), compose.data)) - .value(); + .flatMap(group => _.map(group.data, (compose, index) => ({data: compose, file: `${group.id}-${index}.yml`}))) + .map(compose => yaml.dump(path.join(dir, compose.file), compose.data)) + .value(); /* * Helper to load raw docker compose files */ exports.loadComposeFiles = (files, dir) => _(exports.validateFiles(files, dir)) - .map(file => yaml.load(file)) - .value(); + .map(file => yaml.load(file)) + .value(); /* * Helper to get default cli envvars @@ -72,9 +72,9 @@ exports.getId = c => c.cid || c.id || c.containerName || c.containerID || c.name * Returns a default info object */ exports.getInfoDefaults = app => _(app.services) - .map(service => ({service, urls: [], type: 'docker-compose', healthy: true})) - .map(service => _.merge({}, service, _.find(app.info, {service: service.service}))) - .value(); + .map(service => ({service, urls: [], type: 'docker-compose', healthy: true})) + .map(service => _.merge({}, service, _.find(app.info, {service: service.service}))) + .value(); /* * Helper to get globals @@ -91,10 +91,10 @@ exports.getGlobals = app => exports.toObject(app.services, { * Helper to find all our services */ exports.getServices = composeData => _(composeData) - .flatMap(data => data.data) - .flatMap(data => _.keys(data.services)) - .uniq() - .value(); + .flatMap(data => data.data) + .flatMap(data => _.keys(data.services)) + .uniq() + .value(); /* * Helper to get user @@ -132,8 +132,8 @@ exports.moveConfig = (src, dest = os.tmpdir()) => { // @todo: why doesn't the below work for PLD? copydir.sync(src, dest, filter); exports.makeExecutable(_(fs.readdirSync(dest)) - .filter(file => path.extname(file) === '.sh') - .value() + .filter(file => path.extname(file) === '.sh') + .value() , dest); } catch (error) { const code = _.get(error, 'code'); @@ -149,8 +149,8 @@ exports.moveConfig = (src, dest = os.tmpdir()) => { fs.unlinkSync(f); copydir.sync(src, dest, filter); exports.makeExecutable(_(fs.readdirSync(dest)) - .filter(file => path.extname(file) === '.sh') - .value() + .filter(file => path.extname(file) === '.sh') + .value() , dest); }; @@ -206,15 +206,15 @@ exports.toLandoContainer = ({Names, Labels, Id, Status}) => { * Helper to build an obkect from an array of keys and data */ exports.toObject = (keys, data = {}) => _(keys) - .map(service => data) - .map((service, index) => _.set({}, keys[index], service)) - .thru(services => _.reduce(services, (sum, service) => _.merge(sum, service), {})) - .value(); + .map(service => data) + .map((service, index) => _.set({}, keys[index], service)) + .thru(services => _.reduce(services, (sum, service) => _.merge(sum, service), {})) + .value(); /* * Validates compose files returns legit ones */ exports.validateFiles = (files = [], base = process.cwd()) => _(files) - .map(file => (path.isAbsolute(file) ? file : path.join(base, file))) - .filter(file => fs.existsSync(file)) - .value(); + .map(file => (path.isAbsolute(file) ? file : path.join(base, file))) + .filter(file => fs.existsSync(file)) + .value(); diff --git a/package.json b/package.json index 5e522b3b1..857d42769 100644 --- a/package.json +++ b/package.json @@ -130,14 +130,14 @@ "yargs": "^16.1.0" }, "devDependencies": { - "@babel/core": "^7.17.5", - "@babel/eslint-parser": "^7.16.0", + "@babel/core": "^7.24.6", + "@babel/eslint-parser": "^7.24.6", "chai": "^3.5.0", - "chai-as-promised": "^7.1.1", + "chai-as-promised": "^7.1.2", "chai-events": "^0.0.1", "command-line-test": "^1.0.10", "eslint": "^8.57.0", - "eslint-config-google": "^0.9.1", + "eslint-config-google": "^0.14.0", "leia-parser": "^0.4.0", "mocha": "^10.0.0", "mock-fs": "^5.2.0", diff --git a/plugins/lando-core/app.js b/plugins/lando-core/app.js index bc7bf4922..545b7e0ee 100644 --- a/plugins/lando-core/app.js +++ b/plugins/lando-core/app.js @@ -55,14 +55,14 @@ module.exports = (app, lando) => { app.log.verbose('attempting to find open services...'); return app.engine.list({project: app.project}) // Return running containers - .filter(container => app.engine.isRunning(container.id)) + .filter(container => app.engine.isRunning(container.id)) // Make sure they are still a defined service (eg if the user changes their lando yml) - .filter(container => _.includes(app.services, container.service)) + .filter(container => _.includes(app.services, container.service)) // Inspect each and add new URLS - .map(container => app.engine.scan(container)) + .map(container => app.engine.scan(container)) // Scan all the http ports - .map(data => utils.getUrls(data, getHttpPorts(data), getHttpsPorts(data), lando.config.bindAddress)) - .map(data => _.find(app.info, {service: data.service}).urls = data.urls); + .map(data => utils.getUrls(data, getHttpPorts(data), getHttpsPorts(data), lando.config.bindAddress)) + .map(data => _.find(app.info, {service: data.service}).urls = data.urls); }); }); @@ -83,9 +83,9 @@ module.exports = (app, lando) => { services: [service], }, }) - .catch(err => { - app.addWarning(warnings.serviceNotRunningWarning(service), err); - }); + .catch(err => { + app.addWarning(warnings.serviceNotRunningWarning(service), err); + }); })); }); @@ -106,9 +106,9 @@ module.exports = (app, lando) => { services: [service], }, }) - .catch(err => { - app.addWarning(warnings.serviceNotRunningWarning(service), err); - }); + .catch(err => { + app.addWarning(warnings.serviceNotRunningWarning(service), err); + }); })); } }); @@ -121,9 +121,9 @@ module.exports = (app, lando) => { // Get keys on host const sshDir = path.resolve(lando.config.home, '.ssh'); const keys = _(fs.readdirSync(sshDir)) - .filter(file => !_.includes(['config', 'known_hosts'], file)) - .filter(file => path.extname(file) !== '.pub') - .value(); + .filter(file => !_.includes(['config', 'known_hosts'], file)) + .filter(file => path.extname(file) !== '.pub') + .value(); // Determine the key size const keySize = _.size(_.get(app, 'config.keys', keys)); @@ -166,38 +166,38 @@ module.exports = (app, lando) => { }); }); - // Add some logic that extends start until healthchecked containers report as healthy + // Add some logic that extends start until healthchecked containers report as healthy app.events.on('post-start', 1, () => lando.engine.list({project: app.project}) - // Filter out containers without a healthcheck - .filter(container => _.has(_.find(app.info, {service: container.service}), 'healthcheck')) - // Map to info - .map(container => _.find(app.info, {service: container.service})) - // Map to a retry of the healthcheck command - .map(info => lando.Promise.retry(() => { - return app.engine.run({ - id: app.getServiceContainerId(info.service), - cmd: info.healthcheck, - compose: app.compose, - project: app.project, - opts: { - user: 'root', - cstdio: 'pipe', - silent: true, - noTTY: true, - services: [info.service], - }, - }) - .catch(err => { - console.log('Waiting until %s service is ready...', info.service); - app.log.debug('running healthcheck %s for %s...', info.healthcheck, info.service); - // app.log.silly(err); - return Promise.reject(info.service); - }); - }, {max: 25, backoff: 1000}) - .catch(service => { - info.healthy = false; - app.addWarning(warnings.serviceUnhealthyWarning(service), Error(`${service} reported as unhealthy.`)); - }))); + // Filter out containers without a healthcheck + .filter(container => _.has(_.find(app.info, {service: container.service}), 'healthcheck')) + // Map to info + .map(container => _.find(app.info, {service: container.service})) + // Map to a retry of the healthcheck command + .map(info => lando.Promise.retry(() => { + return app.engine.run({ + id: app.getServiceContainerId(info.service), + cmd: info.healthcheck, + compose: app.compose, + project: app.project, + opts: { + user: 'root', + cstdio: 'pipe', + silent: true, + noTTY: true, + services: [info.service], + }, + }) + .catch(err => { + console.log('Waiting until %s service is ready...', info.service); + app.log.debug('running healthcheck %s for %s...', info.healthcheck, info.service); + // app.log.silly(err); + return Promise.reject(info.service); + }); + }, {max: 25, backoff: 1000}) + .catch(service => { + info.healthy = false; + app.addWarning(warnings.serviceUnhealthyWarning(service), Error(`${service} reported as unhealthy.`)); + }))); // If the app already is installed but we can't determine the builtAgainst, then set it to something bogus app.events.on('pre-start', () => { diff --git a/plugins/lando-core/compose/lando/builder.js b/plugins/lando-core/compose/lando/builder.js index 8d84f9a3b..67903b2cf 100644 --- a/plugins/lando-core/compose/lando/builder.js +++ b/plugins/lando-core/compose/lando/builder.js @@ -17,41 +17,41 @@ module.exports = { parent: '_compose', builder: parent => class LandoCompose extends parent { constructor( - id, - { - name, - type, - userConfRoot, - version, - app = '', - confDest = '', - confSrc = '', - config = {}, - data = `data_${name}`, - dataHome = `home_${name}`, - entrypoint = '/lando-entrypoint.sh', - home = '', - moreHttpPorts = [], - info = {}, - legacy = [], - meUser = 'www-data', - patchesSupported = false, - pinPairs = {}, - ports = [], - project = '', - overrides = {}, - refreshCerts = false, - remoteFiles = {}, - scripts = [], - sport = '443', - ssl = false, - sslExpose = true, - supported = ['custom'], - supportedIgnore = false, - root = '', - webroot = '/app', - } = {}, - ...sources + id, + { + name, + type, + userConfRoot, + version, + app = '', + confDest = '', + confSrc = '', + config = {}, + data = `data_${name}`, + dataHome = `home_${name}`, + entrypoint = '/lando-entrypoint.sh', + home = '', + moreHttpPorts = [], + info = {}, + legacy = [], + meUser = 'www-data', + patchesSupported = false, + pinPairs = {}, + ports = [], + project = '', + overrides = {}, + refreshCerts = false, + remoteFiles = {}, + scripts = [], + sport = '443', + ssl = false, + sslExpose = true, + supported = ['custom'], + supportedIgnore = false, + root = '', + webroot = '/app', + } = {}, + ...sources ) { // Add custom to list of supported supported.push('custom'); diff --git a/plugins/lando-core/lib/utils.js b/plugins/lando-core/lib/utils.js index ee771efb9..8e6d29512 100644 --- a/plugins/lando-core/lib/utils.js +++ b/plugins/lando-core/lib/utils.js @@ -16,20 +16,20 @@ exports.getHostPath = mount => _.dropRight(mount.split(':')).join(':'); */ exports.getUrls = (data, scan = ['80, 443'], secured = ['443'], bindAddress = '127.0.0.1') => { return _(_.merge(_.get(data, 'Config.ExposedPorts', []), {'443/tcp': {}})) - .map((value, port) => ({ - port: _.head(port.split('/')), - protocol: (_.includes(secured, port.split('/')[0])) ? 'https' : 'http'} - )) - .filter(exposed => _.includes(scan, exposed.port)) - .flatMap(ports => _.map(_.get(data, `NetworkSettings.Ports.${ports.port}/tcp`, []), i => _.merge({}, ports, i))) - .filter(ports => _.includes([bindAddress, '0.0.0.0'], ports.HostIp)) - .map(ports => url.format({ - protocol: ports.protocol, - hostname: 'localhost', - port: _.includes(scan, ports.port) ? ports.HostPort : '', - })) - .thru(urls => ({service: data.Config.Labels['com.docker.compose.service'], urls})) - .value(); + .map((value, port) => ({ + port: _.head(port.split('/')), + protocol: (_.includes(secured, port.split('/')[0])) ? 'https' : 'http'} + )) + .filter(exposed => _.includes(scan, exposed.port)) + .flatMap(ports => _.map(_.get(data, `NetworkSettings.Ports.${ports.port}/tcp`, []), i => _.merge({}, ports, i))) + .filter(ports => _.includes([bindAddress, '0.0.0.0'], ports.HostIp)) + .map(ports => url.format({ + protocol: ports.protocol, + hostname: 'localhost', + port: _.includes(scan, ports.port) ? ports.HostPort : '', + })) + .thru(urls => ({service: data.Config.Labels['com.docker.compose.service'], urls})) + .value(); }; /* @@ -85,9 +85,9 @@ exports.startTable = app => { name: app.name, location: app.root, services: _(app.info) - .map(info => (info.healthy) ? chalk.green(info.service) : chalk.yellow(info.service)) - .values() - .join(', '), + .map(info => (info.healthy) ? chalk.green(info.service) : chalk.yellow(info.service)) + .values() + .join(', '), }; const urls = {}; @@ -119,5 +119,5 @@ exports.stripPatch = version => _.slice(version.split('.'), 0, 2).join('.'); * Helper to help us allow wildcard patch versions eg when there is no minor version tag available */ exports.stripWild = versions => _(versions) - .map(version => (version.split('.')[2] === 'x') ? _.slice(version.split('.'), 0, 2).join('.') : version) - .value(); + .map(version => (version.split('.')[2] === 'x') ? _.slice(version.split('.'), 0, 2).join('.') : version) + .value(); diff --git a/plugins/lando-core/tasks/info.js b/plugins/lando-core/tasks/info.js index d4ad62006..150131436 100644 --- a/plugins/lando-core/tasks/info.js +++ b/plugins/lando-core/tasks/info.js @@ -31,14 +31,14 @@ module.exports = lando => ({ // Go deep if we need to if (app && options.deep) { return app.init().then(() => lando.engine.list({project: app.project}) - .filter(container => filterServices(container.service, options.service)) - .each(container => lando.engine.scan(container) - .then(data => console.log(lando.cli.formatData(data, options)))), - ); + .filter(container => filterServices(container.service, options.service)) + .each(container => lando.engine.scan(container) + .then(data => console.log(lando.cli.formatData(data, options)))), + ); } else if (app && !options.deep) { return app.init().then(() => console.log(lando.cli.formatData( - _.filter(app.info, service => filterServices(service.service, options.service)), - options, + _.filter(app.info, service => filterServices(service.service, options.service)), + options, ))); } }, diff --git a/plugins/lando-core/tasks/list.js b/plugins/lando-core/tasks/list.js index 52fb9c692..74d07f9ed 100644 --- a/plugins/lando-core/tasks/list.js +++ b/plugins/lando-core/tasks/list.js @@ -23,12 +23,12 @@ module.exports = lando => { // List all the apps return lando.engine.list(options) // Map each app to a summary and print results - .then(containers => console.log(lando.cli.formatData( - _(containers) - .map(container => _.omit(container, ['lando', 'id', 'instance'])) - .value(), - options, - ))); + .then(containers => console.log(lando.cli.formatData( + _(containers) + .map(container => _.omit(container, ['lando', 'id', 'instance'])) + .value(), + options, + ))); }, }; }; diff --git a/plugins/lando-core/tasks/poweroff.js b/plugins/lando-core/tasks/poweroff.js index 60b47b276..aea2b5ff6 100644 --- a/plugins/lando-core/tasks/poweroff.js +++ b/plugins/lando-core/tasks/poweroff.js @@ -10,13 +10,13 @@ module.exports = lando => { // Get all our containers return lando.engine.list() // SHUT IT ALL DOWN - .each(container => console.log('Bye bye %s ... ', container.name)) - .delay(200) - .map(container => lando.engine.stop({id: container.id})) + .each(container => console.log('Bye bye %s ... ', container.name)) + .delay(200) + .map(container => lando.engine.stop({id: container.id})) // Emit poweroff - .then(() => lando.events.emit('poweroff')) + .then(() => lando.events.emit('poweroff')) // Finish up - .then(() => console.log(lando.cli.makeArt('poweroff', {phase: 'post'}))); + .then(() => console.log(lando.cli.makeArt('poweroff', {phase: 'post'}))); }, }; }; diff --git a/plugins/lando-core/tasks/restart.js b/plugins/lando-core/tasks/restart.js index 593726db2..04ea29bf5 100644 --- a/plugins/lando-core/tasks/restart.js +++ b/plugins/lando-core/tasks/restart.js @@ -21,11 +21,11 @@ module.exports = lando => { console.log(''); }) // Provide help if there is an error - .catch(err => { - app.log.error(err.message, err); - console.log(lando.cli.makeArt('appStart', {phase: 'error'})); - return lando.Promise.reject(err); - }); + .catch(err => { + app.log.error(err.message, err); + console.log(lando.cli.makeArt('appStart', {phase: 'error'})); + return lando.Promise.reject(err); + }); } }, }; diff --git a/plugins/lando-core/tasks/start.js b/plugins/lando-core/tasks/start.js index aa42e0148..cf6f609a1 100644 --- a/plugins/lando-core/tasks/start.js +++ b/plugins/lando-core/tasks/start.js @@ -21,11 +21,11 @@ module.exports = lando => { console.log(''); }) // Provide help if there is an error - .catch(err => { - app.log.error(err.message, err); - console.log(lando.cli.makeArt('appStart', {phase: 'error'})); - return lando.Promise.reject(err); - }); + .catch(err => { + app.log.error(err.message, err); + console.log(lando.cli.makeArt('appStart', {phase: 'error'})); + return lando.Promise.reject(err); + }); } }, }; diff --git a/plugins/lando-networking/app.js b/plugins/lando-networking/app.js index 934678209..5872d8fc9 100644 --- a/plugins/lando-networking/app.js +++ b/plugins/lando-networking/app.js @@ -15,21 +15,21 @@ module.exports = (app, lando) => { // List all our app containers return lando.engine.list({project: app.project}) // Go through each container - .map(container => { - // Define the internal aliae - const internalAlias = `${container.service}.${container.app}.internal`; - // Sometimes you need to disconnect before you reconnect - return landonet.disconnect({Container: container.id, Force: true}) - // Only throw non not connected errors - .catch(error => { - if (!_.includes(error.message, 'is not connected to network lando')) throw error; - }) - // Connect - .then(() => { - landonet.connect({Container: container.id, EndpointConfig: {Aliases: [internalAlias]}}); - app.log.debug('connected %s to the landonet', container.name); - }); - }); + .map(container => { + // Define the internal aliae + const internalAlias = `${container.service}.${container.app}.internal`; + // Sometimes you need to disconnect before you reconnect + return landonet.disconnect({Container: container.id, Force: true}) + // Only throw non not connected errors + .catch(error => { + if (!_.includes(error.message, 'is not connected to network lando')) throw error; + }) + // Connect + .then(() => { + landonet.connect({Container: container.id, EndpointConfig: {Aliases: [internalAlias]}}); + app.log.debug('connected %s to the landonet', container.name); + }); + }); }); // Add proxy routes to the proxy as aliases. This should resolve a long @@ -54,26 +54,26 @@ module.exports = (app, lando) => { // @NOTE: Do we need to handle wildcards and paths? const aliasPath = `NetworkSettings.Networks.${lando.config.networkBridge}.Aliases`; const aliases = _(_.get(app, 'config.proxy', [])) - .map(route => route) - .flatten() - .map(entry => _.isString(entry) ? entry : entry.hostname) - .map(entry => _.first(entry.split(':'))) - .compact() - .thru(routes => routes.concat(_.get(data, aliasPath, []))) - .uniq() - .value(); + .map(route => route) + .flatten() + .map(entry => _.isString(entry) ? entry : entry.hostname) + .map(entry => _.first(entry.split(':'))) + .compact() + .thru(routes => routes.concat(_.get(data, aliasPath, []))) + .uniq() + .value(); // Disconnect so we can reconnect return bridgeNet.disconnect({Container: proxyContainer, Force: true}) - // Only throw non not connected errors - .catch(error => { - if (!_.includes(error.message, 'is not connected to network lando')) throw error; - }) - // Connect - .then(() => { - bridgeNet.connect({Container: proxyContainer, EndpointConfig: {Aliases: aliases}}); - app.log.debug('aliased %j to the proxynet', aliases); - }); + // Only throw non not connected errors + .catch(error => { + if (!_.includes(error.message, 'is not connected to network lando')) throw error; + }) + // Connect + .then(() => { + bridgeNet.connect({Container: proxyContainer, EndpointConfig: {Aliases: aliases}}); + app.log.debug('aliased %j to the proxynet', aliases); + }); }); }); }); diff --git a/plugins/lando-networking/index.js b/plugins/lando-networking/index.js index 1ac940d22..9d3313b59 100644 --- a/plugins/lando-networking/index.js +++ b/plugins/lando-networking/index.js @@ -7,40 +7,40 @@ const _ = require('lodash'); * Helper to clean out any old networks when we hit the limit */ const cleanNetworks = lando => lando.engine.getNetworks() - .then(networks => { - if (_.size(networks) >= 32) { + .then(networks => { + if (_.size(networks) >= 32) { // Warn user about this action - lando.log.warn('Lando has detected you are at Docker\'s network limit!'); - lando.log.warn('Give us a moment as we try to make space by cleaning up old networks...'); - // Go through lando containers and add in networks - return lando.engine.list() - .filter(container => container.kind === 'app') - // And add them to our default list - .map(container => `${container.app}_default`) - .then(networks => { - const nets = _.uniq(networks).concat(['bridge', 'host', 'none', lando.config.networkBridge]); - if (_.has(lando, 'config.proxyNet')) nets.push(lando.config.proxyNet); - return nets; - }) - // Filter out landoy ones - .then(nets => _.filter(networks, network => !_.includes(nets, network.Name))) - // Inspect remaining networks to make sure we don't remove any with attached containers - .map(network => lando.engine.getNetwork(network.Id)) - .map(network => network.inspect()) - // Filter out any with containers - .filter(network => _.isEmpty(network.Containers)) - // Return the oldest 5 unused networks - // @TODO: what is the best assumption here? - .then(networks => _.slice(_.orderBy(networks, 'Created', 'asc'), 0, 5)) - // Get the Network object - .map(network => lando.engine.getNetwork(network.Id)) - // and remove it - .each(net => { - lando.log.warn('Removing old network %s', net.id); - net.remove(); - }); - } - }); + lando.log.warn('Lando has detected you are at Docker\'s network limit!'); + lando.log.warn('Give us a moment as we try to make space by cleaning up old networks...'); + // Go through lando containers and add in networks + return lando.engine.list() + .filter(container => container.kind === 'app') + // And add them to our default list + .map(container => `${container.app}_default`) + .then(networks => { + const nets = _.uniq(networks).concat(['bridge', 'host', 'none', lando.config.networkBridge]); + if (_.has(lando, 'config.proxyNet')) nets.push(lando.config.proxyNet); + return nets; + }) + // Filter out landoy ones + .then(nets => _.filter(networks, network => !_.includes(nets, network.Name))) + // Inspect remaining networks to make sure we don't remove any with attached containers + .map(network => lando.engine.getNetwork(network.Id)) + .map(network => network.inspect()) + // Filter out any with containers + .filter(network => _.isEmpty(network.Containers)) + // Return the oldest 5 unused networks + // @TODO: what is the best assumption here? + .then(networks => _.slice(_.orderBy(networks, 'Created', 'asc'), 0, 5)) + // Get the Network object + .map(network => lando.engine.getNetwork(network.Id)) + // and remove it + .each(net => { + lando.log.warn('Removing old network %s', net.id); + net.remove(); + }); + } + }); module.exports = lando => { // Preemptively make sure we have enough networks and if we don't smartly prune some of them @@ -53,13 +53,13 @@ module.exports = lando => { const landonet = lando.engine.getNetwork(lando.config.networkBridge); // Remove the old network return landonet.inspect() - .then(data => _.keys(data.Containers)) - .each(id => landonet.disconnect({Container: id, Force: true})) - .then(() => landonet.remove()) - .catch(err => { - lando.log.verbose('Error inspecting lando_bridge_network, probably does not exit yet'); - lando.log.debug(err); - }); + .then(data => _.keys(data.Containers)) + .each(id => landonet.disconnect({Container: id, Force: true})) + .then(() => landonet.remove()) + .catch(err => { + lando.log.verbose('Error inspecting lando_bridge_network, probably does not exit yet'); + lando.log.debug(err); + }); } }); @@ -70,16 +70,16 @@ module.exports = lando => { // Let's get a list of network return lando.engine.getNetworks() // Try to find our net - .then(networks => _.some(networks, network => network.Name === lando.config.networkBridge)) + .then(networks => _.some(networks, network => network.Name === lando.config.networkBridge)) // Create if needed and set our network version number - .then(exists => { - if (!exists) { - return lando.engine.createNetwork(lando.config.networkBridge).then(() => { - lando.cache.set('versions', _.merge({}, lando.versions, {networking: 2}), {persist: true}); - lando.versions = lando.cache.get('versions'); + .then(exists => { + if (!exists) { + return lando.engine.createNetwork(lando.config.networkBridge).then(() => { + lando.cache.set('versions', _.merge({}, lando.versions, {networking: 2}), {persist: true}); + lando.versions = lando.cache.get('versions'); + }); + } }); - } - }); }); // Return our default config diff --git a/plugins/lando-proxy/app.js b/plugins/lando-proxy/app.js index cc1a8ee0d..f6e55d641 100644 --- a/plugins/lando-proxy/app.js +++ b/plugins/lando-proxy/app.js @@ -15,15 +15,15 @@ const findProxyPorts = (lando, status) => lando.Promise.try(() => { return scanPorts(lando, status).then(ports => _.merge(lando.config.proxyCurrentPorts, ports)); } else { return lando.engine.list() - .filter(container => container.name === lando.config.proxyContainer) - .then(containers => _.isEmpty(containers) ? scanPorts(lando) : lando.config.proxyLastPorts); + .filter(container => container.name === lando.config.proxyContainer) + .then(containers => _.isEmpty(containers) ? scanPorts(lando) : lando.config.proxyLastPorts); }; }); /* * Helper to get all ports */ -const getAllPorts = (noHttp = false, noHttps = false, config) => { +const getAllPorts = (noHttp, noHttps, config) => { const {proxyHttpPort, proxyHttpsPort, proxyHttpFallbacks, proxyHttpsFallbacks} = config; const ports = []; if (noHttp) { @@ -46,12 +46,12 @@ const scanPorts = (lando, status = {http: true, https: true}) => { utils.getFirstOpenPort(lando.scanUrls, lando.config.proxyScanHttps), ]) // @TODO: below could live in utils and would be easy to test - .then(results => ({http: results[0], https: results[1]})) - .then(ports => { - if (!status.http) delete ports.http; - if (!status.https) delete ports.https; - return ports; - }); + .then(results => ({http: results[0], https: results[1]})) + .then(ports => { + if (!status.http) delete ports.http; + if (!status.https) delete ports.https; + return ports; + }); }; /* @@ -102,6 +102,7 @@ module.exports = (app, lando) => { // Fail immediately with a warning if we dont have the ports we need if (_.isEmpty(ports.http) || _.isEmpty(ports.https)) { const allPorts = getAllPorts(_.isEmpty(ports.http), _.isEmpty(ports.https), lando.config); + // eslint-disable-next-line prefer-promise-reject-errors return Promise.reject(`Lando could not detect an open port amongst: ${allPorts}`); } @@ -117,77 +118,77 @@ module.exports = (app, lando) => { }) // Parse the proxy config to get traefix labels - .then(() => { - const urlCounts = utils.getUrlsCounts(app.config.proxy); - if (_.max(_.values(urlCounts)) > 1) { - app.log.error('You cannot assign url %s to more than one service!', _.findKey(urlCounts, c => c > 1)); - } - - // Get list of services that *should* have certs for SSL - const sslReady = _(_.get(app, 'config.services', {})) - .map((data, name) => _.merge({}, data, {name})) - .filter(data => data.ssl) - .map(data => data.name) - .value(); - - // Make sure we augment ssl ready if we have served by candidates - // and also add to their info eg hasCerts - const servedBy = _(app.info) - .filter(info => _.includes(sslReady, info.service)) - .filter(info => _.has(info, 'served_by') || _.has(info, 'ssl_served_by')) - .map(info => info.served_by || info.ssl_served_by) - .value(); - - // Add hasCerts to servedBys - _.forEach(servedBy, name => { - const service = _.find(app.info, {service: name}); - service.hasCerts = true; - }); - - // Parse config - return utils.parseConfig(app.config.proxy, _.compact(_.flatten([sslReady, servedBy]))); - }) + .then(() => { + const urlCounts = utils.getUrlsCounts(app.config.proxy); + if (_.max(_.values(urlCounts)) > 1) { + app.log.error('You cannot assign url %s to more than one service!', _.findKey(urlCounts, c => c > 1)); + } + + // Get list of services that *should* have certs for SSL + const sslReady = _(_.get(app, 'config.services', {})) + .map((data, name) => _.merge({}, data, {name})) + .filter(data => data.ssl) + .map(data => data.name) + .value(); + + // Make sure we augment ssl ready if we have served by candidates + // and also add to their info eg hasCerts + const servedBy = _(app.info) + .filter(info => _.includes(sslReady, info.service)) + .filter(info => _.has(info, 'served_by') || _.has(info, 'ssl_served_by')) + .map(info => info.served_by || info.ssl_served_by) + .value(); + + // Add hasCerts to servedBys + _.forEach(servedBy, name => { + const service = _.find(app.info, {service: name}); + service.hasCerts = true; + }); + + // Parse config + return utils.parseConfig(app.config.proxy, _.compact(_.flatten([sslReady, servedBy]))); + }) // Map to docker compose things - .map(service => { - // Throw error but proceed if we don't have the service - if (!_.includes(app.services, service.name)) { - app.addWarning(warnings.unknownServiceWarning(service.name)); - return {}; - } - - // Build out the docker compose augment and return - service.labels['traefik.enable'] = true; - service.labels['traefik.docker.network'] = lando.config.proxyNet; - service.environment.LANDO_PROXY_PASSTHRU = _.toString(lando.config.proxyPassThru); - const proxyVolume = `${lando.config.proxyName}_proxy_config`; - return { - services: _.set({}, service.name, { - networks: {'lando_proxyedge': {}}, - labels: service.labels, - environment: service.environment, - volumes: [ - `${proxyVolume}:/proxy_config`, - `${lando.config.userConfRoot}/scripts/proxy-certs.sh:/scripts/100-proxy-certs`, - ], - }), - networks: {'lando_proxyedge': {name: lando.config.proxyNet, external: true}}, - volumes: _.set({}, proxyVolume, {external: true}), - }; - }) + .map(service => { + // Throw error but proceed if we don't have the service + if (!_.includes(app.services, service.name)) { + app.addWarning(warnings.unknownServiceWarning(service.name)); + return {}; + } + + // Build out the docker compose augment and return + service.labels['traefik.enable'] = true; + service.labels['traefik.docker.network'] = lando.config.proxyNet; + service.environment.LANDO_PROXY_PASSTHRU = _.toString(lando.config.proxyPassThru); + const proxyVolume = `${lando.config.proxyName}_proxy_config`; + return { + services: _.set({}, service.name, { + networks: {'lando_proxyedge': {}}, + labels: service.labels, + environment: service.environment, + volumes: [ + `${proxyVolume}:/proxy_config`, + `${lando.config.userConfRoot}/scripts/proxy-certs.sh:/scripts/100-proxy-certs`, + ], + }), + networks: {'lando_proxyedge': {name: lando.config.proxyNet, external: true}}, + volumes: _.set({}, proxyVolume, {external: true}), + }; + }) // Add to our app // @NOTE: we can't add this in the normal way since this happens AFTER our app // has been initialized - .then(result => { - const proxyData = new app.ComposeService('proxy', {}, ...result); - const proxyFiles = lando.utils.dumpComposeData(proxyData, app._dir); - app.compose = app.compose.concat(proxyFiles); - app.log.debug('app has proxy compose files', proxyFiles); - }) + .then(result => { + const proxyData = new app.ComposeService('proxy', {}, ...result); + const proxyFiles = lando.utils.dumpComposeData(proxyData, app._dir); + app.compose = app.compose.concat(proxyFiles); + app.log.debug('app has proxy compose files', proxyFiles); + }) // Warn the user if this fails - .catch(error => app.addWarning(warnings.cannotStartProxyWarning(error), error)); + .catch(error => app.addWarning(warnings.cannotStartProxyWarning(error), error)); }); // Add proxy URLS to our app info @@ -199,13 +200,13 @@ module.exports = (app, lando) => { // @TODO: do something more meaningful below like logging?, obviously starting to not GAS if (ports) { _(app.info) - .filter(service => _.has(app, `config.proxy.${service.service}`)) - .flatMap(s => s.urls = _.uniq(s.urls.concat(utils.parse2Info( - app.config.proxy[s.service], - ports, - _.get(s, 'hasCerts', false), - )))) - .value(); + .filter(service => _.has(app, `config.proxy.${service.service}`)) + .flatMap(s => s.urls = _.uniq(s.urls.concat(utils.parse2Info( + app.config.proxy[s.service], + ports, + _.get(s, 'hasCerts', false), + )))) + .value(); } }); }); diff --git a/plugins/lando-proxy/lib/utils.js b/plugins/lando-proxy/lib/utils.js index f54ef256d..102f115ec 100644 --- a/plugins/lando-proxy/lib/utils.js +++ b/plugins/lando-proxy/lib/utils.js @@ -23,9 +23,9 @@ const getInfoUrls = (url, ports, hasCerts = false) => { * Reduces urls to first open port */ exports.getFirstOpenPort = (scanner, urls = []) => scanner(urls, {max: 1, waitCodes: []}) - .filter(url => url.status === false) - .map(port => _.last(port.url.split(':'))) - .then(ports => ports[0]); + .filter(url => url.status === false) + .map(port => _.last(port.url.split(':'))) + .then(ports => ports[0]); /* * Helper to determine what ports have changed @@ -65,40 +65,40 @@ exports.getRule = rule => { * Get a list of URLs and their counts */ exports.getUrlsCounts = config => _(config) - .flatMap(service => service) - .map(url => exports.parseUrl(url)) - .map(data => `${data.host}${data.pathname}:${data.port}`) - .countBy() - .value(); + .flatMap(service => service) + .map(url => exports.parseUrl(url)) + .map(data => `${data.host}${data.pathname}:${data.port}`) + .countBy() + .value(); /* * Parse config into urls we can merge to app.info */ exports.parse2Info = (urls, ports, hasCerts = false) => _(urls) - .map(url => exports.parseUrl(url)) - .flatMap(url => getInfoUrls(url, ports, hasCerts)) - .value(); + .map(url => exports.parseUrl(url)) + .flatMap(url => getInfoUrls(url, ports, hasCerts)) + .value(); /* * Parse urls into SANS */ exports.parse2Sans = urls => _(urls) - .map(url => exports.parseUrl(url).host) - .map((host, index) => `DNS.${10+index} = ${host}`) - .value() - .join('\n'); + .map(url => exports.parseUrl(url).host) + .map((host, index) => `DNS.${10+index} = ${host}`) + .value() + .join('\n'); /* * Parse hosts for traefik */ exports.parseConfig = (config, sslReady = []) => _(config) - .map((urls, service) => ({ - environment: { - LANDO_PROXY_NAMES: exports.parse2Sans(urls), - }, - name: service, - labels: exports.parseRoutes(service, urls, sslReady)})) - .value(); + .map((urls, service) => ({ + environment: { + LANDO_PROXY_NAMES: exports.parse2Sans(urls), + }, + name: service, + labels: exports.parseRoutes(service, urls, sslReady)})) + .value(); /* * Helper to parse the routes @@ -106,10 +106,10 @@ exports.parseConfig = (config, sslReady = []) => _(config) exports.parseRoutes = (service, urls = [], sslReady, labels = {}) => { // Prepare our URLs for traefik const parsedUrls = _(urls) - .map(url => exports.parseUrl(url)) - .map(parsedUrl => _.merge({}, parsedUrl, {id: hasher(parsedUrl)})) - .uniqBy('id') - .value(); + .map(url => exports.parseUrl(url)) + .map(parsedUrl => _.merge({}, parsedUrl, {id: hasher(parsedUrl)})) + .uniqBy('id') + .value(); // Add things into the labels _.forEach(parsedUrls, rule => { @@ -121,8 +121,8 @@ exports.parseRoutes = (service, urls = [], sslReady, labels = {}) => { }; // Ensure we prefix all middleware with the ruleid rule.middlewares = _(rule.middlewares) - .map(middleware => _.merge({}, middleware, {name: `${rule.id}-${middleware.name}`})) - .value(); + .map(middleware => _.merge({}, middleware, {name: `${rule.id}-${middleware.name}`})) + .value(); // Set up all the middlewares _.forEach(rule.middlewares, m => { @@ -138,9 +138,9 @@ exports.parseRoutes = (service, urls = [], sslReady, labels = {}) => { labels[`traefik.http.routers.${rule.id}.rule`] = exports.getRule(rule); // Set none secure middlewares labels[`traefik.http.routers.${rule.id}.middlewares`] = _(_.map(rule.middlewares, 'name')) - .filter(name => !_.endsWith(name, '-secured')) - .value() - .join(','); + .filter(name => !_.endsWith(name, '-secured')) + .value() + .join(','); // Add https if we can if (_.includes(sslReady, service)) { @@ -180,5 +180,5 @@ exports.parseUrl = data => { * Maps ports to urls */ exports.ports2Urls = (ports, secure = false, hostname = '127.0.0.1') => _(ports) - .map(port => url.format({protocol: (secure) ? 'https' : 'http', hostname, port})) - .value(); + .map(port => url.format({protocol: (secure) ? 'https' : 'http', hostname, port})) + .value(); diff --git a/plugins/lando-recipes/lib/build.js b/plugins/lando-recipes/lib/build.js index d86d67bc6..27ff1fc06 100644 --- a/plugins/lando-recipes/lib/build.js +++ b/plugins/lando-recipes/lib/build.js @@ -33,8 +33,8 @@ exports.buildRun = config => ({ // Helper to run exports.run = (lando, run) => lando.engine.run(run).catch(err => { return lando.engine.stop(killRun(run)) - .then(() => lando.engine.destroy(killRun(run))) - .then(() => lando.Promise.reject(err)); + .then(() => lando.engine.destroy(killRun(run))) + .then(() => lando.Promise.reject(err)); }); // Helper to get run defaults @@ -42,12 +42,12 @@ exports.runDefaults = (lando, options) => { // Handle all the compose stuff const LandoInit = lando.factory.get('_init'); const initData = new LandoInit( - lando.config.userConfRoot, - lando.config.home, - options.destination, - _.cloneDeep(lando.config.appEnv), - _.cloneDeep(lando.config.appLabels), - _.get(options, 'initImage', 'devwithlando/util:4'), + lando.config.userConfRoot, + lando.config.home, + options.destination, + _.cloneDeep(lando.config.appEnv), + _.cloneDeep(lando.config.appLabels), + _.get(options, 'initImage', 'devwithlando/util:4'), ); const initDir = path.join(lando.config.userConfRoot, 'init', options.name); const initFiles = lando.utils.dumpComposeData(initData, initDir); diff --git a/plugins/lando-recipes/lib/utils.js b/plugins/lando-recipes/lib/utils.js index 14146ded8..3d6b52241 100644 --- a/plugins/lando-recipes/lib/utils.js +++ b/plugins/lando-recipes/lib/utils.js @@ -59,10 +59,10 @@ exports.getDbTooling = database => { * Helper to get the phar build command */ exports.getDrush = (version, status) => exports.getPhar( - getDrushUrl(version), - '/tmp/drush.phar', - '/usr/local/bin/drush', - status, + getDrushUrl(version), + '/tmp/drush.phar', + '/usr/local/bin/drush', + status, ); /* diff --git a/plugins/lando-recipes/tasks/init.js b/plugins/lando-recipes/tasks/init.js index d616ec504..501075297 100644 --- a/plugins/lando-recipes/tasks/init.js +++ b/plugins/lando-recipes/tasks/init.js @@ -69,35 +69,35 @@ module.exports = lando => { return lando.events.emit('pre-init', options, buildSteps).then(() => runBuild(lando, options, buildSteps)) // Run any config steps // @NOTE: config steps are designed to augmnet the landofile with additional metadata - .then(() => configStep(options, lando)) + .then(() => configStep(options, lando)) // Compile and dump the yaml - .then((config = {}) => { - // Where are we going? - const dest = path.join(options.destination, '.lando.yml'); - const landoFile = getYaml(dest, options, lando); + .then((config = {}) => { + // Where are we going? + const dest = path.join(options.destination, '.lando.yml'); + const landoFile = getYaml(dest, options, lando); - // Get a lower level config if needed, merge in current recipe config - if (options.full) { - const Recipe = lando.factory.get(options.recipe); - const recipeConfig = _.merge({}, landoFile, {app: landoFile.name, _app: {_config: lando.config}}); - _.merge(landoFile, new Recipe(landoFile.name, recipeConfig).config); - } + // Get a lower level config if needed, merge in current recipe config + if (options.full) { + const Recipe = lando.factory.get(options.recipe); + const recipeConfig = _.merge({}, landoFile, {app: landoFile.name, _app: {_config: lando.config}}); + _.merge(landoFile, new Recipe(landoFile.name, recipeConfig).config); + } - // Merge in any additional configuration options specified - _.forEach(options.option, option => { - const key = _.first(option.split('=')); - _.set(landoFile, `config.${key}`, _.last(option.split('='))); - }); + // Merge in any additional configuration options specified + _.forEach(options.option, option => { + const key = _.first(option.split('=')); + _.set(landoFile, `config.${key}`, _.last(option.split('='))); + }); - // Merge and dump the config file - lando.yaml.dump(dest, _.merge(landoFile, config)); - // Show it - showInit(lando, options); - }) + // Merge and dump the config file + lando.yaml.dump(dest, _.merge(landoFile, config)); + // Show it + showInit(lando, options); + }) // Post init event - .then(() => lando.events.emit('post-init', options)); + .then(() => lando.events.emit('post-init', options)); }, }; }; diff --git a/plugins/lando-services/app.js b/plugins/lando-services/app.js index 6ae518b0a..33099955d 100644 --- a/plugins/lando-services/app.js +++ b/plugins/lando-services/app.js @@ -90,19 +90,19 @@ module.exports = (app, lando) => { app.log.verbose('discovering dynamic portforward info...'); const forwarders = _.filter(app.info, service => _.get(service, 'external_connection.port', false)); return lando.engine.list({project: app.project}) - .filter(service => _.includes(_.flatMap(forwarders, service => service.service), service.service)) - .map(service => ({ - id: service.id, - service: service.service, - internal: _.get(_.find(app.info, {service: service.service}), 'internal_connection.port'), - })) - .map(service => lando.engine.scan(service).then(data => { - const key = `NetworkSettings.Ports.${service.internal}/tcp`; - const port = _.filter(_.get(data, key, []), forward => forward.HostIp === lando.config.bindAddress); - if (_.has(port[0], 'HostPort')) { - _.set(_.find(app.info, {service: service.service}), 'external_connection.port', port[0].HostPort); - } - })); + .filter(service => _.includes(_.flatMap(forwarders, service => service.service), service.service)) + .map(service => ({ + id: service.id, + service: service.service, + internal: _.get(_.find(app.info, {service: service.service}), 'internal_connection.port'), + })) + .map(service => lando.engine.scan(service).then(data => { + const key = `NetworkSettings.Ports.${service.internal}/tcp`; + const port = _.filter(_.get(data, key, []), forward => forward.HostIp === lando.config.bindAddress); + if (_.has(port[0], 'HostPort')) { + _.set(_.find(app.info, {service: service.service}), 'external_connection.port', port[0].HostPort); + } + })); }); // Determine pullable and locally built images @@ -110,8 +110,8 @@ module.exports = (app, lando) => { app.log.verbose('determining pullable services...'); // Determine local vs pullable services const whereats = _(_.get(app, 'config.services', {})) - .map((data, service) => ({service, isLocal: _.has(data, 'overrides.build') || _.has(data, 'services.build')})) - .value(); + .map((data, service) => ({service, isLocal: _.has(data, 'overrides.build') || _.has(data, 'services.build')})) + .value(); // Set local and pullys for downstream concerns app.log.debug('determined pullable services', whereats); diff --git a/plugins/lando-services/lib/utils.js b/plugins/lando-services/lib/utils.js index 731efdde1..fdf03b922 100644 --- a/plugins/lando-services/lib/utils.js +++ b/plugins/lando-services/lib/utils.js @@ -31,9 +31,9 @@ exports.cloneOverrides = (overrides = {}) => { * @TODO: this looks pretty testable? should services have libs? */ exports.getInstallCommands = (deps, pkger, prefix = []) => _(deps) - .map((version, pkg) => _.flatten([prefix, pkger(pkg, version)])) - .map(command => command.join(' ')) - .value(); + .map((version, pkg) => _.flatten([prefix, pkger(pkg, version)])) + .map(command => command.join(' ')) + .value(); /* * Filter and map build steps @@ -95,26 +95,26 @@ exports.filterBuildSteps = (services, app, rootSteps = [], buildSteps= [], prest * Parse config into raw materials for our factory */ exports.parseConfig = (config, app) => _(config) - // Arrayify - .map((service, name) => _.merge({}, service, {name})) - // Filter out any services without a type, this implicitly assumes these - // services are "managed" by lando eg their type/version details are provided - // by another service - .filter(service => _.has(service, 'type')) - // Build the config - .map(service => _.merge({}, service, { - _app: app, - data: `data_${service.name}`, - app: app.name, - confDest: path.join(app._config.userConfRoot, 'config', service.type.split(':')[0]), - home: app._config.home, - project: app.project, - type: service.type.split(':')[0], - root: app.root, - userConfRoot: app._config.userConfRoot, - version: service.type.split(':')[1], - })) - .value(); +// Arrayify + .map((service, name) => _.merge({}, service, {name})) +// Filter out any services without a type, this implicitly assumes these +// services are "managed" by lando eg their type/version details are provided +// by another service + .filter(service => _.has(service, 'type')) +// Build the config + .map(service => _.merge({}, service, { + _app: app, + data: `data_${service.name}`, + app: app.name, + confDest: path.join(app._config.userConfRoot, 'config', service.type.split(':')[0]), + home: app._config.home, + project: app.project, + type: service.type.split(':')[0], + root: app.root, + userConfRoot: app._config.userConfRoot, + version: service.type.split(':')[1], + })) + .value(); /* * Run build @@ -124,20 +124,20 @@ exports.runBuild = (app, steps, lockfile, hash = 'YOU SHALL NOT PASS') => { app.log.info('running build steps...'); return app.engine.run(steps) // Save the new hash if everything works out ok - .then(() => { - app._lando.cache.set(lockfile, hash, {persist: true}); - app.log.info('build steps completed. and locked with %s', lockfile); - }) + .then(() => { + app._lando.cache.set(lockfile, hash, {persist: true}); + app.log.info('build steps completed. and locked with %s', lockfile); + }) // Make sure we don't save a hash if our build fails - .catch(error => { - app.addWarning({ - title: `One of your build steps failed`, - detail: [ - 'This **MAY** prevent your app from working.', - 'Check for errors above, fix them in your Landofile, and try again by running:', - ], - command: 'lando rebuild', - }, error); - }); + .catch(error => { + app.addWarning({ + title: `One of your build steps failed`, + detail: [ + 'This **MAY** prevent your app from working.', + 'Check for errors above, fix them in your Landofile, and try again by running:', + ], + command: 'lando rebuild', + }, error); + }); } }; diff --git a/plugins/lando-sharing/app.js b/plugins/lando-sharing/app.js index 148673f0b..a4818daff 100644 --- a/plugins/lando-sharing/app.js +++ b/plugins/lando-sharing/app.js @@ -8,11 +8,11 @@ const utils = require('./lib/utils'); // Helper to get excludes const getExcludes = (data = [], inverse = false) => _(data) - .filter(exclude => _.startsWith(exclude, '!') === inverse) - .map(exclude => _.trimStart(exclude, '!')) - .uniq() - .compact() - .value(); + .filter(exclude => _.startsWith(exclude, '!') === inverse) + .map(exclude => _.trimStart(exclude, '!')) + .uniq() + .compact() + .value(); // Helper to get includes const getIncludes = data => getExcludes(data, true); @@ -54,10 +54,10 @@ module.exports = (app, lando) => { }; return lando.engine.run(run) // Destroy on fail - .catch(err => { - run.opts = {purge: true, mode: 'attach'}; - return lando.engine.stop(run).then(() => lando.engine.destroy(run)).then(() => lando.Promise.reject(err)); - }); + .catch(err => { + run.opts = {purge: true, mode: 'attach'}; + return lando.engine.stop(run).then(() => lando.engine.destroy(run)).then(() => lando.Promise.reject(err)); + }); } }); diff --git a/plugins/lando-sharing/lib/utils.js b/plugins/lando-sharing/lib/utils.js index bff526132..bf7ffa66b 100644 --- a/plugins/lando-sharing/lib/utils.js +++ b/plugins/lando-sharing/lib/utils.js @@ -7,26 +7,26 @@ const toObject = require('./../../../lib/utils').toObject; // Helper to get named volume const getNamedVolumeName = exclude => 'exclude_' + path - .normalize(exclude).replace(/\W/g, '').split(path.sep).join('_'); + .normalize(exclude).replace(/\W/g, '').split(path.sep).join('_'); // Helper to map exclude directories to named volume name const getNamedVolumeNames = (excludes = []) => _(excludes) - .map(exclude => getNamedVolumeName(exclude)) - .value(); + .map(exclude => getNamedVolumeName(exclude)) + .value(); // Helper to get named volumes exports.getNamedVolumes = (excludes = []) => _(excludes) - .thru(excludes => toObject(getNamedVolumeNames(excludes))) - .value(); + .thru(excludes => toObject(getNamedVolumeNames(excludes))) + .value(); // Get service volumes exports.getServiceVolumes = (excludes = [], base = '/tmp') => _(excludes) - .map(exclude => ({mount: getNamedVolumeName(exclude), path: path.posix.join(base, exclude)})) - .map(exclude => `${exclude.mount}:${exclude.path}`) - .value(); + .map(exclude => ({mount: getNamedVolumeName(exclude), path: path.posix.join(base, exclude)})) + .map(exclude => `${exclude.mount}:${exclude.path}`) + .value(); // Get directories to include exports.getIncludeVolumes = (excludes = [], base = '/app', mount = 'cached') => _(excludes) - .map(exclude => `${base}/${exclude}:/app/${exclude}:${mount}`) - .value(); + .map(exclude => `${base}/${exclude}:/app/${exclude}:${mount}`) + .value(); diff --git a/plugins/lando-tooling/lib/build.js b/plugins/lando-tooling/lib/build.js index 463002d14..97a3f87d6 100644 --- a/plugins/lando-tooling/lib/build.js +++ b/plugins/lando-tooling/lib/build.js @@ -14,28 +14,28 @@ module.exports = (config, injected) => { // Get the event name handler const eventName = name.split(' ')[0]; const run = answers => injected.Promise.try(() => (_.isEmpty(app.compose)) ? app.init() : true) - // Kick off the pre event wrappers - .then(() => app.events.emit(`pre-${eventName}`, config, answers)) - // Get an interable of our commandz - .then(() => _.map(utils.parseConfig(cmd, service, options, answers))) - // Build run objects - .map(({command, service}) => utils.buildCommand(app, command, service, user, env, dir)) - // Try to run the task quickly first and then fallback to compose launch - .each(runner => utils.dockerExec(injected, stdio, runner).catch(execError => { - return injected.engine.isRunning(runner.id).then(isRunning => { - if (!isRunning) { - return injected.engine.run(runner).catch(composeError => { - composeError.hide = true; - throw composeError; - }); - } else { - execError.hide = true; - throw execError; - } - }); - })) - // Post event - .then(() => app.events.emit(`post-${eventName}`, config, answers)); + // Kick off the pre event wrappers + .then(() => app.events.emit(`pre-${eventName}`, config, answers)) + // Get an interable of our commandz + .then(() => _.map(utils.parseConfig(cmd, service, options, answers))) + // Build run objects + .map(({command, service}) => utils.buildCommand(app, command, service, user, env, dir)) + // Try to run the task quickly first and then fallback to compose launch + .each(runner => utils.dockerExec(injected, stdio, runner).catch(execError => { + return injected.engine.isRunning(runner.id).then(isRunning => { + if (!isRunning) { + return injected.engine.run(runner).catch(composeError => { + composeError.hide = true; + throw composeError; + }); + } else { + execError.hide = true; + throw execError; + } + }); + })) + // Post event + .then(() => app.events.emit(`post-${eventName}`, config, answers)); // Return our tasks return { diff --git a/plugins/lando-tooling/lib/utils.js b/plugins/lando-tooling/lib/utils.js index aeac05c5c..a06ea5824 100644 --- a/plugins/lando-tooling/lib/utils.js +++ b/plugins/lando-tooling/lib/utils.js @@ -48,11 +48,11 @@ const getExecOpts = (docker, datum) => { * Helper to get dynamic service keys for stripping */ const getDynamicKeys = (answer, answers = {}) => _(answers) - .map((value, key) => ({key, value})) - .filter(data => data.value === answer) - .map(data => data.key) - .map(key => (_.size(key) === 1) ? `-${key}` : `--${key}`) - .value(); + .map((value, key) => ({key, value})) + .filter(data => data.value === answer) + .map(data => data.key) + .map(key => (_.size(key) === 1) ? `-${key}` : `--${key}`) + .value(); /* * Helper to handle dynamic services @@ -92,10 +92,10 @@ const handleOpts = (config, argopts = []) => { * Helper to get passthru options */ const handlePassthruOpts = (options = {}, answers = {}) => _(options) - .map((value, key) => _.merge({}, {name: key}, value)) - .filter(value => value.passthrough === true && !_.isNil(answers[value.name])) - .map(value => `--${value.name}=${answers[value.name]}`) - .value(); + .map((value, key) => _.merge({}, {name: key}, value)) + .filter(value => value.passthrough === true && !_.isNil(answers[value.name])) + .map(value => `--${value.name}=${answers[value.name]}`) + .value(); /* * Helper to convert a command into config object @@ -139,26 +139,26 @@ exports.dockerExec = (injected, stdio, datum = {}) => { * Helper to get tts */ exports.getToolingTasks = (config, app) => _(config) - .map((task, name) => _.merge({}, task, {app, name})) - .filter(task => _.isObject(task)) - .value(); + .map((task, name) => _.merge({}, task, {app, name})) + .filter(task => _.isObject(task)) + .value(); /* * Helper to parse tooling config options */ exports.parseConfig = (cmd, service, options = {}, answers = {}) => _(cmd) - // Put into an object so we can handle "multi-service" tooling - .map(cmd => parseCommand(cmd, service)) - // Handle dynamic services - .map(config => handleDynamic(config, options, answers)) - // Add in any argv extras if they've been passed in - .map(config => handleOpts(config, handlePassthruOpts(options, answers))) - // Wrap the command in /bin/sh if that makes sense - .map(config => _.merge({}, config, {command: escape(config.command, true, config.args)})) - // Add any args to the command and compact to remove undefined - .map(config => _.merge({}, config, {command: _.compact(config.command.concat(config.args))})) - // Put into an object - .value(); +// Put into an object so we can handle "multi-service" tooling + .map(cmd => parseCommand(cmd, service)) +// Handle dynamic services + .map(config => handleDynamic(config, options, answers)) +// Add in any argv extras if they've been passed in + .map(config => handleOpts(config, handlePassthruOpts(options, answers))) +// Wrap the command in /bin/sh if that makes sense + .map(config => _.merge({}, config, {command: escape(config.command, true, config.args)})) +// Add any args to the command and compact to remove undefined + .map(config => _.merge({}, config, {command: _.compact(config.command.concat(config.args))})) +// Put into an object + .value(); /* * Helper to get defaults @@ -174,7 +174,7 @@ exports.toolingDefaults = ({ service = '', stdio = ['inherit', 'pipe', 'pipe'], user = null, - } = {}) => +} = {}) => ({ name, app: app, diff --git a/scripts/dev-version.js b/scripts/dev-version.js index e32f2a4f4..a3464e665 100644 --- a/scripts/dev-version.js +++ b/scripts/dev-version.js @@ -19,18 +19,18 @@ const shell = new Shell(log); return shell.sh(['git', 'describe', '--tags', '--always', '--abbrev=1'], {mode: 'collect'}) // Trim the tag -.then(data => _.trim(data.slice(1))) + .then(data => _.trim(data.slice(1))) // Replace the version for our files -.then(version => { - const packageJson = require('./../package.json'); - packageJson.version = version; - log.info('Updating package.json to dev version %s', packageJson.version); - fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, 2)); -}) + .then(version => { + const packageJson = require('./../package.json'); + packageJson.version = version; + log.info('Updating package.json to dev version %s', packageJson.version); + fs.writeFileSync('./package.json', JSON.stringify(packageJson, null, 2)); + }) // Catch errors and do stuff so we can break builds when this fails -.catch(error => { - log.error(error); - process.exit(error.code || 555); -}); + .catch(error => { + log.error(error); + process.exit(error.code || 555); + }); diff --git a/scripts/yaml2json.js b/scripts/yaml2json.js index 9eb05bc78..0fba94d30 100755 --- a/scripts/yaml2json.js +++ b/scripts/yaml2json.js @@ -23,15 +23,15 @@ if (_.isEmpty(inputFiles)) { // Validate filenames const inputFilePaths = _(inputFiles) - // Map to absolute paths relative to cwd - .map(file => path.resolve(process.cwd(), file)) - // Filter out nonexistent files and warn - .filter(file => { - if (!fs.existsSync(file)) log.warn('Could not locate file %s! Skipping that one...', file); - return fs.existsSync(file); - }) - // warn - .value(); +// Map to absolute paths relative to cwd + .map(file => path.resolve(process.cwd(), file)) +// Filter out nonexistent files and warn + .filter(file => { + if (!fs.existsSync(file)) log.warn('Could not locate file %s! Skipping that one...', file); + return fs.existsSync(file); + }) +// warn + .value(); // Make sure output dir is dialed if (!fs.existsSync(outputDir)) { @@ -41,12 +41,12 @@ if (!fs.existsSync(outputDir)) { // Finalize inputs and outputs const files = _(inputFilePaths) - // Map to input/output pairs - .map(file => ({ - input: file, - output: path.resolve(outputDir, `${path.basename(file, path.extname(file))}.json`), - })) - .value(); +// Map to input/output pairs + .map(file => ({ + input: file, + output: path.resolve(outputDir, `${path.basename(file, path.extname(file))}.json`), + })) + .value(); // Write the files diff --git a/test/cache.spec.js b/test/cache.spec.js index 7fa48756f..e31e4be3c 100644 --- a/test/cache.spec.js +++ b/test/cache.spec.js @@ -146,9 +146,9 @@ describe('cache', () => { filesystem(); const cache = new Cache({cacheDir: '/tmp/cache/'}); cache.set( - 'subdivisions', - 'Sprawling on the fringes of the city', - {persist: true}, + 'subdivisions', + 'Sprawling on the fringes of the city', + {persist: true}, ); fs.existsSync('/tmp/cache/subdivisions').should.be.true; diff --git a/test/compose.spec.js b/test/compose.spec.js index d282600b0..02e5c7b91 100644 --- a/test/compose.spec.js +++ b/test/compose.spec.js @@ -55,9 +55,9 @@ describe('compose', () => { it('should return the correct default options when not specified'); it('#build should return an object.', () => { const buildResult = compose.build( - ['string1', 'string2'], - 'my_project', - myOpts, + ['string1', 'string2'], + 'my_project', + myOpts, ); expect(buildResult).to.be.an('object'); }); @@ -65,9 +65,9 @@ describe('compose', () => { it('#build should throw and error if project string is not defined.', () => { expect(() => { compose.build( - ['string test'], - null, - myOpts, + ['string test'], + null, + myOpts, ); }).to.throw(Error); }); @@ -77,9 +77,9 @@ describe('compose', () => { it('should return the correct default options when not specified'); it('#getId should return an object.', () => { const getIdResult = compose.getId( - ['string1', 'string2'], - 'my_project', - myOpts, + ['string1', 'string2'], + 'my_project', + myOpts, ); expect(getIdResult).to.be.an('object'); }); @@ -99,9 +99,9 @@ describe('compose', () => { it('should not pull any services that are beind built locally'); it('#pull should return an object.', () => { const pullResult = compose.pull( - ['string1', 'string2'], - 'my_project', - myOpts, + ['string1', 'string2'], + 'my_project', + myOpts, ); expect(pullResult).to.be.an('object'); }); @@ -115,9 +115,9 @@ describe('compose', () => { it('should return the correct default options for remove when not specified'); it('#remove should return an object.', () => { const removeResult = compose.remove( - ['string1', 'string2'], - 'my_project', - myOpts, + ['string1', 'string2'], + 'my_project', + myOpts, ); expect(removeResult).to.be.an('object'); }); @@ -130,9 +130,9 @@ describe('compose', () => { it('should set opts.cmd into an array at index 2 with /bin/sh and -c as indeces 0 and 1'); it('#run should return an object.', () => { const runResult = compose.run( - ['string1', 'string2'], - 'my_project', - myOpts, + ['string1', 'string2'], + 'my_project', + myOpts, ); expect(runResult).to.be.an('object'); }); @@ -142,18 +142,18 @@ describe('compose', () => { it('should return the correct default options when not specified'); it('#start should return an object.', () => { const startResult = compose.start( - ['string1', 'string2'], - 'my_project', - myOpts, + ['string1', 'string2'], + 'my_project', + myOpts, ); expect(startResult).to.be.an('object'); }); it('#start without options should work.', () => { const startResult = compose.start( - ['string'], - 'a_project', - false, + ['string'], + 'a_project', + false, ); expect(startResult).to.be.an('object'); }); @@ -162,9 +162,9 @@ describe('compose', () => { myOpts.entrypoint = 'astring'; myOpts.cmd = 'acommand'; const startResult = compose.start( - ['string test'], - 'another_project', - myOpts, + ['string test'], + 'another_project', + myOpts, ); expect(startResult).to.be.an('object'); myOpts.entrypoint = []; @@ -172,9 +172,9 @@ describe('compose', () => { it('#start with no opts (false).', () => { const startResult = compose.start( - ['string test'], - 'another_project', - myOpts, + ['string test'], + 'another_project', + myOpts, ); expect(startResult).to.be.an('object'); }); @@ -182,9 +182,9 @@ describe('compose', () => { it('#start with cmd as a string should still return and object.', () => { myOpts.cmd = ['cmdstring']; const startResult = compose.start( - ['string test'], - 'one_project_more', - myOpts, + ['string test'], + 'one_project_more', + myOpts, ); expect(startResult).to.be.an('object'); myOpts.cmd = ['one', 'two']; @@ -195,9 +195,9 @@ describe('compose', () => { it('should return the correct default options when not specified'); it('#stop should return an object.', () => { const stopResult = compose.stop( - ['string1', 'string2'], - 'my_project', - myOpts, + ['string1', 'string2'], + 'my_project', + myOpts, ); expect(stopResult).to.be.an('object'); }); diff --git a/test/docker.spec.js b/test/docker.spec.js index f3bc28445..d91db3aef 100644 --- a/test/docker.spec.js +++ b/test/docker.spec.js @@ -20,23 +20,23 @@ const _ = require('lodash'); const dummyContainer = (overrides = {}) => { return _.assign( - new Dockerode.Container(), - { - Id: '8675309', - app: 'Death Star', - Names: ['/Death Star_Exhaust Port_73'], - Labels: { - 'com.docker.compose.project': 'Death Star', - 'com.docker.compose.service': 'Exhaust Port', - 'com.docker.compose.container-number': 73, - 'com.docker.compose.oneoff': 'no', - 'io.lando.container': 'TRUE', - 'io.lando.src': '/tmp/.lando.yml', - 'io.lando.id': 'lando', - 'io.lando.service-container': 'no', + new Dockerode.Container(), + { + Id: '8675309', + app: 'Death Star', + Names: ['/Death Star_Exhaust Port_73'], + Labels: { + 'com.docker.compose.project': 'Death Star', + 'com.docker.compose.service': 'Exhaust Port', + 'com.docker.compose.container-number': 73, + 'com.docker.compose.oneoff': 'no', + 'io.lando.container': 'TRUE', + 'io.lando.src': '/tmp/.lando.yml', + 'io.lando.id': 'lando', + 'io.lando.service-container': 'no', + }, }, - }, - overrides, + overrides, ); }; @@ -50,8 +50,8 @@ describe('docker', () => { describe('#createNetwork', () => { it('should merge name correctly into opts', () => { const stub = sinon.stub(landerode, 'createNetwork') - .usingPromise(Promise) - .resolves(); + .usingPromise(Promise) + .resolves(); return landerode.createNet('elthree', {}).then(() => { // for some reason should/expect doesn't work here... stub.calledWith({Name: 'elthree'}); @@ -62,9 +62,9 @@ describe('docker', () => { const stub = sinon.stub(landerode, 'createNetwork').rejects('Too Many Capes'); return landerode.createNet('hardtimes').should.be.rejectedWith('Too Many Capes') - .then(() => { - stub.restore(); - }); + .then(() => { + stub.restore(); + }); }); }); @@ -72,7 +72,7 @@ describe('docker', () => { it('should throw an error if inspect fails', () => { // We need this chain of stubs to send our call down an explicit line that // will eventually throw a solid exception. - let bogusContainer = dummyContainer(); + const bogusContainer = dummyContainer(); const inspectStub = sinon.stub(bogusContainer, 'inspect').throws(); const stub = sinon.stub(landerode, 'getContainer').returns(bogusContainer); @@ -100,10 +100,10 @@ describe('docker', () => { const getStub = sinon.stub(landerode, 'getContainer').returns(bogusContainer); return landerode.isRunning('YT-1300').should.eventually.be.false - .then(() => { - inspectStub.restore(); - getStub.restore(); - }); + .then(() => { + inspectStub.restore(); + getStub.restore(); + }); }); it('should return true if State.Running inspect data is true', () => { @@ -119,10 +119,10 @@ describe('docker', () => { const getStub = sinon.stub(landerode, 'getContainer').returns(bogusContainer); return landerode.isRunning('YT-1300').should.eventually.be.true - .then(() => { - inspectStub.restore(); - getStub.restore(); - }); + .then(() => { + inspectStub.restore(); + getStub.restore(); + }); }); it('should return false if container doesn\'t exist', () => { @@ -130,10 +130,10 @@ describe('docker', () => { const bogusContainer = new Dockerode.Container(); // Throw the proper error. const inspectStub = sinon.stub(bogusContainer, 'inspect') - .rejects(new Error('No such container: foo')); + .rejects(new Error('No such container: foo')); // Make sure we return the proper failing container. const getStub = sinon.stub(landerode, 'getContainer') - .returns(bogusContainer); + .returns(bogusContainer); return landerode.isRunning('foo').should.eventually.be.false.then(() => { getStub.restore(); @@ -150,10 +150,10 @@ describe('docker', () => { const getStub = sinon.stub(landerode, 'getContainer').returns(bogusContainer); return landerode.isRunning('foo').should.be - .rejectedWith('It\'s a trap!').then(() => { - getStub.restore(); - inspectStub.restore(); - }); + .rejectedWith('It\'s a trap!').then(() => { + getStub.restore(); + inspectStub.restore(); + }); }); }); @@ -164,74 +164,74 @@ describe('docker', () => { it('should filter out any containers that are pending removal', () => { const listStub = sinon.stub(landerode, 'listContainers') - .usingPromise(Promise) - .resolves([ - dummyContainer({Status: 'Being Awesome'}), - dummyContainer({Status: 'Removal In Progress'}), - ]); + .usingPromise(Promise) + .resolves([ + dummyContainer({Status: 'Being Awesome'}), + dummyContainer({Status: 'Removal In Progress'}), + ]); return landerode.list() - .should.eventually.be.an('Array').with.a.lengthOf(1) - .then(() => { - listStub.restore(); - }); + .should.eventually.be.an('Array').with.a.lengthOf(1) + .then(() => { + listStub.restore(); + }); }); it('should remove any "null" containers', () => { const listStub = sinon.stub(landerode, 'listContainers') - .usingPromise(Promise) - .resolves([ - null, - dummyContainer({identity: 'Solo'}), - ]); + .usingPromise(Promise) + .resolves([ + null, + dummyContainer({identity: 'Solo'}), + ]); return landerode.list() - .should.eventually.be.an('Array').with.a.lengthOf(1) - .then(() => { - listStub.restore(); - }); + .should.eventually.be.an('Array').with.a.lengthOf(1) + .then(() => { + listStub.restore(); + }); }); it('should filter out non-lando containers', () => { const listStub = sinon.stub(landerode, 'listContainers') - .usingPromise(Promise) - .resolves([ - dummyContainer({Labels: {'io.lando.container': 'FALSE'}}), - dummyContainer(), - ]); + .usingPromise(Promise) + .resolves([ + dummyContainer({Labels: {'io.lando.container': 'FALSE'}}), + dummyContainer(), + ]); return landerode.list() - .should.eventually.be.an('Array').with.a.lengthOf(1) - .then(() => { - listStub.restore(); - }); + .should.eventually.be.an('Array').with.a.lengthOf(1) + .then(() => { + listStub.restore(); + }); }); it('should filter by app name if given', () => { const listStub = sinon.stub(landerode, 'listContainers') - .usingPromise(Promise) - .resolves([ - dummyContainer({Labels: { - 'com.docker.compose.project': 'alderaan', - 'com.docker.compose.service': 'Rescue Mission', - 'com.docker.compose.container-number': 73, - 'com.docker.compose.oneoff': 'no', - 'io.lando.container': 'TRUE', - 'io.lando.id': 'lando', - 'io.lando.service-container': 'no', - 'io.lando.src': '/tmp/.lando.yml', - }}), - dummyContainer(), - ]); + .usingPromise(Promise) + .resolves([ + dummyContainer({Labels: { + 'com.docker.compose.project': 'alderaan', + 'com.docker.compose.service': 'Rescue Mission', + 'com.docker.compose.container-number': 73, + 'com.docker.compose.oneoff': 'no', + 'io.lando.container': 'TRUE', + 'io.lando.id': 'lando', + 'io.lando.service-container': 'no', + 'io.lando.src': '/tmp/.lando.yml', + }}), + dummyContainer(), + ]); return landerode.list({app: 'alderaan'}) - .should.eventually.be.an('Array').with.a.lengthOf(1) - .then(() => { - listStub.restore(); - }); + .should.eventually.be.an('Array').with.a.lengthOf(1) + .then(() => { + listStub.restore(); + }); }); it('should throw an error on all other catches', () => { - let container = dummyContainer(); + const container = dummyContainer(); delete container.lando; const listStub = sinon.stub(landerode, 'listContainers').resolves([container]); @@ -251,14 +251,14 @@ describe('docker', () => { const getStub = sinon.stub(landerode, 'getContainer').returns(container); const removeStub = sinon.stub(container, 'remove') - .usingPromise(Promise) - .rejects('Oh No!'); + .usingPromise(Promise) + .rejects('Oh No!'); landerode.remove('1234').should.be.rejectedWith('Oh No!') - .then(() => { - getStub.restore(); - removeStub.restore(); - }); + .then(() => { + getStub.restore(); + removeStub.restore(); + }); }); }); }); diff --git a/test/env.spec.js b/test/env.spec.js index 776b00e4b..91088965a 100644 --- a/test/env.spec.js +++ b/test/env.spec.js @@ -98,8 +98,8 @@ describe('env', () => { filesystem({'/usr/bin/docker-compose': 'CODEZ'}); const composeExecutable = env.getComposeExecutable(true); expect(composeExecutable) - .to - .equal('/usr/bin/docker-compose'); + .to + .equal('/usr/bin/docker-compose'); filesystem.restore(); resetPlatform(); }); diff --git a/test/events.spec.js b/test/events.spec.js index 1a555b7cc..4d3aa5782 100644 --- a/test/events.spec.js +++ b/test/events.spec.js @@ -40,9 +40,9 @@ describe('events', () => { events.on('event', same1); events.on('event', same2); const priorityFiveEvents = _(events._listeners) - .filter(event => event.name === 'event') - .filter(event => event.priority === 5) - .size(); + .filter(event => event.name === 'event') + .filter(event => event.priority === 5) + .size(); priorityFiveEvents.should.equal(2); }); diff --git a/test/scan.spec.js b/test/scan.spec.js index 267ef2f1f..c29005cd6 100644 --- a/test/scan.spec.js +++ b/test/scan.spec.js @@ -16,7 +16,7 @@ chai.should(); describe('scan', () => { describe('#scanUrls', () => { beforeEach(() => { - let counter = {}; + const counter = {}; sinon.stub(axios, 'create').callsFake(() => ({ get: url => { counter[url] = counter[url] + 1 || 0; @@ -28,6 +28,7 @@ describe('scan', () => { } else { code = isFinite(_.last(url.split('.'))) ? _.last(url.split('.')) : 200; } + // eslint-disable-next-line prefer-promise-reject-errors return (_.startsWith(code, 2)) ? Promise.resolve() : Promise.reject({response: {status: _.toInteger(code)}}); }, })); @@ -37,11 +38,11 @@ describe('scan', () => { const scan = require('./../lib/scan')(); const urls = ['http://www.thecultofscottbakula.com', 'http://anumalak.com:']; return scan(urls) - .each(result => { - result.status.should.be.true; - result.color.should.equal('green'); - }) - .should.be.fulfilled; + .each(result => { + result.status.should.be.true; + result.color.should.equal('green'); + }) + .should.be.fulfilled; }); // @todo: should these return good? @@ -49,44 +50,44 @@ describe('scan', () => { const scan = require('./../lib/scan')(); const urls = ['http://thecultofscottbakula.com:503', 'http://anumalak.com:503']; return scan(urls) - .each(result => { - result.status.should.be.true; - result.color.should.equal('green'); - }) - .should.be.fulfilled; + .each(result => { + result.status.should.be.true; + result.color.should.equal('green'); + }) + .should.be.fulfilled; }); it('should return "ok" status objects on wildcard entries', () => { const scan = require('./../lib/scan')(); const urls = ['http://*.thecultofscottbakula.com', 'http://*.anumalak.com:']; return scan(urls) - .each(result => { - result.status.should.be.true; - result.color.should.equal('yellow'); - }) - .should.be.fulfilled; + .each(result => { + result.status.should.be.true; + result.color.should.equal('yellow'); + }) + .should.be.fulfilled; }); it('should return "bad" status objects on wait codes that don\'t change after max retries', () => { const scan = require('./../lib/scan')(); const urls = ['http://thecultofscottbakula.com.666', 'http://anumalak.com.404']; return scan(urls, {max: 1, waitCodes: [666, 404]}) - .each(result => { - result.status.should.be.false; - result.color.should.equal('red'); - }) - .should.be.fulfilled; + .each(result => { + result.status.should.be.false; + result.color.should.equal('red'); + }) + .should.be.fulfilled; }); it('should return "good" status objects on wait codes that become non-wait codes after retry', () => { const scan = require('./../lib/scan')(); const urls = ['http://thecultofscottbakula.com.666:2']; return scan(urls, {max: 2, waitCodes: [666]}) - .each(result => { - result.status.should.be.true; - result.color.should.equal('green'); - }) - .should.be.fulfilled; + .each(result => { + result.status.should.be.true; + result.color.should.equal('green'); + }) + .should.be.fulfilled; }); afterEach(() => { diff --git a/test/shell.spec.js b/test/shell.spec.js index 68d6f9a28..ea9798b13 100644 --- a/test/shell.spec.js +++ b/test/shell.spec.js @@ -30,8 +30,8 @@ const fakeSpawn = (cmd, args, opts) => { return { connected: !_.includes(command, 'van the man'), stdout: {on: (type, fn) => { - fn(Buffer.from('SPAWN: ' + command, 'utf8')); - }, + fn(Buffer.from('SPAWN: ' + command, 'utf8')); + }, }, on: (type, fn) => { if (type === 'error') fn(Buffer.from('ERROR', 'utf8')); @@ -59,11 +59,11 @@ describe('shell', () => { const shell = new Shell(); sinon.stub(_shell, 'exec').callsFake(fakeExec); return shell.sh(['slip', 'sliding', 'away']) - .then(result => { - result.should.equal('EXEC: slip sliding away'); - }) - .then(_shell.exec.restore()) - .should.be.fulfilled; + .then(result => { + result.should.equal('EXEC: slip sliding away'); + }) + .then(_shell.exec.restore()) + .should.be.fulfilled; }); it('should use child_process.spawn when mode is not exec or detached is true', () => { @@ -71,10 +71,10 @@ describe('shell', () => { sinon.stub(child, 'spawn').callsFake(fakeSpawn); _.forEach([{mode: 'collect'}, {detached: true}], opts => { return shell.sh(['tupelo', 'honey', 'baby'], opts) - .then(result => { - result.should.equal('SPAWN: tupelo honey baby'); - }) - .should.be.fulfilled; + .then(result => { + result.should.equal('SPAWN: tupelo honey baby'); + }) + .should.be.fulfilled; }); child.spawn.restore(); }); @@ -83,14 +83,14 @@ describe('shell', () => { const shell = new Shell(); sinon.stub(child, 'spawn').callsFake(fakeSpawn); return shell.sh(['set', 'us', 'up', 'the', 'bomb'], {mode: 'attach'}).should.be.rejected - .then(child.spawn.restore()); + .then(child.spawn.restore()); }); it('should resolve immediately when detached is true and run.connected is false', () => { const shell = new Shell(); sinon.stub(child, 'spawn').callsFake(fakeSpawn); return shell.sh(['van', 'the', 'man'], {detached: true}).should.be.fulfilled - .then(child.spawn.restore()); + .then(child.spawn.restore()); }); it('should inherit stdio', () => { @@ -106,10 +106,10 @@ describe('shell', () => { }; }); return shell.sh(['van', 'the', 'man'], {mode: 'attach'}).should.be.fulfilled - .then(child.spawn.restore()) - .then(() => { - delete process.lando; - }); + .then(child.spawn.restore()) + .then(() => { + delete process.lando; + }); }); }); diff --git a/test/util.spec.js b/test/util.spec.js index c22854be0..eef12925d 100644 --- a/test/util.spec.js +++ b/test/util.spec.js @@ -21,7 +21,7 @@ const resetPlatform = function() { const util = require('./../scripts/util'); describe('scripts', () => { - describe('#cliTargetOs', () => { + describe('#cliTargetOs', () => { it('should return macos on darwin', () => { setPlatform('darwin'); util.cliTargetOs().should.equal('macos'); diff --git a/yarn.lock b/yarn.lock index babe8f21f..b4fdd4807 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23,26 +23,39 @@ "@babel/highlight" "^7.23.4" chalk "^2.4.2" +"@babel/code-frame@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.6.tgz#ab88da19344445c3d8889af2216606d3329f3ef2" + integrity sha512-ZJhac6FkEd1yhG2AHOmfcXG4ceoLltoCVJjN5XsWN9BifBQr+cHJbWi0h68HZuSORq+3WtJ2z0hwF2NG1b5kcA== + dependencies: + "@babel/highlight" "^7.24.6" + picocolors "^1.0.0" + "@babel/compat-data@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.23.5.tgz#ffb878728bb6bdcb6f4510aa51b1be9afb8cfd98" integrity sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw== -"@babel/core@^7.17.5": - version "7.23.9" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.23.9.tgz#b028820718000f267870822fec434820e9b1e4d1" - integrity sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw== +"@babel/compat-data@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.24.6.tgz#b3600217688cabb26e25f8e467019e66d71b7ae2" + integrity sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ== + +"@babel/core@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.24.6.tgz#8650e0e4b03589ebe886c4e4a60398db0a7ec787" + integrity sha512-qAHSfAdVyFmIvl0VHELib8xar7ONuSHrE2hLnsaWkYNTI68dmi1x8GYDhJjMI/e7XWal9QBlZkwbOnkcw7Z8gQ== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.23.5" - "@babel/generator" "^7.23.6" - "@babel/helper-compilation-targets" "^7.23.6" - "@babel/helper-module-transforms" "^7.23.3" - "@babel/helpers" "^7.23.9" - "@babel/parser" "^7.23.9" - "@babel/template" "^7.23.9" - "@babel/traverse" "^7.23.9" - "@babel/types" "^7.23.9" + "@babel/code-frame" "^7.24.6" + "@babel/generator" "^7.24.6" + "@babel/helper-compilation-targets" "^7.24.6" + "@babel/helper-module-transforms" "^7.24.6" + "@babel/helpers" "^7.24.6" + "@babel/parser" "^7.24.6" + "@babel/template" "^7.24.6" + "@babel/traverse" "^7.24.6" + "@babel/types" "^7.24.6" convert-source-map "^2.0.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -70,10 +83,10 @@ json5 "^2.2.3" semver "^6.3.1" -"@babel/eslint-parser@^7.16.0": - version "7.23.10" - resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.23.10.tgz#2d4164842d6db798873b40e0c4238827084667a2" - integrity sha512-3wSYDPZVnhseRnxRJH6ZVTNknBz76AEnyC+AYYhasjP3Yy23qz0ERR7Fcd2SHmYuSFJ2kY9gaaDd3vyqU09eSw== +"@babel/eslint-parser@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.24.6.tgz#7f0ecc0f29307b8696e83ff6a9d8b4f3e0421ad2" + integrity sha512-Q1BfQX42zXHx732PLW0w4+Y3wJjoZKEMaatFUEAmQ7Z+jCXxinzeqX9bvv2Q8xNPes/H6F0I23oGkcgjaItmLw== dependencies: "@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1" eslint-visitor-keys "^2.1.0" @@ -98,7 +111,17 @@ "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-compilation-targets@^7.22.15", "@babel/helper-compilation-targets@^7.23.6": +"@babel/generator@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.24.6.tgz#dfac82a228582a9d30c959fe50ad28951d4737a7" + integrity sha512-S7m4eNa6YAPJRHmKsLHIDJhNAGNKoWNiWefz1MBbpnt8g9lvMDl1hir4P9bo/57bQEmuwEhnRU/AMWsD0G/Fbg== + dependencies: + "@babel/types" "^7.24.6" + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + jsesc "^2.5.1" + +"@babel/helper-compilation-targets@^7.22.15": version "7.23.6" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz#4d79069b16cbcf1461289eccfbbd81501ae39991" integrity sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ== @@ -109,11 +132,27 @@ lru-cache "^5.1.1" semver "^6.3.1" +"@babel/helper-compilation-targets@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.6.tgz#4a51d681f7680043d38e212715e2a7b1ad29cb51" + integrity sha512-VZQ57UsDGlX/5fFA7GkVPplZhHsVc+vuErWgdOiysI9Ksnw0Pbbd6pnPiR/mmJyKHgyIW0c7KT32gmhiF+cirg== + dependencies: + "@babel/compat-data" "^7.24.6" + "@babel/helper-validator-option" "^7.24.6" + browserslist "^4.22.2" + lru-cache "^5.1.1" + semver "^6.3.1" + "@babel/helper-environment-visitor@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz#96159db61d34a29dba454c959f5ae4a649ba9167" integrity sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA== +"@babel/helper-environment-visitor@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.6.tgz#ac7ad5517821641550f6698dd5468f8cef78620d" + integrity sha512-Y50Cg3k0LKLMjxdPjIl40SdJgMB85iXn27Vk/qbHZCFx/o5XO3PSnpi675h1KEmmDb6OFArfd5SCQEQ5Q4H88g== + "@babel/helper-function-name@^7.23.0": version "7.23.0" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz#1f9a3cdbd5b2698a670c30d2735f9af95ed52759" @@ -122,6 +161,14 @@ "@babel/template" "^7.22.15" "@babel/types" "^7.23.0" +"@babel/helper-function-name@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.24.6.tgz#cebdd063386fdb95d511d84b117e51fc68fec0c8" + integrity sha512-xpeLqeeRkbxhnYimfr2PC+iA0Q7ljX/d1eZ9/inYbmfG2jpl8Lu3DyXvpOAnrS5kxkfOWJjioIMQsaMBXFI05w== + dependencies: + "@babel/template" "^7.24.6" + "@babel/types" "^7.24.6" + "@babel/helper-hoist-variables@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz#c01a007dac05c085914e8fb652b339db50d823bb" @@ -129,6 +176,13 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-hoist-variables@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.6.tgz#8a7ece8c26756826b6ffcdd0e3cf65de275af7f9" + integrity sha512-SF/EMrC3OD7dSta1bLJIlrsVxwtd0UpjRJqLno6125epQMJ/kyFmpTT4pbvPbdQHzCHg+biQ7Syo8lnDtbR+uA== + dependencies: + "@babel/types" "^7.24.6" + "@babel/helper-module-imports@^7.22.15": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz#16146307acdc40cc00c3b2c647713076464bdbf0" @@ -136,7 +190,14 @@ dependencies: "@babel/types" "^7.22.15" -"@babel/helper-module-transforms@^7.23.0", "@babel/helper-module-transforms@^7.23.3": +"@babel/helper-module-imports@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.24.6.tgz#65e54ffceed6a268dc4ce11f0433b82cfff57852" + integrity sha512-a26dmxFJBF62rRO9mmpgrfTLsAuyHk4e1hKTUkD/fcMfynt8gvEKwQPQDVxWhca8dHoDck+55DFt42zV0QMw5g== + dependencies: + "@babel/types" "^7.24.6" + +"@babel/helper-module-transforms@^7.23.0": version "7.23.3" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz#d7d12c3c5d30af5b3c0fcab2a6d5217773e2d0f1" integrity sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ== @@ -147,6 +208,17 @@ "@babel/helper-split-export-declaration" "^7.22.6" "@babel/helper-validator-identifier" "^7.22.20" +"@babel/helper-module-transforms@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.24.6.tgz#22346ed9df44ce84dee850d7433c5b73fab1fe4e" + integrity sha512-Y/YMPm83mV2HJTbX1Qh2sjgjqcacvOlhbzdCCsSlblOKjSYmQqEbO6rUniWQyRo9ncyfjT8hnUjlG06RXDEmcA== + dependencies: + "@babel/helper-environment-visitor" "^7.24.6" + "@babel/helper-module-imports" "^7.24.6" + "@babel/helper-simple-access" "^7.24.6" + "@babel/helper-split-export-declaration" "^7.24.6" + "@babel/helper-validator-identifier" "^7.24.6" + "@babel/helper-simple-access@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz#4938357dc7d782b80ed6dbb03a0fba3d22b1d5de" @@ -154,6 +226,13 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-simple-access@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.24.6.tgz#1d6e04d468bba4fc963b4906f6dac6286cfedff1" + integrity sha512-nZzcMMD4ZhmB35MOOzQuiGO5RzL6tJbsT37Zx8M5L/i9KSrukGXWTjLe1knIbb/RmxoJE9GON9soq0c0VEMM5g== + dependencies: + "@babel/types" "^7.24.6" + "@babel/helper-split-export-declaration@^7.22.6": version "7.22.6" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz#322c61b7310c0997fe4c323955667f18fcefb91c" @@ -161,6 +240,13 @@ dependencies: "@babel/types" "^7.22.5" +"@babel/helper-split-export-declaration@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.6.tgz#e830068f7ba8861c53b7421c284da30ae656d7a3" + integrity sha512-CvLSkwXGWnYlF9+J3iZUvwgAxKiYzK3BWuo+mLzD/MDGOZDj7Gq8+hqaOkMxmJwmlv0iu86uH5fdADd9Hxkymw== + dependencies: + "@babel/types" "^7.24.6" + "@babel/helper-string-parser@^7.18.10": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz#533f36457a25814cf1df6488523ad547d784a99f" @@ -171,17 +257,32 @@ resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz#9478c707febcbbe1ddb38a3d91a2e054ae622d83" integrity sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ== +"@babel/helper-string-parser@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.24.6.tgz#28583c28b15f2a3339cfafafeaad42f9a0e828df" + integrity sha512-WdJjwMEkmBicq5T9fm/cHND3+UlFa2Yj8ALLgmoSQAJZysYbBjw+azChSGPN4DSPLXOcooGRvDwZWMcF/mLO2Q== + "@babel/helper-validator-identifier@^7.18.6", "@babel/helper-validator-identifier@^7.22.20": version "7.22.20" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz#c4ae002c61d2879e724581d96665583dbc1dc0e0" integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== +"@babel/helper-validator-identifier@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz#08bb6612b11bdec78f3feed3db196da682454a5e" + integrity sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw== + "@babel/helper-validator-option@^7.23.5": version "7.23.5" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz#907a3fbd4523426285365d1206c423c4c5520307" integrity sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw== -"@babel/helpers@^7.23.2", "@babel/helpers@^7.23.9": +"@babel/helper-validator-option@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.24.6.tgz#59d8e81c40b7d9109ab7e74457393442177f460a" + integrity sha512-Jktc8KkF3zIkePb48QO+IapbXlSapOW9S+ogZZkcO6bABgYAxtZcjZ/O005111YLf+j4M84uEgwYoidDkXbCkQ== + +"@babel/helpers@^7.23.2": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.23.9.tgz#c3e20bbe7f7a7e10cb9b178384b4affdf5995c7d" integrity sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ== @@ -190,6 +291,14 @@ "@babel/traverse" "^7.23.9" "@babel/types" "^7.23.9" +"@babel/helpers@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.24.6.tgz#cd124245299e494bd4e00edda0e4ea3545c2c176" + integrity sha512-V2PI+NqnyFu1i0GyTd/O/cTpxzQCYioSkUIRmgo7gFEHKKCg5w46+r/A6WeUR1+P3TeQ49dspGPNd/E3n9AnnA== + dependencies: + "@babel/template" "^7.24.6" + "@babel/types" "^7.24.6" + "@babel/highlight@^7.23.4": version "7.23.4" resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.23.4.tgz#edaadf4d8232e1a961432db785091207ead0621b" @@ -199,6 +308,16 @@ chalk "^2.4.2" js-tokens "^4.0.0" +"@babel/highlight@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.24.6.tgz#6d610c1ebd2c6e061cade0153bf69b0590b7b3df" + integrity sha512-2YnuOp4HAk2BsBrJJvYCbItHx0zWscI1C3zgWkz+wDyD9I7GIVrfnLyrR4Y1VR+7p+chAEcrgRQYZAGIKMV7vQ== + dependencies: + "@babel/helper-validator-identifier" "^7.24.6" + chalk "^2.4.2" + js-tokens "^4.0.0" + picocolors "^1.0.0" + "@babel/parser@7.18.4": version "7.18.4" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.4.tgz#6774231779dd700e0af29f6ad8d479582d7ce5ef" @@ -209,6 +328,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.23.9.tgz#7b903b6149b0f8fa7ad564af646c4c38a77fc44b" integrity sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA== +"@babel/parser@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.24.6.tgz#5e030f440c3c6c78d195528c3b688b101a365328" + integrity sha512-eNZXdfU35nJC2h24RznROuOpO94h6x8sg9ju0tT9biNtLZ2vuP8SduLqqV+/8+cebSLV9SJEAN5Z3zQbJG/M+Q== + "@babel/template@^7.22.15", "@babel/template@^7.23.9": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.23.9.tgz#f881d0487cba2828d3259dcb9ef5005a9731011a" @@ -218,6 +342,15 @@ "@babel/parser" "^7.23.9" "@babel/types" "^7.23.9" +"@babel/template@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.24.6.tgz#048c347b2787a6072b24c723664c8d02b67a44f9" + integrity sha512-3vgazJlLwNXi9jhrR1ef8qiB65L1RK90+lEQwv4OxveHnqC3BfmnHdgySwRLzf6akhlOYenT+b7AfWq+a//AHw== + dependencies: + "@babel/code-frame" "^7.24.6" + "@babel/parser" "^7.24.6" + "@babel/types" "^7.24.6" + "@babel/traverse@^7.23.2", "@babel/traverse@^7.23.9": version "7.23.9" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.23.9.tgz#2f9d6aead6b564669394c5ce0f9302bb65b9d950" @@ -234,6 +367,22 @@ debug "^4.3.1" globals "^11.1.0" +"@babel/traverse@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.24.6.tgz#0941ec50cdeaeacad0911eb67ae227a4f8424edc" + integrity sha512-OsNjaJwT9Zn8ozxcfoBc+RaHdj3gFmCmYoQLUII1o6ZrUwku0BMg80FoOTPx+Gi6XhcQxAYE4xyjPTo4SxEQqw== + dependencies: + "@babel/code-frame" "^7.24.6" + "@babel/generator" "^7.24.6" + "@babel/helper-environment-visitor" "^7.24.6" + "@babel/helper-function-name" "^7.24.6" + "@babel/helper-hoist-variables" "^7.24.6" + "@babel/helper-split-export-declaration" "^7.24.6" + "@babel/parser" "^7.24.6" + "@babel/types" "^7.24.6" + debug "^4.3.1" + globals "^11.1.0" + "@babel/types@7.19.0": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.19.0.tgz#75f21d73d73dc0351f3368d28db73465f4814600" @@ -261,6 +410,15 @@ "@babel/helper-validator-identifier" "^7.22.20" to-fast-properties "^2.0.0" +"@babel/types@^7.24.6": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.24.6.tgz#ba4e1f59870c10dc2fa95a274ac4feec23b21912" + integrity sha512-WaMsgi6Q8zMgMth93GvWPXkhAIEobfsIkLTacoVZoK1J0CevIPGYY2Vo5YvJGqyHqXM6P4ppOYGsIRU8MM9pFQ== + dependencies: + "@babel/helper-string-parser" "^7.24.6" + "@babel/helper-validator-identifier" "^7.24.6" + to-fast-properties "^2.0.0" + "@balena/dockerignore@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@balena/dockerignore/-/dockerignore-1.0.2.tgz#9ffe4726915251e8eb69f44ef3547e0da2c03e0d" @@ -347,6 +505,15 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" @@ -357,6 +524,11 @@ resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + "@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" @@ -370,6 +542,14 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + "@lando/compose@1.0.0": version "1.0.0" resolved "https://registry.yarnpkg.com/@lando/compose/-/compose-1.0.0.tgz#1e24bdfe39fb536f43e86aa179c4671d62ffb771" @@ -758,10 +938,10 @@ caniuse-lite@^1.0.30001587: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001589.tgz#7ad6dba4c9bf6561aec8291976402339dc157dfb" integrity sha512-vNQWS6kI+q6sBlHbh71IIeC+sRwK2N3EDySc/updIGhIee2x5z00J4c1242/5/d6EpEMdOnk/m+6tuk4/tcsqg== -chai-as-promised@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.1.tgz#08645d825deb8696ee61725dbf590c012eb00ca0" - integrity sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA== +chai-as-promised@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/chai-as-promised/-/chai-as-promised-7.1.2.tgz#70cd73b74afd519754161386421fb71832c6d041" + integrity sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw== dependencies: check-error "^1.0.2" @@ -1175,10 +1355,10 @@ escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== -eslint-config-google@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/eslint-config-google/-/eslint-config-google-0.9.1.tgz#83353c3dba05f72bb123169a4094f4ff120391eb" - integrity sha512-5A83D+lH0PA81QMESKbLJd/a3ic8tPZtwUmqNrxMRo54nfFaUvtt89q/+icQ+fd66c2xQHn0KyFkzJDoAUfpZA== +eslint-config-google@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/eslint-config-google/-/eslint-config-google-0.14.0.tgz#4f5f8759ba6e11b424294a219dbfa18c508bcc1a" + integrity sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw== eslint-scope@5.1.1: version "5.1.1"