mirror of
https://github.com/serverless/serverless.git
synced 2026-01-18 14:58:43 +00:00
ProjectCreate: Create new meta scaffolding. Add CF to s-project.json template and stop using resources-cf.json
This commit is contained in:
parent
80ab74991f
commit
3026632048
@ -33,6 +33,7 @@ class Serverless {
|
||||
this._version = require('./../package.json').version;
|
||||
this._projectRootPath = SUtils.getProjectPath(process.cwd());
|
||||
this._project = false;
|
||||
this._meta = false;
|
||||
this.actions = {};
|
||||
this.hooks = {};
|
||||
this.commands = {};
|
||||
@ -46,12 +47,12 @@ class Serverless {
|
||||
this._loadPlugins(this._projectRootPath, this._project.plugins);
|
||||
}
|
||||
|
||||
// If within project, add further queued data
|
||||
// If within private, add further queued data
|
||||
if (this._projectRootPath) {
|
||||
|
||||
// Get Project Information
|
||||
this._project = SUtils.getProject(this._projectRootPath);
|
||||
this._meta = SUtils.getProjectMeta(this._projectRootPath);
|
||||
this._meta = SUtils.getMeta(this._projectRootPath);
|
||||
|
||||
// Load Admin ENV information
|
||||
require('dotenv').config({
|
||||
@ -63,7 +64,6 @@ class Serverless {
|
||||
this._awsAdminKeyId = process.env.SERVERLESS_ADMIN_AWS_ACCESS_KEY_ID;
|
||||
this._awsAdminSecretKey = process.env.SERVERLESS_ADMIN_AWS_SECRET_ACCESS_KEY;
|
||||
}
|
||||
console.log(this._meta.project.stages.development.regions);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -268,7 +268,7 @@ class Serverless {
|
||||
let PluginClass;
|
||||
if (pluginMetadatum.path.indexOf('.') == 0) {
|
||||
|
||||
// Load non-npm plugin from the project plugins folder
|
||||
// Load non-npm plugin from the private plugins folder
|
||||
let pluginAbsPath = path.join(relDir, pluginMetadatum.path);
|
||||
SUtils.sDebug('Attempting to load plugin from ' + pluginAbsPath);
|
||||
PluginClass = require(pluginAbsPath);
|
||||
|
||||
@ -228,7 +228,7 @@ class ServerlessPlugin {
|
||||
cliPromptSelectStage(message, stage, addLocalStage) {
|
||||
|
||||
let _this = this,
|
||||
stages = Object.keys(_this.S._meta.project.stages);
|
||||
stages = Object.keys(_this.S._meta.private.stages);
|
||||
|
||||
// Resolve stage if provided
|
||||
if (stage) return BbPromise.resolve(stage);
|
||||
@ -236,7 +236,7 @@ class ServerlessPlugin {
|
||||
// Skip if not interactive
|
||||
if (!_this.S._interactive) return BbPromise.resolve();
|
||||
|
||||
// if project has 1 stage, skip prompt
|
||||
// if private has 1 stage, skip prompt
|
||||
if (stages.length === 1) {
|
||||
return BbPromise.resolve(stages[0]);
|
||||
}
|
||||
@ -274,8 +274,8 @@ class ServerlessPlugin {
|
||||
if (stage === 'local') return BbPromise.resolve('local');
|
||||
|
||||
// If stage has one region, skip prompt and return that instead
|
||||
if (stage && Object.keys(_this.S._meta.project.stages[stage].regions).length === 1 && existing) {
|
||||
return BbPromise.resolve(Object.keys(_this.S._meta.project.stages[stage].regions)[0]);
|
||||
if (stage && Object.keys(_this.S._meta.private.stages[stage].regions).length === 1 && existing) {
|
||||
return BbPromise.resolve(Object.keys(_this.S._meta.private.stages[stage].regions)[0]);
|
||||
}
|
||||
|
||||
// Skip if not interactive or stage is local
|
||||
@ -286,8 +286,8 @@ class ServerlessPlugin {
|
||||
// if stage is provided, limit region list
|
||||
if (stage){
|
||||
|
||||
// Make sure stage exists in project
|
||||
if (!_this.S._meta.project.stages[stage]) {
|
||||
// Make sure stage exists in private
|
||||
if (!_this.S._meta.private.stages[stage]) {
|
||||
return BbPromise.reject(new SError('Stage ' + stage + ' does not exist in your project', SError.errorCodes.UNKNOWN));
|
||||
}
|
||||
|
||||
@ -296,18 +296,18 @@ class ServerlessPlugin {
|
||||
|
||||
// List only regions in stage
|
||||
regionChoices = [];
|
||||
Object.keys(_this.S._meta.project.stages[stage].regions).forEach(function(region) {
|
||||
Object.keys(_this.S._meta.private.stages[stage].regions).forEach(function(region) {
|
||||
regionChoices.push(region)
|
||||
});
|
||||
} else {
|
||||
|
||||
// Make sure there are regions left in stage
|
||||
if (Object.keys(_this.S._meta.project.stages[stage].regions).length === 4) {
|
||||
if (Object.keys(_this.S._meta.private.stages[stage].regions).length === 4) {
|
||||
return BbPromise.reject(new SError('Stage ' + stage + ' already have all possible regions.', SError.errorCodes.UNKNOWN));
|
||||
}
|
||||
|
||||
// List only regions NOT in stage
|
||||
Object.keys(_this.S._meta.project.stages[stage].regions).forEach(function(regionInStage) {
|
||||
Object.keys(_this.S._meta.private.stages[stage].regions).forEach(function(regionInStage) {
|
||||
let index = regionChoices.indexOf(regionInStage.region);
|
||||
regionChoices.splice(index, 1);
|
||||
});
|
||||
|
||||
@ -225,7 +225,7 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
|
||||
// If no region specified, deploy to all regions in stage
|
||||
if (!evt.regions.length) {
|
||||
evt.regions = Object.keys(this.S._meta.project.stages[evt.stage].regions);
|
||||
evt.regions = Object.keys(this.S._meta.private.stages[evt.stage].regions);
|
||||
}
|
||||
|
||||
// Delete region for neatness
|
||||
|
||||
@ -157,7 +157,7 @@ usage: serverless env get`,
|
||||
}
|
||||
|
||||
// validate stage: make sure stage exists
|
||||
if (!_this.S._meta.project.stages[_this.evt.stage] && _this.evt.stage != 'local') {
|
||||
if (!_this.S._meta.private.stages[_this.evt.stage] && _this.evt.stage != 'local') {
|
||||
return BbPromise.reject(new SError('Stage ' + _this.evt.stage + ' does not exist in your project', SError.errorCodes.UNKNOWN));
|
||||
}
|
||||
|
||||
@ -165,7 +165,7 @@ usage: serverless env get`,
|
||||
if (_this.evt.stage != 'local' && _this.evt.region != 'all') {
|
||||
|
||||
// validate region: make sure region exists in stage
|
||||
if (!_this.S._meta.project.stages[_this.evt.stage].regions[_this.evt.region]) {
|
||||
if (!_this.S._meta.private.stages[_this.evt.stage].regions[_this.evt.region]) {
|
||||
return BbPromise.reject(new SError('Region "' + _this.evt.region + '" does not exist in stage "' + _this.evt.stage + '"'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,7 +144,7 @@ Usage: serverless env list`,
|
||||
}
|
||||
|
||||
// Validate stage: make sure stage exists
|
||||
if (!_this.S._meta.project.stages[_this.evt.stage] && _this.evt.stage != 'local') {
|
||||
if (!_this.S._meta.private.stages[_this.evt.stage] && _this.evt.stage != 'local') {
|
||||
return BbPromise.reject(new SError('Stage ' + _this.evt.stage + ' does not exist in your project', SError.errorCodes.UNKNOWN));
|
||||
}
|
||||
|
||||
@ -152,7 +152,7 @@ Usage: serverless env list`,
|
||||
if (_this.evt.stage != 'local' && _this.evt.region != 'all') {
|
||||
|
||||
// Validate region: make sure region exists in stage
|
||||
if (!_this.S._meta.project.stages[_this.evt.stage].regions[_this.evt.region]) {
|
||||
if (!_this.S._meta.private.stages[_this.evt.stage].regions[_this.evt.region]) {
|
||||
return BbPromise.reject(new SError('Region "' + _this.evt.region + '" does not exist in stage "' + _this.evt.stage + '"'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,7 +168,7 @@ usage: serverless env set`,
|
||||
}
|
||||
|
||||
// Validate stage: make sure stage exists
|
||||
if (!_this.S._meta.project.stages[_this.evt.stage] && _this.evt.stage != 'local') {
|
||||
if (!_this.S._meta.private.stages[_this.evt.stage] && _this.evt.stage != 'local') {
|
||||
return BbPromise.reject(new SError('Stage ' + _this.evt.stage + ' does not exist in your project', SError.errorCodes.UNKNOWN));
|
||||
}
|
||||
|
||||
@ -176,7 +176,7 @@ usage: serverless env set`,
|
||||
if (_this.evt.stage != 'local' && _this.evt.region != 'all') {
|
||||
|
||||
// validate region: make sure region exists in stage
|
||||
if (!_this.S._meta.project.stages[_this.evt.stage].regions[region]) {
|
||||
if (!_this.S._meta.private.stages[_this.evt.stage].regions[region]) {
|
||||
return BbPromise.reject(new SError('Region "' + _this.evt.region + '" does not exist in stage "' + _this.evt.stage + '"'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -165,7 +165,7 @@ usage: serverless env unset`,
|
||||
if (_this.evt.stage != 'local' && _this.evt.region != 'all') {
|
||||
|
||||
// Validate region: make sure region exists in stage
|
||||
if (!_this.S._meta.project.stages[_this.evt.stage].regions[_this.evt.region]) {
|
||||
if (!_this.S._meta.private.stages[_this.evt.stage].regions[_this.evt.region]) {
|
||||
return BbPromise.reject(new SError('Region "' + _this.evt.region + '" does not exist in stage "' + _this.evt.stage + '"'));
|
||||
}
|
||||
}
|
||||
|
||||
@ -226,7 +226,7 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
|
||||
// If no region specified, deploy to all regions in stage
|
||||
if (!evt.regions.length) {
|
||||
evt.regions = Object.keys(this.S._meta.project.stages);
|
||||
evt.regions = Object.keys(this.S._meta.private.stages);
|
||||
}
|
||||
|
||||
return evt;
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
* - Generates scaffolding for the new project in CWD
|
||||
* - Creates a new project S3 bucket and puts env and CF files
|
||||
* - Creates CF stack by default, unless noExeCf option is set to true
|
||||
* - Generates the final s-project.json file
|
||||
* - Generates project JSON files
|
||||
*
|
||||
* Event Properties:
|
||||
* - name (String) a name for new project
|
||||
@ -20,14 +20,15 @@
|
||||
*/
|
||||
|
||||
module.exports = function(SPlugin, serverlessPath) {
|
||||
const path = require('path'),
|
||||
SError = require( path.join( serverlessPath, 'ServerlessError' ) ),
|
||||
SCli = require( path.join( serverlessPath, 'utils/cli' ) ),
|
||||
SUtils = require( path.join( serverlessPath, 'utils' ) ),
|
||||
os = require('os'),
|
||||
fs = require('fs'),
|
||||
BbPromise = require('bluebird'),
|
||||
awsMisc = require( path.join( serverlessPath, 'utils/aws/Misc' ) );
|
||||
|
||||
const path = require('path'),
|
||||
SError = require( path.join( serverlessPath, 'ServerlessError' ) ),
|
||||
SCli = require( path.join( serverlessPath, 'utils/cli' ) ),
|
||||
SUtils = require( path.join( serverlessPath, 'utils' ) ),
|
||||
os = require('os'),
|
||||
fs = require('fs'),
|
||||
BbPromise = require('bluebird'),
|
||||
awsMisc = require( path.join( serverlessPath, 'utils/aws/Misc' ) );
|
||||
|
||||
BbPromise.promisifyAll(fs);
|
||||
|
||||
@ -100,7 +101,7 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
|
||||
let _this = this;
|
||||
|
||||
if(evt) {
|
||||
if (evt) {
|
||||
_this.evt = evt;
|
||||
_this.S._interactive = false;
|
||||
}
|
||||
@ -108,15 +109,12 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
// If CLI, parse arguments
|
||||
if (_this.S.cli) {
|
||||
_this.evt = JSON.parse(JSON.stringify(this.S.cli.options)); // Important: Clone objects, don't refer to them
|
||||
if (_this.S.cli.options.nonInteractive) {
|
||||
_this.S._interactive = false;
|
||||
}
|
||||
|
||||
if (_this.S.cli.options.nonInteractive) _this.S._interactive = false;
|
||||
}
|
||||
|
||||
// Add default runtime
|
||||
if (!_this.evt.runtime) {
|
||||
_this.evt.runtime = 'nodejs';
|
||||
}
|
||||
if (!_this.evt.runtime) _this.evt.runtime = 'nodejs';
|
||||
|
||||
// Always create "development" stage on ProjectCreate
|
||||
_this.evt.stage = 'development';
|
||||
@ -345,7 +343,7 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
}
|
||||
|
||||
// Set Serverless Regional Bucket
|
||||
this.evt.regionBucket = SUtils.generateRegionBucketName(this.evt.region, this.evt.domain);
|
||||
this.evt.projectBucket = SUtils.generateProjectBucketName(this.evt.region, this.evt.domain);
|
||||
|
||||
return BbPromise.resolve();
|
||||
}
|
||||
@ -376,11 +374,18 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
)
|
||||
.then(function() {
|
||||
|
||||
// Create Folders
|
||||
fs.mkdirSync(path.join(_this._projectRootPath, 'back', 'modules'));
|
||||
fs.mkdirSync(path.join(_this._projectRootPath, 'meta'));
|
||||
fs.mkdirSync(path.join(_this._projectRootPath, 'meta', 'private'));
|
||||
fs.mkdirSync(path.join(_this._projectRootPath, 'meta', 'public'));
|
||||
fs.mkdirSync(path.join(_this._projectRootPath, 'meta', 'private', 'variables'));
|
||||
fs.mkdirSync(path.join(_this._projectRootPath, 'meta', 'public', 'variables'));
|
||||
fs.mkdirSync(path.join(_this._projectRootPath, 'meta', 'private', 'resources'));
|
||||
fs.mkdirSync(path.join(_this._projectRootPath, 'plugins'));
|
||||
fs.mkdirSync(path.join(_this._projectRootPath, 'plugins', 'custom'));
|
||||
|
||||
return BbPromise.all([
|
||||
fs.mkdirAsync(path.join(_this._projectRootPath, 'back', 'modules')),
|
||||
fs.mkdirAsync(path.join(_this._projectRootPath, 'plugins')).then(function(){
|
||||
return fs.mkdirAsync(path.join(_this._projectRootPath, 'plugins', 'custom'));
|
||||
}),
|
||||
SUtils.writeFile(path.join(_this._projectRootPath, 'admin.env'), adminEnv),
|
||||
SUtils.writeFile(path.join(_this._projectRootPath, 'README.md'), readme),
|
||||
SUtils.generateResourcesCf(
|
||||
@ -401,8 +406,8 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
*/
|
||||
|
||||
_createProjectBucket() {
|
||||
SCli.log('Creating a project region bucket on S3: ' + this.evt.regionBucket + '...');
|
||||
return this.S3.sCreateBucket(this.evt.regionBucket);
|
||||
SCli.log('Creating a project region bucket on S3: ' + this.evt.projectBucket + '...');
|
||||
return this.S3.sCreateBucket(this.evt.projectBucket);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -416,7 +421,7 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
SERVERLESS_PROJECT_NAME=${this.evt.name}`;
|
||||
|
||||
return this.S3.sPutEnvFile(
|
||||
this.evt.regionBucket,
|
||||
this.evt.projectBucket,
|
||||
this.evt.name,
|
||||
this.evt.stage,
|
||||
envFileContents);
|
||||
@ -429,7 +434,7 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
_putCfFile() {
|
||||
return this.CF.sPutCfFile(
|
||||
this._projectRootPath,
|
||||
this.evt.regionBucket,
|
||||
this.evt.projectBucket,
|
||||
this.evt.name,
|
||||
this.evt.stage,
|
||||
'resources');
|
||||
@ -466,8 +471,7 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
_this.evt.stage,
|
||||
_this.evt.domain,
|
||||
_this.evt.notificationEmail,
|
||||
cfTemplateURL
|
||||
)
|
||||
cfTemplateURL)
|
||||
.then(cfData => {
|
||||
return _this.CF.sMonitorCf(cfData, 'create')
|
||||
.then(cfStackData => {
|
||||
@ -496,21 +500,38 @@ module.exports = function(SPlugin, serverlessPath) {
|
||||
_this.evt.stageCfStack = cfStackData.StackName;
|
||||
}
|
||||
|
||||
let prjJson = SUtils.readAndParseJsonSync(path.join(_this._templatesDir, 's-project.json'));
|
||||
|
||||
prjJson.stages[_this.evt.stage] = [{
|
||||
region: _this.evt.region,
|
||||
iamRoleArnLambda: _this.evt.iamRoleLambdaArn || '',
|
||||
regionBucket: _this.evt.regionBucket
|
||||
}];
|
||||
|
||||
prjJson.name = _this.evt.name;
|
||||
prjJson.domain = _this.evt.domain;
|
||||
|
||||
// Create s-project.json
|
||||
let prjJson = SUtils.readAndParseJsonSync(path.join(_this._templatesDir, 's-project.json'));
|
||||
prjJson.name = _this.evt.name;
|
||||
prjJson.description = 'A brand new Serverless project';
|
||||
fs.writeFileSync(path.join(_this._projectRootPath, 's-project.json'),
|
||||
JSON.stringify(prjJson, null, 2));
|
||||
|
||||
return prjJson;
|
||||
// Save Meta
|
||||
_this._meta = {
|
||||
private: {
|
||||
stages: {},
|
||||
variables: {
|
||||
domain: _this.evt.domain,
|
||||
projectBucket: _this.evt.projectBucket
|
||||
}
|
||||
},
|
||||
public: {
|
||||
stages: {},
|
||||
variables: {}
|
||||
},
|
||||
};
|
||||
_this._meta.private.stages[_this.evt.stage] = {
|
||||
regions: {},
|
||||
variables: {}
|
||||
};
|
||||
_this._meta.private.stages[_this.evt.stage].regions[_this.evt.region] = {
|
||||
variables: {
|
||||
stackName: _this.evt.stageCfStack,
|
||||
iamRoleLambdaArn: _this.evt.iamRoleLambdaArn
|
||||
}
|
||||
};
|
||||
SUtils.saveMeta(_this._projectRootPath, _this._meta);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -153,7 +153,7 @@ usage: serverless region create`,
|
||||
}
|
||||
|
||||
// validate stage: make sure stage exists
|
||||
if (!_this.S._meta.project.stages[_this.evt.stage]) {
|
||||
if (!_this.S._meta.private.stages[_this.evt.stage]) {
|
||||
return BbPromise.reject(new SError('Stage ' + _this.evt.stage + ' does not exist in your project', SError.errorCodes.UNKNOWN));
|
||||
}
|
||||
|
||||
@ -192,7 +192,7 @@ usage: serverless region create`,
|
||||
*/
|
||||
|
||||
_createRegionBucket() {
|
||||
this.evt.regionBucket = SUtils.generateRegionBucketName(this.evt.region, this.S._meta.variables.domain);
|
||||
this.evt.regionBucket = SUtils.generateProjectBucketName(this.evt.region, this.S._meta.variables.domain);
|
||||
SCli.log('Creating a region bucket on S3: ' + this.evt.regionBucket + '...');
|
||||
return this.S3.sCreateBucket(this.evt.regionBucket);
|
||||
}
|
||||
@ -286,7 +286,7 @@ SERVERLESS_PROJECT_NAME=${this.S._project.name}`;
|
||||
_this.evt.stageCfStack = cfStackData.StackName;
|
||||
}
|
||||
|
||||
_this.S._meta.project.stages[_this.evt.stage].regions[_this.evt.region] = {
|
||||
_this.S._meta.private.stages[_this.evt.stage].regions[_this.evt.region] = {
|
||||
regionBucket: _this.evt.regionBucket,
|
||||
stackName: cfStackData.StackName,
|
||||
iamRoleArnLambda: _this.evt.iamRoleArnLambda
|
||||
@ -302,7 +302,7 @@ SERVERLESS_PROJECT_NAME=${this.S._project.name}`;
|
||||
|
||||
return SUtils.writeFile(
|
||||
path.join(_this.S._projectRootPath, 'meta', 'variables', variableFile),
|
||||
JSON.stringify(_this.S._meta.project.stages[_this.evt.stage].regions[_this.evt.region], null, 2)
|
||||
JSON.stringify(_this.S._meta.private.stages[_this.evt.stage].regions[_this.evt.region], null, 2)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -145,7 +145,7 @@ usage: serverless resources deploy`,
|
||||
}
|
||||
|
||||
// validate stage: make sure stage exists
|
||||
if (!_this.S._meta.project.stages[_this.evt.stage] && _this.evt.stage != 'local') {
|
||||
if (!_this.S._meta.private.stages[_this.evt.stage] && _this.evt.stage != 'local') {
|
||||
return BbPromise.reject(new SError('Stage ' + _this.evt.stage + ' does not exist in your project', SError.errorCodes.UNKNOWN));
|
||||
}
|
||||
|
||||
|
||||
@ -179,7 +179,7 @@ usage: serverless stage create`,
|
||||
}
|
||||
|
||||
// validate stage: Ensure stage doesn't already exist
|
||||
if (this.S._meta.project.stages[this.evt.stage]) {
|
||||
if (this.S._meta.private.stages[this.evt.stage]) {
|
||||
return BbPromise.reject(new SError('Stage ' + this.evt.stage + ' already exists', SError.errorCodes.UNKNOWN));
|
||||
}
|
||||
|
||||
@ -231,7 +231,7 @@ usage: serverless stage create`,
|
||||
|
||||
// Check if project name is in AllowedValues
|
||||
if (cfTemplate.Parameters.ProjectName.AllowedValues.indexOf(this.S._project.name) == -1) {
|
||||
cfTemplate.Parameters.ProjectName.AllowedValues.push(this.S._meta.project.name);
|
||||
cfTemplate.Parameters.ProjectName.AllowedValues.push(this.S._project.name);
|
||||
}
|
||||
|
||||
// Write it
|
||||
@ -247,7 +247,7 @@ usage: serverless stage create`,
|
||||
*/
|
||||
|
||||
_createRegionBucket() {
|
||||
this.evt.regionBucket = SUtils.generateRegionBucketName(this.evt.region, this.S._project.domain);
|
||||
this.evt.regionBucket = SUtils.generateProjectBucketName(this.evt.region, this.S._project.domain);
|
||||
SCli.log('Creating a region bucket on S3: ' + this.evt.regionBucket + '...');
|
||||
return this.S3.sCreateBucket(this.evt.regionBucket);
|
||||
}
|
||||
@ -296,7 +296,7 @@ SERVERLESS_PROJECT_NAME=${this.S._project.name}`;
|
||||
let stackName = _this.CF.sGetResourcesStackName(_this.evt.stage, _this.S._project.name);
|
||||
|
||||
SCli.log(`Remember to run CloudFormation manually to create stack with name: ${stackName}`);
|
||||
SCli.log('After creating CF stack, remember to put the IAM role outputs and regionBucket in your project s-project.json in the correct stage/region.');
|
||||
SCli.log('After creating CF stack, remember to put the IAM role outputs and regionBucket in your private s-project.json in the correct stage/region.');
|
||||
|
||||
return BbPromise.resolve();
|
||||
}
|
||||
|
||||
@ -37,4 +37,5 @@ node_modules
|
||||
|
||||
#SERVERLESS STUFF
|
||||
admin.env
|
||||
.env
|
||||
.env
|
||||
meta/private
|
||||
@ -1,29 +0,0 @@
|
||||
{
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Description": "The AWS CloudFormation template for this Serverless application's Lambda functions",
|
||||
"Parameters": {
|
||||
"LambdaRoleArn": {
|
||||
"Type": "String",
|
||||
"Default": ""
|
||||
}
|
||||
},
|
||||
"Resources": {
|
||||
"lTemplate": {
|
||||
"Type": "AWS::Lambda::Function",
|
||||
"Properties": {
|
||||
"Code": {
|
||||
"S3Bucket": "",
|
||||
"S3Key": ""
|
||||
},
|
||||
"Description": "",
|
||||
"Handler": "",
|
||||
"MemorySize": 1024,
|
||||
"Role": {
|
||||
"Ref": "LambdaRoleArn"
|
||||
},
|
||||
"Runtime": "",
|
||||
"Timeout": 6
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -70,23 +70,6 @@
|
||||
"Path": "/"
|
||||
}
|
||||
},
|
||||
"IamInstanceProfileLambda": {
|
||||
"Type": "AWS::IAM::InstanceProfile",
|
||||
"Properties": {
|
||||
"Path": "/",
|
||||
"Roles": [
|
||||
{
|
||||
"Ref": "IamRoleLambda"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"IamGroupLambda": {
|
||||
"Type": "AWS::IAM::Group",
|
||||
"Properties": {
|
||||
"Path": "/"
|
||||
}
|
||||
},
|
||||
"IamPolicyLambda": {
|
||||
"Type": "AWS::IAM::Policy",
|
||||
"Properties": {
|
||||
@ -153,4 +136,4 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,150 @@
|
||||
{
|
||||
"name": "",
|
||||
"version": "0.0.1",
|
||||
"profile": "serverless-0",
|
||||
"profile": "serverless-0.1",
|
||||
"location": "https://github.com/...",
|
||||
"author": "",
|
||||
"description": "",
|
||||
"domain": "",
|
||||
"stages": {},
|
||||
"custom": {},
|
||||
"plugins": []
|
||||
"modules": {},
|
||||
"plugins": [],
|
||||
"cloudFormation" : {
|
||||
"AWSTemplateFormatVersion": "2010-09-09",
|
||||
"Description": "The AWS CloudFormation template for this Serverless application's resources outside of Lambdas and Api Gateway",
|
||||
"Parameters": {
|
||||
"ProjectName": {
|
||||
"Type": "String",
|
||||
"AllowedValues": []
|
||||
},
|
||||
"ProjectDomain": {
|
||||
"Type": "String",
|
||||
"Default": "myapp.com"
|
||||
},
|
||||
"Stage": {
|
||||
"Type": "String",
|
||||
"AllowedValues": [
|
||||
]
|
||||
},
|
||||
"DataModelStage": {
|
||||
"Type": "String",
|
||||
"AllowedValues": [
|
||||
]
|
||||
},
|
||||
"NotificationEmail": {
|
||||
"Type": "String",
|
||||
"Default": "you@you.com"
|
||||
},
|
||||
"DynamoRWThroughput": {
|
||||
"Type": "String",
|
||||
"Default": "1"
|
||||
}
|
||||
},
|
||||
"Metadata": {
|
||||
"AWS::CloudFormation::Interface": {
|
||||
"ParameterGroups": [
|
||||
{
|
||||
"Label": {"default": "Project Settings"},
|
||||
"Parameters": ["ProjectName", "ProjectDomain", "Stage", "DataModelStage"]
|
||||
},
|
||||
{
|
||||
"Label": {"default": "Monitoring"},
|
||||
"Parameters": ["NotificationEmail"]
|
||||
},
|
||||
{
|
||||
"Label": {"default": "Database Settings (DynamoDB)"},
|
||||
"Parameters": ["DynamoRWThroughput"]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"Resources": {
|
||||
"IamRoleLambda": {
|
||||
"Type": "AWS::IAM::Role",
|
||||
"Properties": {
|
||||
"AssumeRolePolicyDocument": {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"Service": [
|
||||
"lambda.amazonaws.com"
|
||||
]
|
||||
},
|
||||
"Action": [
|
||||
"sts:AssumeRole"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"Path": "/"
|
||||
}
|
||||
},
|
||||
"IamPolicyLambda": {
|
||||
"Type": "AWS::IAM::Policy",
|
||||
"Properties": {
|
||||
"PolicyName": {
|
||||
"Fn::Join": [
|
||||
"_-_",
|
||||
[
|
||||
{
|
||||
"Ref": "Stage"
|
||||
},
|
||||
{
|
||||
"Ref": "ProjectName"
|
||||
},
|
||||
"lambda"
|
||||
]
|
||||
]
|
||||
},
|
||||
"PolicyDocument": {
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"logs:CreateLogGroup",
|
||||
"logs:CreateLogStream",
|
||||
"logs:PutLogEvents"
|
||||
],
|
||||
"Resource": {
|
||||
"Fn::Join": [
|
||||
":",
|
||||
[
|
||||
"arn:aws:logs",
|
||||
{
|
||||
"Ref": "AWS::Region"
|
||||
},
|
||||
"*:*"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"Roles": [
|
||||
{
|
||||
"Ref": "IamRoleLambda"
|
||||
}
|
||||
],
|
||||
"Groups": [
|
||||
{
|
||||
"Ref": "IamGroupLambda"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"Outputs": {
|
||||
"IamRoleArnLambda": {
|
||||
"Description": "ARN of the lambda IAM role",
|
||||
"Value": {
|
||||
"Fn::GetAtt": [
|
||||
"IamRoleLambda",
|
||||
"Arn"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -126,12 +126,12 @@ module.exports = function(config) {
|
||||
|
||||
let S3 = require('./S3')(config);
|
||||
|
||||
if (['lambdas', 'resources'].indexOf(type) == -1) {
|
||||
BbPromise.reject(new SError(`Type ${type} invalid. Must be lambdas or resources`, SError.errorCodes.UNKNOWN));
|
||||
if (['resources'].indexOf(type) == -1) {
|
||||
BbPromise.reject(new SError(`Type ${type} invalid. Must be resources only`, SError.errorCodes.UNKNOWN));
|
||||
}
|
||||
|
||||
let d = new Date(),
|
||||
cfPath = path.join(projRootPath, 'cloudformation', type + '-cf.json'),
|
||||
cfPath = path.join(projRootPath, 'meta', 'private', 'resources', 's-' + type + '-cf.json'),
|
||||
key = ['Serverless', projName, projStage, 'cloudformation/' + type].join('/') + '@' + d.getTime() + '.json',
|
||||
params = {
|
||||
Bucket: bucketName,
|
||||
|
||||
@ -83,7 +83,7 @@ exports.getProject = function(projectRootPath) {
|
||||
try {
|
||||
|
||||
let module = _this.readAndParseJsonSync(path.join(projectRootPath, 'back', 'modules', moduleList[i], 's-module.json'));
|
||||
project.modules[module.name] = module;
|
||||
project.modules[module.name] = module;
|
||||
project.modules[module.name].pathModule = path.join('back', 'modules', moduleList[i], 's-module.json');
|
||||
project.modules[module.name].functions = {};
|
||||
|
||||
@ -111,76 +111,110 @@ exports.getProject = function(projectRootPath) {
|
||||
};
|
||||
|
||||
/**
|
||||
* GetProjectMeta
|
||||
* Get Meta
|
||||
* - Get Project Meta Information
|
||||
*/
|
||||
|
||||
exports.getProjectMeta = function(projectRootPath) {
|
||||
exports.getMeta = function(projectRootPath) {
|
||||
|
||||
let _this = this,
|
||||
projectMeta = {
|
||||
project: {
|
||||
private: {
|
||||
stages: {},
|
||||
variables: {}
|
||||
},
|
||||
public: {
|
||||
variables: {}
|
||||
}
|
||||
};
|
||||
|
||||
// Create "meta" folder if does not exist
|
||||
if (!_this.dirExistsSync(path.join(projectRootPath, 'meta'))) {
|
||||
fs.mkdirSync(path.join(projectRootPath, 'meta'));
|
||||
}
|
||||
// Re-usable function to traverse public or private variable folders
|
||||
let _getVariables = function(type) {
|
||||
|
||||
// Create "meta/variables" folder if does not exist
|
||||
if (!_this.dirExistsSync(path.join(projectRootPath, 'meta', 'variables'))) {
|
||||
fs.mkdirSync(path.join(projectRootPath, 'meta', 'variables'));
|
||||
}
|
||||
let variableFiles = fs.readdirSync(path.join(projectRootPath, 'meta', type, 'variables'));
|
||||
for (let i = 0; i < variableFiles.length; i++) {
|
||||
|
||||
// Get Variables
|
||||
let variableFiles = fs.readdirSync(path.join(projectRootPath, 'meta', 'variables'));
|
||||
for (let i = 0; i < variableFiles.length; i++) {
|
||||
let variableFile = _this.readAndParseJsonSync(path.join(projectRootPath, 'meta', type, 'variables', variableFiles[i]));
|
||||
|
||||
let variableFile = _this.readAndParseJsonSync(path.join(projectRootPath, 'meta', 'variables', variableFiles[i]));
|
||||
// Parse file name to get stage/region
|
||||
let file = variableFiles[i].replace('s-variables-', '').replace('.json', '');
|
||||
|
||||
// Parse file name to get stage/region
|
||||
let file = variableFiles[i].replace('s-variables-', '').replace('.json', '');
|
||||
if (file === 'common') {
|
||||
|
||||
if (file === 'common') {
|
||||
// Set Common variables
|
||||
projectMeta[type].variables = _this.readAndParseJsonSync(path.join(projectRootPath, 'meta', type, 'variables', variableFiles[i]));
|
||||
|
||||
// Set Common variables
|
||||
projectMeta.project.variables = _this.readAndParseJsonSync(path.join(projectRootPath, 'meta', 'variables', variableFiles[i]));
|
||||
} else {
|
||||
} else {
|
||||
|
||||
// Set Stage/Region variables
|
||||
file = file.split('-');
|
||||
if (!projectMeta.project.stages[file[0]]) projectMeta.project.stages[file[0]] = {
|
||||
regions: {},
|
||||
variables: {}
|
||||
};
|
||||
|
||||
if (file.length === 1) {
|
||||
|
||||
// Set Stage Variables
|
||||
projectMeta.project.stages[file[0]].variables = _this.readAndParseJsonSync(path.join(projectRootPath, 'meta', 'variables', variableFiles[i]));
|
||||
|
||||
} else if (file.length === 2) {
|
||||
|
||||
// Set Stage-Region Variables
|
||||
let region;
|
||||
if (file[1] === 'useast1') region = 'us-east-1';
|
||||
if (file[1] === 'uswest2') region = 'us-west-2';
|
||||
if (file[1] === 'euwest1') region = 'eu-west-1';
|
||||
if (file[1] === 'apnortheast1') region = 'ap-northeast-1';
|
||||
if (!projectMeta.project.stages[file[0]].regions[region]) projectMeta.project.stages[file[0]].regions[region] = {
|
||||
variables: _this.readAndParseJsonSync(path.join(projectRootPath, 'meta', 'variables', variableFiles[i]))
|
||||
// Set Stage/Region variables
|
||||
file = file.split('-');
|
||||
if (!projectMeta[type].stages) projectMeta[type].stages = {};
|
||||
if (!projectMeta[type].stages[file[0]]) projectMeta[type].stages[file[0]] = {
|
||||
regions: {},
|
||||
variables: {}
|
||||
};
|
||||
projectMeta.project.stages[file[0]].regions[region].variables.region = region;
|
||||
|
||||
if (file.length === 1) {
|
||||
|
||||
// Set Stage Variables
|
||||
projectMeta[type].stages[file[0]].variables = _this.readAndParseJsonSync(path.join(projectRootPath, 'meta', type, 'variables', variableFiles[i]));
|
||||
|
||||
} else if (file.length === 2) {
|
||||
|
||||
// Set Stage-Region Variables
|
||||
let region;
|
||||
if (file[1] === 'useast1') region = 'us-east-1';
|
||||
if (file[1] === 'uswest2') region = 'us-west-2';
|
||||
if (file[1] === 'euwest1') region = 'eu-west-1';
|
||||
if (file[1] === 'apnortheast1') region = 'ap-northeast-1';
|
||||
if (!projectMeta[type].stages[file[0]].regions[region]) projectMeta[type].stages[file[0]].regions[region] = {
|
||||
variables: _this.readAndParseJsonSync(path.join(projectRootPath, 'meta', type, 'variables', variableFiles[i]))
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (_this.dirExistsSync(path.join(projectRootPath, 'meta', 'public'))) _getVariables('public');
|
||||
if (_this.dirExistsSync(path.join(projectRootPath, 'meta', 'private'))) _getVariables('private');
|
||||
|
||||
return projectMeta;
|
||||
};
|
||||
|
||||
/**
|
||||
* Save Meta
|
||||
*/
|
||||
|
||||
exports.saveMeta = function(projectRootPath, projectMeta) {
|
||||
|
||||
// Re-usable function to save public or private variables
|
||||
let _saveVariables = function(type) {
|
||||
|
||||
// Save Common Variables
|
||||
fs.writeFileSync(path.join(projectRootPath, 'meta', type, 'variables', 's-variables-common.json'),
|
||||
JSON.stringify(projectMeta[type].variables, null, 2));
|
||||
|
||||
for (let i = 0; i < Object.keys(projectMeta[type].stages).length; i++) {
|
||||
|
||||
let stage = projectMeta[type].stages[Object.keys(projectMeta[type].stages)[i]];
|
||||
|
||||
// Save Stage Variables
|
||||
fs.writeFileSync(path.join(projectRootPath, 'meta', type, 'variables', 's-variables-' + Object.keys(projectMeta[type].stages)[i] + '.json'),
|
||||
JSON.stringify(stage.variables, null, 2));
|
||||
|
||||
// Save Stage Region Variables
|
||||
for (let j = 0; j < Object.keys(stage.regions).length; j++) {
|
||||
fs.writeFileSync(path.join(projectRootPath, 'meta', type, 'variables', 's-variables-' + Object.keys(projectMeta[type].stages)[i] + '-' + Object.keys(stage.regions)[j].replace(/-/g, '') + '.json'),
|
||||
JSON.stringify(stage.regions[Object.keys(stage.regions)[j]].variables, null, 2));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (this.dirExistsSync(path.join(projectRootPath, 'meta', 'public'))) _saveVariables('public');
|
||||
if (this.dirExistsSync(path.join(projectRootPath, 'meta', 'private'))) _saveVariables('private');
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Execute (Command)
|
||||
*/
|
||||
@ -615,7 +649,7 @@ exports.generateShortId = function(maxLen) {
|
||||
* Generate JawsBucket Name
|
||||
*/
|
||||
|
||||
exports.generateRegionBucketName = function(region, projectDomain) {
|
||||
exports.generateProjectBucketName = function(region, projectDomain) {
|
||||
|
||||
// Sanitize
|
||||
region = region.trim().replace(/-/g, '').toLowerCase();
|
||||
@ -685,6 +719,7 @@ exports.npmInstall = function(dir) {
|
||||
*/
|
||||
|
||||
exports.generateResourcesCf = function(projRootPath, projName, projDomain, stage, region, notificationEmail) {
|
||||
|
||||
let cfTemplate = require('../templates/resources-cf');
|
||||
|
||||
cfTemplate.Parameters.ProjectName.Default = projName;
|
||||
@ -694,13 +729,12 @@ exports.generateResourcesCf = function(projRootPath, projName, projDomain, stage
|
||||
cfTemplate.Parameters.Stage.AllowedValues = [stage];
|
||||
cfTemplate.Parameters.DataModelStage.AllowedValues = [stage];
|
||||
|
||||
cfTemplate.Parameters.NotificationEmail.Default = notificationEmail;
|
||||
cfTemplate.Parameters.NotificationEmail.Default = notificationEmail;
|
||||
cfTemplate.Description = projName + ' resources';
|
||||
|
||||
return this.writeFile(
|
||||
path.join(projRootPath, 'cloudformation', 'resources-cf.json'),
|
||||
JSON.stringify(cfTemplate, null, 2)
|
||||
);
|
||||
path.join(projRootPath, 'meta', 'private', 'resources', 's-resources-cf.json'),
|
||||
JSON.stringify(cfTemplate, null, 2));
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"project": {
|
||||
"private": {
|
||||
"stages": {
|
||||
"development": {
|
||||
"regions": {
|
||||
@ -11,5 +11,8 @@
|
||||
}
|
||||
},
|
||||
"variables": {}
|
||||
},
|
||||
"public": {
|
||||
"variables": {}
|
||||
}
|
||||
}
|
||||
@ -11,9 +11,9 @@ let fs = require('fs'),
|
||||
SUtils = require('../lib/utils');
|
||||
|
||||
/**
|
||||
* Create test project
|
||||
* Create test private
|
||||
* @param config see tests/config.js
|
||||
* @param npmInstallDirs list of dirs relative to project root to execute npm install on
|
||||
* @param npmInstallDirs list of dirs relative to private root to execute npm install on
|
||||
* @returns {Promise} full path to proj temp dir that was just created
|
||||
*/
|
||||
|
||||
@ -27,14 +27,14 @@ module.exports.createTestProject = function(config, npmInstallDirs) {
|
||||
// Create Test Project
|
||||
let tmpProjectPath = path.join(os.tmpdir(), projectName);
|
||||
|
||||
SUtils.sDebug('test_utils', 'Creating test project in ' + tmpProjectPath + '\n');
|
||||
SUtils.sDebug('test_utils', 'Creating test private in ' + tmpProjectPath + '\n');
|
||||
|
||||
// Delete test folder if already exists
|
||||
if (fs.existsSync(tmpProjectPath)) {
|
||||
rimraf.sync(tmpProjectPath);
|
||||
}
|
||||
|
||||
// Copy test project to temp directory
|
||||
// Copy test private to temp directory
|
||||
fs.mkdirSync(tmpProjectPath);
|
||||
wrench.copyDirSyncRecursive(path.join(__dirname, './test-prj'), tmpProjectPath, {
|
||||
forceDelete: true,
|
||||
@ -42,12 +42,12 @@ module.exports.createTestProject = function(config, npmInstallDirs) {
|
||||
|
||||
let lambdasCF = SUtils.readAndParseJsonSync(__dirname + '/../lib/templates/lambdas-cf.json'),
|
||||
resourcesCF = SUtils.readAndParseJsonSync(__dirname + '/../lib/templates/resources-cf.json'),
|
||||
projectJSON = SUtils.readAndParseJsonSync(path.join(tmpProjectPath, 's-project.json'));
|
||||
projectJSON = SUtils.readAndParseJsonSync(path.join(tmpProjectPath, 's-private.json'));
|
||||
|
||||
// Delete Lambda Template
|
||||
delete lambdasCF.Resources.lTemplate;
|
||||
|
||||
// Add project name to AllowedValues
|
||||
// Add private name to AllowedValues
|
||||
resourcesCF.Parameters.ProjectName.AllowedValues.push(projectName);
|
||||
|
||||
// Add stages to AllowedValues
|
||||
@ -69,10 +69,10 @@ module.exports.createTestProject = function(config, npmInstallDirs) {
|
||||
projectJSON.stages[projectStage] = [{
|
||||
region: projectRegion,
|
||||
iamRoleArnLambda: projectLambdaIAMRole,
|
||||
regionBucket: SUtils.generateRegionBucketName(projectRegion, projectDomain)
|
||||
regionBucket: SUtils.generateProjectBucketName(projectRegion, projectDomain)
|
||||
},];
|
||||
|
||||
fs.writeFileSync(path.join(tmpProjectPath, 's-project.json'), JSON.stringify(projectJSON, null, 2));
|
||||
fs.writeFileSync(path.join(tmpProjectPath, 's-private.json'), JSON.stringify(projectJSON, null, 2));
|
||||
|
||||
// Write Admin.env file
|
||||
let adminEnv = 'SERVERLESS_ADMIN_AWS_ACCESS_KEY_ID='
|
||||
@ -81,7 +81,7 @@ module.exports.createTestProject = function(config, npmInstallDirs) {
|
||||
+ process.env.TEST_SERVERLESS_AWS_SECRET_KEY + os.EOL;
|
||||
fs.writeFileSync(path.join(tmpProjectPath, 'admin.env'), adminEnv);
|
||||
|
||||
//Need to run npm install on the test project, they recommend NOT doing this programatically
|
||||
//Need to run npm install on the test private, they recommend NOT doing this programatically
|
||||
//https://github.com/npm/npm#using-npm-programmatically
|
||||
if (npmInstallDirs) {
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
/**
|
||||
* Test: Function Create Action
|
||||
* - Creates a new project in your system's temp directory
|
||||
* - Creates a new private in your system's temp directory
|
||||
* - Creates a new Function inside the "users" module
|
||||
*/
|
||||
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
/**
|
||||
* Test: Module Create Action
|
||||
* - Creates a new project in your system's temp directory
|
||||
* - Creates a new Module inside test project
|
||||
* - Creates a new private in your system's temp directory
|
||||
* - Creates a new Module inside test private
|
||||
*/
|
||||
|
||||
let Serverless = require('../../../lib/Serverless.js'),
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
|
||||
/**
|
||||
* Test: Module Install Action
|
||||
* - Creates a new project in your system's temp directory
|
||||
* - Creates a new private in your system's temp directory
|
||||
* - Installs module-test Module from github using the ModuleInstall action
|
||||
* - asserts that the Module was installed correctly
|
||||
*/
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
/**
|
||||
* Test: Project Create Action
|
||||
* - Creates a new project in your system's temp directory
|
||||
* - Deletes the CF stack created by the project
|
||||
* - Creates a new private in your system's temp directory
|
||||
* - Deletes the CF stack created by the private
|
||||
*/
|
||||
|
||||
let Serverless = require('../../../lib/Serverless'),
|
||||
@ -118,7 +118,7 @@ describe('Test action: Project Create', function() {
|
||||
});
|
||||
|
||||
describe('Project Create', function() {
|
||||
it('should create a new project in temp directory', function(done) {
|
||||
it('should create a new private in temp directory', function(done) {
|
||||
|
||||
this.timeout(0);
|
||||
|
||||
@ -139,7 +139,7 @@ describe('Test action: Project Create', function() {
|
||||
validateEvent(evt);
|
||||
|
||||
// Validate Project JSON
|
||||
let projectJson = utils.readAndParseJsonSync(path.join(os.tmpdir(), name, 's-project.json'));
|
||||
let projectJson = utils.readAndParseJsonSync(path.join(os.tmpdir(), name, 's-private.json'));
|
||||
let region = false;
|
||||
|
||||
for (let i = 0; i < projectJson.stages.development.length; i++) {
|
||||
|
||||
@ -2,8 +2,8 @@
|
||||
|
||||
/**
|
||||
* Test: Resources Deploy Action
|
||||
* - Creates a new project in your system's temp directory
|
||||
* - Makes a tiny update to the project's CF template
|
||||
* - Creates a new private in your system's temp directory
|
||||
* - Makes a tiny update to the private's CF template
|
||||
* - Deploy new CF template
|
||||
* - Deploy/Rollback the original CF template for cleaning
|
||||
*/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user