Skip to content
This repository was archived by the owner on Aug 11, 2022. It is now read-only.

Commit 178a6ad

Browse files
iarnaothiym23
authored andcommitted
git: cache git repos by full URL (with branch)
Closes #7202 by caching separate branches individually.
1 parent d725575 commit 178a6ad

6 files changed

+191
-1
lines changed

lib/cache/add-remote-git.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ function tryClone (from, combinedURL, silent, cb) {
115115

116116
// ensure that similarly-named remotes don't collide
117117
var repoID = cloneURL.replace(/[^a-zA-Z0-9]+/g, '-') + '-' +
118-
crypto.createHash('sha1').update(cloneURL).digest('hex').slice(0, 8)
118+
crypto.createHash('sha1').update(combinedURL).digest('hex').slice(0, 8)
119119
var cachedRemote = path.join(remotes, repoID)
120120

121121
cb = inflight(repoID, cb)
12.3 KB
Binary file not shown.
10.6 KB
Binary file not shown.
12.4 KB
Binary file not shown.
9.3 KB
Binary file not shown.

test/tap/git-races.js

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
var path = require('path')
2+
var zlib = require('zlib')
3+
var execFile = require('child_process').execFile
4+
5+
var fs = require('graceful-fs')
6+
var test = require('tap').test
7+
var tmpdir = require('osenv').tmpdir
8+
var mkdirp = require('mkdirp')
9+
var rimraf = require('rimraf')
10+
var tar = require('tar')
11+
var once = require('once')
12+
var which = require('which')
13+
var requireInject = require('require-inject')
14+
var asyncMap = require('slide').asyncMap
15+
16+
var wd = path.resolve(tmpdir(), 'git-races')
17+
var fixtures = path.resolve(__dirname, '../fixtures')
18+
var testcase = 'github-com-BryanDonovan-npm-git-test'
19+
var testcase_git = path.resolve(wd, testcase + '.git')
20+
var testcase_path = path.resolve(wd, testcase)
21+
var testcase_tgz = path.resolve(fixtures, testcase + '.git.tar.gz')
22+
23+
var testtarballs = []
24+
var testrepos = {}
25+
var testurls = {}
26+
27+
/*
28+
This test is specifically for #7202, where the bug was if you tried installing multiple git urls that
29+
pointed at the same repo but had different comittishes, you'd sometimes get the wrong version.
30+
The test cases, provided by @BryanDonovan, have a dependency tree like this:
31+
32+
top
33+
bar#4.0.0
34+
buzz#3.0.0
35+
foo#3.0.0
36+
buzz#3.0.0
37+
foo#4.0.0
38+
buzz#2.0.0
39+
40+
But what would happen is that buzz#2.0.0 would end up installed under bar#4.0.0.
41+
42+
bar#4.0.0 shouldn't have gotten its own copy if buzz, and if it did, it shouldn've been buzz#3.0.0
43+
*/
44+
45+
;['bar', 'foo', 'buzz'].forEach(function (name) {
46+
var mockurl = 'ssh://[email protected]/BryanDonovan/dummy-npm-' + name + '.git'
47+
var realrepo = path.resolve(wd, 'github-com-BryanDonovan-dummy-npm-' + name + '.git')
48+
var tgz = path.resolve(fixtures, 'github-com-BryanDonovan-dummy-npm-' + name + '.git.tar.gz')
49+
50+
testrepos[mockurl] = realrepo
51+
testtarballs.push(tgz)
52+
})
53+
54+
function cleanup () {
55+
process.chdir(tmpdir())
56+
rimraf.sync(wd)
57+
}
58+
59+
var npm = requireInject.installGlobally('../../lib/npm.js', {
60+
'child_process': {
61+
'execFile': function (cmd, args, options, cb) {
62+
// If it's a clone we swap any requests for any of the urls we're mocking
63+
// with the path to the bare repo
64+
if (args[0] === 'clone') {
65+
var m2 = args.length - 2
66+
var m1 = args.length - 1
67+
if (testrepos[args[m2]]) {
68+
testurls[args[m1]] = args[m2]
69+
args[m2] = testrepos[args[m2]]
70+
}
71+
execFile(cmd, args, options, cb)
72+
// here, we intercept npm validating the remote origin url on one of the
73+
// clones we've done previously and return the original url that was requested
74+
} else if (args[0] === 'config' && args[1] === '--get' && args[2] === 'remote.origin.url') {
75+
process.nextTick(function () {
76+
cb(null, testurls[options.cwd], '')
77+
})
78+
} else {
79+
execFile(cmd, args, options, cb)
80+
}
81+
}
82+
}
83+
})
84+
85+
function extract (tarball, target, cb) {
86+
cb = once(cb)
87+
fs.createReadStream(tarball).on('error', function (er) { cb(er) })
88+
.pipe(zlib.createGunzip()).on('error', function (er) { cb(er) })
89+
.pipe(tar.Extract({path: target})).on('error', function (er) { cb(er) })
90+
.on('end', function () {
91+
cb()
92+
})
93+
}
94+
95+
// Copied from lib/utils/git, because we need to use
96+
// it before calling npm.load and lib/utils/git uses npm.js
97+
// which doesn't allow that. =( =(
98+
99+
function prefixGitArgs () {
100+
return process.platform === 'win32' ? ['-c', 'core.longpaths=true'] : []
101+
}
102+
103+
var gitcmd
104+
105+
function execGit (args, options, cb) {
106+
var fullArgs = prefixGitArgs().concat(args || [])
107+
return execFile(gitcmd, fullArgs, options, cb)
108+
}
109+
110+
function gitWhichAndExec (args, options, cb) {
111+
if (gitcmd) return execGit(args, options, cb)
112+
113+
which('git', function (err, pathtogit) {
114+
if (err) {
115+
err.code = 'ENOGIT'
116+
return cb(err)
117+
}
118+
gitcmd = pathtogit
119+
120+
execGit(args, options, cb)
121+
})
122+
}
123+
124+
function andClone (gitdir, repodir, cb) {
125+
return function (er) {
126+
if (er) return cb(er)
127+
gitWhichAndExec(['clone', gitdir, repodir], {}, cb)
128+
}
129+
}
130+
131+
function setup (cb) {
132+
cleanup()
133+
mkdirp.sync(wd)
134+
135+
extract(testcase_tgz, wd, andClone(testcase_git, testcase_path, andExtractPackages))
136+
137+
function andExtractPackages (er) {
138+
if (er) return cb(er)
139+
asyncMap(testtarballs, function (tgz, done) {
140+
extract(tgz, wd, done)
141+
}, andChdir)
142+
}
143+
function andChdir (er) {
144+
if (er) return cb(er)
145+
process.chdir(testcase_path)
146+
andLoadNpm()
147+
}
148+
function andLoadNpm () {
149+
var opts = {
150+
cache: path.resolve(wd, 'cache')
151+
}
152+
npm.load(opts, cb)
153+
}
154+
}
155+
156+
test('setup', function (t) {
157+
setup(function (er) {
158+
t.ifError(er, 'setup ran OK')
159+
t.end()
160+
})
161+
})
162+
163+
test('correct versions are installed for git dependency', function (t) {
164+
t.plan(4)
165+
t.comment('test for https://github.com/npm/npm/issues/7202')
166+
npm.commands.install([], function (er) {
167+
t.ifError(er, 'installed OK')
168+
npm.commands.ls([], true, function (er, result) {
169+
t.ifError(er, 'ls OK')
170+
t.deepEqual(toSimple(result), [
171+
172+
173+
174+
]],
175+
176+
177+
178+
]]
179+
]], 'install tree is correct')
180+
})
181+
})
182+
})
183+
184+
function toSimple (tree) {
185+
var deps = []
186+
Object.keys(tree.dependencies || {}).forEach(function (dep) {
187+
deps.push(toSimple(tree.dependencies[dep]))
188+
})
189+
return [ tree['name'] + '@' + tree['version'], deps ]
190+
}

0 commit comments

Comments
 (0)