hook.js 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. 'use strict'
  2. const BB = require('bluebird')
  3. const hookApi = require('libnpm/hook')
  4. const npmConfig = require('./config/figgy-config.js')
  5. const output = require('./utils/output.js')
  6. const otplease = require('./utils/otplease.js')
  7. const pudding = require('figgy-pudding')
  8. const relativeDate = require('tiny-relative-date')
  9. const Table = require('cli-table3')
  10. const validate = require('aproba')
  11. const npm = require('./npm')
  12. hook.usage = [
  13. 'npm hook add <pkg> <url> <secret> [--type=<type>]',
  14. 'npm hook ls [pkg]',
  15. 'npm hook rm <id>',
  16. 'npm hook update <id> <url> <secret>'
  17. ].join('\n')
  18. hook.completion = (opts, cb) => {
  19. validate('OF', [opts, cb])
  20. return cb(null, []) // fill in this array with completion values
  21. }
  22. const HookConfig = pudding({
  23. json: {},
  24. loglevel: {},
  25. parseable: {},
  26. silent: {},
  27. unicode: {}
  28. })
  29. function UsageError () {
  30. throw Object.assign(new Error(hook.usage), {code: 'EUSAGE'})
  31. }
  32. module.exports = (args, cb) => BB.try(() => hook(args)).then(
  33. val => cb(null, val),
  34. err => err.code === 'EUSAGE' ? cb(err.message) : cb(err)
  35. )
  36. function hook (args) {
  37. if (args.length === 4) { // secret is passed in the args
  38. // we have the user secret in the CLI args, we need to redact it from the referer.
  39. redactUserSecret()
  40. }
  41. return otplease(npmConfig(), opts => {
  42. opts = HookConfig(opts)
  43. switch (args[0]) {
  44. case 'add':
  45. return add(args[1], args[2], args[3], opts)
  46. case 'ls':
  47. return ls(args[1], opts)
  48. case 'rm':
  49. return rm(args[1], opts)
  50. case 'update':
  51. case 'up':
  52. return update(args[1], args[2], args[3], opts)
  53. default:
  54. UsageError()
  55. }
  56. })
  57. }
  58. function add (pkg, uri, secret, opts) {
  59. return hookApi.add(pkg, uri, secret, opts).then(hook => {
  60. if (opts.json) {
  61. output(JSON.stringify(hook, null, 2))
  62. } else if (opts.parseable) {
  63. output(Object.keys(hook).join('\t'))
  64. output(Object.keys(hook).map(k => hook[k]).join('\t'))
  65. } else if (!opts.silent && opts.loglevel !== 'silent') {
  66. output(`+ ${hookName(hook)} ${
  67. opts.unicode ? ' ➜ ' : ' -> '
  68. } ${hook.endpoint}`)
  69. }
  70. })
  71. }
  72. function ls (pkg, opts) {
  73. return hookApi.ls(opts.concat({package: pkg})).then(hooks => {
  74. if (opts.json) {
  75. output(JSON.stringify(hooks, null, 2))
  76. } else if (opts.parseable) {
  77. output(Object.keys(hooks[0]).join('\t'))
  78. hooks.forEach(hook => {
  79. output(Object.keys(hook).map(k => hook[k]).join('\t'))
  80. })
  81. } else if (!hooks.length) {
  82. output("You don't have any hooks configured yet.")
  83. } else if (!opts.silent && opts.loglevel !== 'silent') {
  84. if (hooks.length === 1) {
  85. output('You have one hook configured.')
  86. } else {
  87. output(`You have ${hooks.length} hooks configured.`)
  88. }
  89. const table = new Table({head: ['id', 'target', 'endpoint']})
  90. hooks.forEach((hook) => {
  91. table.push([
  92. {rowSpan: 2, content: hook.id},
  93. hookName(hook),
  94. hook.endpoint
  95. ])
  96. if (hook.last_delivery) {
  97. table.push([
  98. {
  99. colSpan: 1,
  100. content: `triggered ${relativeDate(hook.last_delivery)}`
  101. },
  102. hook.response_code
  103. ])
  104. } else {
  105. table.push([{colSpan: 2, content: 'never triggered'}])
  106. }
  107. })
  108. output(table.toString())
  109. }
  110. })
  111. }
  112. function rm (id, opts) {
  113. return hookApi.rm(id, opts).then(hook => {
  114. if (opts.json) {
  115. output(JSON.stringify(hook, null, 2))
  116. } else if (opts.parseable) {
  117. output(Object.keys(hook).join('\t'))
  118. output(Object.keys(hook).map(k => hook[k]).join('\t'))
  119. } else if (!opts.silent && opts.loglevel !== 'silent') {
  120. output(`- ${hookName(hook)} ${
  121. opts.unicode ? ' ✘ ' : ' X '
  122. } ${hook.endpoint}`)
  123. }
  124. })
  125. }
  126. function update (id, uri, secret, opts) {
  127. return hookApi.update(id, uri, secret, opts).then(hook => {
  128. if (opts.json) {
  129. output(JSON.stringify(hook, null, 2))
  130. } else if (opts.parseable) {
  131. output(Object.keys(hook).join('\t'))
  132. output(Object.keys(hook).map(k => hook[k]).join('\t'))
  133. } else if (!opts.silent && opts.loglevel !== 'silent') {
  134. output(`+ ${hookName(hook)} ${
  135. opts.unicode ? ' ➜ ' : ' -> '
  136. } ${hook.endpoint}`)
  137. }
  138. })
  139. }
  140. function hookName (hook) {
  141. let target = hook.name
  142. if (hook.type === 'scope') { target = '@' + target }
  143. if (hook.type === 'owner') { target = '~' + target }
  144. return target
  145. }
  146. function redactUserSecret () {
  147. const referer = npm.referer
  148. if (!referer) return
  149. const splittedReferer = referer.split(' ')
  150. splittedReferer[4] = '[REDACTED]'
  151. npm.referer = splittedReferer.join(' ')
  152. }