Skip to content

Commit

Permalink
feat(bootstrap): Add bootstrap event handling and fix lando is not ye…
Browse files Browse the repository at this point in the history
…t setup errors
  • Loading branch information
florianPat committed Feb 19, 2025
1 parent a21b7d0 commit 8fb8545
Show file tree
Hide file tree
Showing 8 changed files with 64 additions and 31 deletions.
3 changes: 3 additions & 0 deletions app.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ module.exports = async (app, lando) => {
// Add tooling if applicable
app.events.on('post-init', async () => await require('./hooks/app-add-tooling')(app, lando));

// Add _init tooling for bootstrap reference
app.events.on('pre-bootstrap', async () => await require('./hooks/app-add-init-tooling')(app, lando));

// Collect info so we can inject LANDO_INFO
// @NOTE: this is not currently the full lando info because a lot of it requires the app to be on
app.events.on('post-init', 10, async () => await require('./hooks/app-set-lando-info')(app, lando));
Expand Down
21 changes: 21 additions & 0 deletions hooks/app-add-init-tooling.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

const _ = require('lodash');

module.exports = async (app, lando) => {
if (!_.isEmpty(_.get(app, 'config.tooling', {}))) {
app.log.verbose('additional tooling detected');

// Add the _init tasks for the bootstrap event!
// TODO(flo): They are duplicated through "app-add-tooling" but I do not care for now!
_.forEach(require('../utils/get-tooling-tasks')(app.config.tooling, app), task => {
if (task.service !== '_init') {
return;
}

app.log.debug('adding app cli task %s', task.name);
const injectable = _.has(app, 'engine') ? app : lando;
app.tasks.push(require('../utils/build-tooling-task')(task, injectable));
});
}
};
54 changes: 30 additions & 24 deletions lib/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ const fs = require('node:fs');
/*
* Helper to init and then report
*/
const initAndReport = (app, method = 'start') => {
return app.init().then(() => {
const initAndReport = (app, method, shouldBootstrap = false) => {
return app.init({shouldBootstrap}).then(() => {
app.metrics.report(method, utils.metricsParse(app));
return Promise.resolve(true);
});
Expand Down Expand Up @@ -257,6 +257,8 @@ module.exports = class App {
.then(() => this.log.info('destroyed app.'));
};

static isBootstrapCommand = undefined;

/**
* Initializes the app
*
Expand All @@ -270,21 +272,37 @@ module.exports = class App {
* @fires ready
* @return {Promise} A Promise.
*/
init({noEngine = false} = {}) {
init({noEngine = false, shouldBootstrap = false} = {}) {
// We should only need to initialize once, if we have just go right to app ready
if (this.initialized) return this.events.emit('ready', this);
if (undefined === App.isBootstrapCommand) {
App.isBootstrapCommand = !fs.existsSync(this._dir);
}
const bootstrapping = App.isBootstrapCommand && shouldBootstrap && !noEngine;
if (bootstrapping) {
console.log(require('yargonaut').chalk().cyan('Looks like this is the first time to start the app. Lets bootstrap it...'));
}

return loadPlugins(this, this._lando)
/**
* Event that only gets triggered if the app never started before (or was destroyed)
*
* @since 3.23.25
* @alias app.events:pre-bootstrap
* @event pre-bootstrap
* @property {App} app The app instance.
*/
.then(() => bootstrapping ? this.events.emit('pre-bootstrap', this) : undefined)
// Get compose data if we have any, otherwise set to []
return require('../utils/load-compose-files')(
.then(() => noEngine === true ? [] : require('../utils/load-compose-files')(
_.get(this, 'config.compose', []),
this.root,
this._dir,
(composeFiles, outputFilePath) =>
this.engine.getComposeConfig({compose: composeFiles, project: this.project, outputFilePath}),
)
))
.then(composeFileData => {
if (undefined !== composeFileData) {
this.composeData = [new this.ComposeService('compose', {}, composeFileData)];
}
this.composeData = [new this.ComposeService('compose', {}, ...composeFileData)];
// Validate and set env files
this.envFiles = require('../utils/normalize-files')(_.get(this, 'config.env_file', []), this.root);
// Log some things
Expand All @@ -303,8 +321,6 @@ module.exports = class App {
* @event pre_init
* @property {App} app The app instance.
*/
.then(() => loadPlugins(this, this._lando))

.then(() => this.events.emit('pre-init', this))
// Actually assemble this thing so its ready for that engine
.then(() => {
Expand Down Expand Up @@ -501,25 +517,15 @@ module.exports = class App {
* @alias app.start
* @fires pre_start
* @fires post_start
* @fires post_bootstrap
* @return {Promise} A Promise.
*
*/
start() {
// Log
this.log.info('starting app...');
const shouldBootstrap = fs.existsSync(this._dir);

return initAndReport(this)

/**
* Event that only gets triggered if the app never started before (or was destroyed)
*
* @since 3.22.3
* @alias app.events:pre-bootstrap
* @event pre-bootstrap
* @property {App} app The app instance.
*/
.then(() => shouldBootstrap ? this.events.emit('pre-bootstrap', this) : undefined)
return initAndReport(this, 'start', true)

/**
* Event that runs before an app starts up.
Expand Down Expand Up @@ -551,12 +557,12 @@ module.exports = class App {
/**
* Event that only gets triggered if the app never started before (or was destroyed)
*
* @since 3.22.3
* @since 3.23.25
* @alias app.events:post-bootstrap
* @event post-bootstrap
* @property {App} app The app instance.
*/
.then(() => shouldBootstrap ? this.events.emit('post-bootstrap', this) : undefined)
.then(() => App.isBootstrapCommand ? this.events.emit('post-bootstrap', this) : undefined)

.then(() => this.log.info('started app.'));
};
Expand Down
2 changes: 1 addition & 1 deletion lib/engine.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ module.exports = class Engine {
* return lando.engine.exists(compose);
*/
exists(data) {
return this.engineCmd('exists', {...data, separator: this.separator});
return this.engineCmd('exists', _.merge({}, {separator: this.separator}, data));
};

/*
Expand Down
2 changes: 1 addition & 1 deletion lib/formatters.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ exports.handleInteractive = (inquiry, argv, command, lando) => lando.Promise.try
// NOTE: We need to clone deep here otherwise any apps with interactive options get 2x all their events
// NOTE: Not exactly clear on why app here gets conflated with the app returned from lando.getApp
const app = _.cloneDeep(lando.getApp(argv._app.root));
return app.init().then(() => {
return app.init({noEngine: true}).then(() => {
inquiry = exports.getInteractive(_.find(app.tasks.concat(lando.tasks), {command: command}).options, argv);
return inquirer.prompt(_.sortBy(inquiry, 'weight'));
});
Expand Down
3 changes: 2 additions & 1 deletion utils/build-tooling-task.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ module.exports = (config, injected) => {
const run = answers => {
let initToolingRunner = null;

return injected.Promise.try(() => (_.isEmpty(app.compose)) ? app.init() : true)
return injected.Promise.try(() => (_.isEmpty(app.compose) && '_init' !== service) ? app.init() : true)
// Kick off the pre event wrappers
.then(() => app.events.emit(`pre-${eventName}`, config, answers))
// Get an interable of our commandz
Expand Down Expand Up @@ -74,5 +74,6 @@ module.exports = (config, injected) => {
describe,
run,
options,
service,
};
};
6 changes: 4 additions & 2 deletions utils/get-tasks.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const _ = require('lodash');
const fs = require('fs');
const path = require('path');
const App = require('../lib/app');

/*
* Paths to /
Expand Down Expand Up @@ -40,9 +41,10 @@ const loadCacheFile = file => {
*/
const appRunner = command => (argv, lando) => {
const app = lando.getApp(argv._app.root);
const service = _.get(app.config, `tooling.${command}.service`, '');
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(() => app.init({noEngine: '_init' === service}).then(() => _.find(app.tasks, {command}).run(argv)));
};

/*
Expand Down Expand Up @@ -128,7 +130,7 @@ module.exports = (config = {}, argv = {}, tasks = []) => {

// If the tooling command is being called lets assess whether we can get away with engine bootstrap level
const ids = _(config.tooling).map(task => task.id).filter(_.identity).value();
const level = (_.includes(ids, argv._[0])) ? getBsLevel(config, argv._[0]) : 'app';
const level = !App.isBootstrapCommand && (_.includes(ids, argv._[0])) ? getBsLevel(config, argv._[0]) : 'app';

// Load all the tasks, remember we need to remove "disabled" tasks (eg non-object tasks) here
_.forEach(_.get(config, 'tooling', {}), (task, command) => {
Expand Down
4 changes: 2 additions & 2 deletions utils/load-compose-files.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const remove = require('./remove');
module.exports = async (files, dir, landoComposeConfigDir = undefined, outputConfigFunction = undefined) => {
const composeFilePaths = _(require('./normalize-files')(files, dir)).value();
if (_.isEmpty(composeFilePaths)) {
return {};
return [];
}

if (undefined === outputConfigFunction) {
Expand All @@ -29,5 +29,5 @@ module.exports = async (files, dir, landoComposeConfigDir = undefined, outputCon
fs.unlinkSync(outputFile);
remove(path.dirname(outputFile));

return result;
return [result];
};

0 comments on commit 8fb8545

Please sign in to comment.