From efaf4acfe4108d20f0b49b945a9c29d3587c1185 Mon Sep 17 00:00:00 2001 From: Brian Whitton Date: Wed, 24 Jan 2018 17:47:31 -0500 Subject: [PATCH 1/7] add support for `awsPrefix` config --- README.md | 12 ++++++++++++ index.js | 26 +++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 343120e..c1fe8a1 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,18 @@ The region your bucket is located in. (e.g. set this to `eu-west-1` if your buck *Default:* `undefined` +### awsPrefix + +A optional prefix to add to the uploaded destination of your built fastboot assets. Useful if your app is hosted at a nested path on S3. For example if you set `awsPrefix` to `'blog'`, `fastboot-deploy-info.json` will look something like this: +```json +{ + "bucket": "my-bucket", + "key": "blog/dist-0.0.0+a3323e2.zip" +} +``` + +*Default:* `''` + ### revisionKey The unique revision number for the version of the app. By default this option will use either the `revision` passed in from the command line or the `revisionData.revisionKey` property from the deployment context. diff --git a/index.js b/index.js index 137f0d1..a3483df 100644 --- a/index.js +++ b/index.js @@ -91,7 +91,8 @@ module.exports = { return context.fastbootDownloaderManifestContent; }, - manifestKey: 'fastboot-deploy-info.json' + manifestKey: 'fastboot-deploy-info.json', + awsPrefix: '' }, requiredConfig: ['bucket', 'region'], @@ -100,10 +101,16 @@ module.exports = { let revisionKey = this.readConfig('revisionKey'); let bucket = this.readConfig('bucket'); let archivePrefix = this.readConfig('archivePrefix'); + let awsPrefix = this.readConfig('awsPrefix'); // update manifest-file to point to passed revision let downloaderManifestContent = this.readConfig('downloaderManifestContent'); - let manifest = downloaderManifestContent(bucket, `${archivePrefix}${revisionKey}.zip`); + let buildKey = `${archivePrefix}${revisionKey}.zip`; + if (awsPrefix) { + buildKey = `${awsPrefix}/${buildKey}`; + } + + let manifest = downloaderManifestContent(bucket, buildKey); let AWS = require('aws-sdk'); let RSVP = require('rsvp'); let accessKeyId = this.readConfig('accessKeyId'); @@ -111,6 +118,8 @@ module.exports = { let region = this.readConfig('region'); let manifestKey = this.readConfig('manifestKey'); + manifestKey = awsPrefix ? `${awsPrefix}/${manifestKey}` : manifestKey; + let client = new AWS.S3({ accessKeyId, secretAccessKey, @@ -135,6 +144,7 @@ module.exports = { let secretAccessKey = this.readConfig('secretAccessKey'); let bucket = this.readConfig('bucket'); let region = this.readConfig('region'); + let awsPrefix = this.readConfig('awsPrefix'); let client = new AWS.S3({ accessKeyId, @@ -146,10 +156,12 @@ module.exports = { let data = fs.readFileSync(context.fastbootArchivePath); + let key = awsPrefix ? `${awsPrefix}/${context.fastbootArchiveName}` : context.fastbootArchiveName; + return putObject({ Bucket: bucket, Body: data, - Key: context.fastbootArchiveName + Key: key }); }, @@ -160,6 +172,10 @@ module.exports = { let bucket = this.readConfig('bucket'); let region = this.readConfig('region'); let manifestKey = this.readConfig('manifestKey'); + let awsPrefix = this.readConfig('awsPrefix'); + + archivePrefix = awsPrefix ? `${awsPrefix}/${archivePrefix}` : archivePrefix; + manifestKey = awsPrefix ? `${awsPrefix}/${manifestKey}` : manifestKey; let opts = { accessKeyId, secretAccessKey, archivePrefix, bucket, region, manifestKey @@ -175,6 +191,10 @@ module.exports = { let bucket = this.readConfig('bucket'); let region = this.readConfig('region'); let manifestKey = this.readConfig('manifestKey'); + let awsPrefix = this.readConfig('awsPrefix'); + + archivePrefix = awsPrefix ? `${awsPrefix}/${archivePrefix}` : archivePrefix; + manifestKey = awsPrefix ? `${awsPrefix}/${manifestKey}` : manifestKey; let opts = { accessKeyId, secretAccessKey, archivePrefix, bucket, region, manifestKey From c7aa2913851cef6af9dd04c382487c77b91bf0eb Mon Sep 17 00:00:00 2001 From: Brian Whitton Date: Wed, 24 Jan 2018 18:09:06 -0500 Subject: [PATCH 2/7] tweak this regex --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index a3483df..d0b4e3b 100644 --- a/index.js +++ b/index.js @@ -52,7 +52,7 @@ function _list(opts) { return new Date(b.LastModified) - new Date(a.LastModified); }) .map((d) => { - let match = d.Key.match(new RegExp(archivePrefix+'([^.]*)\\.zip')); + let match = d.Key.match(new RegExp(archivePrefix+'(.*)\\.zip')); if (!match) { return; // ignore files that are no zipped app builds } From 6b719ac32deb6acb24342a1e451f6b0e1f3dee8f Mon Sep 17 00:00:00 2001 From: Brian Whitton Date: Wed, 24 Jan 2018 18:17:27 -0500 Subject: [PATCH 3/7] adds some logging to verbose output return data from log statements for testing purposes --- index.js | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index d0b4e3b..f794abb 100644 --- a/index.js +++ b/index.js @@ -110,6 +110,7 @@ module.exports = { buildKey = `${awsPrefix}/${buildKey}`; } + this.log(`creating manifest for bucket: ${bucket} and buildKey: ${buildKey}`, {verbose: true}); let manifest = downloaderManifestContent(bucket, buildKey); let AWS = require('aws-sdk'); let RSVP = require('rsvp'); @@ -127,6 +128,8 @@ module.exports = { }); let putObject = RSVP.denodeify(client.putObject.bind(client)); + this.log(`updating manifest at ${manifestKey}`, {verbose: true}); + return putObject({ Bucket: bucket, Key: manifestKey, @@ -158,6 +161,7 @@ module.exports = { let key = awsPrefix ? `${awsPrefix}/${context.fastbootArchiveName}` : context.fastbootArchiveName; + this.log(`uploading fastboot archive to ${bucket}/${key}`, {verbose: true}); return putObject({ Bucket: bucket, Body: data, @@ -181,7 +185,13 @@ module.exports = { accessKeyId, secretAccessKey, archivePrefix, bucket, region, manifestKey }; - return _list(opts); + return _list(opts) + .then(({ revisions }) => { + revisions.forEach(r => { + this.log(`${r.revision} | ${r.timestamp} | active: ${r.active}`, {verbose: true}); + }); + return { revisions }; + }); }, fetchInitialRevisions: function() { @@ -200,9 +210,13 @@ module.exports = { accessKeyId, secretAccessKey, archivePrefix, bucket, region, manifestKey }; - return _list(opts) - .then((data) => { - return { initialRevisions: data.revisions }; + return _list(opts, this) + .then(({ revisions }) => { + revisions.forEach(r => { + this.log(`${r.revision} | ${r.timestamp} | active: ${r.active}`, {verbose: true}); + }); + + return { initialRevisions: revisions }; }); } }); From 08fd3359afd4110ffdf35078d6cafd9eb5f913ec Mon Sep 17 00:00:00 2001 From: Brian Whitton Date: Wed, 24 Jan 2018 20:56:47 -0800 Subject: [PATCH 4/7] awsPrefix note --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c1fe8a1..3235c6b 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,7 @@ A optional prefix to add to the uploaded destination of your built fastboot asse "key": "blog/dist-0.0.0+a3323e2.zip" } ``` +**Note** that a trailing slash is added to the value to separate it from the `archivePrefix` value. *Default:* `''` From 38c2135305948c67d5ccabe5481f0595ae2a5512 Mon Sep 17 00:00:00 2001 From: Brian Whitton Date: Thu, 25 Jan 2018 07:06:57 -0800 Subject: [PATCH 5/7] add some assertions for using `awsPrefix` --- tests/index-test.js | 54 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/index-test.js b/tests/index-test.js index b417db2..7384133 100644 --- a/tests/index-test.js +++ b/tests/index-test.js @@ -154,6 +154,31 @@ describe('fastboot-app-server-aws plugin', function() { assert.isTrue(false, 'upload failed'); }); }); + + it('uploads objects to a nested path if `awsPrefix` is set', function() { + let FILE_NAME = 'dist-78.zip'; + let CONTENT = 'testtest'; + let PREFIX = 'blog'; + let KEY = `${PREFIX}/${FILE_NAME}`; + + fs.writeFileSync(FILE_NAME, CONTENT); + + context.fastbootArchivePath = FILE_NAME; + context.fastbootArchiveName = FILE_NAME; + + context.config['fastboot-app-server-aws'].awsPrefix = PREFIX; + + return plugin.upload(context) + .then(() => { + return get({ Bucket: process.env.TEST_BUCKET, Key: KEY }); + }) + .then((data) => { + assert.equal(data.Body, CONTENT, 'file was uploaded correctly'); + }) + .catch(() => { + assert.isTrue(false, 'upload failed'); + }); + }); }); describe('#fetchRevisions', function() { @@ -226,6 +251,35 @@ describe('fastboot-app-server-aws plugin', function() { assert.isTrue(false, "can't find manifest-file"); }); }); + + it('uploads the manifest with a key prefix if `awsPrefix` is set', function() { + let PREFIX = 'blog'; + + context.commandOptions = { + revision: '56' + }; + context.config['fastboot-app-server-aws'].awsPrefix = PREFIX; + + return plugin.activate(context) + .then(() => { + return get({ + Bucket: process.env.TEST_BUCKET, + Key: `${PREFIX}/fastboot-deploy-info.json` + }); + }) + .then((data) => { + let expected = { + bucket: process.env.TEST_BUCKET, + key: `${PREFIX}/dist-56.zip` + }; + let actual = JSON.parse(data.Body.toString()); + + assert.deepEqual(actual, expected, 'manifest file updated as expected'); + }) + .catch(() => { + assert.isTrue(false, "can't find manifest-file"); + }); + }); }); }); }); From d6e569ee88e53fd1847d29431cf5c4c5d80bdce8 Mon Sep 17 00:00:00 2001 From: Brian Whitton Date: Thu, 25 Jan 2018 07:44:48 -0800 Subject: [PATCH 6/7] accept `awsPrefix` option in `setupTestData` for awsPrefix tests --- tests/index-test.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tests/index-test.js b/tests/index-test.js index 7384133..eb6d8d6 100644 --- a/tests/index-test.js +++ b/tests/index-test.js @@ -33,18 +33,21 @@ function cleanBucket() { }); } -function setupTestData() { +function setupTestData(ops = {}) { function addTestData() { let existingDists = ['dist-12.zip', 'dist-34.zip', 'dist-56.zip']; let promises = existingDists.map((n) => { + if (ops.awsPrefix) { + n = `${ops.awsPrefix}/${n}`; + } return put({ Bucket: process.env.TEST_BUCKET, Key: n, Body: 'Body: ' + n }); }); promises.push(put({ Bucket: process.env.TEST_BUCKET, - Key: 'fastboot-deploy-info.json', + Key: (ops.awsPrefix ? `${ops.awsPrefix}/` : '') + 'fastboot-deploy-info.json', Body: JSON.stringify({ bucket: process.env.TEST_BUCKET, - key: 'dist-34.zip' + key: (ops.awsPrefix ? `${ops.awsPrefix}/` : '') + 'dist-34.zip' }) })); From e5ccc2f2c5ac91100b3d91836706c736111faf5e Mon Sep 17 00:00:00 2001 From: Brian Whitton Date: Thu, 25 Jan 2018 07:45:08 -0800 Subject: [PATCH 7/7] add a couple more assertions for fectching revisions from nested paths --- tests/index-test.js | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/index-test.js b/tests/index-test.js index eb6d8d6..921a61c 100644 --- a/tests/index-test.js +++ b/tests/index-test.js @@ -194,6 +194,19 @@ describe('fastboot-app-server-aws plugin', function() { }); }); + it('respects `awsPrefix` when looking for revisions', function() { + let PREFIX = 'blog'; + return setupTestData({ awsPrefix: PREFIX }).then(() => { + context.config['fastboot-app-server-aws'].awsPrefix = PREFIX; + return plugin.fetchRevisions(context) + .then((data) => { + let revisions = data.revisions.map((d) => d.revision); + assert.deepEqual(revisions, ['12', '34', '56']); + assert.isTrue(data.revisions[1].active, 'revision 34 marked current'); + }); + }) + }); + it('does not fail when bucket is empty', function() { return cleanBucket() .then(() => { @@ -216,6 +229,19 @@ describe('fastboot-app-server-aws plugin', function() { }); }); + it('respects `awsPrefix` when fetching initial revisions', function() { + let PREFIX = 'blog'; + return setupTestData({ awsPrefix: PREFIX }).then(() => { + context.config['fastboot-app-server-aws'].awsPrefix = PREFIX; + return plugin.fetchInitialRevisions(context) + .then((data) => { + let revisions = data.initialRevisions.map((d) => d.revision); + assert.deepEqual(revisions, ['12', '34', '56']); + assert.isTrue(data.initialRevisions[1].active, 'revision 34 marked current'); + }); + }) + }); + it('does not fail when bucket is empty', function() { return cleanBucket() .then(() => {