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.
 
 
 
 
 
 

254 lines
7.3 KiB

module.exports = owner
owner.usage = "npm owner add <username> <pkg>"
+ "\nnpm owner rm <username> <pkg>"
+ "\nnpm owner ls <pkg>"
var npm = require("./npm.js")
, log = require("npmlog")
, mapToRegistry = require("./utils/map-to-registry.js")
, readLocalPkg = require("./utils/read-local-package.js")
owner.completion = function (opts, cb) {
var argv = opts.conf.argv.remain
if (argv.length > 4) return cb()
if (argv.length <= 2) {
var subs = ["add", "rm"]
if (opts.partialWord === "l") subs.push("ls")
else subs.push("ls", "list")
return cb(null, subs)
}
npm.commands.whoami([], true, function (er, username) {
if (er) return cb()
var un = encodeURIComponent(username)
var byUser, theUser
switch (argv[2]) {
case "ls":
// FIXME: there used to be registry completion here, but it stopped
// making sense somewhere around 50,000 packages on the registry
return cb()
case "rm":
if (argv.length > 3) {
theUser = encodeURIComponent(argv[3])
byUser = "-/by-user/" + theUser + "|" + un
return mapToRegistry(byUser, npm.config, function (er, uri, auth) {
if (er) return cb(er)
console.error(uri)
npm.registry.get(uri, { auth : auth }, function (er, d) {
if (er) return cb(er)
// return the intersection
return cb(null, d[theUser].filter(function (p) {
// kludge for server adminery.
return un === "isaacs" || d[un].indexOf(p) === -1
}))
})
})
}
// else fallthrough
case "add":
if (argv.length > 3) {
theUser = encodeURIComponent(argv[3])
byUser = "-/by-user/" + theUser + "|" + un
return mapToRegistry(byUser, npm.config, function (er, uri, auth) {
if (er) return cb(er)
console.error(uri)
npm.registry.get(uri, { auth : auth }, function (er, d) {
console.error(uri, er || d)
// return mine that they're not already on.
if (er) return cb(er)
var mine = d[un] || []
, theirs = d[theUser] || []
return cb(null, mine.filter(function (p) {
return theirs.indexOf(p) === -1
}))
})
})
}
// just list all users who aren't me.
return mapToRegistry("-/users", npm.config, function (er, uri, auth) {
if (er) return cb(er)
npm.registry.get(uri, { auth : auth }, function (er, list) {
if (er) return cb()
return cb(null, Object.keys(list).filter(function (n) {
return n !== un
}))
})
})
default:
return cb()
}
})
}
function owner (args, cb) {
var action = args.shift()
switch (action) {
case "ls": case "list": return ls(args[0], cb)
case "add": return add(args[0], args[1], cb)
case "rm": case "remove": return rm(args[0], args[1], cb)
default: return unknown(action, cb)
}
}
function ls (pkg, cb) {
if (!pkg) return readLocalPkg(function (er, pkg) {
if (er) return cb(er)
if (!pkg) return cb(owner.usage)
ls(pkg, cb)
})
mapToRegistry(pkg, npm.config, function (er, uri, auth) {
if (er) return cb(er)
npm.registry.get(uri, { auth : auth }, function (er, data) {
var msg = ""
if (er) {
log.error("owner ls", "Couldn't get owner data", pkg)
return cb(er)
}
var owners = data.maintainers
if (!owners || !owners.length) msg = "admin party!"
else msg = owners.map(function (o) {
return o.name + " <" + o.email + ">"
}).join("\n")
console.log(msg)
cb(er, owners)
})
})
}
function add (user, pkg, cb) {
if (!user) return cb(owner.usage)
if (!pkg) return readLocalPkg(function (er, pkg) {
if (er) return cb(er)
if (!pkg) return cb(new Error(owner.usage))
add(user, pkg, cb)
})
log.verbose("owner add", "%s to %s", user, pkg)
mutate(pkg, user, function (u, owners) {
if (!owners) owners = []
for (var i = 0, l = owners.length; i < l; i ++) {
var o = owners[i]
if (o.name === u.name) {
log.info( "owner add"
, "Already a package owner: " + o.name + " <" + o.email + ">")
return false
}
}
owners.push(u)
return owners
}, cb)
}
function rm (user, pkg, cb) {
if (!pkg) return readLocalPkg(function (er, pkg) {
if (er) return cb(er)
if (!pkg) return cb(new Error(owner.usage))
rm(user, pkg, cb)
})
log.verbose("owner rm", "%s from %s", user, pkg)
mutate(pkg, user, function (u, owners) {
var found = false
, m = owners.filter(function (o) {
var match = (o.name === user)
found = found || match
return !match
})
if (!found) {
log.info("owner rm", "Not a package owner: " + user)
return false
}
if (!m.length) return new Error(
"Cannot remove all owners of a package. Add someone else first.")
return m
}, cb)
}
function mutate (pkg, user, mutation, cb) {
if (user) {
var byUser = "-/user/org.couchdb.user:" + user
mapToRegistry(byUser, npm.config, function (er, uri, auth) {
if (er) return cb(er)
npm.registry.get(uri, { auth : auth }, mutate_)
})
} else {
mutate_(null, null)
}
function mutate_ (er, u) {
if (!er && user && (!u || u.error)) er = new Error(
"Couldn't get user data for " + user + ": " + JSON.stringify(u))
if (er) {
log.error("owner mutate", "Error getting user data for %s", user)
return cb(er)
}
if (u) u = { "name" : u.name, "email" : u.email }
mapToRegistry(pkg, npm.config, function (er, uri, auth) {
if (er) return cb(er)
npm.registry.get(uri, { auth : auth }, function (er, data) {
if (er) {
log.error("owner mutate", "Error getting package data for %s", pkg)
return cb(er)
}
// save the number of maintainers before mutation so that we can figure
// out if maintainers were added or removed
var beforeMutation = data.maintainers.length
var m = mutation(u, data.maintainers)
if (!m) return cb() // handled
if (m instanceof Error) return cb(m) // error
data = {
_id : data._id,
_rev : data._rev,
maintainers : m
}
var dataPath = pkg.replace("/", "%2f") + "/-rev/" + data._rev
mapToRegistry(dataPath, npm.config, function (er, uri, auth) {
if (er) return cb(er)
var params = {
method : "PUT",
body : data,
auth : auth
}
npm.registry.request(uri, params, function (er, data) {
if (!er && data.error) {
er = new Error("Failed to update package metadata: "+JSON.stringify(data))
}
if (er) {
log.error("owner mutate", "Failed to update package metadata")
}
else if (m.length > beforeMutation) {
console.log("+ %s (%s)", user, pkg)
}
else if (m.length < beforeMutation) {
console.log("- %s (%s)", user, pkg)
}
cb(er, data)
})
})
})
})
}
}
function unknown (action, cb) {
cb("Usage: \n" + owner.usage)
}