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 }) }) } }
Name | Type | Size | Permission | Actions |
---|---|---|---|---|
auth | Folder | 0755 |
|
|
config | Folder | 0755 |
|
|
doctor | Folder | 0755 |
|
|
install | Folder | 0755 |
|
|
search | Folder | 0755 |
|
|
utils | Folder | 0755 |
|
|
access.js | File | 5.54 KB | 0644 |
|
adduser.js | File | 1.31 KB | 0644 |
|
audit.js | File | 10.56 KB | 0644 |
|
bin.js | File | 515 B | 0644 |
|
bugs.js | File | 864 B | 0644 |
|
build.js | File | 4.44 KB | 0644 |
|
cache.js | File | 4.66 KB | 0644 |
|
ci.js | File | 1.31 KB | 0644 |
|
completion.js | File | 7.11 KB | 0644 |
|
config.js | File | 7.43 KB | 0644 |
|
dedupe.js | File | 4.88 KB | 0644 |
|
deprecate.js | File | 2.11 KB | 0644 |
|
dist-tag.js | File | 4.11 KB | 0644 |
|
docs.js | File | 1.04 KB | 0644 |
|
doctor.js | File | 3.98 KB | 0644 |
|
edit.js | File | 1.37 KB | 0644 |
|
explore.js | File | 1.67 KB | 0644 |
|
fetch-package-metadata.js | File | 3.97 KB | 0644 |
|
fetch-package-metadata.md | File | 1.77 KB | 0644 |
|
fund.js | File | 4.91 KB | 0644 |
|
get.js | File | 235 B | 0644 |
|
help-search.js | File | 5.64 KB | 0644 |
|
help.js | File | 6.35 KB | 0644 |
|
hook.js | File | 4.62 KB | 0644 |
|
init.js | File | 2.74 KB | 0644 |
|
install-ci-test.js | File | 486 B | 0644 |
|
install-test.js | File | 507 B | 0644 |
|
install.js | File | 36.47 KB | 0644 |
|
link.js | File | 5.6 KB | 0644 |
|
logout.js | File | 1.26 KB | 0644 |
|
ls.js | File | 16.09 KB | 0644 |
|
npm.js | File | 14.37 KB | 0644 |
|
org.js | File | 4.18 KB | 0644 |
|
outdated.js | File | 12.28 KB | 0644 |
|
owner.js | File | 6.6 KB | 0644 |
|
pack.js | File | 11.79 KB | 0644 |
|
ping.js | File | 1.11 KB | 0644 |
|
prefix.js | File | 330 B | 0644 |
|
profile.js | File | 11.13 KB | 0644 |
|
prune.js | File | 2.23 KB | 0644 |
|
publish.js | File | 5.14 KB | 0644 |
|
rebuild.js | File | 2.09 KB | 0644 |
|
repo.js | File | 1.44 KB | 0644 |
|
restart.js | File | 64 B | 0644 |
|
root.js | File | 320 B | 0644 |
|
run-script.js | File | 5.41 KB | 0644 |
|
search.js | File | 3.36 KB | 0644 |
|
set.js | File | 276 B | 0644 |
|
shrinkwrap.js | File | 9.82 KB | 0644 |
|
star.js | File | 2.11 KB | 0644 |
|
stars.js | File | 1.03 KB | 0644 |
|
start.js | File | 62 B | 0644 |
|
stop.js | File | 61 B | 0644 |
|
substack.js | File | 509 B | 0644 |
|
team.js | File | 4.61 KB | 0644 |
|
test.js | File | 374 B | 0644 |
|
token.js | File | 6.66 KB | 0644 |
|
unbuild.js | File | 4.27 KB | 0644 |
|
uninstall.js | File | 2.21 KB | 0644 |
|
unpublish.js | File | 3.51 KB | 0644 |
|
update.js | File | 2.16 KB | 0644 |
|
version.js | File | 9.79 KB | 0644 |
|
view.js | File | 15.11 KB | 0644 |
|
visnup.js | File | 4.01 KB | 0644 |
|
whoami.js | File | 1.77 KB | 0644 |
|
xmas.js | File | 1.62 KB | 0644 |
|