gulp-awspublish
Advanced tools
Comparing version 0.0.24 to 1.0.0
1.0.0 / 2015-01-29 | ||
================== | ||
* refactored to use aws-sdk instead of knox pull request #38 from ronik-design | ||
0.0.24 / 2015-01-06 | ||
@@ -3,0 +8,0 @@ ================== |
188
lib/index.js
@@ -1,6 +0,4 @@ | ||
var Stream = require('stream'), | ||
util = require('util'), | ||
S3Lister = require('s3-lister'), | ||
S3Deleter = require('s3-deleter'), | ||
es = require('event-stream'), | ||
var AWS = require('aws-sdk'), | ||
converter = require('xml-json'), | ||
Stream = require('stream'), | ||
fs = require('fs'), | ||
@@ -10,6 +8,8 @@ through = require('through2'), | ||
crypto = require('crypto'), | ||
knox = require('knox'), | ||
mime = require('mime'), | ||
pascalCase = require('pascal-case'), | ||
gutil = require('gulp-util'); | ||
var PLUGIN_NAME = 'gulp-awspublish'; | ||
/** | ||
@@ -31,15 +31,78 @@ * calculate file hash | ||
/** | ||
* S3 Error class | ||
* @param {Response} res | ||
* Hunt for appropriate creds | ||
* @param {Options} opts | ||
* | ||
* @return {Credentials} obj | ||
* @api private | ||
*/ | ||
function S3Error(res) { | ||
Error.call(this); | ||
this.message = 'HTTP ' + res.statusCode + ' Response returned from S3'; | ||
this.res = res; | ||
function getCredentials(opts) { | ||
if (opts && opts instanceof AWS.SharedIniFileCredentials && !opts.accessKeyId) { | ||
return new gutil.PluginError({ | ||
plugin: PLUGIN_NAME, | ||
message: 'Bad or invalid credentials' | ||
}); | ||
} | ||
// compatibility | ||
if (opts && opts.key && (opts.secret || opts.token)) { | ||
return { | ||
accessKeyId: opts.key, | ||
secretAccessKey: opts.secret, | ||
sessionToken: opts.token | ||
}; | ||
} | ||
// When passing to S3, the non-enumerated secretKey won't get copied | ||
if (opts && opts instanceof AWS.SharedIniFileCredentials) { | ||
return { | ||
accessKeyId: opts.accessKeyId, | ||
secretAccessKey: opts.secretAccessKey, | ||
sessionToken: opts.sessionToken | ||
}; | ||
} | ||
if (opts && opts.accessKeyId && (opts.secretAccessKey || opts.sessionToken)) { | ||
return { | ||
accessKeyId: opts.accessKeyId, | ||
secretAccessKey: opts.secretAccessKey, | ||
sessionToken: opts.sessionToken | ||
}; | ||
} | ||
if (AWS.config.credentials) { | ||
return getCredentials(AWS.config.credentials); | ||
} | ||
return getCredentials(new AWS.SharedIniFileCredentials(opts)); | ||
} | ||
util.inherits(S3Error, Error); | ||
/** | ||
* Turn the HTTP style headers into AWS Object params | ||
*/ | ||
function toAwsParams(file) { | ||
var params = {}; | ||
var headers = file.s3.headers || {}; | ||
for (var header in headers) { | ||
if (header === 'x-amz-acl') { | ||
params.ACL = headers[header]; | ||
} else { | ||
params[pascalCase(header)] = headers[header]; | ||
} | ||
} | ||
params.Key = file.s3.path; | ||
params.Body = file.contents; | ||
return params; | ||
} | ||
module.exports._toAwsParams = toAwsParams; | ||
/** | ||
@@ -62,2 +125,16 @@ * init file s3 hash | ||
function buildDeleteMultiple(keys) { | ||
if (!keys || !keys.length) return; | ||
var deleteObjects = keys.map(function (k) { return { Key: k }; }); | ||
return { | ||
Delete: { | ||
Objects: deleteObjects | ||
} | ||
}; | ||
} | ||
module.exports._buildDeleteMultiple = buildDeleteMultiple; | ||
/** | ||
@@ -88,3 +165,3 @@ * create a through stream that gzip files | ||
this.emit('error', | ||
new gutil.PluginError('gulp-awspublish', 'Stream content is not supported')); | ||
new gutil.PluginError(PLUGIN_NAME, 'Stream content is not supported')); | ||
return cb(); | ||
@@ -139,7 +216,26 @@ } | ||
function Publisher(config) { | ||
var filename = '.awspublish-' + config.bucket; | ||
if (!config.bucket) { | ||
throw new gutil.PluginError({ | ||
plugin: PLUGIN_NAME, | ||
message: 'No bucket specified' | ||
}); | ||
} | ||
this._bucket = config.bucket; | ||
var filename = '.awspublish-' + this._bucket; | ||
// create client | ||
this.client = knox.createClient(config); | ||
var credentials = getCredentials(config); | ||
if (credentials instanceof Error) { | ||
throw credentials; | ||
} | ||
var s3Opts = credentials; | ||
s3Opts.params = { | ||
Bucket: this._bucket | ||
}; | ||
this.client = new AWS.S3(s3Opts); | ||
// load cache | ||
@@ -160,3 +256,3 @@ try { | ||
Publisher.prototype.saveCache = function() { | ||
var filename = '.awspublish-' + this.client.bucket; | ||
var filename = '.awspublish-' + this._bucket; | ||
fs.writeFileSync(filename, JSON.stringify(this._cache)); | ||
@@ -240,3 +336,3 @@ }; | ||
this.emit('error', | ||
new gutil.PluginError('gulp-awspublish', 'Stream content is not supported')); | ||
new gutil.PluginError(PLUGIN_NAME, 'Stream content is not supported')); | ||
return cb(); | ||
@@ -278,11 +374,12 @@ } | ||
// get s3 headers | ||
_this.client.headFile(file.s3.path, function(err, res) { | ||
if (err) return cb(err); | ||
if (res.statusCode !== 200 && res.statusCode !== 307 && res.statusCode !== 404) { | ||
return cb(new S3Error(res)); | ||
} | ||
_this.client.headObject({ Key: file.s3.path }, function(err, res) { | ||
if (err && err.statusCode !== 404) return cb(err); | ||
res = res || {}; | ||
// skip: no updates allowed | ||
var noUpdate = options.createOnly && res.headers.etag; | ||
var noUpdate = options.createOnly && res.ETag; | ||
// skip: file are identical | ||
var noChange = !options.force && res.headers.etag === etag; | ||
var noChange = !options.force && res.ETag === etag; | ||
@@ -292,3 +389,3 @@ if (noUpdate || noChange) { | ||
file.s3.etag = etag; | ||
file.s3.date = new Date(res.headers['last-modified']); | ||
file.s3.date = new Date(res.LastModified); | ||
cb(err, file); | ||
@@ -298,13 +395,10 @@ | ||
} else { | ||
file.s3.state = res.headers.etag | ||
file.s3.state = res.ETag | ||
? 'update' | ||
: 'create'; | ||
_this.client.putBuffer(file.contents, file.s3.path, file.s3.headers, function(err, res) { | ||
_this.client.putObject(toAwsParams(file), function(err) { | ||
if (err) return cb(err); | ||
if (res.statusCode !== 200 && res.statusCode !== 307) { | ||
return cb(new S3Error(res)); | ||
} | ||
file.s3.date = new Date(res.headers.date); | ||
file.s3.date = new Date(); | ||
file.s3.etag = etag; | ||
@@ -342,14 +436,14 @@ cb(err, file); | ||
stream._flush = function(cb) { | ||
var lister = new S3Lister(client, { prefix : prefix }), | ||
deleter = new S3Deleter(client), | ||
filter; | ||
var toDelete = [], | ||
lister; | ||
// filter out newfiles and add deleted file to stream | ||
filter = es.mapSync(function(data) { | ||
var s3path = data.Key; | ||
if (newFiles[s3path]) return; | ||
lister = client.listObjects({ Prefix: prefix }) | ||
.createReadStream() | ||
.pipe(converter('Key')); | ||
lister.on('data', function (key) { | ||
if (newFiles[key]) return; | ||
stream.push({ | ||
s3: { | ||
path: s3path, | ||
path: key, | ||
state: 'delete', | ||
@@ -359,13 +453,9 @@ headers: {} | ||
}); | ||
toDelete.push(key); | ||
}); | ||
return data; | ||
lister.on('end', function() { | ||
if (!toDelete.length) return cb(); | ||
client.deleteObjects(buildDeleteMultiple(toDelete), cb); | ||
}); | ||
lister.on('error', cb); | ||
deleter.on('error', cb); | ||
lister | ||
.pipe(filter) | ||
.pipe(deleter) | ||
.on('finish', cb); | ||
}; | ||
@@ -372,0 +462,0 @@ |
{ | ||
"name": "gulp-awspublish", | ||
"version": "0.0.24", | ||
"version": "1.0.0", | ||
"description": "gulp plugin to publish files to amazon s3", | ||
@@ -28,11 +28,10 @@ "keywords": [ | ||
"dependencies": { | ||
"through2": "0.x", | ||
"aws-sdk": "^2.1.7", | ||
"clone": "0.x", | ||
"gulp-util": "2.x", | ||
"knox": "0.x", | ||
"mime": "1.x", | ||
"clone": "0.x", | ||
"pad-component": "0.x", | ||
"event-stream": "3.x", | ||
"s3-lister": "0.x", | ||
"s3-deleter": "0.x" | ||
"pascal-case": "^1.1.0", | ||
"through2": "0.x", | ||
"xml-json": "^2.0.2" | ||
}, | ||
@@ -43,2 +42,3 @@ "devDependencies": { | ||
"coveralls": "*", | ||
"event-stream": "^3.2.1", | ||
"gulp-rename": "*", | ||
@@ -45,0 +45,0 @@ "istanbul": "*", |
@@ -6,9 +6,2 @@ # gulp-awspublish | ||
> **warning** the module does not work currently with node v.0.11.14 see https://github.com/pgherveou/gulp-awspublish/issues/33 for more info | ||
> **warning** users reported a bug in node v.0.10.34 see http://stackoverflow.com/questions/27583347/travis-ci-networkingerror-cert-untrusted-error-between-node-js-and-aws-s3-buck for more info | ||
## Usage | ||
@@ -30,3 +23,3 @@ | ||
// create a new publisher | ||
var publisher = awspublish.create({ key: '...', secret: '...', bucket: '...' }); | ||
var publisher = awspublish.create({ bucket: '...' }); | ||
@@ -63,2 +56,5 @@ // define custom headers | ||
* Note: If you follow the [aws-sdk suggestions](http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/node-configuring.html) for | ||
providing your credentials you don't need to pass them in to create the publisher. | ||
## Testing | ||
@@ -89,5 +85,9 @@ | ||
Create a Publisher. | ||
Options are passed to knox to create a s3 client. | ||
Please see [knox](https://github.com/LearnBoost/knox#client-creation-options) for more details about these options | ||
Options are used to create an `aws-sdk` S3 client. At a minimum you must pass | ||
a `bucket` option, to define the site bucket. If you are using the [aws-sdk suggestions](http://docs.aws.amazon.com/AWSJavaScriptSDK/guide/node-configuring.html) for credentials you do not need | ||
to provide anything else. | ||
Also supports credentials specified in the old [knox](https://github.com/LearnBoost/knox#client-creation-options) | ||
format, a `profile` property for choosing a specific set of shared AWS creds, or and `accessKeyId` and `secretAccessKey` provided explicitly. | ||
#### Publisher.publish([headers], [options]) | ||
@@ -144,3 +144,3 @@ | ||
The knox client object exposed to let you do other s3 operations. | ||
The `aws-sdk` S3 client is exposed to let you do other s3 operations. | ||
@@ -147,0 +147,0 @@ ### awspublish.reporter([options]) |
@@ -25,7 +25,10 @@ /* global describe, before, it */ | ||
publisher._cache = {}; | ||
publisher.client.deleteMultiple([ | ||
var deleteParams = awspublish._buildDeleteMultiple([ | ||
'test/hello.txt', | ||
'test/hello2.txt', | ||
'test/hello.txtgz' | ||
], done); | ||
]); | ||
publisher.client.deleteObjects(deleteParams, done); | ||
}); | ||
@@ -45,3 +48,3 @@ | ||
expect(err).to.be.ok; | ||
expect(err.res.statusCode).to.eq(403); | ||
expect(err.statusCode).to.eq(403); | ||
done(); | ||
@@ -59,22 +62,2 @@ }); | ||
it('should emit error when user does not have upload rights', function(done) { | ||
var badCredentials = JSON.parse(fs.readFileSync('bad-aws-credentials.json', 'utf8')), | ||
badPublisher = awspublish.create(badCredentials), | ||
stream = badPublisher.publish(); | ||
stream.on('error', function(err) { | ||
expect(err).to.be.ok; | ||
expect(err.res.statusCode).to.eq(403); | ||
done(); | ||
}); | ||
stream.write(new gutil.File({ | ||
path: '/test/hello.txt', | ||
base: '/', | ||
contents: new Buffer('hello world 2') | ||
})); | ||
stream.end(); | ||
}); | ||
it('should produce gzip file with s3 headers', function (done) { | ||
@@ -125,4 +108,4 @@ | ||
expect(files).to.have.length(1); | ||
publisher.client.headFile('test/hello.txt.gz', function(err, res) { | ||
expect(res.headers.etag).to.exist; | ||
publisher.client.headObject({ Key: 'test/hello.txt.gz' }, function(err, res) { | ||
expect(res.ETag).to.exist; | ||
done(err); | ||
@@ -164,4 +147,4 @@ }); | ||
expect(files[0].s3.headers['Content-Length']).to.eq(files[0].contents.length); | ||
publisher.client.headFile('/test/hello.txt', function(err, res) { | ||
expect(res.headers.etag).to.exist; | ||
publisher.client.headObject({ Key: 'test/hello.txt' }, function(err, res) { | ||
expect(res.ETag).to.exist; | ||
done(err); | ||
@@ -297,5 +280,5 @@ }); | ||
expect(files[0].s3.path).to.eq('test/simulate.txt'); | ||
publisher.client.headFile('/test/simulate.txt', function(err, res) { | ||
expect(res.statusCode).to.eq(404); | ||
done() | ||
publisher.client.headObject({ Key: '/test/simulate.txt' }, function(err) { | ||
expect(err.statusCode).to.eq(404); | ||
done(); | ||
}); | ||
@@ -312,7 +295,8 @@ })); | ||
before(function(done) { | ||
publisher.client.deleteMultiple([ | ||
var deleteParams = awspublish._buildDeleteMultiple([ | ||
'test/hello.txt', | ||
'test/hello2.txt', | ||
'test/hello.txt.gz' | ||
], done); | ||
'test/hello.txtgz' | ||
]); | ||
publisher.client.deleteObjects(deleteParams, done); | ||
}); | ||
@@ -322,6 +306,14 @@ | ||
['bar', 'foo/1', 'foo/2', 'foo/3'].forEach(function (name) { | ||
var filename = name + '.txt', | ||
headers = {'Content-Type': 'text/plain; charset=utf-8'}; | ||
var file = { | ||
s3: { | ||
path: name + '.txt', | ||
headers: {'Content-Type': 'text/plain; charset=utf-8'} | ||
}, | ||
contents: new Buffer('hello world') | ||
}; | ||
before(function(done) { | ||
publisher.client.putBuffer(name, filename, headers, done); | ||
var params = awspublish._toAwsParams(file); | ||
publisher.client.putObject(params, done); | ||
}); | ||
@@ -351,2 +343,2 @@ }); | ||
}); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
34321
8
20
741
0
8
6
+ Addedaws-sdk@^2.1.7
+ Addedpascal-case@^1.1.0
+ Addedxml-json@^2.0.2
+ Addedavailable-typed-arrays@1.0.7(transitive)
+ Addedaws-sdk@2.1692.0(transitive)
+ Addedbase64-js@1.5.1(transitive)
+ Addedbuffer@4.9.2(transitive)
+ Addedcall-bind@1.0.7(transitive)
+ Addedcamel-case@1.2.2(transitive)
+ Addeddefine-data-property@1.1.4(transitive)
+ Addedduplexify@3.7.1(transitive)
+ Addedend-of-stream@1.4.4(transitive)
+ Addedes-define-property@1.0.0(transitive)
+ Addedes-errors@1.3.0(transitive)
+ Addedevents@1.1.1(transitive)
+ Addedextend@1.3.0(transitive)
+ Addedfor-each@0.3.3(transitive)
+ Addedget-intrinsic@1.2.4(transitive)
+ Addedgopd@1.0.1(transitive)
+ Addedhas-property-descriptors@1.0.2(transitive)
+ Addedhas-proto@1.0.3(transitive)
+ Addedhas-symbols@1.0.3(transitive)
+ Addedhas-tostringtag@1.0.2(transitive)
+ Addedieee754@1.1.13(transitive)
+ Addedis-arguments@1.1.1(transitive)
+ Addedis-callable@1.2.7(transitive)
+ Addedis-generator-function@1.0.10(transitive)
+ Addedis-typed-array@1.1.13(transitive)
+ Addedisarray@1.0.0(transitive)
+ Addedjmespath@0.16.0(transitive)
+ Addedldjson-stream@1.2.1(transitive)
+ Addedlower-case@1.1.4(transitive)
+ Addedpascal-case@1.1.2(transitive)
+ Addedpossible-typed-array-names@1.0.0(transitive)
+ Addedprocess-nextick-args@2.0.1(transitive)
+ Addedpump@2.0.1(transitive)
+ Addedpumpify@1.5.1(transitive)
+ Addedpunycode@1.3.2(transitive)
+ Addedquerystring@0.2.0(transitive)
+ Addedreadable-stream@2.3.8(transitive)
+ Addedsafe-buffer@5.1.2(transitive)
+ Addedsax@1.2.1(transitive)
+ Addedsentence-case@1.1.3(transitive)
+ Addedset-function-length@1.2.2(transitive)
+ Addedsplit2@0.2.1(transitive)
+ Addedstream-shift@1.0.3(transitive)
+ Addedstring_decoder@1.1.1(transitive)
+ Addedupper-case@1.1.3(transitive)
+ Addedupper-case-first@1.1.2(transitive)
+ Addedurl@0.10.3(transitive)
+ Addedutil@0.12.5(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
+ Addeduuid@8.0.0(transitive)
+ Addedwhich-typed-array@1.1.16(transitive)
+ Addedxml-json@2.0.2(transitive)
+ Addedxml-nodes@0.1.5(transitive)
+ Addedxml-objects@0.0.1(transitive)
+ Addedxml2js@0.6.2(transitive)
- Removedevent-stream@3.x
- Removedknox@0.x
- Removeds3-deleter@0.x
- Removeds3-lister@0.x
- Removedasync@0.9.2(transitive)
- Removeddebug@1.0.5(transitive)
- Removedduplexer@0.1.2(transitive)
- Removedevent-stream@3.3.5(transitive)
- Removedfrom@0.1.7(transitive)
- Removedknox@0.9.2(transitive)
- Removedmap-stream@0.0.7(transitive)
- Removedms@2.0.0(transitive)
- Removedpause-stream@0.0.11(transitive)
- Removeds3-deleter@0.1.2(transitive)
- Removeds3-lister@0.1.0(transitive)
- Removedsax@1.4.1(transitive)
- Removedsplit@1.0.1(transitive)
- Removedstream-combiner@0.2.2(transitive)
- Removedstream-counter@1.0.0(transitive)
- Removedthrough@2.3.8(transitive)