serverless/lib/plugins/aws/deploy/lib/checkForChanges.js
2017-07-03 11:10:29 +02:00

120 lines
3.6 KiB
JavaScript

'use strict';
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const globby = require('globby');
const BbPromise = require('bluebird');
const _ = require('lodash');
const normalizeFiles = require('../../lib/normalizeFiles');
module.exports = {
checkForChanges() {
this.serverless.service.provider.shouldNotDeploy = false;
if (this.options.force) {
return BbPromise.resolve();
}
return BbPromise.bind(this)
.then(this.getMostRecentObjects)
.then(this.getObjectMetadata)
.then(this.checkIfDeploymentIsNecessary);
},
getMostRecentObjects() {
const service = this.serverless.service.service;
const params = {
Bucket: this.bucketName,
Prefix: `serverless/${service}/${this.options.stage}`,
};
return this.provider.request('S3',
'listObjectsV2',
params,
this.options.stage,
this.options.region
).then((result) => {
if (result && result.Contents && result.Contents.length) {
const objects = result.Contents;
const ordered = _.orderBy(objects, ['Key'], ['desc']);
const firstKey = ordered[0].Key;
const directory = firstKey.substring(0, firstKey.lastIndexOf('/'));
const mostRecentObjects = ordered.filter((obj) => {
const objKey = obj.Key;
const objDirectory = objKey.substring(0, objKey.lastIndexOf('/'));
return directory === objDirectory;
});
return BbPromise.resolve(mostRecentObjects);
}
return BbPromise.resolve([]);
});
},
getObjectMetadata(objects) {
if (objects && objects.length) {
const headObjectObjects = objects
.map((obj) => this.provider.request('S3',
'headObject',
{
Bucket: this.bucketName,
Key: obj.Key,
},
this.options.stage,
this.options.region
));
return BbPromise.all(headObjectObjects)
.then((result) => result);
}
return BbPromise.resolve([]);
},
checkIfDeploymentIsNecessary(objects) {
if (objects && objects.length) {
const remoteHashes = objects.map((object) => object.Metadata.filesha256 || '');
const serverlessDirPath = path.join(this.serverless.config.servicePath, '.serverless');
// create a hash of the CloudFormation body
const compiledCfTemplate = this.serverless.service.provider.compiledCloudFormationTemplate;
const normCfTemplate = normalizeFiles.normalizeCloudFormationTemplate(compiledCfTemplate);
const localCfHash = crypto
.createHash('sha256')
.update(JSON.stringify(normCfTemplate))
.digest('base64');
// create hashes for all the zip files
const zipFiles = globby.sync(['**.zip'], { cwd: serverlessDirPath, dot: true, silent: true });
const zipFilePaths = zipFiles.map((zipFile) => path.join(serverlessDirPath, zipFile));
const zipFileHashes = zipFilePaths.map((zipFilePath) => {
// TODO refactor to be async (use util function to compute checksum async)
const zipFile = fs.readFileSync(zipFilePath);
return crypto.createHash('sha256').update(zipFile).digest('base64');
});
const localHashes = zipFileHashes;
localHashes.push(localCfHash);
if (_.isEqual(remoteHashes.sort(), localHashes.sort())) {
this.serverless.service.provider.shouldNotDeploy = true;
const message = [
'Service files not changed. Skipping deployment...',
].join('');
this.serverless.cli.log(message);
}
}
return BbPromise.resolve();
},
};