|
|
|
|
|
|
|
module.exports = publish
|
|
|
|
|
|
|
|
var request = require("./request.js")
|
|
|
|
, GET = request.GET
|
|
|
|
, PUT = request.PUT
|
|
|
|
, DELETE = request.DELETE
|
|
|
|
, reg = request.reg
|
|
|
|
, upload = request.upload
|
|
|
|
, log = require("../log.js")
|
|
|
|
, path = require("path")
|
|
|
|
, npm = require("../../npm.js")
|
|
|
|
, url = require("url")
|
|
|
|
|
|
|
|
function publish (data, prebuilt, readme, cb) {
|
|
|
|
if (typeof readme === "function") cb = readme, readme = ""
|
|
|
|
if (typeof prebuilt === "function") cb = prebuilt, prebuilt = null
|
|
|
|
// add the dist-url to the data, pointing at the tarball.
|
|
|
|
// if the {name} isn't there, then create it.
|
|
|
|
// if the {version} is already there, then fail.
|
|
|
|
// then:
|
|
|
|
// PUT the data to {config.registry}/{data.name}/{data.version}
|
|
|
|
var registry = reg()
|
|
|
|
if (registry instanceof Error) return cb(registry)
|
|
|
|
|
|
|
|
readme = readme ? "" + readme : ""
|
|
|
|
|
|
|
|
var fullData =
|
|
|
|
{ _id : data.name
|
|
|
|
, name : data.name
|
|
|
|
, description : data.description
|
|
|
|
, "dist-tags" : {}
|
|
|
|
, versions : {}
|
|
|
|
, readme: readme
|
|
|
|
, maintainers :
|
|
|
|
[ { name : npm.config.get("username")
|
|
|
|
, email : npm.config.get("email")
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
var tbName = data.name + "-" + data.version + ".tgz"
|
|
|
|
, bd = npm.config.get("bindist")
|
|
|
|
, pbName = data.name + "-" + data.version + "-" + bd + ".tgz"
|
|
|
|
, tbURI = data.name + "/-/" + tbName
|
|
|
|
, pbURI = data.name + "/-/" + pbName
|
|
|
|
|
|
|
|
data._id = data.name+"@"+data.version
|
|
|
|
data.dist = data.dist || {}
|
|
|
|
data.dist.tarball = url.resolve(registry, tbURI)
|
|
|
|
.replace(/^https:\/\//, "http://")
|
|
|
|
|
|
|
|
if (prebuilt && bd) {
|
|
|
|
data.dist.bin[bd] = data.dist.bin[bd] || {}
|
|
|
|
data.dist.bin[bd].tarball = url.resolve(registry, pbURI)
|
|
|
|
.replace(/^https:\/\//, "http://")
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// first try to just PUT the whole fullData, and this will fail if it's
|
|
|
|
// already there, because it'll be lacking a _rev, so couch'll bounce it.
|
|
|
|
PUT(encodeURIComponent(data.name), fullData,
|
|
|
|
function (er, parsed, json, response) {
|
|
|
|
// get the rev and then upload the attachment
|
|
|
|
// a 409 is expected here, if this is a new version of an existing package.
|
|
|
|
if (er
|
|
|
|
&& !(response && response.statusCode === 409)
|
|
|
|
&& !( parsed
|
|
|
|
&& parsed.reason ===
|
|
|
|
"must supply latest _rev to update existing package" )) {
|
|
|
|
return log.er(cb, "Failed PUT response "
|
|
|
|
+(response && response.statusCode))(er)
|
|
|
|
}
|
|
|
|
var dataURI = encodeURIComponent(data.name)
|
|
|
|
+ "/" + encodeURIComponent(data.version)
|
|
|
|
|
|
|
|
var tag = data.tag || npm.config.get("tag")
|
|
|
|
if (npm.config.get("pre")) dataURI += "/-pre/true"
|
|
|
|
else if (tag) dataURI += "/-tag/" + tag
|
|
|
|
else dataURI += "/-tag/latest"
|
|
|
|
|
|
|
|
// let's see what verions are already published.
|
|
|
|
// could be that we just need to update the bin dist values.
|
|
|
|
GET(data.name, function (er, fullData) {
|
|
|
|
if (er) return cb(er)
|
|
|
|
|
|
|
|
var exists = fullData.versions && fullData.versions[data.version]
|
|
|
|
if (exists) {
|
|
|
|
log(exists._id, "Already published")
|
|
|
|
var ebin = exists.dist.bin || {}
|
|
|
|
, nbin = data.dist.bin || {}
|
|
|
|
, needs = Object.keys(nbin).filter(function (bd) {
|
|
|
|
return !ebin.hasOwnProperty(bd)
|
|
|
|
})
|
|
|
|
log.verbose(needs, "uploading bin dists")
|
|
|
|
if (!needs.length) return cb(conflictError(data._id))
|
|
|
|
// attach the needed bindists, upload the new metadata
|
|
|
|
exists.dist.bin = ebin
|
|
|
|
needs.forEach(function (bd) { exists.dist.bin[bd] = nbin[bd] })
|
|
|
|
return PUT(dataURI + "/-rev/" + fullData._rev, exists, function (er) {
|
|
|
|
if (er) return cb(er)
|
|
|
|
attach(data.name, prebuilt, pbName, cb)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// this way, it'll also get attached to packages that were previously
|
|
|
|
// published with a version of npm that lacked this feature.
|
|
|
|
if (!fullData.readme) {
|
|
|
|
data.readme = readme
|
|
|
|
}
|
|
|
|
PUT(dataURI, data, function (er) {
|
|
|
|
if (er) {
|
|
|
|
if (er.message.indexOf("conflict Document update conflict.") === 0) {
|
|
|
|
return cb(conflictError(data._id))
|
|
|
|
}
|
|
|
|
return log.er(cb, "Error sending version data")(er)
|
|
|
|
}
|
|
|
|
|
|
|
|
var c = path.resolve(npm.cache, data.name, data.version)
|
|
|
|
, tb = path.resolve(c, "package.tgz")
|
|
|
|
|
|
|
|
cb = rollbackFailure(data, cb)
|
|
|
|
|
|
|
|
log.verbose([data.name, tb, tbName], "attach 2")
|
|
|
|
attach(data.name, tb, tbName, function (er) {
|
|
|
|
log.verbose([er, data.name, prebuilt, pbName], "attach 3")
|
|
|
|
if (er || !prebuilt) return cb(er)
|
|
|
|
attach(data.name, prebuilt, pbName, cb)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
function conflictError (pkgid) {
|
|
|
|
var e = new Error("publish fail")
|
|
|
|
e.errno = npm.EPUBLISHCONFLICT
|
|
|
|
e.pkgid = pkgid
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
|
|
|
|
function attach (doc, file, filename, cb) {
|
|
|
|
doc = encodeURIComponent(doc)
|
|
|
|
GET(doc, function (er, d) {
|
|
|
|
if (er) return cb(er)
|
|
|
|
if (!d) return cb(new Error(
|
|
|
|
"Attempting to upload to invalid doc "+doc))
|
|
|
|
var rev = "-rev/"+d._rev
|
|
|
|
, attURI = doc + "/-/" + encodeURIComponent(filename) + "/" + rev
|
|
|
|
log.verbose([attURI, file], "uploading")
|
|
|
|
upload(attURI, file, cb)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
function rollbackFailure (data, cb) { return function (er) {
|
|
|
|
if (!er) return cb()
|
|
|
|
npm.ROLLBACK = true
|
|
|
|
log.error(er, "publish failed")
|
|
|
|
log("rollback", "publish failed")
|
|
|
|
npm.commands.unpublish([data.name+"@"+data.version], function (er_) {
|
|
|
|
if (er_) {
|
|
|
|
log.error(er_, "rollback failed")
|
|
|
|
log.error( "Invalid data in registry! Please report this."
|
|
|
|
, "rollback failed" )
|
|
|
|
} else log("rolled back", "publish failed")
|
|
|
|
cb(er)
|
|
|
|
})
|
|
|
|
}}
|