Skip to content
This repository has been archived by the owner on Jul 3, 2019. It is now read-only.

Commit

Permalink
fix(tar): bring back the .gitignore -> .npmignore logic (#113)
Browse files Browse the repository at this point in the history
pacote@5 dropped support for a Very Legacy™ thing where npm would, on
extract, rename .gitignore to .npmignore if there was not already an
.npmignore.

This was primarily to support the use case where folks would use npm
install on github release tarballs.

Ref: npm/npm#5658

BREAKING CHANGE: this reverts a previous change to disable this feature.
  • Loading branch information
zkat committed Aug 19, 2017
1 parent 8e6f49d commit 0dd518e
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 5 deletions.
21 changes: 20 additions & 1 deletion lib/extract-stream.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
'use strict'

const path = require('path')
const tar = require('tar')

module.exports = extractStream
function extractStream (dest, opts, cb) {
function extractStream (dest, opts) {
opts = opts || {}
const sawIgnores = new Set()
return tar.x({
cwd: dest,
filter: (name, entry) => !entry.header.type.match(/^.*link$/i),
Expand All @@ -18,6 +20,23 @@ function extractStream (dest, opts, cb) {
} else if (entry.type.toLowerCase() === 'directory') {
entry.mode = opts.dmode & ~(opts.umask || 0)
}

// Note: This mirrors logic in the fs read operations that are
// employed during tarball creation, in the fstream-npm module.
// It is duplicated here to handle tarballs that are created
// using other means, such as system tar or git archive.
if (entry.type.toLowerCase() === 'file') {
const base = path.basename(entry.path)
if (base === '.npmignore') {
sawIgnores.add(entry.path)
} else if (base === '.gitignore') {
const npmignore = entry.path.replace(/\.gitignore$/, '.npmignore')
if (!sawIgnores.has(npmignore)) {
// Rename, may be clobbered later.
entry.path = npmignore
}
}
}
}
})
}
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"safe-buffer": "^5.1.1",
"semver": "^5.4.1",
"ssri": "^4.1.6",
"tar": "^3.2.0",
"tar": "^4.0.0",
"unique-filename": "^1.1.0",
"which": "^1.3.0"
},
Expand Down
83 changes: 83 additions & 0 deletions test/extract-stream.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
const BB = require('bluebird')

const fs = BB.promisifyAll(require('fs'))
const mkdirp = BB.promisify(require('mkdirp'))
const mockTar = require('./util/mock-tarball')
const npmlog = require('npmlog')
const path = require('path')
Expand Down Expand Up @@ -118,6 +119,88 @@ test('excludes symlinks', t => {
})
})

// Yes, this logic is terrible and seriously confusing, but
// I'm pretty sure this is exactly what npm is doing.
// ...we should really deprecate this cluster.
test('renames .gitignore to .npmignore if not present', t => {
return mkdirp('./no-npmignore').then(() => {
return mockTar({
'package.json': JSON.stringify({
name: 'foo',
version: '1.0.0'
}),
'index.js': 'console.log("hello world!")',
'.gitignore': 'tada!'
}, {stream: true}).then(tarStream => {
return pipe(tarStream, extractStream('./no-npmignore', OPTS))
}).then(() => {
return fs.readFileAsync(
'./no-npmignore/.npmignore', 'utf8'
).then(data => {
t.deepEqual(data, 'tada!', '.gitignore renamed to .npmignore')
})
})
}).then(() => {
return mkdirp('./has-npmignore1')
}).then(() => {
return mockTar({
'package.json': JSON.stringify({
name: 'foo',
version: '1.0.0'
}),
'index.js': 'console.log("hello world!")',
'.gitignore': 'git!',
'.npmignore': 'npm!'
}, {stream: true}).then(tarStream => {
return pipe(tarStream, extractStream('./has-npmignore1', OPTS))
}).then(() => {
return BB.join(
fs.readFileAsync(
'./has-npmignore1/.npmignore', 'utf8'
).then(data => {
t.deepEqual(data, 'npm!', '.npmignore left intact if present')
}),
fs.readFileAsync(
'./has-npmignore1/.gitignore', 'utf8'
).then(
() => { throw new Error('expected an error') },
err => {
t.ok(err, 'got expected error on reading .gitignore')
t.equal(err.code, 'ENOENT', '.gitignore missing')
}
)
)
})
}).then(() => {
return mkdirp('./has-npmignore2')
}).then(() => {
return mockTar({
'package.json': JSON.stringify({
name: 'foo',
version: '1.0.0'
}),
'index.js': 'console.log("hello world!")',
'.npmignore': 'npm!',
'.gitignore': 'git!'
}, {stream: true}).then(tarStream => {
return pipe(tarStream, extractStream('./has-npmignore2', OPTS))
}).then(() => {
return BB.join(
fs.readFileAsync(
'./has-npmignore2/.npmignore', 'utf8'
).then(data => {
t.deepEqual(data, 'npm!', '.npmignore left intact if present')
}),
fs.readFileAsync(
'./has-npmignore2/.gitignore', 'utf8'
).then(data => {
t.deepEqual(data, 'git!', '.gitignore intact if we previously had an .npmignore')
})
)
})
})
})

test('accepts dmode/fmode/umask opts', {
skip: process.platform === 'win32'
}, t => {
Expand Down

0 comments on commit 0dd518e

Please sign in to comment.