seperated packaging and deployment

This commit is contained in:
Eslam A. Hefnawy 2017-03-09 23:01:45 +08:00
parent 4d7eeb0119
commit e38f22585f
76 changed files with 361 additions and 251 deletions

View File

@ -103,6 +103,7 @@ class Service {
if (serverlessFile.package) {
that.package.individually = serverlessFile.package.individually;
that.package.path = serverlessFile.package.path;
that.package.artifact = serverlessFile.package.artifact;
that.package.exclude = serverlessFile.package.exclude;
that.package.include = serverlessFile.package.include;

View File

@ -3,7 +3,7 @@
"./config/config.js",
"./create/create.js",
"./install/install.js",
"./package/index.js",
"./package/package.js",
"./deploy/deploy.js",
"./invoke/invoke.js",
"./info/info.js",
@ -15,22 +15,23 @@
"./aws/configCredentials/awsConfigCredentials.js",
"./aws/provider/awsProvider.js",
"./aws/deploy/index.js",
"./aws/package/index.js",
"./aws/invoke/index.js",
"./aws/info/index.js",
"./aws/logs/index.js",
"./aws/metrics/awsMetrics.js",
"./aws/remove/index.js",
"./aws/rollback/index.js",
"./aws/deploy/compile/functions/index.js",
"./aws/deploy/compile/events/schedule/index.js",
"./aws/deploy/compile/events/s3/index.js",
"./aws/deploy/compile/events/apiGateway/index.js",
"./aws/deploy/compile/events/sns/index.js",
"./aws/deploy/compile/events/stream/index.js",
"./aws/deploy/compile/events/alexaSkill/index.js",
"./aws/deploy/compile/events/iot/index.js",
"./aws/deploy/compile/events/cloudWatchEvent/index.js",
"./aws/deploy/compile/events/cloudWatchLog/index.js",
"./aws/package/compile/functions/index.js",
"./aws/package/compile/events/schedule/index.js",
"./aws/package/compile/events/s3/index.js",
"./aws/package/compile/events/apiGateway/index.js",
"./aws/package/compile/events/sns/index.js",
"./aws/package/compile/events/stream/index.js",
"./aws/package/compile/events/alexaSkill/index.js",
"./aws/package/compile/events/iot/index.js",
"./aws/package/compile/events/cloudWatchEvent/index.js",
"./aws/package/compile/events/cloudWatchLog/index.js",
"./aws/deployFunction/index.js",
"./aws/deployList/index.js",
"./aws/invokeLocal/index.js"

View File

@ -1,17 +1,23 @@
'use strict';
/*
* serverless package => package in default .serverless dir
* serverless package --package => package in custom path
*
* serverless deploy => package in default .serverless & deploy from default .serverless
* serverless deploy --package => deploy from custom path
*/
const BbPromise = require('bluebird');
const validate = require('../lib/validate');
const runPackage = require('./lib/runPackage');
const extendedValidate = require('./lib/extendedValidate');
const monitorStack = require('../lib/monitorStack');
const createStack = require('./lib/createStack');
const mergeCustomProviderResources = require('./lib/mergeCustomProviderResources');
const generateArtifactDirectoryName = require('./lib/generateArtifactDirectoryName');
const setBucketName = require('../lib/setBucketName');
const cleanupS3Bucket = require('./lib/cleanupS3Bucket');
const uploadArtifacts = require('./lib/uploadArtifacts');
const updateStack = require('../lib/updateStack');
const configureStack = require('./lib/configureStack');
const mergeIamTemplates = require('./lib/mergeIamTemplates');
class AwsDeploy {
constructor(serverless, options) {
@ -22,41 +28,30 @@ class AwsDeploy {
Object.assign(
this,
validate,
runPackage,
extendedValidate,
createStack,
generateArtifactDirectoryName,
mergeCustomProviderResources,
setBucketName,
cleanupS3Bucket,
uploadArtifacts,
updateStack,
monitorStack,
configureStack,
mergeIamTemplates
monitorStack
);
this.hooks = {
'before:deploy:initialize': () => BbPromise.bind(this)
.then(this.validate),
'deploy:initialize': () => BbPromise.bind(this)
.then(this.configureStack),
'deploy:setupProviderConfiguration': () => BbPromise.bind(this)
.then(this.createStack)
.then(this.mergeIamTemplates),
'before:deploy:compileFunctions': () => BbPromise.bind(this)
.then(this.generateArtifactDirectoryName),
.then(this.validate)
.then(this.runPackage)
.then(this.extendedValidate),
'deploy:deploy': () => BbPromise.bind(this)
.then(this.mergeCustomProviderResources)
.then(this.createStack)
.then(this.setBucketName)
.then(this.uploadArtifacts)
.then(this.updateStack)
.then(this.cleanupS3Bucket)
.then(() => {
if (this.options.noDeploy) this.serverless.cli.log('Did not deploy due to --noDeploy');
}),
.then(this.updateStack),
'deploy:finalize': () => BbPromise.bind(this)
.then(this.cleanupS3Bucket),
};
}
}

View File

@ -51,10 +51,6 @@ module.exports = {
},
cleanupS3Bucket() {
if (this.options.noDeploy) {
return BbPromise.resolve();
}
return BbPromise.bind(this)
.then(this.getObjectsToRemove)
.then(this.removeObjects);

View File

@ -1,7 +1,6 @@
'use strict';
const _ = require('lodash');
const path = require('path');
const BbPromise = require('bluebird');
module.exports = {
@ -9,6 +8,7 @@ module.exports = {
// Note: using three dots instead of ellipsis to support non uni-code consoles.
this.serverless.cli.log('Creating Stack...');
const stackName = this.provider.naming.getStackName();
let stackTags = { STAGE: this.options.stage };
// Merge additional stack tags
@ -24,8 +24,7 @@ module.exports = {
'CAPABILITY_NAMED_IAM',
],
Parameters: [],
TemplateBody: JSON.stringify(this.serverless.service.provider
.compiledCloudFormationTemplate),
TemplateBody: JSON.stringify(this.coreTemplateFileBody),
Tags: Object.keys(stackTags).map((key) => ({ Key: key, Value: stackTags[key] })),
};
@ -56,19 +55,12 @@ module.exports = {
}
return BbPromise.bind(this)
// always write the template to disk, whether we are deploying or not
.then(this.writeCreateTemplateToDisk)
.then(() => {
if (this.options.noDeploy) {
return BbPromise.resolve();
}
return this.provider.request('CloudFormation',
.then(() => this.provider.request('CloudFormation',
'describeStackResources',
{ StackName: stackName },
this.options.stage,
this.options.region)
.then(() => BbPromise.resolve('alreadyCreated'));
})
.then(() => BbPromise.resolve('alreadyCreated')))
.catch((e) => {
if (e.message.indexOf('does not exist') > -1) {
if (this.serverless.service.provider.deploymentBucket) {
@ -82,18 +74,4 @@ module.exports = {
throw new this.serverless.classes.Error(e);
});
},
// helper methods
writeCreateTemplateToDisk() {
if (this.serverless.service.provider.deploymentBucket) {
return BbPromise.resolve();
}
const cfTemplateFilePath = path.join(this.serverless.config.servicePath,
'.serverless', 'cloudformation-template-create-stack.json');
this.serverless.utils.writeFileSync(cfTemplateFilePath,
this.serverless.service.provider.compiledCloudFormationTemplate);
return BbPromise.resolve();
},
};

View File

@ -0,0 +1,75 @@
'use strict';
const path = require('path');
const BbPromise = require('bluebird');
const _ = require('lodash');
module.exports = {
getArtifactS3Key() {
const getArtifactS3KeysArray = function (obj, key) {
if (_.has(obj, key)) {
return obj[key];
}
return _.flatten(_
.map(obj, (v) => typeof v === 'object' ? getArtifactS3KeysArray(v, key) : false), true);
};
return getArtifactS3KeysArray(this.compiledTemplateFileBody, 'S3Key')
.find(item => item !== false);
},
extendedValidate() {
this.packagePath = this.options.package ||
this.serverless.service.package.path ||
path.join(this.serverless.config.servicePath, '.serverless');
if (this.serverless.service.package.individually) {
// artifact file validation (multiple function artifacts)
this.serverless.service.getAllFunctions().forEach(functionName => {
const artifactFileName = this.provider.naming.getFunctionArtifactName(functionName);
const artifactFilePath = path.join(this.packagePath, artifactFileName);
if (!this.serverless.utils.fileExistsSync(artifactFilePath)) {
throw new this.serverless.classes
.Error(`No ${artifactFileName} file found in the package path you provided.`);
}
});
} else {
// artifact file validation (single service artifact)
const artifactFileName = this.provider.naming.getServiceArtifactName();
const artifactFilePath = path.join(this.packagePath, artifactFileName);
if (!this.serverless.utils.fileExistsSync(artifactFilePath)) {
throw new this.serverless.classes
.Error(`No ${artifactFileName} file found in the package path you provided.`);
}
}
// compiled template file validation
const compiledTemplateFileName = this.provider.naming.getCompiledTemplateFileName();
this.compiledTemplateFilePath = path.join(this.packagePath, compiledTemplateFileName);
if (!this.serverless.utils.fileExistsSync(this.compiledTemplateFilePath)) {
throw new this.serverless.classes
.Error(`No ${compiledTemplateFileName} file found in the package path you provided.`);
}
this.compiledTemplateFileBody = this.serverless
.utils.readFileSync(this.compiledTemplateFilePath);
if (!this.serverless.service.deploymentBucket) {
// core template file validation
const coreTemplateFileName = this.provider.naming.getCoreTemplateFileName();
this.coreTemplateFilePath = path.join(this.packagePath, coreTemplateFileName);
if (!this.serverless.utils.fileExistsSync(this.coreTemplateFilePath)) {
throw new this.serverless.classes
.Error(`No ${coreTemplateFileName} file found in the package path you provided.`);
}
this.coreTemplateFileBody = this.serverless
.utils.readFileSync(this.coreTemplateFilePath);
}
this.packageS3DirKey = this.getArtifactS3Key().split(path.sep);
this.packageS3DirKey.pop();
this.packageS3DirKey = this.packageS3DirKey.join(path.sep);
return BbPromise.resolve();
},
};

View File

@ -0,0 +1,18 @@
'use strict';
const path = require('path');
const BbPromise = require('bluebird');
const execSync = require('child_process').execSync;
module.exports = {
runPackage() {
const serverlessExec = path.join(this.serverless.config
.serverlessPath, '..', 'bin', 'serverless');
if (!this.options.package) {
execSync(`${serverlessExec} package`, { stdio: 'inherit' });
}
return BbPromise.resolve();
},
};

View File

@ -1,21 +1,21 @@
'use strict';
const fs = require('fs');
const path = require('path');
const BbPromise = require('bluebird');
const filesize = require('filesize');
const path = require('path');
module.exports = {
uploadCloudFormationFile() {
this.serverless.cli.log('Uploading CloudFormation file to S3...');
const body = JSON.stringify(this.serverless.service.provider.compiledCloudFormationTemplate);
const compiledTemplateFileName = this.provider.naming.getCompiledTemplateFileName();
const fileName = 'compiled-cloudformation-template.json';
const body = JSON.stringify(this.compiledTemplateFileBody);
const params = {
Bucket: this.bucketName,
Key: `${this.serverless.service.package.artifactDirectoryName}/${fileName}`,
Key: `${this.packageS3DirKey}/${compiledTemplateFileName}`,
Body: body,
ContentType: 'application/json',
};
@ -27,17 +27,17 @@ module.exports = {
this.options.region);
},
uploadZipFile(artifactFilePath) {
if (!artifactFilePath) {
throw new this.serverless.classes.Error('artifactFilePath was not supplied');
uploadZipFile(functionName) {
let fileName = this.provider.naming.getServiceArtifactName();
if (functionName) {
fileName = this.provider.naming.getFunctionArtifactName(functionName);
}
const fileName = artifactFilePath.split(path.sep).pop();
const artifactFilePath = path.join(this.packagePath, fileName);
const params = {
Bucket: this.bucketName,
Key: `${this.serverless.service.package.artifactDirectoryName}/${fileName}`,
Key: `${this.packageS3DirKey}/${fileName}`,
Body: fs.createReadStream(artifactFilePath),
ContentType: 'application/zip',
};
@ -72,13 +72,24 @@ module.exports = {
}
return BbPromise.resolve();
});
if (this.serverless.service.package.individually) {
this.serverless.cli.log('Uploading function .zip files to S3...');
const functionNames = this.serverless.service.getAllFunctions();
const uploadPromises = functionNames.map(name => this.uploadZipFile(name));
return BbPromise.all(uploadPromises);
}
const fileName = this.provider.naming.getServiceArtifactName();
const artifactFilePath = path.join(this.packagePath, fileName);
const stats = fs.statSync(artifactFilePath);
this.serverless.cli.log(`Uploading service .zip file to S3 (${filesize(stats.size)})...`);
return this.uploadZipFile();
},
uploadArtifacts() {
if (this.options.noDeploy) {
return BbPromise.resolve();
}
return BbPromise.bind(this)
.then(this.uploadCloudFormationFile)
.then(this.uploadFunctions);

View File

@ -1,17 +1,21 @@
'use strict';
const BbPromise = require('bluebird');
const path = require('path');
const fs = require('fs');
const validate = require('../lib/validate');
const filesize = require('filesize');
// The Package plugin which is used to zip the service
const Package = require('../../package');
const Package = require('../package');
class AwsDeployFunction {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options || {};
this.packagePath = this.options.package ||
this.serverless.service.package.path ||
path.join(this.serverless.config.servicePath, '.serverless');
this.provider = this.serverless.getProvider('aws');
this.pkg = new Package(this.serverless, this.options);
@ -72,7 +76,10 @@ class AwsDeployFunction {
}
deployFunction() {
const data = fs.readFileSync(this.options.functionObj.artifact);
const artifactFileName = this.provider.naming
.getFunctionArtifactName(this.options.function);
const artifactFilePath = path.join(this.packagePath, artifactFileName);
const data = fs.readFileSync(artifactFilePath);
const params = {
FunctionName: this.options.functionObj.name,
@ -80,7 +87,7 @@ class AwsDeployFunction {
};
// Get function stats
const stats = fs.statSync(this.options.functionObj.artifact);
const stats = fs.statSync(artifactFilePath);
this.serverless.cli.log(
`Uploading function: ${this.options.function} (${filesize(stats.size)})...`
);

View File

@ -6,8 +6,6 @@ const chalk = require('chalk');
module.exports = {
monitorStack(action, cfData, frequency) {
// Skip monitoring if a deployment should not be performed
if (this.options.noDeploy) return BbPromise.bind(this).then(BbPromise.resolve());
// Skip monitoring if stack was already created
if (cfData === 'alreadyCreated') return BbPromise.bind(this).then(BbPromise.resolve());

View File

@ -49,6 +49,22 @@ module.exports = {
return `${this.provider.serverless.service.service}-${this.provider.getStage()}`;
},
getServiceArtifactName() {
return `${this.provider.serverless.service.service}.zip`;
},
getFunctionArtifactName(functionName) {
return `${functionName}.zip`;
},
getCompiledTemplateFileName() {
return 'cf-compiled-template.json';
},
getCoreTemplateFileName() {
return 'cf-core-template.json';
},
// Role
getRolePath() {
return '/';

View File

@ -8,10 +8,6 @@ module.exports = {
return BbPromise.resolve(this.bucketName);
}
if (this.options.noDeploy) {
return BbPromise.resolve();
}
return this.provider.getServerlessDeploymentBucketName(this.options.stage, this.options.region)
.then((bucketName) => {
this.bucketName = bucketName;

View File

@ -13,11 +13,9 @@ module.exports = {
const stackName = this.provider.naming.getStackName();
let stackTags = { STAGE: this.options.stage };
const templateUrl = `https://s3.amazonaws.com/${
this.bucketName
}/${
this.serverless.service.package.artifactDirectoryName
}/compiled-cloudformation-template.json`;
const compiledTemplateFileName = this.provider.naming.getCompiledTemplateFileName();
const templateUrl = `https://s3.amazonaws.com/${this.bucketName}/${this.packageS3DirKey}/${compiledTemplateFileName}`;
// Merge additional stack tags
if (typeof this.serverless.service.provider.stackTags === 'object') {
stackTags = _.extend(stackTags, this.serverless.service.provider.stackTags);
@ -44,11 +42,8 @@ module.exports = {
},
update() {
const templateUrl = `https://s3.amazonaws.com/${
this.bucketName
}/${
this.serverless.service.package.artifactDirectoryName
}/compiled-cloudformation-template.json`;
const compiledTemplateFileName = this.provider.naming.getCompiledTemplateFileName();
const templateUrl = `https://s3.amazonaws.com/${this.bucketName}/${this.packageS3DirKey}/${compiledTemplateFileName}`;
this.serverless.cli.log('Updating Stack...');
const stackName = this.provider.naming.getStackName();
@ -97,13 +92,9 @@ module.exports = {
},
updateStack() {
// just write the template to disk if a deployment should not be performed
return BbPromise.bind(this)
.then(this.writeUpdateTemplateToDisk)
.then(() => {
if (this.options.noDeploy) {
return BbPromise.resolve();
} else if (this.createLater) {
if (this.createLater) {
return BbPromise.bind(this)
.then(this.createFallback);
}
@ -111,16 +102,4 @@ module.exports = {
.then(this.update);
});
},
// helper methods
writeUpdateTemplateToDisk() {
const updateOrCreate = this.createLater ? 'create' : 'update';
const cfTemplateFilePath = path.join(this.serverless.config.servicePath,
'.serverless', `cloudformation-template-${updateOrCreate}-stack.json`);
this.serverless.utils.writeFileSync(cfTemplateFilePath,
this.serverless.service.provider.compiledCloudFormationTemplate);
return BbPromise.resolve();
},
};

View File

@ -8,7 +8,7 @@ class AwsCompileAlexaSkillEvents {
this.provider = this.serverless.getProvider('aws');
this.hooks = {
'deploy:compileEvents': this.compileAlexaSkillEvents.bind(this),
'package:compileEvents': this.compileAlexaSkillEvents.bind(this),
};
}

View File

@ -38,7 +38,7 @@ class AwsCompileApigEvents {
);
this.hooks = {
'deploy:compileEvents': () => {
'package:compileEvents': () => {
this.validated = this.validate();
if (this.validated.events.length === 0) {

View File

@ -8,7 +8,7 @@ class AwsCompileCloudWatchEventEvents {
this.provider = this.serverless.getProvider('aws');
this.hooks = {
'deploy:compileEvents': this.compileCloudWatchEventEvents.bind(this),
'package:compileEvents': this.compileCloudWatchEventEvents.bind(this),
};
}

View File

@ -8,7 +8,7 @@ class AwsCompileIoTEvents {
this.provider = this.serverless.getProvider('aws');
this.hooks = {
'deploy:compileEvents': this.compileIoTEvents.bind(this),
'package:compileEvents': this.compileIoTEvents.bind(this),
};
}

View File

@ -8,7 +8,7 @@ class AwsCompileS3Events {
this.provider = this.serverless.getProvider('aws');
this.hooks = {
'deploy:compileEvents': this.compileS3Events.bind(this),
'package:compileEvents': this.compileS3Events.bind(this),
};
}

View File

@ -8,7 +8,7 @@ class AwsCompileScheduledEvents {
this.provider = this.serverless.getProvider('aws');
this.hooks = {
'deploy:compileEvents': this.compileScheduledEvents.bind(this),
'package:compileEvents': this.compileScheduledEvents.bind(this),
};
}

View File

@ -8,7 +8,7 @@ class AwsCompileSNSEvents {
this.provider = this.serverless.getProvider('aws');
this.hooks = {
'deploy:compileEvents': this.compileSNSEvents.bind(this),
'package:compileEvents': this.compileSNSEvents.bind(this),
};
}

View File

@ -8,7 +8,7 @@ class AwsCompileStreamEvents {
this.provider = this.serverless.getProvider('aws');
this.hooks = {
'deploy:compileEvents': this.compileStreamEvents.bind(this),
'package:compileEvents': this.compileStreamEvents.bind(this),
};
}

View File

@ -9,6 +9,11 @@ class AwsCompileFunctions {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
this.packagePath = this.options.package ||
this.serverless.service.package.path ||
path.join(this.serverless.config.servicePath, '.serverless');
this.provider = this.serverless.getProvider('aws');
this.compileFunctions = this.compileFunctions.bind(this);
@ -20,7 +25,7 @@ class AwsCompileFunctions {
}
this.hooks = {
'deploy:compileFunctions': this.compileFunctions,
'package:compileFunctions': this.compileFunctions,
};
}
@ -74,6 +79,14 @@ class AwsCompileFunctions {
} else {
artifactFilePath = this.serverless.service.package.artifact;
}
const serviceArtifactFileName = this.provider.naming.getServiceArtifactName();
const functionArtifactFileName = this.provider.naming.getFunctionArtifactName(functionName);
const artifactFileName = this.serverless.service.package.individually ?
functionArtifactFileName :
serviceArtifactFileName;
const artifactFilePath = path.join(this.packagePath, artifactFileName);
if (!artifactFilePath) {
throw new Error(`No artifact path is set for function: "${functionName}"`);

View File

@ -0,0 +1,57 @@
'use strict';
const BbPromise = require('bluebird');
const path = require('path');
const validate = require('../lib/validate');
const mergeCustomProviderResources = require('./lib/mergeCustomProviderResources');
const generateArtifactDirectoryName = require('./lib/generateArtifactDirectoryName');
const generateCoreTemplate = require('./lib/generateCoreTemplate');
const generateCompiledTemplate = require('./lib/generateCompiledTemplate');
const mergeIamTemplates = require('./lib/mergeIamTemplates');
const zipService = require('./lib/zipService');
const packageService = require('./lib/packageService');
const cleanup = require('./lib/cleanup');
class AwsPackage {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
this.packagePath = this.options.package ||
this.serverless.service.package.path ||
path.join(this.serverless.config.servicePath, '.serverless');
this.provider = this.serverless.getProvider('aws');
Object.assign(
this,
cleanup,
validate,
packageService,
zipService,
generateCoreTemplate,
mergeIamTemplates,
generateArtifactDirectoryName,
mergeCustomProviderResources,
generateCompiledTemplate
);
this.hooks = {
'package:cleanup': () => BbPromise.bind(this)
.then(this.cleanup),
'package:createDeploymentArtifacts': () => BbPromise.bind(this)
.then(this.validate)
.then(this.packageService),
'package:initialize': () => BbPromise.bind(this)
.then(this.generateCoreTemplate)
.then(this.mergeIamTemplates)
.then(this.generateArtifactDirectoryName),
'package:finalize': () => BbPromise.bind(this)
.then(this.mergeCustomProviderResources)
.then(this.generateCompiledTemplate),
};
}
}
module.exports = AwsPackage;

View File

@ -0,0 +1,18 @@
'use strict';
const BbPromise = require('bluebird');
const path = require('path');
module.exports = {
generateCompiledTemplate() {
const compiledTemplateFileName = this.provider.naming.getCompiledTemplateFileName();
const compiledTemplateFilePath = path.join(this.packagePath, compiledTemplateFileName);
this.serverless.utils.writeFileSync(compiledTemplateFilePath,
this.serverless.service.provider.compiledCloudFormationTemplate);
return BbPromise.resolve();
},
};

View File

@ -4,13 +4,13 @@ const BbPromise = require('bluebird');
const path = require('path');
module.exports = {
configureStack() {
generateCoreTemplate() {
this.serverless.service.provider
.compiledCloudFormationTemplate = this.serverless.utils.readFileSync(
path.join(this.serverless.config.serverlessPath,
'plugins',
'aws',
'deploy',
'package',
'lib',
'core-cloudformation-template.json')
);
@ -47,6 +47,17 @@ module.exports = {
});
}
if (this.serverless.service.provider.deploymentBucket) {
return BbPromise.resolve();
}
const coreTemplateFileName = this.provider.naming.getCoreTemplateFileName();
const coreTemplateFilePath = path.join(this.packagePath, coreTemplateFileName);
this.serverless.utils.writeFileSync(coreTemplateFilePath,
this.serverless.service.provider.compiledCloudFormationTemplate);
return BbPromise.resolve();
},

View File

@ -6,7 +6,7 @@ const expect = require('chai').expect;
const AwsProvider = require('../../provider/awsProvider');
const Serverless = require('../../../../Serverless');
const validate = require('../../lib/validate');
const configureStack = require('../lib/configureStack');
const configureStack = require('generateStackCreateTemplate');
describe('#configureStack', () => {
let serverless;

View File

@ -53,7 +53,7 @@ module.exports = {
path.join(this.serverless.config.serverlessPath,
'plugins',
'aws',
'deploy',
'package',
'lib',
'iam-role-lambda-execution-template.json')
);

View File

@ -26,14 +26,6 @@ module.exports = {
return _.union(this.defaultExcludes, packageExcludes, exclude);
},
getServiceArtifactName() {
return `${this.serverless.service.service}.zip`;
},
getFunctionArtifactName(functionObject) {
return `${functionObject.name}.zip`;
},
packageService() {
// check if the user has specified an own artifact
if (this.serverless.service.package.artifact) {
@ -42,6 +34,13 @@ module.exports = {
let shouldPackageService = false;
this.serverless.cli.log('Packaging service...');
if (this.serverless.service.package.individually) {
const allFunctions = this.serverless.service.getAllFunctions();
const packagePromises = _.map(allFunctions, functionName =>
this.packageFunction(functionName));
this.serverless.cli.log('Packaging service...');
const allFunctions = this.serverless.service.getAllFunctions();
const packagePromises = _.map(allFunctions, functionName => {
@ -65,42 +64,21 @@ module.exports = {
},
packageAll() {
const servicePath = this.serverless.config.servicePath;
const exclude = this.getExcludes();
const include = this.getIncludes();
const zipFileName = this.getServiceArtifactName();
const zipFileName = this.provider.naming.getServiceArtifactName();
return this.zipDirectory(servicePath, exclude, include, zipFileName).then(filePath => {
this.serverless.service.package.artifact = filePath;
return filePath;
});
return this.zipDirectory(exclude, include, zipFileName);
},
packageFunction(functionName) {
const functionObject = this.serverless.service.getFunction(functionName);
const funcPackageConfig = functionObject.package || {};
functionObject.artifact = null; // reset the current artifact
if (funcPackageConfig.artifact) {
if (process.env.SLS_DEBUG) {
this.serverless.cli.log('package.artifact is defined, skipping packaging');
}
functionObject.artifact = funcPackageConfig.artifact;
return BbPromise.resolve(funcPackageConfig.artifact);
}
const servicePath = this.serverless.config.servicePath;
const exclude = this.getExcludes(funcPackageConfig.exclude);
const include = this.getIncludes(funcPackageConfig.include);
const zipFileName = this.getFunctionArtifactName(functionObject);
const zipFileName = this.provider.naming.getFunctionArtifactName(functionName);
return this.zipDirectory(servicePath, exclude, include, zipFileName).then((artifactPath) => {
functionObject.artifact = artifactPath;
return artifactPath;
});
return this.zipDirectory(exclude, include, zipFileName);
},
};

View File

@ -7,7 +7,7 @@ const fs = require('fs');
const globby = require('globby');
module.exports = {
zipDirectory(servicePath, exclude, include, zipFileName) {
zipDirectory(exclude, include, zipFileName) {
const patterns = ['**'];
exclude.forEach((pattern) => {
@ -26,9 +26,7 @@ module.exports = {
const zip = archiver.create('zip');
const artifactFilePath = path.join(
servicePath,
'.serverless',
const artifactFilePath = path.join(this.packagePath,
zipFileName
);
@ -40,7 +38,7 @@ module.exports = {
zip.pipe(output);
const files = globby.sync(patterns, {
cwd: servicePath,
cwd: this.serverless.config.servicePath,
dot: true,
silent: true,
follow: true,
@ -48,7 +46,7 @@ module.exports = {
files.forEach((filePath) => {
const fullPath = path.resolve(
servicePath,
this.serverless.config.servicePath,
filePath
);

View File

@ -8,13 +8,9 @@ class Deploy {
deploy: {
usage: 'Deploy a Serverless service',
lifecycleEvents: [
'cleanup',
'initialize',
'setupProviderConfiguration',
'createDeploymentArtifacts',
'compileFunctions',
'compileEvents',
'deploy',
'finalize',
],
options: {
stage: {
@ -25,9 +21,9 @@ class Deploy {
usage: 'Region of the service',
shortcut: 'r',
},
noDeploy: {
usage: 'Build artifacts without deploying',
shortcut: 'n',
package: {
usage: 'Path of the deployment package',
shortcut: 'p',
},
verbose: {
usage: 'Show all stack events during deployment',

View File

@ -1,33 +0,0 @@
'use strict';
const BbPromise = require('bluebird');
const validate = require('./lib/validate');
const zipService = require('./lib/zipService');
const packageService = require('./lib/packageService');
const cleanup = require('./lib/cleanup');
class Package {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
Object.assign(
this,
validate,
zipService,
packageService,
cleanup
);
this.hooks = {
'deploy:cleanup': () => BbPromise.bind(this)
.then(this.cleanup),
'deploy:createDeploymentArtifacts': () => BbPromise.bind(this)
.then(this.validate)
.then(this.packageService),
};
}
}
module.exports = Package;

View File

@ -1,14 +0,0 @@
'use strict';
const BbPromise = require('bluebird');
module.exports = {
validate() {
if (!this.serverless.config.servicePath) {
throw new this.serverless.classes
.Error('This command can only be run inside a service directory');
}
return BbPromise.resolve();
},
};

View File

@ -1,25 +0,0 @@
'use strict';
const expect = require('chai').expect;
const Package = require('../index');
const Serverless = require('../../../../lib/Serverless');
describe('#validate()', () => {
let serverless;
let packageService;
beforeEach(() => {
serverless = new Serverless();
packageService = new Package(serverless);
});
it('should throw error if not inside service (servicePath not defined)', () => {
packageService.serverless.config.servicePath = false;
expect(() => packageService.validate()).to.throw(Error);
});
it('should resolve if servicePath is given', (done) => {
packageService.serverless.config.servicePath = true;
packageService.validate().then(() => done());
});
});

View File

@ -0,0 +1,39 @@
'use strict';
class Package {
constructor(serverless, options) {
this.serverless = serverless;
this.options = options;
this.commands = {
package: {
usage: 'Packages a Serverless service',
lifecycleEvents: [
'cleanup',
'createDeploymentArtifacts',
'initialize',
'compileFunctions',
'compileEvents',
'finalize',
],
options: {
stage: {
usage: 'Stage of the service',
shortcut: 's',
},
region: {
usage: 'Region of the service',
shortcut: 'r',
},
package: {
usage: 'Output path for the package',
shortcut: 'p',
},
},
},
};
}
}
module.exports = Package;

View File

@ -2,7 +2,8 @@
const expect = require('chai').expect;
const sinon = require('sinon');
const Package = require('./index');
const BbPromise = require('bluebird');
const Package = require('./package');
const Serverless = require('../../../lib/Serverless');
describe('#constructor()', () => {