diff options
-rw-r--r-- | lib/cache/add-remote-git.js | 22 | ||||
-rw-r--r-- | test/tap/add-remote-git-get-resolved.js | 101 |
2 files changed, 122 insertions, 1 deletions
diff --git a/lib/cache/add-remote-git.js b/lib/cache/add-remote-git.js index ce4163375..bd186f0b0 100644 --- a/lib/cache/add-remote-git.js +++ b/lib/cache/add-remote-git.js @@ -31,7 +31,8 @@ var VALID_VARIABLES = [ 'GIT_SSL_NO_VERIFY' ] -module.exports = function addRemoteGit (uri, _cb) { +module.exports = addRemoteGit +function addRemoteGit (uri, _cb) { assert(typeof uri === 'string', 'must have git URL') assert(typeof _cb === 'function', 'must have callback') var cb = dezalgo(_cb) @@ -286,6 +287,12 @@ function resolveHead (from, cloneURL, treeish, cachedRemote, cb) { log.silly('resolveHead', from, 'resolved treeish:', resolvedTreeish) var resolvedURL = getResolved(cloneURL, resolvedTreeish) + if (!resolvedURL) { + return cb(new Error( + 'unable to clone ' + from + ' because git clone string ' + + cloneURL + ' is in a form npm can\'t handle' + )) + } log.verbose('resolveHead', from, 'resolved Git URL:', resolvedURL) // generate a unique filename @@ -411,8 +418,21 @@ function gitEnv () { return gitEnv_ } +addRemoteGit.getResolved = getResolved function getResolved (uri, treeish) { + // normalize hosted-git-info clone URLs back into regular URLs + // this will only work on URLs that hosted-git-info recognizes + // https://github.com/npm/npm/issues/7961 + var rehydrated = hostedFromURL(uri) + if (rehydrated) uri = rehydrated.toString() + var parsed = url.parse(uri) + + // non-hosted SSH strings that are not URLs (git@whatever.com:foo.git) are + // no bueno + // https://github.com/npm/npm/issues/7961 + if (!parsed.protocol) return + parsed.hash = treeish if (!/^git[+:]/.test(parsed.protocol)) { parsed.protocol = 'git+' + parsed.protocol diff --git a/test/tap/add-remote-git-get-resolved.js b/test/tap/add-remote-git-get-resolved.js new file mode 100644 index 000000000..4a4f0a5fe --- /dev/null +++ b/test/tap/add-remote-git-get-resolved.js @@ -0,0 +1,101 @@ +'use strict' +var test = require('tap').test + +var npm = require('../../lib/npm.js') +var common = require('../common-tap.js') + +test('setup', function (t) { + var opts = { + registry: common.registry, + loglevel: 'silent' + } + npm.load(opts, function (er) { + t.ifError(er, 'npm loaded without error') + + t.end() + }) +}) + +test('add-remote-git#get-resolved git: passthru', function (t) { + var getResolved = require('../../lib/cache/add-remote-git.js').getResolved + + verify('git:github.com/foo/repo') + verify('git:github.com/foo/repo.git') + verify('git://github.com/foo/repo#decadacefadabade') + verify('git://github.com/foo/repo.git#decadacefadabade') + + function verify (uri) { + t.equal( + getResolved(uri, 'decadacefadabade'), + 'git://github.com/foo/repo.git#decadacefadabade', + uri + ' normalized to canonical form git://github.com/foo/repo.git#decadacefadabade' + ) + } + t.end() +}) + +test('add-remote-git#get-resolved SSH', function (t) { + var getResolved = require('../../lib/cache/add-remote-git.js').getResolved + + t.comment('tests for https://github.com/npm/npm/issues/7961') + verify('git@github.com:foo/repo') + verify('git@github.com:foo/repo#master') + verify('git+ssh://git@github.com/foo/repo#master') + verify('git+ssh://git@github.com/foo/repo#decadacefadabade') + + function verify (uri) { + t.equal( + getResolved(uri, 'decadacefadabade'), + 'git+ssh://git@github.com/foo/repo.git#decadacefadabade', + uri + ' normalized to canonical form git+ssh://git@github.com/foo/repo.git#decadacefadabade' + ) + } + t.end() +}) + +test('add-remote-git#get-resolved HTTPS', function (t) { + var getResolved = require('../../lib/cache/add-remote-git.js').getResolved + + verify('https://github.com/foo/repo') + verify('https://github.com/foo/repo#master') + verify('git+https://github.com/foo/repo.git#master') + verify('git+https://github.com/foo/repo#decadacefadabade') + + function verify (uri) { + t.equal( + getResolved(uri, 'decadacefadabade'), + 'git+https://github.com/foo/repo.git#decadacefadabade', + uri + ' normalized to canonical form git+https://github.com/foo/repo.git#decadacefadabade' + ) + } + t.end() +}) + +test('add-remote-git#get-resolved edge cases', function (t) { + var getResolved = require('../../lib/cache/add-remote-git.js').getResolved + + t.notOk( + getResolved('git@bananaboat.com:galbi.git', 'decadacefadabade'), + 'non-hosted Git SSH non-URI strings are invalid' + ) + + t.equal( + getResolved('git+ssh://git.bananaboat.net/foo', 'decadacefadabade'), + 'git+ssh://git.bananaboat.net/foo#decadacefadabade', + 'don\'t break non-hosted SSH URLs' + ) + + t.equal( + getResolved('git://gitbub.com/foo/bar.git', 'decadacefadabade'), + 'git://gitbub.com/foo/bar.git#decadacefadabade', + 'don\'t break non-hosted git: URLs' + ) + + t.comment('test for https://github.com/npm/npm/issues/3224') + t.equal( + getResolved('git+ssh://git@git.example.com:my-repo.git#9abe82cb339a70065e75300f62b742622774693c', 'decadacefadabade'), + 'git+ssh://git@git.example.com:my-repo.git#decadacefadabade', + 'preserve weird colon in semi-standard ssh:// URLs' + ) + t.end() +}) |