mirror of
https://github.com/serverless/serverless.git
synced 2026-01-18 14:58:43 +00:00
Fix `print` The print command is highly linked to the `Variables` and `Service` codebases, keep those in sync and leave reminders about the link. Made these explicit and separately implemented to avoid complexity. Additionally, the print command re-populates an object with the *very similar* content as the previously pre-populated service (just not augmented as just mentioned). This can lead to cross contamination between the two. As such, all caches must be cleared per unique invocation of service/object/property population. Add tests for some expected but previously unverified behaviors. Clean pre-population The previous implementation worked okay but was unnecessary and would have been a maintenance problem. Instead, just knock out the population of variables depending on those config dependent services and use the standard means of resolution. Fix cyclic bug (resulting from running print against a self-referencing serverless.yml) The caching of values could lead to a cyclic object remaining in the caches for variable population. This causes crashes and pain. Solved by the cache cleaning logic.
104 lines
3.9 KiB
JavaScript
104 lines
3.9 KiB
JavaScript
'use strict';
|
|
|
|
const _ = require('lodash');
|
|
const BbPromise = require('bluebird');
|
|
const getServerlessConfigFile = require('../../utils/getServerlessConfigFile');
|
|
const jc = require('json-cycle');
|
|
const YAML = require('js-yaml');
|
|
|
|
class Print {
|
|
constructor(serverless) {
|
|
this.serverless = serverless;
|
|
this.cache = {};
|
|
|
|
this.commands = {
|
|
print: {
|
|
usage: 'Print your compiled and resolved config file',
|
|
lifecycleEvents: [
|
|
'print',
|
|
],
|
|
},
|
|
};
|
|
this.hooks = {
|
|
'print:print': () => BbPromise.bind(this)
|
|
.then(this.print),
|
|
};
|
|
}
|
|
|
|
adorn(svc) {
|
|
const service = svc;
|
|
// service object treatment
|
|
this.cache.serviceService = service.service;
|
|
if (_.isObject(this.cache.serviceService)) {
|
|
service.service = service.service.name;
|
|
service.serviceObject = this.cache.serviceService;
|
|
}
|
|
// provider treatment and defaults
|
|
this.cache.serviceProvider = service.provider;
|
|
if (_.isString(this.cache.serviceProvider)) {
|
|
service.provider = { name: this.cache.serviceProvider };
|
|
}
|
|
service.provider = _.merge({
|
|
stage: 'dev',
|
|
region: 'us-east-1',
|
|
variableSyntax: '\\${([ ~:a-zA-Z0-9._\'",\\-\\/\\(\\)]+?)}',
|
|
}, service.provider);
|
|
}
|
|
strip(svc) {
|
|
const service = svc;
|
|
if (_.isObject(this.cache.serviceService)) {
|
|
service.service = this.cache.serviceService;
|
|
delete service.serviceObject;
|
|
}
|
|
if (_.isString(this.cache.serviceProvider)) {
|
|
service.provider = this.cache.serviceProvider;
|
|
} else { // is object
|
|
if (!this.cache.serviceProvider.stage) {
|
|
delete service.provider.stage;
|
|
}
|
|
if (!this.cache.serviceProvider.region) {
|
|
delete service.provider.region;
|
|
}
|
|
if (this.cache.serviceProvider.variableSyntax) {
|
|
service.provider.variableSyntax = this.cache.serviceProvider.variableSyntax;
|
|
}
|
|
}
|
|
}
|
|
print() {
|
|
// #####################################################################
|
|
// ## KEEP SYNCHRONIZED WITH EQUIVALENT IN ~/lib/classes/Variables.js ##
|
|
// ## there, see `populateService` ##
|
|
// ## here, see below ##
|
|
// #####################################################################
|
|
// ###################################################################
|
|
// ## KEEP SYNCHRONIZED WITH EQUIVALENT IN ~/lib/classes/Service.js ##
|
|
// ## there, see `constructor` and `loadServiceFileParam` ##
|
|
// ## here, see `strip` and `adorn` ##
|
|
// ###################################################################
|
|
// The *already loaded* Service (i.e. serverless.yml) is adorned with extras for use by the
|
|
// framework and throughout its codebase. We could try using the populated service but that
|
|
// would require playing whack-a-mole on issues as changes are made to the service anywhere in
|
|
// the codebase. Avoiding that, this method must read the serverless.yml file itself, adorn it
|
|
// as the Service class would and then populate it, reversing the adornments thereafter in
|
|
// preparation for printing the service for the user.
|
|
return getServerlessConfigFile(process.cwd())
|
|
.then((svc) => {
|
|
const service = svc;
|
|
this.adorn(service);
|
|
// Need to delete variableSyntax to avoid self-matching errors
|
|
this.serverless.variables.loadVariableSyntax();
|
|
delete service.provider.variableSyntax; // cached by adorn, restored by strip
|
|
this.serverless.variables.service = service;
|
|
return this.serverless.variables.populateObject(service)
|
|
.then((populated) => {
|
|
this.strip(populated);
|
|
const out = JSON.parse(jc.stringify(populated));
|
|
this.serverless.cli.consoleLog(YAML.dump(out, { noRefs: true }));
|
|
});
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
module.exports = Print;
|