UNPKG

hubot-scripts

Version:

Allows you to opt in to a variety of scripts

141 lines (125 loc) 6.83 kB
# Description: # Interact with Gerrit. (http://code.google.com/p/gerrit/) # # Dependencies: # # Configuration: # HUBOT_GERRIT_SSH_URL # HUBOT_GERRIT_EVENTSTREAM_ROOMS # # Commands: # hubot gerrit search <query> - Search Gerrit for changes - the query should follow the normal Gerrit query rules # hubot gerrit (ignore|report) events for (project|user|event) <thing> - Tell Hubot how to report Gerrit events # # Notes: # Hubot has to be running as a user who has registered a SSH key with Gerrit # # Author: # nparry cp = require "child_process" url = require "url" # Required - The SSH URL for your Gerrit server. sshUrl = process.env.HUBOT_GERRIT_SSH_URL || "" # Optional - A comma separated list of rooms to receive spam about Gerrit events. # If not set, messages will be sent to all room of which Hubot is a member. # To disable event stream spam, use the value "disabled" eventStreamRooms = process.env.HUBOT_GERRIT_EVENTSTREAM_ROOMS # TODO: Make these template driven with env-var overrides possible. # See the following for descriptions of the input JSON data: # http://gerrit-documentation.googlecode.com/svn/Documentation/2.4/json.html # http://gerrit-documentation.googlecode.com/svn/Documentation/2.4/cmd-stream-events.html formatters = queryResult: (json) -> "'#{json.change.subject}' for #{json.change.project}/#{json.change.branch} by #{extractName json.change} on #{formatDate json.change.lastUpdated}: #{json.change.url}" events: "patchset-created": (json) -> "#{extractName json} uploaded patchset #{json.patchSet.number} of '#{json.change.subject}' for #{json.change.project}/#{json.change.branch}: #{json.change.url}" "change-abandoned": (json) -> "#{extractName json} abandoned '#{json.change.subject}' for #{json.change.project}/#{json.change.branch}: #{json.change.url}" "change-restored": (json) -> "#{extractName json} restored '#{json.change.subject}' for #{json.change.project}/#{json.change.branch}: #{json.change.url}" "change-merged": (json) -> "#{extractName json} merged patchset #{json.patchSet.number} of '#{json.change.subject}' for #{json.change.project}/#{json.change.branch}: #{json.change.url}" "comment-added": (json) -> "#{extractName json} reviewed patchset #{json.patchSet.number} (#{extractReviews json}) of '#{json.change.subject}' for #{json.change.project}/#{json.change.branch}: #{json.change.url}" "ref-updated": (json) -> "#{extractName json} updated reference #{json.refUpdate.project}/#{json.refUpdate.refName}" formatDate = (seconds) -> new Date(seconds * 1000).toDateString() extractName = (json) -> account = json.uploader || json.abandoner || json.restorer || json.submitter || json.author || json.owner account?.name || account?.email || "Gerrit" extractReviews = (json) -> ("#{a.description}=#{a.value}" for a in json.approvals).join "," module.exports = (robot) -> gerrit = url.parse sshUrl gerrit.port = 22 unless gerrit.port if gerrit.protocol != "ssh:" || gerrit.hostname == "" robot.logger.error "Gerrit commands inactive because HUBOT_GERRIT_SSH_URL=#{gerrit.href} is not a valid SSH URL" else eventStreamMe robot, gerrit unless eventStreamRooms == "disabled" robot.respond /gerrit (?:search|query)(?: me)? (.+)/i, searchMe robot, gerrit robot.respond /gerrit (ignore|report)(?: me)? events for (project|user|event) (.+)/i, ignoreOrReportEventsMe robot, gerrit searchMe = (robot, gerrit) -> (msg) -> cp.exec "ssh #{gerrit.hostname} -p #{gerrit.port} gerrit query --format=JSON #{msg.match[1]}", (err, stdout, stderr) -> if err msg.send "Sorry, something went wrong talking with Gerrit: #{stderr}" else results = (JSON.parse l for l in stdout.split "\n" when l isnt "") status = results[results.length - 1] if status.type == "error" msg.send "Sorry, Gerrit didn't like your query: #{status.message}" else if status.rowCount == 0 msg.send "Gerrit didn't find anything matching your query" else msg.send formatters.queryResult change: r for r in results when r.id ignoreOrReportEventsMe = (robot, gerrit) -> (msg) -> type = msg.match[2].toLowerCase() thing = msg.match[3] ignores = (t for t in ignoresOfType robot, type when t isnt thing) ignores.push thing if msg.match[1] == "ignore" robot.brain.data.gerrit ?= { } robot.brain.data.gerrit.eventStream ?= { } robot.brain.data.gerrit.eventStream.ignores ?= { } robot.brain.data.gerrit.eventStream.ignores[type] = ignores msg.send "Got it, the updated list of Gerrit #{type}s to ignore is #{ignores.join(', ') || 'empty'}" eventStreamMe = (robot, gerrit) -> robot.logger.info "Gerrit stream-events: Starting connection" streamEvents = cp.spawn "ssh", [gerrit.hostname, "-p", gerrit.port, "gerrit", "stream-events"] done = false reconnect = null robot.brain.on "close", -> done = true clearTimeout reconnect if reconnect streamEvents.stdin.end() streamEvents.on "exit", (code) -> robot.logger.info "Gerrit stream-events: Connection lost (rc=#{code})" reconnect = setTimeout (-> eventStreamMe robot, gerrit), 10 * 1000 unless done isIgnored = (type, thing) -> (t for t in ignoresOfType robot, type when t is thing).length != 0 isWanted = (event) -> !( isIgnored("project", (event.change || event.refUpdate).project) || isIgnored("user", extractName event) || isIgnored("event", event.type)) streamEvents.stderr.on "data", (data) -> robot.logger.info "Gerrit stream-events: #{data}" streamEvents.stdout.on "data", (data) -> robot.logger.debug "Gerrit stream-events: #{data}" json = try JSON.parse data catch error robot.logger.error "Gerrit stream-events: Error parsing Gerrit JSON. Error=#{error}, Event=#{data}" null return unless json formatter = formatters.events[json.type] msg = try formatter json if formatter catch error robot.logger.error "Gerrit stream-events: Error formatting event. Error=#{error}, Event=#{data}" null if formatter == null robot.logger.info "Gerrit stream-events: Unrecognized event #{data}" else if msg && isWanted json # Bug in messageRoom? Doesn't work with multiple rooms #robot.messageRoom room, "Gerrit: #{msg}" for room in robotRooms robot robot.send room: room, "Gerrit: #{msg}" for room in robotRooms robot ignoresOfType = (robot, type) -> robot.brain.data.gerrit?.eventStream?.ignores?[type] || [] robotRooms = (robot) -> roomlists = if eventStreamRooms [ eventStreamRooms ] else v for k,v of process.env when /^HUBOT_.+_ROOMS/i.exec(k) isnt null robot.logger.error "Gerrit stream-events: Unable to determine the list of rooms" if roomlists.length == 0 r for r in (roomlists[0] || "").split "," when r isnt ""