Current File : //lib/node_modules/npm/lib/owner.js |
module.exports = owner
const BB = require('bluebird')
const log = require('npmlog')
const npa = require('libnpm/parse-arg')
const npmConfig = require('./config/figgy-config.js')
const npmFetch = require('libnpm/fetch')
const output = require('./utils/output.js')
const otplease = require('./utils/otplease.js')
const packument = require('libnpm/packument')
const readLocalPkg = BB.promisify(require('./utils/read-local-package.js'))
const usage = require('./utils/usage')
const whoami = BB.promisify(require('./whoami.js'))
owner.usage = usage(
'owner',
'npm owner add <user> [<@scope>/]<pkg>' +
'\nnpm owner rm <user> [<@scope>/]<pkg>' +
'\nnpm owner ls [<@scope>/]<pkg>'
)
owner.completion = function (opts, cb) {
const 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)
}
BB.try(() => {
const opts = npmConfig()
return whoami([], true).then(username => {
const un = encodeURIComponent(username)
let 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
case 'rm':
if (argv.length > 3) {
theUser = encodeURIComponent(argv[3])
byUser = `/-/by-user/${theUser}|${un}`
return npmFetch.json(byUser, opts).then(d => {
return d[theUser].filter(
// kludge for server adminery.
p => un === 'isaacs' || d[un].indexOf(p) === -1
)
})
}
// else fallthrough
/* eslint no-fallthrough:0 */
case 'add':
if (argv.length > 3) {
theUser = encodeURIComponent(argv[3])
byUser = `/-/by-user/${theUser}|${un}`
return npmFetch.json(byUser, opts).then(d => {
var mine = d[un] || []
var theirs = d[theUser] || []
return mine.filter(p => theirs.indexOf(p) === -1)
})
} else {
// just list all users who aren't me.
return npmFetch.json('/-/users', opts).then(list => {
return Object.keys(list).filter(n => n !== un)
})
}
default:
return cb()
}
})
}).nodeify(cb)
}
function UsageError () {
throw Object.assign(new Error(owner.usage), {code: 'EUSAGE'})
}
function owner ([action, ...args], cb) {
const opts = npmConfig()
BB.try(() => {
switch (action) {
case 'ls': case 'list': return ls(args[0], opts)
case 'add': return add(args[0], args[1], opts)
case 'rm': case 'remove': return rm(args[0], args[1], opts)
default: UsageError()
}
}).then(
data => cb(null, data),
err => err.code === 'EUSAGE' ? cb(err.message) : cb(err)
)
}
function ls (pkg, opts) {
if (!pkg) {
return readLocalPkg().then(pkg => {
if (!pkg) { UsageError() }
return ls(pkg, opts)
})
}
const spec = npa(pkg)
return packument(spec, opts.concat({fullMetadata: true})).then(
data => {
var owners = data.maintainers
if (!owners || !owners.length) {
output('admin party!')
} else {
output(owners.map(o => `${o.name} <${o.email}>`).join('\n'))
}
return owners
},
err => {
log.error('owner ls', "Couldn't get owner data", pkg)
throw err
}
)
}
function add (user, pkg, opts) {
if (!user) { UsageError() }
if (!pkg) {
return readLocalPkg().then(pkg => {
if (!pkg) { UsageError() }
return add(user, pkg, opts)
})
}
log.verbose('owner add', '%s to %s', user, pkg)
const spec = npa(pkg)
return withMutation(spec, user, opts, (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
})
}
function rm (user, pkg, opts) {
if (!user) { UsageError() }
if (!pkg) {
return readLocalPkg().then(pkg => {
if (!pkg) { UsageError() }
return add(user, pkg, opts)
})
}
log.verbose('owner rm', '%s from %s', user, pkg)
const spec = npa(pkg)
return withMutation(spec, user, opts, function (u, owners) {
let found = false
const 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) {
throw new Error(
'Cannot remove all owners of a package. Add someone else first.'
)
}
return m
})
}
function withMutation (spec, user, opts, mutation) {
return BB.try(() => {
if (user) {
const uri = `/-/user/org.couchdb.user:${encodeURIComponent(user)}`
return npmFetch.json(uri, opts).then(mutate_, err => {
log.error('owner mutate', 'Error getting user data for %s', user)
throw err
})
} else {
return mutate_(null)
}
})
function mutate_ (u) {
if (user && (!u || u.error)) {
throw new Error(
"Couldn't get user data for " + user + ': ' + JSON.stringify(u)
)
}
if (u) u = { name: u.name, email: u.email }
return packument(spec, opts.concat({
fullMetadata: true
})).then(data => {
// save the number of maintainers before mutation so that we can figure
// out if maintainers were added or removed
const beforeMutation = data.maintainers.length
const m = mutation(u, data.maintainers)
if (!m) return // handled
if (m instanceof Error) throw m // error
data = {
_id: data._id,
_rev: data._rev,
maintainers: m
}
const dataPath = `/${spec.escapedName}/-rev/${encodeURIComponent(data._rev)}`
return otplease(opts, opts => {
const reqOpts = opts.concat({
method: 'PUT',
body: data,
spec
})
return npmFetch.json(dataPath, reqOpts)
}).then(data => {
if (data.error) {
throw new Error('Failed to update package metadata: ' + JSON.stringify(data))
} else if (m.length > beforeMutation) {
output('+ %s (%s)', user, spec.name)
} else if (m.length < beforeMutation) {
output('- %s (%s)', user, spec.name)
}
return data
})
})
}
}