diff --git a/lib/Serverless.js b/lib/Serverless.js index 0a540ba80..0cb5aed75 100644 --- a/lib/Serverless.js +++ b/lib/Serverless.js @@ -253,9 +253,7 @@ class Serverless { } // Load Plugin - if (!PluginClass) { - console.log('WARNING: This plugin was requested by this project but could not be found: ' + pluginMetadatum); - } else { + if (PluginClass) { SUtils.sDebug(PluginClass.getName() + ' plugin loaded'); this.addPlugin(new PluginClass(_this)); } diff --git a/lib/ServerlessProject.js b/lib/ServerlessProject.js index a053c5ef5..cc9dc189c 100644 --- a/lib/ServerlessProject.js +++ b/lib/ServerlessProject.js @@ -244,30 +244,29 @@ class ServerlessProject { save(options) { - let _this = this; + let _this = this, + files = []; + + // Validate: Check project path is set + if (!_this._S.config.projectPath) throw new SError('Project could not be saved because no project path has been set on Serverless instance'); return new BbPromise.try(function () { - // Validate: Check project path is set - if (!_this._S.config.projectPath) throw new SError('Project could not be saved because no project path has been set on Serverless instance'); + // If project folder does not exist, create it + if (!SUtils.dirExistsSync(path.join(_this._S.config.projectPath))) { + fs.mkdirSync(path.join(_this._S.config.projectPath)); + } - // Create if does not exist - if (!SUtils.fileExistsSync(path.join(_this._S.config.projectPath, 's-project.json'))) { - return _this._create(); + // Save all nested components + if (options && options.deep) { + return BbPromise.try(function () { + return Object.keys(_this.components); + }) + .each(function (c) { + return _this.components[c].save(); + }) } }) - .then(function () { - - // Save all nested components - if (options && options.deep) { - return BbPromise.try(function () { - return Object.keys(_this.components); - }) - .each(function (c) { - return _this.components[c].save(); - }) - } - }) .then(function () { let clone = _this.get(); @@ -276,66 +275,17 @@ class ServerlessProject { if (clone.components) delete clone.components; if (clone.templates) delete clone.templates; - // Write file - return SUtils.writeFile(path.join(_this._S.config.projectPath, 's-project.json'), - JSON.stringify(clone, null, 2)); + // Save s-project.json + files.push(SUtils.writeFile(path.join(_this._S.config.projectPath, 's-project.json'), + JSON.stringify(clone, null, 2))); + + // Write files + return BbPromise.all(files); }) .then(function () { return _this; }) } - - /** - * Create (scaffolding) - * - Returns promise - */ - - _create() { - - let _this = this, - adminEnv, - readme; - - return BbPromise.try(function () { - - // Prepare admin.env - adminEnv = 'SERVERLESS_ADMIN_AWS_ACCESS_KEY_ID=' + _this._S.config.awsAdminKeyId + os.EOL - + 'SERVERLESS_ADMIN_AWS_SECRET_ACCESS_KEY=' + _this._S.config.awsAdminSecretKey + os.EOL; - - // Prepare README.md - readme = '#' + _this.name; - - // Prepare Package.json - let packageJson = SUtils.readAndParseJsonSync(path.join(_this._S.config.serverlessPath, 'templates', 'nodejs', 'package.json')); - packageJson.name = _this.name; - packageJson.description = 'A Serverless Project and its Serverless Plugin dependencies.'; - packageJson.private = false; - packageJson.dependencies = {}; - if (packageJson.devDependencies) delete packageJson.devDependencies; - if (packageJson.keywords) delete packageJson.keywords; - - // Create Folders - fs.mkdirSync(path.join(_this._S.config.projectPath)); - fs.mkdirSync(path.join(_this._S.config.projectPath, '_meta')); - fs.mkdirSync(path.join(_this._S.config.projectPath, '_meta', 'variables')); - fs.mkdirSync(path.join(_this._S.config.projectPath, '_meta', 'resources')); - - // Write files - return BbPromise.all([ - SUtils.writeFile(path.join(_this._S.config.projectPath, 'admin.env'), adminEnv), - SUtils.writeFile(path.join(_this._S.config.projectPath, 'README.md'), readme), - SUtils.writeFile(path.join(_this._S.config.projectPath, 'package.json'), JSON.stringify(packageJson, null, 2)), - fs.writeFileAsync(path.join(_this._S.config.projectPath, '.gitignore'), fs.readFileSync(path.join(_this._S.config.serverlessPath, 'templates', 'gitignore'))) - ]); - }).then(function() { - return SUtils.writeFile( - path.join(_this._S.config.projectPath, '.env'), - 'SERVERLESS_STAGE=development' - + '\nSERVERLESS_DATA_MODEL_STAGE=development' - + '\nSERVERLESS_PROJECT_NAME=' + _this.name - ) - }); - } } module.exports = ServerlessProject; \ No newline at end of file diff --git a/lib/actions/ProjectInit.js b/lib/actions/ProjectInit.js index 2224f91e9..280adf023 100644 --- a/lib/actions/ProjectInit.js +++ b/lib/actions/ProjectInit.js @@ -117,9 +117,10 @@ module.exports = function(SPlugin, serverlessPath) { .then(_this._installComponentDeps) .then(function() { + console.log(''); // For neatness SCli.log('Successfully initialized project "' + _this.evt.options.name - +'"'); + + '"'); /** * Return EVT @@ -136,10 +137,21 @@ module.exports = function(SPlugin, serverlessPath) { _prompt() { - let _this = this; + let _this = this, + name; - // Set temp name - let name = _this.evt.options.name || ('serverless' + SUtils.generateShortId(6)).toLowerCase(); + // Check if s-project.json exists + if (_this.S.config.projectPath && SUtils.fileExistsSync(path.join(_this.S.config.projectPath, 's-project.json'))) { + + // Set temp name + let projectJson = SUtils.readAndParseJsonSync(path.join(_this.S.config.projectPath, 's-project.json')); + name = _this.evt.options.name ? (_this.evt.options.name + '-' + SUtils.generateShortId(6)).toLowerCase() : projectJson.name; + + } else { + + // Set temp name + name = _this.evt.options.name ? (_this.evt.options.name + '-' + SUtils.generateShortId(6)).toLowerCase() : ('serverless-' + SUtils.generateShortId(6)).toLowerCase(); + } // Skip if non-interactive if (!_this.S.config.interactive) return BbPromise.resolve(); @@ -170,8 +182,8 @@ module.exports = function(SPlugin, serverlessPath) { } }, domain: { - description: 'Enter a domain for this project (used for the Serverless Project Bucket): '.yellow, - default: name + '.com', + description: 'Enter a universally unique project bucket name: '.yellow, + default: (name + '-' + SUtils.generateShortId(6)).toLowerCase() + '.com', message: 'Domain must only contain lowercase letters, numbers, periods and dashes', required: true, conform: function(bucket) { @@ -332,55 +344,111 @@ module.exports = function(SPlugin, serverlessPath) { return BbPromise.try(function() { - // If project does not exist, create scaffolding - if (!_this.S.config.projectPath) { - // Update Global Serverless Instance - _this.S.updateConfig({ - projectPath: path.resolve(path.join(path.dirname('.'), _this.evt.options.name)) - }); - } + if (!_this.S.config.projectPath) { + _this.S.updateConfig({ + projectPath: path.resolve(path.join(path.dirname('.'), _this.evt.options.name)) + }); + } - // Fill in meta attributes - _this.meta = _this.S.state.getMeta(); - _this.meta.variables.project = _this.evt.options.name; - _this.meta.variables.projectBucket = SUtils.generateProjectBucketName(_this.evt.options.domain, _this.evt.options.region); - _this.meta.variables.domain = _this.evt.options.domain; - _this.meta.variables.notificationEmail = _this.evt.options.notificationEmail; + // Fill in meta attributes + _this.meta = _this.S.state.getMeta(); + _this.meta.variables.project = _this.evt.options.name; + _this.meta.variables.projectBucket = SUtils.generateProjectBucketName(_this.evt.options.domain, _this.evt.options.region); + _this.meta.variables.domain = _this.evt.options.domain; + _this.meta.variables.notificationEmail = _this.evt.options.notificationEmail; - // Fill in project attributes - _this.project = _this.S.state.getProject(); - _this.project.name = _this.evt.options.name; + // Fill in project attributes + _this.project = _this.S.state.getProject(); + _this.project.name = _this.evt.options.name; - // Delete unnecessary package.json properties - if (_this.project.readme) delete _this.project.readme; - if (_this.project.readmeFilename) delete _this.project.readmeFilename; - if (_this.project.gitHead) delete _this.project.gitHead; - if (_this.project._id) delete _this.project._id; - if (_this.project._shasum) delete _this.project._shasum; - if (_this.project._from) delete _this.project._from; - if (_this.project._npmVersion) delete _this.project._npmVersion; - if (_this.project._nodeVersion) delete _this.project._nodeVersion; - if (_this.project._npmUser) delete _this.project._npmUser; - if (_this.project.dist) delete _this.project.dist; - if (_this.project.maintainers) delete _this.project.maintainers; - if (_this.project.directories) delete _this.project.directories; - if (_this.project._resolved) delete _this.project._resolved; - if (_this.project._args) delete _this.project._args; - if (_this.project._inCache) delete _this.project._inCache; - if (_this.project._installable) delete _this.project._installable; - if (_this.project._location) delete _this.project._location; - if (_this.project._phantomChildren) delete _this.project._phantomChildren; - if (_this.project._requested) delete _this.project._requested; - if (_this.project._requiredBy) delete _this.project._requiredBy; - if (_this.project._shrinkwrap || _this.project._shrinkwrap === null) delete _this.project._shrinkwrap; - if (_this.project._spec) delete _this.project._spec; - if (_this.project._where) delete _this.project._where; + // Save State + return _this.S.state.save(); - // Save State - return _this.S.state.save(); + }) + .then(function() { - }); + // Create other scaffolding + let files = []; + + // If admin.env does not exist, save it + if (!SUtils.fileExistsSync(path.join(_this.S.config.projectPath, 'admin.env'))) { + let adminEnv = 'SERVERLESS_ADMIN_AWS_ACCESS_KEY_ID=' + _this.S.config.awsAdminKeyId + os.EOL + + 'SERVERLESS_ADMIN_AWS_SECRET_ACCESS_KEY=' + _this.S.config.awsAdminSecretKey + os.EOL; + files.push(fs.writeFileAsync(path.join(_this.S.config.projectPath, 'admin.env'), adminEnv)); + } + + // If package.json does not exist, save it + let packageJson; + if (!SUtils.fileExistsSync(path.join(_this.S.config.projectPath, 'package.json'))) { + + // Prepare new package.json + packageJson = SUtils.readAndParseJsonSync(path.join(_this.S.config.serverlessPath, 'templates', 'nodejs', 'package.json')); + packageJson.name = _this.project.name; + packageJson.description = 'A Serverless Project and its Serverless Plugin dependencies.'; + packageJson.private = false; + packageJson.dependencies = {}; + if (packageJson.devDependencies) delete packageJson.devDependencies; + if (packageJson.keywords) delete packageJson.keywords; + files.push(fs.writeFileAsync(path.join(_this.S.config.projectPath, 'package.json'), JSON.stringify(packageJson, null, 2))) + + } else { + + // Modify existing package.json + packageJson = SUtils.readAndParseJsonSync(path.join(_this.S.config.projectPath, 'package.json')); + packageJson = _this.project.name; + + // Delete unnecessary package.json properties, if they exist + if (packageJson.readme) delete packageJson.readme; + if (packageJson.readmeFilename) delete packageJson.readmeFilename; + if (packageJson.gitHead) delete packageJson.gitHead; + if (packageJson._id) delete packageJson._id; + if (packageJson._shasum) delete packageJson._shasum; + if (packageJson._from) delete packageJson._from; + if (packageJson._npmVersion) delete packageJson._npmVersion; + if (packageJson._nodeVersion) delete packageJson._nodeVersion; + if (packageJson._npmUser) delete packageJson._npmUser; + if (packageJson.dist) delete packageJson.dist; + if (packageJson.maintainers) delete packageJson.maintainers; + if (packageJson.directories) delete packageJson.directories; + if (packageJson._resolved) delete packageJson._resolved; + if (packageJson._args) delete packageJson._args; + if (packageJson._inCache) delete packageJson._inCache; + if (packageJson._installable) delete packageJson._installable; + if (packageJson._location) delete packageJson._location; + if (packageJson._phantomChildren) delete packageJson._phantomChildren; + if (packageJson._requested) delete packageJson._requested; + if (packageJson._requiredBy) delete packageJson._requiredBy; + if (packageJson._shrinkwrap || packageJson._shrinkwrap === null) delete packageJson._shrinkwrap; + if (packageJson._spec) delete packageJson._spec; + if (packageJson._where) delete packageJson._where; + files.push(fs.writeFileAsync(path.join(_this.S.config.projectPath, 'package.json'), JSON.stringify(packageJson, null, 2))) + + } + + // If README.md does not exist, save it + if (!SUtils.fileExistsSync(path.join(_this.S.config.projectPath, 'README.md'))) { + let readme = '#' + _this.project.name; + files.push(fs.writeFileAsync(path.join(_this.S.config.projectPath, 'README.md'), readme)); + } + + // If .gitignore does not exist, save it + if (!SUtils.fileExistsSync(path.join(_this.S.config.projectPath, '.gitignore'))) { + files.push(fs.writeFileAsync(path.join(_this.S.config.projectPath, '.gitignore'), fs.readFileSync(path.join(_this.S.config.serverlessPath, 'templates', 'gitignore')))); + } + + // If .env does not exist, save it + if (!SUtils.fileExistsSync(path.join(_this.S.config.projectPath, '.env'))) { + files.push(SUtils.writeFile( + path.join(_this.S.config.projectPath, '.env'), + 'SERVERLESS_STAGE=' + _this.evt.options.stage + + '\nSERVERLESS_DATA_MODEL_STAGE=' + _this.evt.options.stage + + '\nSERVERLESS_PROJECT_NAME=' + _this.name + )); + } + + return BbPromise.all(files); + }); } /** @@ -408,7 +476,7 @@ module.exports = function(SPlugin, serverlessPath) { components = _this.S.state.getComponents(); components.forEach(function(component) { - SCli.log(`Installing ${component.runtime} dependencies for component: ${component.name}`); + SCli.log(`Installing ${component.runtime} dependencies for component: ${component.name}...`); if (component.runtime === 'nodejs') { SUtils.npmInstall(path.join(_this.S.config.projectPath, component.name)); } else if (component.runtime === 'python2.7') { diff --git a/lib/actions/RegionCreate.js b/lib/actions/RegionCreate.js index 3c034f2e8..389d7df68 100644 --- a/lib/actions/RegionCreate.js +++ b/lib/actions/RegionCreate.js @@ -191,8 +191,6 @@ usage: serverless region create`, } else { - console.log(this.S.state.getMeta().variables.projectBucket, this.S.state.getMeta().variables); - // Create bucket, or skip if already exists return this.S3.sCreateBucket(this.S.state.getMeta().variables.projectBucket); } diff --git a/lib/actions/StageCreate.js b/lib/actions/StageCreate.js index c0e040b09..094798f68 100644 --- a/lib/actions/StageCreate.js +++ b/lib/actions/StageCreate.js @@ -5,7 +5,6 @@ * - Creates new stage, and new region in that stage for your project. * - Creates a new project S3 bucket for the new region and puts env and CF files * - Creates CF stack by default, unless noExeCf option is set to true - * - Updates the project's s-project.json file with the new stage and region * * Options: * - stage (String) the name of the new stage