Skip to content
This repository has been archived by the owner on Feb 18, 2024. It is now read-only.

Commit

Permalink
remove releases API use, bluebird promises
Browse files Browse the repository at this point in the history
  • Loading branch information
guybedford committed Dec 2, 2015
1 parent 8e16a92 commit b03c230
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 273 deletions.
330 changes: 60 additions & 270 deletions github.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ var rimraf = require('rimraf');
var request = require('request');
var expandTilde = require('expand-tilde');

var Promise = require('rsvp').Promise;
var asp = require('rsvp').denodeify;
var Promise = require('bluebird');
var asp = require('bluebird').Promise.promisify;

var tar = require('tar');
var zlib = require('zlib');

var yauzl = require('yauzl');

var semver = require('semver');

var which = require('which');
Expand Down Expand Up @@ -252,31 +250,7 @@ function configureCredentials(config, ui) {
});
}

function checkRateLimit(headers) {
if (headers.status.match(/^401/))
throw 'Unauthorized response for GitHub API.\n' +
'Use %jspm registry config github% to reconfigure the credentials, or update them in your ~/.netrc file.';
if (headers.status.match(/^406/))
throw 'Unauthorized response for GitHub API.\n' +
'If using an access token ensure it has public_repo access.\n' +
'Use %jspm registry config github% to configure the credentials, or add them to your ~/.netrc file.';

if (headers['x-ratelimit-remaining'] != '0')
return;

var remaining = (headers['x-ratelimit-reset'] * 1000 - new Date(headers.date).getTime()) / 60000;

if (this.auth)
return Promise.reject('\nGitHub rate limit reached, with authentication enabled.' +
'\nThe rate limit will reset in `' + Math.round(remaining) + ' minutes`.');

var err = new Error('GitHub rate limit reached.');
err.config = true;
err.hideStack = true;

return Promise.reject(err);
}

var apiWarned = false;

// static configuration function
GithubLocation.configure = function(config, ui) {
Expand Down Expand Up @@ -421,31 +395,48 @@ GithubLocation.prototype = {
if (meta.vPrefix)
version = 'v' + version;

return asp(request)(extend({
var self = this;
var ui = this.ui;

return asp(request)({
uri: this.apiRemoteString + 'repos/' + repo + '/contents/package.json',
headers: {
'User-Agent': 'jspm',
'Accept': 'application/vnd.github.v3.raw'
},
qs: {
ref: version
},
strictSSL: this.strictSSL
}).then(function(res) {
// API auth failure warnings
function apiFailWarn(reason, showAuthCommand) {
if (apiWarned)
return;

ui.log('warn', 'Unable to use the GitHub API to speed up dependency downloads due to ' + reason
+ (showAuthCommand ? '\nTo resolve use %jspm registry config github% to configure the credentials, or update them in your ~/.netrc file.' : ''));
apiWarned = true;
}
}, this.defaultRequestOptions
)).then(function(res) {
var rateLimitResponse = checkRateLimit.call(this, res.headers);
if (rateLimitResponse)
return rateLimitResponse;

if (res.statusCode == 404) {
// it is quite valid for a repo not to have a package.json
return {};

if (res.headers.status.match(/^401/))
return apiFailWarn('lack of authorization', true);
if (res.headers.status.match(/^406/))
return apiFailWarn('insufficient permissions. Ensure you have public_repo access.');
if (res.headers['x-ratelimit-remaining'] == '0') {
if (self.auth)
return apiFailWarn('the rate limit being reached, which will be reset in `' +
Math.round((res.headers['x-ratelimit-reset'] * 1000 - new Date(res.headers.date).getTime()) / 60000) + ' minutes`.');
return apiFailWarn('the rate limit being reached.', true);
}

if (res.statusCode != 200)
throw 'Unable to check repo package.json for release, status code ' + res.statusCode;
return apiFailWarn('invalid response code ' + res.statusCode + '.');

var packageJSON;
// it is quite valid for a repo not to have a package.json
if (res.statusCode == 404)
return {};

var packageJSON;
try {
packageJSON = JSON.parse(res.body);
}
Expand Down Expand Up @@ -540,241 +531,40 @@ GithubLocation.prototype = {

var self = this;

return this.checkReleases(repo, version)
.then(function(release) {
if (!release)
return true;

// Download from the release archive
return new Promise(function(resolve, reject) {
var inPipe;

if (release.type == 'tar') {
(inPipe = zlib.createGunzip())
.pipe(tar.Extract({
path: outDir,
strip: 0,
filter: function() {
return !this.type.match(/^.*Link$/);
}
}))
.on('end', function() {
resolve();
})
.on('error', reject);
}
else if (release.type == 'zip') {
var tmpDir = path.resolve(execOpt.cwd, 'release-' + repo.replace('/', '#') + '-' + version);
var tmpFile = tmpDir + '.' + release.type;

var repoDir;

inPipe = fs.createWriteStream(tmpFile)
.on('finish', function() {
return clearDir(tmpDir)
.then(function() {
return asp(fs.mkdir(tmpDir));
})
.then(function() {
return new Promise(function(resolve, reject) {
var files = [];
yauzl.open(tmpFile, function(err, zipFile) {
if (err)
return reject(err);

zipFile.on('entry', function(entry) {
var fileName = tmpDir + '/' + entry.fileName;

if (fileName[fileName.length - 1] == '/')
return;

zipFile.openReadStream(entry, function(err, readStream) {
if (err)
return reject(err);
mkdirp(path.dirname(fileName), function(err) {
if (err)
return reject(err);
files.push(new Promise(function(_resolve, _reject) {
var p = fs.createWriteStream(fileName).on("close", function(err) {
if (err) _reject(err);
_resolve();
});
readStream.pipe(p);
}));
});
});
});
zipFile.on('close', function() {
Promise.all(files).then(function() {
resolve();
}).catch(function(e) {
reject(e);
});
});
});


})
})
.then(function() {
return checkStripDir(tmpDir);
})
.then(function(_repoDir) {
repoDir = _repoDir;
return asp(fs.rmdir)(outDir);
})
.then(function() {
return asp(fs.rename)(repoDir, outDir);
})
.then(function() {
return asp(fs.unlink)(tmpFile);
})
.then(resolve, reject);
})
.on('error', reject);
}
else {
throw 'GitHub release found, but no archive present.';
}

// now that the inPipe is ready, do the request
request(extend({
uri: release.url,
headers: {
'accept': 'application/octet-stream',
'user-agent': 'jspm'
},
followRedirect: false,
auth: self.auth && {
user: self.auth.username,
pass: self.auth.password
}
}, self.defaultRequestOptions
)).on('response', function(archiveRes) {
var rateLimitResponse = checkRateLimit.call(this, archiveRes.headers);
if (rateLimitResponse)
return rateLimitResponse.then(resolve, reject);

if (archiveRes.statusCode != 302)
return reject('Bad response code ' + archiveRes.statusCode + '\n' + JSON.stringify(archiveRes.headers));

request(extend({
uri: archiveRes.headers.location, headers: {
'accept': 'application/octet-stream',
'user-agent': 'jspm'
}
}, self.defaultRequestOptions
))
.on('response', function(archiveRes) {

if (max_repo_size && archiveRes.headers['content-length'] > max_repo_size)
return reject('Response too large.');

archiveRes.pause();

archiveRes.pipe(inPipe);
// Download from the git archive
return new Promise(function(resolve, reject) {
request({
uri: remoteString + repo + '/archive/' + version + '.tar.gz',
headers: { 'accept': 'application/octet-stream' },
strictSSL: self.strictSSL
})
.on('response', function(pkgRes) {
if (pkgRes.statusCode != 200)
return reject('Bad response code ' + pkgRes.statusCode);

archiveRes.on('error', reject);
if (max_repo_size && pkgRes.headers['content-length'] > max_repo_size)
return reject('Response too large.');

archiveRes.resume();
pkgRes.pause();

})
.on('error', reject);
})
.on('error', reject);
});
})
.then(function(git) {
if (!git)
return;
var gzip = zlib.createGunzip();

// Download from the git archive
return new Promise(function(resolve, reject) {
request(extend({
uri: remoteString + repo + '/archive/' + version + '.tar.gz',
headers: { 'accept': 'application/octet-stream' }
}, self.defaultRequestOptions
))
.on('response', function(pkgRes) {
if (pkgRes.statusCode != 200)
return reject('Bad response code ' + pkgRes.statusCode);

if (max_repo_size && pkgRes.headers['content-length'] > max_repo_size)
return reject('Response too large.');

pkgRes.pause();

var gzip = zlib.createGunzip();

pkgRes
.pipe(gzip)
.pipe(tar.Extract({
path: outDir,
strip: 1,
filter: function() {
return !this.type.match(/^.*Link$/);
}
}))
.on('error', reject)
.on('end', resolve);

pkgRes.resume();

})
.on('error', reject);
});
});
},
pkgRes
.pipe(gzip)
.pipe(tar.Extract({
path: outDir,
strip: 1,
filter: function() {
return !this.type.match(/^.*Link$/);
}
}))
.on('error', reject)
.on('end', resolve);

checkReleases: function(repo, version) {
// NB cache this on disk with etags
var reqOptions = extend({
uri: this.apiRemoteString + 'repos/' + repo + '/releases',
headers: {
'User-Agent': 'jspm',
'Accept': 'application/vnd.github.v3+json'
},
followRedirect: false
}, this.defaultRequestOptions);
pkgRes.resume();

return asp(request)(reqOptions)
.then(function(res) {
var rateLimitResponse = checkRateLimit.call(this, res.headers);
if (rateLimitResponse)
return rateLimitResponse;
return Promise.resolve()
.then(function() {
try {
return JSON.parse(res.body);
}
catch(e) {
throw 'Unable to parse GitHub API response';
}
})
.then(function(releases) {
// run through releases list to see if we have this version tag
for (var i = 0; i < releases.length; i++) {
var tagName = (releases[i].tag_name || '').trim();

if (tagName == version) {
var firstAsset = releases[i].assets[0];
if (!firstAsset)
return false;

var assetType;

if (firstAsset.name.substr(firstAsset.name.length - 7, 7) == '.tar.gz' || firstAsset.name.substr(firstAsset.name.length - 4, 4) == '.tgz')
assetType = 'tar';
else if (firstAsset.name.substr(firstAsset.name.length - 4, 4) == '.zip')
assetType = 'zip';
else
return false;

return { url: firstAsset.url, type: assetType };
}
}
return false;
});
.on('error', reject);
});
},

Expand Down
5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,15 @@
},
"dependencies": {
"expand-tilde": "^1.2.0",
"bluebird": "^3.0.5",
"graceful-fs": "^3.0.6",
"mkdirp": "~0.5.0",
"netrc": "^0.1.3",
"request": "~2.53.0",
"rimraf": "~2.3.2",
"rsvp": "^3.0.17",
"semver": "^5.0.1",
"tar": "^2.1.1",
"which": "^1.0.9",
"yauzl": "^2.3.1"
"which": "^1.0.9"
},
"homepage": "https://github.com/jspm/github",
"devDependencies": {
Expand Down

0 comments on commit b03c230

Please sign in to comment.