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.
 
 
 
 
 
 

170 lines
5.5 KiB

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)
})
}}