diff --git a/README.md b/README.md index 93acd11..51a6a26 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,8 @@ custom: CacheControl: 'no-cache' - "*.js": CacheControl: 'public, max-age=31536000' + - bucketNameKey: AnotherBucketNameOutputKey + localDir: path/to/another resources: Resources: @@ -55,6 +57,11 @@ resources: WebsiteConfiguration: IndexDocument: index.html ErrorDocument: error.html + AnotherBucket: + Type: AWS::S3::Bucket + Outputs: + AnotherBucketNameOutputKey: + Value: !Ref AnotherBucket ``` ## Usage diff --git a/index.js b/index.js index 5309c27..6069456 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,7 @@ const s3 = require('@auth0/s3'); const chalk = require('chalk'); const minimatch = require('minimatch'); const path = require('path'); +const resolveStackOutput = require('./resolveStackOutput') const messagePrefix = 'S3 Sync: '; @@ -67,7 +68,7 @@ class ServerlessS3Sync { if (s.hasOwnProperty('defaultContentType')) { defaultContentType = s.defaultContentType; } - if (!s.bucketName || !s.localDir) { + if ((!s.bucketName && !s.bucketNameKey) || !s.localDir) { throw 'Invalid custom.s3Sync'; } let deleteRemoved = true; @@ -75,56 +76,59 @@ class ServerlessS3Sync { deleteRemoved = s.deleteRemoved; } - return new Promise((resolve) => { - const localDir = [servicePath, s.localDir].join('/'); - - const params = { - maxAsyncS3: 5, - localDir, - deleteRemoved, - followSymlinks: followSymlinks, - getS3Params: (localFile, stat, cb) => { - const s3Params = {}; - - if(Array.isArray(s.params)) { - s.params.forEach((param) => { - const glob = Object.keys(param)[0]; - if(minimatch(localFile, `${path.resolve(localDir)}/${glob}`)) { - Object.assign(s3Params, this.extractMetaParams(param) || {}); + return this.getBucketName(s) + .then(bucketName => { + return new Promise((resolve) => { + const localDir = [servicePath, s.localDir].join('/'); + + const params = { + maxAsyncS3: 5, + localDir, + deleteRemoved, + followSymlinks: followSymlinks, + getS3Params: (localFile, stat, cb) => { + const s3Params = {}; + + if(Array.isArray(s.params)) { + s.params.forEach((param) => { + const glob = Object.keys(param)[0]; + if(minimatch(localFile, `${path.resolve(localDir)}/${glob}`)) { + Object.assign(s3Params, this.extractMetaParams(param) || {}); + } + }); } - }); - } - cb(null, s3Params); - }, - s3Params: { - Bucket: s.bucketName, - Prefix: bucketPrefix, - ACL: acl - } - }; - if (typeof(defaultContentType) != 'undefined') { - Object.assign(params, {defaultContentType: defaultContentType}) - } - const uploader = this.client().uploadDir(params); - uploader.on('error', (err) => { - throw err; - }); - let percent = 0; - uploader.on('progress', () => { - if (uploader.progressTotal === 0) { - return; - } - const current = Math.round((uploader.progressAmount / uploader.progressTotal) * 10) * 10; - if (current > percent) { - percent = current; - cli.printDot(); - } - }); - uploader.on('end', () => { - resolve('done'); + cb(null, s3Params); + }, + s3Params: { + Bucket: bucketName, + Prefix: bucketPrefix, + ACL: acl + } + }; + if (typeof(defaultContentType) != 'undefined') { + Object.assign(params, {defaultContentType: defaultContentType}) + } + const uploader = this.client().uploadDir(params); + uploader.on('error', (err) => { + throw err; + }); + let percent = 0; + uploader.on('progress', () => { + if (uploader.progressTotal === 0) { + return; + } + const current = Math.round((uploader.progressAmount / uploader.progressTotal) * 10) * 10; + if (current > percent) { + percent = current; + cli.printDot(); + } + }); + uploader.on('end', () => { + resolve('done'); + }); + }); }); - }); }); return Promise.all(promises) .then(() => { @@ -146,30 +150,33 @@ class ServerlessS3Sync { if (s.hasOwnProperty('bucketPrefix')) { bucketPrefix = s.bucketPrefix; } - return new Promise((resolve) => { - const params = { - Bucket: s.bucketName, - Prefix: bucketPrefix - }; - const uploader = this.client().deleteDir(params); - uploader.on('error', (err) => { - throw err; + return this.getBucketName(s) + .then(bucketName => { + return new Promise((resolve) => { + const params = { + Bucket: bucketName, + Prefix: bucketPrefix + }; + const uploader = this.client().deleteDir(params); + uploader.on('error', (err) => { + throw err; + }); + let percent = 0; + uploader.on('progress', () => { + if (uploader.progressTotal === 0) { + return; + } + const current = Math.round((uploader.progressAmount / uploader.progressTotal) * 10) * 10; + if (current > percent) { + percent = current; + cli.printDot(); + } + }); + uploader.on('end', () => { + resolve('done'); + }); + }); }); - let percent = 0; - uploader.on('progress', () => { - if (uploader.progressTotal === 0) { - return; - } - const current = Math.round((uploader.progressAmount / uploader.progressTotal) * 10) * 10; - if (current > percent) { - percent = current; - cli.printDot(); - } - }); - uploader.on('end', () => { - resolve('done'); - }); - }); }); return Promise.all(promises) .then(() => { @@ -187,6 +194,16 @@ class ServerlessS3Sync { } return validParams; } + + getBucketName(s) { + if (s.bucketName) { + return Promise.resolve(s.bucketName) + } else if (s.bucketNameKey) { + return resolveStackOutput(this, s.bucketNameKey) + } else { + return Promise.reject("Unable to find bucketName. Please provide a value for bucketName or bucketNameKey") + } + } } module.exports = ServerlessS3Sync; diff --git a/resolveStackOutput.js b/resolveStackOutput.js new file mode 100644 index 0000000..693dbef --- /dev/null +++ b/resolveStackOutput.js @@ -0,0 +1,24 @@ +function resolveStackOutput(plugin, outputKey) { + const provider = plugin.serverless.getProvider('aws'); + const awsCredentials = provider.getCredentials(); + const cfn = new provider.sdk.CloudFormation({ + region: awsCredentials.region, + credentials: awsCredentials.credentials + }); + const stackName = provider.naming.getStackName(); + + return cfn + .describeStacks({ StackName: stackName }) + .promise() + .then(data => { + const output = data.Stacks[0].Outputs.find( + e => e.OutputKey === outputKey + ); + if (!output) { + throw `Failed to resolve stack Output '${outputKey}' in stack '${stackName}'`; + } + return output.OutputValue; + }); +} + +module.exports = resolveStackOutput;