Skip to content

Commit

Permalink
Merge pull request #7 from lpetre/plugin_restructure
Browse files Browse the repository at this point in the history
Large restructure of s3-index plugin
  • Loading branch information
LevelbossMike committed Sep 27, 2015
2 parents 2d51449 + 54ce6e6 commit 7ac7bed
Show file tree
Hide file tree
Showing 2 changed files with 114 additions and 167 deletions.
115 changes: 42 additions & 73 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
/* jshint node: true */
'use strict';
var path = require('path');
var fs = require('fs');
var Promise = require('ember-cli/lib/ext/promise');
var DeployPluginBase = require('ember-cli-deploy-plugin');
var S3 = require('./lib/s3');

var readFile = Promise.denodeify(fs.readFile);

module.exports = {
name: 'ember-cli-deploy-s3-index',

Expand All @@ -18,13 +15,10 @@ module.exports = {
defaultConfig: {
region: 'us-east-1',
filePattern: 'index.html',
currentRevisionIdentifier: "current.json",
prefix: '',
distDir: function(context) {
return context.distDir;
},
keyPrefix: function(context) {
return context.project.name() + ':index';
},
revisionKey: function(context) {
var revisionKey = context.revisionData && context.revisionData.revisionKey;
return context.commandOptions.revision || revisionKey;
Expand All @@ -36,88 +30,63 @@ module.exports = {
requiredConfig: ['accessKeyId', 'secretAccessKey', 'bucket'],

upload: function(context) {
var distDir = this.readConfig('distDir');
var filePattern = this.readConfig('filePattern');
var indexUploadKey = this._buildIndexUploadKey();
var filePath = path.join(distDir, filePattern);
var bucket = this.readConfig('bucket');
var prefix = this.readConfig('prefix');
var revisionKey = this.readConfig('revisionKey');
var distDir = this.readConfig('distDir');
var filePattern = this.readConfig('filePattern');
var filePath = path.join(distDir, filePattern);

this.log('trying to upload index to S3! ' + indexUploadKey);
var s3 = new S3({ plugin: this });
var options = {
bucket: bucket,
prefix: prefix,
filePattern: filePattern,
filePath: filePath,
revisionKey: revisionKey,
};

return this._readFileContents(filePath)
.then(s3.upload.bind(s3, indexUploadKey));
this.log('preparing to upload revision to S3 bucket `' + bucket + '`');

var s3 = new S3({ plugin: this });
return s3.upload(options);
},

activate: function(context) {
var bucket = this.readConfig('bucket');
var prefix = this.readConfig('prefix');
var revisionKey = this.readConfig('revisionKey');
var keyPrefix = this.readConfig('keyPrefix');
var filePattern = this.readConfig('filePattern');

if (revisionKey.indexOf(keyPrefix) === -1) {
revisionKey = this._buildIndexUploadKey();
}
var options = {
bucket: bucket,
prefix: prefix,
filePattern: filePattern,
revisionKey: revisionKey,
};

this.log('preparing to activate `' + revisionKey + '`');

return this._fetchRevisionsData()
.then(this._createRevisionsList)
.then(this._activateRevision.bind(this, revisionKey));
var s3 = new S3({ plugin: this });
return s3.activate(options);
},

fetchRevisions: function(context) {
return this._fetchRevisionsData();
},
var bucket = this.readConfig('bucket');
var prefix = this.readConfig('prefix');
var filePattern = this.readConfig('filePattern');

_fetchRevisionsData: function() {
var s3 = new S3({ plugin: this })
var options = {
bucket: bucket,
prefix: prefix,
filePattern: filePattern,
};

return s3.fetchRevisions()
var s3 = new S3({ plugin: this });
return s3.fetchRevisions(options)
.then(function(revisions) {
return { revisions: revisions };
});
},

_createRevisionsList: function(revisionsData) {
var revisions = revisionsData.revisions;

return revisions.map(function(r) { return r.revision; });
},

_activateRevision: function(revisionKey, availableRevisions) {
this.log('Activating revision `' + revisionKey + '`');

if (availableRevisions.indexOf(revisionKey) > -1) {
return this._overwriteCurrentIndex(revisionKey)
.then(this._updateCurrentRevisionPointer.bind(this, revisionKey));
} else {
return Promise.reject("REVISION NOT FOUND!"); // see how we should handle a pipeline failure
}
},

_buildIndexUploadKey: function() {
var keyPrefix = this.readConfig('keyPrefix');
var revisionKey = this.readConfig('revisionKey');

return keyPrefix+':'+revisionKey;
},

_readFileContents: function(path) {
return readFile(path)
.then(function(buffer) {
return buffer.toString();
context.revisions = revisions;
});
},

_updateCurrentRevisionPointer: function(newRevisionKey) {
var s3 = new S3({ plugin: this })
var currentRevisionIdentifier = this.readConfig('currentRevisionIdentifier');

return s3.upload(currentRevisionIdentifier, JSON.stringify({ revision: newRevisionKey }));
},

_overwriteCurrentIndex: function(newRevisionKey) {
var s3 = new S3({ plugin: this });
var bucket = this.readConfig('bucket');

return s3.overwriteCurrentIndex(newRevisionKey, bucket);
}
});

return new DeployPlugin();
Expand Down
166 changes: 72 additions & 94 deletions lib/s3.js
Original file line number Diff line number Diff line change
@@ -1,129 +1,107 @@
var AWS = require('aws-sdk');
var CoreObject = require('core-object');
var Promise = require('ember-cli/lib/ext/promise');
var fs = require('fs');
var path = require('path');
var readFile = Promise.denodeify(fs.readFile);

var headObject = function(client, params) {
return new Promise(function(resolve, reject) {
client.headObject(params, function(err, data) {
if (err && err.code === 'NotFound') {
return resolve();
}
else if (err) {
return reject(err);
}
else {
return resolve(data);
}
});
});
}

module.exports = CoreObject.extend({
init: function(options) {
var plugin = options.plugin;
var config = plugin.pluginConfig;
var readConfig = plugin.readConfig;

this._client = plugin.readConfig('s3Client') || new AWS.S3(config);
this._bucket = plugin.readConfig('bucket');
this._keyPrefix = plugin.readConfig('keyPrefix');
this._filePattern = plugin.readConfig('filePattern');
this._currentRevisionIdentifier = plugin.readConfig('currentRevisionIdentifier');
this._plugin = plugin;
this._client = plugin.readConfig('s3Client') || new AWS.S3(config);
},

fetchRevisions: function() {
return Promise.hash({
revisions: this._getBucketContents(),
current: this._getCurrentData()
})
.then(this._createRevisionDataFromList.bind(this));
},

upload: function(key, fileContent) {
var client = this._client;
upload: function(options) {
var client = this._client;
var plugin = this._plugin;
var bucket = options.bucket;
var acl = options.acl || 'public-read';
var key = path.join(options.prefix, options.filePattern + ":" + options.revisionKey);
var putObject = Promise.denodeify(client.putObject.bind(client));

var params = {
Bucket: this._bucket,
Bucket: bucket,
Key: key,
Body: fileContent,
ACL: acl,
ContentType: 'text/html',
CacheControl: 'max-age=0, no-cache'
}

return new Promise(function(resolve, reject) {
client.putObject(params, function(err, data) {
if (err) {
return reject(err);
} else {
return resolve(data);
}
});
return readFile(options.filePath).then(function(fileContents) {
params.Body = fileContents;
return putObject(params).then(function() {
plugin.log('✔ ' + key);
})
});
},

overwriteCurrentIndex: function(newRevisionKey) {
var client = this._client;
var bucket = this._bucket;
var copySource = encodeURIComponent(bucket+'/'+newRevisionKey);
activate: function(options) {
var plugin = this._plugin;
var client = this._client;
var bucket = options.bucket;
var acl = options.acl || 'public-read';
var revisionKey = path.join(options.prefix, options.filePattern + ":" + options.revisionKey);
var indexKey = path.join(options.prefix, options.filePattern);
var copySource = encodeURIComponent(path.join(bucket, revisionKey));
var copyObject = Promise.denodeify(client.copyObject.bind(client));

var params = {
Bucket: bucket,
CopySource: copySource,
Key: this._filePattern
Key: indexKey,
ACL: acl,
};

return new Promise(function(resolve, reject) {
client.copyObject(params, function(err, data) {
if (err) {
return reject(err);
} else {
return resolve(data);
}
});
});
},

_createRevisionDataFromList: function(data) {
const revisionsData = this._sortBucketContents(data.revisions).Contents;
const currentRevision = data.current;

return revisionsData.map(function(d) {
var revision = d.Key;

return { revision: revision, active: revision === currentRevision };
return this.fetchRevisions(options).then(function(revisions) {
var found = revisions.map(function(element) { return element.revision; }).indexOf(options.revisionKey);
if (found >= 0) {
return copyObject(params).then(function() {
plugin.log('✔ ' + revisionKey + " => " + indexKey);
})
} else {
return Promise.reject("REVISION NOT FOUND!"); // see how we should handle a pipeline failure
}
});
},

_sortBucketContents: function(data) {
data.Contents = data.Contents.sort(function(a, b) {
return new Date(b.LastModified) - new Date(a.LastModified);
});
return data;
},

_getBucketContents: function() {
var client = this._client;
var keyPrefix = this._keyPrefix;
var bucket = this._bucket;
fetchRevisions: function(options) {
var client = this._client;
var bucket = options.bucket;
var revisionPrefix = path.join(options.prefix, options.filePattern + ":");
var indexKey = path.join(options.prefix, options.filePattern);
var listObjects = Promise.denodeify(client.listObjects.bind(client));

return new Promise(function(resolve, reject) {
var params = { Bucket: bucket, Prefix: keyPrefix };

client.listObjects(params, function(err, data) {
if (err) {
return reject(err);
} else {
return resolve(data);
}
return Promise.hash({
revisions: listObjects({ Bucket: bucket, Prefix: revisionPrefix }),
current: headObject(client, { Bucket: bucket, Key: indexKey }),
})
.then(function(data) {
return data.revisions.Contents.sort(function(a, b) {
return new Date(b.LastModified) - new Date(a.LastModified);
}).map(function(d) {
var revision = d.Key.substr(revisionPrefix.length);
var active = data.current && d.ETag === data.current.ETag;
return { revision: revision, timestamp: d.LastModified, active: active };
});
});
},

_getCurrentData: function() {
var client = this._client;
var bucket = this._bucket;
var currentRevisionIdentifier = this._currentRevisionIdentifier;

return new Promise(function(resolve, reject) {
var params = { Bucket: bucket, Key: currentRevisionIdentifier };

client.getObject(params, function(err, data) {
if (err && err.code === 'NoSuchKey') {
return resolve();
}
else if (err) {
return reject(err);
}
else {
var json = JSON.parse(data.Body.toString('utf8'));

return resolve(json.revision);
}
});
});
}
});

0 comments on commit 7ac7bed

Please sign in to comment.