mirror of https://github.com/lukechilds/node.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
228 lines
6.7 KiB
228 lines
6.7 KiB
var mkdir = require("mkdirp")
|
|
, assert = require("assert")
|
|
, fs = require("graceful-fs")
|
|
, readJson = require("read-package-json")
|
|
, log = require("npmlog")
|
|
, path = require("path")
|
|
, sha = require("sha")
|
|
, npm = require("../npm.js")
|
|
, tar = require("../utils/tar.js")
|
|
, pathIsInside = require("path-is-inside")
|
|
, locker = require("../utils/locker.js")
|
|
, lock = locker.lock
|
|
, unlock = locker.unlock
|
|
, getCacheStat = require("./get-stat.js")
|
|
, chownr = require("chownr")
|
|
, inflight = require("inflight")
|
|
, once = require("once")
|
|
|
|
module.exports = addLocalTarball
|
|
|
|
function addLocalTarball (p, pkgData, shasum, cb_) {
|
|
assert(typeof p === "string", "must have path")
|
|
assert(typeof cb_ === "function", "must have callback")
|
|
|
|
if (!pkgData) pkgData = {}
|
|
var name = pkgData.name || ""
|
|
|
|
// If we don't have a shasum yet, then get the shasum now.
|
|
if (!shasum) {
|
|
return sha.get(p, function (er, shasum) {
|
|
if (er) return cb_(er)
|
|
addLocalTarball(p, pkgData, shasum, cb_)
|
|
})
|
|
}
|
|
|
|
// if it's a tar, and not in place,
|
|
// then unzip to .tmp, add the tmp folder, and clean up tmp
|
|
if (pathIsInside(p, npm.tmp))
|
|
return addTmpTarball(p, pkgData, shasum, cb_)
|
|
|
|
if (pathIsInside(p, npm.cache)) {
|
|
if (path.basename(p) !== "package.tgz") return cb_(new Error(
|
|
"Not a valid cache tarball name: "+p))
|
|
return addPlacedTarball(p, pkgData, shasum, cb_)
|
|
}
|
|
|
|
function cb (er, data) {
|
|
if (data) {
|
|
data._resolved = p
|
|
data._shasum = data._shasum || shasum
|
|
}
|
|
return cb_(er, data)
|
|
}
|
|
|
|
// just copy it over and then add the temp tarball file.
|
|
var tmp = path.join(npm.tmp, name + Date.now()
|
|
+ "-" + Math.random(), "tmp.tgz")
|
|
mkdir(path.dirname(tmp), function (er) {
|
|
if (er) return cb(er)
|
|
var from = fs.createReadStream(p)
|
|
, to = fs.createWriteStream(tmp)
|
|
, errState = null
|
|
function errHandler (er) {
|
|
if (errState) return
|
|
return cb(errState = er)
|
|
}
|
|
from.on("error", errHandler)
|
|
to.on("error", errHandler)
|
|
to.on("close", function () {
|
|
if (errState) return
|
|
log.verbose("chmod", tmp, npm.modes.file.toString(8))
|
|
fs.chmod(tmp, npm.modes.file, function (er) {
|
|
if (er) return cb(er)
|
|
addTmpTarball(tmp, pkgData, shasum, cb)
|
|
})
|
|
})
|
|
from.pipe(to)
|
|
})
|
|
}
|
|
|
|
function addPlacedTarball (p, pkgData, shasum, cb) {
|
|
assert(pkgData, "should have package data by now")
|
|
assert(typeof cb === "function", "cb function required")
|
|
|
|
getCacheStat(function (er, cs) {
|
|
if (er) return cb(er)
|
|
return addPlacedTarball_(p, pkgData, cs.uid, cs.gid, shasum, cb)
|
|
})
|
|
}
|
|
|
|
function addPlacedTarball_ (p, pkgData, uid, gid, resolvedSum, cb) {
|
|
// now we know it's in place already as .cache/name/ver/package.tgz
|
|
var name = pkgData.name
|
|
, version = pkgData.version
|
|
, folder = path.join(npm.cache, name, version, "package")
|
|
|
|
// First, make sure we have the shasum, if we don't already.
|
|
if (!resolvedSum) {
|
|
sha.get(p, function (er, shasum) {
|
|
if (er) return cb(er)
|
|
addPlacedTarball_(p, pkgData, uid, gid, shasum, cb)
|
|
})
|
|
return
|
|
}
|
|
|
|
lock(folder, function (er) {
|
|
if (er) return cb(er)
|
|
|
|
// async try/finally
|
|
var originalCb = cb
|
|
cb = function (er, data) {
|
|
unlock(folder, function (er2) {
|
|
return originalCb(er || er2, data)
|
|
})
|
|
}
|
|
|
|
mkdir(folder, function (er) {
|
|
if (er) return cb(er)
|
|
var pj = path.join(folder, "package.json")
|
|
var json = JSON.stringify(pkgData, null, 2)
|
|
fs.writeFile(pj, json, "utf8", function (er) {
|
|
cb(er, pkgData)
|
|
})
|
|
})
|
|
})
|
|
}
|
|
|
|
function addTmpTarball (tgz, pkgData, shasum, cb) {
|
|
assert(typeof cb === "function", "must have callback function")
|
|
assert(shasum, "should have shasum by now")
|
|
|
|
cb = inflight("addTmpTarball:" + tgz, cb)
|
|
if (!cb) return
|
|
|
|
// we already have the package info, so just move into place
|
|
if (pkgData && pkgData.name && pkgData.version) {
|
|
return addTmpTarball_(tgz, pkgData, shasum, cb)
|
|
}
|
|
|
|
// This is a tarball we probably downloaded from the internet.
|
|
// The shasum's already been checked, but we haven't ever had
|
|
// a peek inside, so we unpack it here just to make sure it is
|
|
// what it says it is.
|
|
// Note: we might not have any clue what we think it is, for
|
|
// example if the user just did `npm install ./foo.tgz`
|
|
|
|
var target = tgz + "-unpack"
|
|
getCacheStat(function (er, cs) {
|
|
tar.unpack(tgz, target, null, null, cs.uid, cs.gid, next)
|
|
})
|
|
|
|
function next (er) {
|
|
if (er) return cb(er)
|
|
var pj = path.join(target, "package.json")
|
|
readJson(pj, function (er, data) {
|
|
// XXX dry with similar stanza in add-local.js
|
|
er = needName(er, data)
|
|
er = needVersion(er, data)
|
|
// check that this is what we expected.
|
|
if (!er && pkgData.name && pkgData.name !== data.name) {
|
|
er = new Error( "Invalid Package: expected "
|
|
+ pkgData.name + " but found "
|
|
+ data.name )
|
|
}
|
|
|
|
if (!er && pkgData.version && pkgData.version !== data.version) {
|
|
er = new Error( "Invalid Package: expected "
|
|
+ pkgData.name + "@" + pkgData.version
|
|
+ " but found "
|
|
+ data.name + "@" + data.version )
|
|
}
|
|
|
|
if (er) return cb(er)
|
|
|
|
addTmpTarball_(tgz, data, shasum, cb)
|
|
})
|
|
}
|
|
}
|
|
|
|
function addTmpTarball_ (tgz, data, shasum, cb) {
|
|
assert(typeof cb === "function", "must have callback function")
|
|
cb = once(cb)
|
|
|
|
var name = data.name
|
|
var version = data.version
|
|
assert(name, "should have package name by now")
|
|
assert(version, "should have package version by now")
|
|
|
|
var root = path.resolve(npm.cache, name, version)
|
|
var pkg = path.resolve(root, "package")
|
|
var target = path.resolve(root, "package.tgz")
|
|
getCacheStat(function (er, cs) {
|
|
if (er) return cb(er)
|
|
mkdir(pkg, function (er, created) {
|
|
|
|
// chown starting from the first dir created by mkdirp,
|
|
// or the root dir, if none had to be created, so that
|
|
// we know that we get all the children.
|
|
function chown (er) {
|
|
chownr(created || root, cs.uid, cs.gid, done)
|
|
}
|
|
|
|
if (er) return cb(er)
|
|
var read = fs.createReadStream(tgz)
|
|
var write = fs.createWriteStream(target)
|
|
var fin = cs.uid && cs.gid ? chown : done
|
|
read.on("error", cb).pipe(write).on("error", cb).on("close", fin)
|
|
})
|
|
|
|
})
|
|
|
|
function done() {
|
|
data._shasum = data._shasum || shasum
|
|
cb(null, data)
|
|
}
|
|
}
|
|
|
|
function needName(er, data) {
|
|
return er ? er
|
|
: (data && !data.name) ? new Error("No name provided")
|
|
: null
|
|
}
|
|
|
|
function needVersion(er, data) {
|
|
return er ? er
|
|
: (data && !data.version) ? new Error("No version provided")
|
|
: null
|
|
}
|
|
|