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.
 
 
 
 
 
 

155 lines
4.4 KiB

module.exports = publish
var url = require("url")
, semver = require("semver")
, crypto = require("crypto")
, fs = require("fs")
function publish (uri, data, tarball, cb) {
var email = this.conf.get('email')
var auth = this.conf.get('_auth')
var username = this.conf.get('username')
if (!email || !auth || !username) {
var er = new Error("auth and email required for publishing")
er.code = 'ENEEDAUTH'
return cb(er)
}
if (data.name !== encodeURIComponent(data.name))
return cb(new Error('invalid name: must be url-safe'))
var ver = semver.clean(data.version)
if (!ver)
return cb(new Error('invalid semver: ' + data.version))
data.version = ver
var self = this
fs.stat(tarball, function(er, s) {
if (er) return cb(er)
fs.readFile(tarball, function(er, tarbuffer) {
if (er) return cb(er)
putFirst.call(self, uri, data, tarbuffer, s, username, email, cb)
})
})
}
function putFirst (registry, data, tarbuffer, stat, username, email, cb) {
// optimistically try to PUT all in one single atomic thing.
// If 409, then GET and merge, try again.
// If other error, then fail.
var root =
{ _id : data.name
, name : data.name
, description : data.description
, "dist-tags" : {}
, versions : {}
, readme: data.readme || ""
, maintainers :
[ { name : username
, email : email
}
]
}
root.versions[ data.version ] = data
data.maintainers = JSON.parse(JSON.stringify(root.maintainers))
var tag = data.tag || this.conf.get('tag') || "latest"
root["dist-tags"][tag] = data.version
var tbName = data.name + "-" + data.version + ".tgz"
, tbURI = data.name + "/-/" + tbName
data._id = data.name+"@"+data.version
data.dist = data.dist || {}
data.dist.shasum = crypto.createHash("sha1").update(tarbuffer).digest("hex")
data.dist.tarball = url.resolve(registry, tbURI)
.replace(/^https:\/\//, "http://")
root._attachments = {}
root._attachments[ tbName ] = {
content_type: 'application/octet-stream',
data: tarbuffer.toString('base64'),
length: stat.size
};
var fixed = url.resolve(registry, data.name)
this.request("PUT", fixed, { body : root }, function (er, parsed, json, res) {
var r409 = "must supply latest _rev to update existing package"
var r409b = "Document update conflict."
var conflict = res && res.statusCode === 409
if (parsed && (parsed.reason === r409 || parsed.reason === r409b))
conflict = true
// a 409 is typical here. GET the data and merge in.
if (er && !conflict) {
this.log.error("publish", "Failed PUT "
+(res && res.statusCode))
return cb(er)
}
if (!er && !conflict)
return cb(er, parsed, json, res)
// let's see what versions are already published.
var getUrl = url.resolve(registry, data.name + "?write=true")
this.request("GET", getUrl, null, function (er, current) {
if (er) return cb(er)
putNext.call(this, registry, data.version, root, current, cb)
}.bind(this))
}.bind(this))
}
function putNext(registry, newVersion, root, current, cb) {
// already have the tardata on the root object
// just merge in existing stuff
var curVers = Object.keys(current.versions || {}).map(function (v) {
return semver.clean(v, true)
}).concat(Object.keys(current.time || {}).map(function(v) {
if (semver.valid(v, true))
return semver.clean(v, true)
}).filter(function(v) {
return v
}))
if (curVers.indexOf(newVersion) !== -1) {
return cb(conflictError(root.name, newVersion))
}
current.versions[newVersion] = root.versions[newVersion]
current._attachments = current._attachments || {}
for (var i in root) {
switch (i) {
// objects that copy over the new stuffs
case 'dist-tags':
case 'versions':
case '_attachments':
for (var j in root[i])
current[i][j] = root[i][j]
break
// ignore these
case 'maintainers':
break;
// copy
default:
current[i] = root[i]
}
}
var maint = JSON.parse(JSON.stringify(root.maintainers))
root.versions[newVersion].maintainers = maint
this.request("PUT", url.resolve(registry, root.name), { body : current }, cb)
}
function conflictError (pkgid, version) {
var e = new Error("cannot modify pre-existing version")
e.code = "EPUBLISHCONFLICT"
e.pkgid = pkgid
e.version = version
return e
}