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.
150 lines
4.2 KiB
150 lines
4.2 KiB
|
|
module.exports = publish
|
|
|
|
var path = require("path")
|
|
, url = require("url")
|
|
, semver = require("semver")
|
|
, crypto = require("crypto")
|
|
, fs = require("fs")
|
|
|
|
function publish (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).toLowerCase())
|
|
return cb(new Error('invalid name: must be lowercase and 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, 'base64', function(er, tardata) {
|
|
if (er) return cb(er)
|
|
putFirst.call(self, data, tardata, s, username, email, cb)
|
|
})
|
|
})
|
|
}
|
|
|
|
function putFirst (data, tardata, 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 registry = this.conf.get('registry')
|
|
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(tardata, 'base64').digest("hex")
|
|
data.dist.tarball = url.resolve(registry, tbURI)
|
|
.replace(/^https:\/\//, "http://")
|
|
|
|
root._attachments = {}
|
|
root._attachments[ tbName ] = {
|
|
content_type: 'application/octet-stream',
|
|
data: tardata,
|
|
length: stat.size
|
|
};
|
|
|
|
this.request("PUT", data.name, 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.
|
|
this.request("GET", data.name, function (er, current) {
|
|
if (er)
|
|
return cb(er)
|
|
putNext.call(this, data.version, root, current, cb)
|
|
}.bind(this))
|
|
}.bind(this))
|
|
}
|
|
|
|
function putNext(newVersion, root, current, cb) {
|
|
// already have the tardata on the root object
|
|
// just merge in existing stuff
|
|
// if the version already exists, and not a --force, then raise error
|
|
var force = this.conf.get('force')
|
|
var curVers = Object.keys(current.versions || {}).map(function (v) {
|
|
return semver.clean(v, true)
|
|
})
|
|
|
|
if (!force && curVers.indexOf(newVersion) !== -1) {
|
|
return cb(conflictError(root.name))
|
|
}
|
|
|
|
current.versions[newVersion] = root.versions[newVersion]
|
|
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", root.name, current, cb)
|
|
}
|
|
|
|
function conflictError (pkgid) {
|
|
var e = new Error("cannot modify existing version")
|
|
e.code = "EPUBLISHCONFLICT"
|
|
e.pkgid = pkgid
|
|
return e
|
|
}
|
|
|