|
| 1 | +# Description: |
| 2 | +# Playing with Drush integration. Simple implementation of informational drush commands, and a base |
| 3 | +# interface for further drush command integration. |
| 4 | +# |
| 5 | +# Dependencies: |
| 6 | +# None |
| 7 | +# |
| 8 | +# Configuration: |
| 9 | +# The hubot user will need permissions to run drush on the server that it is installed on. |
| 10 | +# If the site aliases are to remote servers (likely the case) then the hubot user will also need |
| 11 | +# ssh keys setup in order to access these sites. |
| 12 | +# |
| 13 | +# Notes: |
| 14 | +# It would have been easier and more elegant to simply allow the user to funnel drush commands directly |
| 15 | +# to the spawn method; however, being that is a colossal security risk, I opted to limit the commands that |
| 16 | +# can be executed as well as the options provided to those commands. By default this is limited to |
| 17 | +# relatively harmless "info" commands. |
| 18 | +# |
| 19 | +# Commands: |
| 20 | +# hubot drush sa - show the list of available sites ( --update-aliases will refresh this list ) |
| 21 | +# hubot drush rq - show pending core requirements at a warning level or above |
| 22 | +# hubot drush <site alias> cc - Clears "all" cache for a given site alias. |
| 23 | +# hubot drush <site alias> pml - Lists the site modules ( "enabled" and "non-core" by default this can be changed with --disbaled or --core ) |
| 24 | +# hubot drush <site alias> pmi <module/theme> - Show detailed info about a module or theme |
| 25 | +# hubot drush <site alias> uinf <user> - Display information about the user specified by uid, email, or username |
| 26 | +# hubot drush <site alias> ws - Show the 10 most recent watchdog messages |
| 27 | +# hubot drush <site alias> vget <variable name> - Show the value of a given variable |
| 28 | +# |
| 29 | +# Author: |
| 30 | +# rh0 |
| 31 | + |
| 32 | +spawn = require("child_process").spawn |
| 33 | + |
| 34 | +drush_interface = -> |
| 35 | + site_aliases = [] |
| 36 | + |
| 37 | + # helper method to propagate the site aliases in memory |
| 38 | + update_aliases = (msg) -> |
| 39 | + output = '' |
| 40 | + raw_aliases = '' |
| 41 | + fetch_aliases = spawn("drush", ["sa"]) |
| 42 | + fetch_aliases.stdout.on "data", (data) -> |
| 43 | + raw_aliases += data |
| 44 | + fetch_aliases.stderr.on "data", (data) -> |
| 45 | + output = "Update experienced an error: " + data |
| 46 | + fetch_aliases.on "exit", (code) -> |
| 47 | + if code is 0 |
| 48 | + site_aliases = raw_aliases.split('\n') |
| 49 | + output = "Alias update complete." |
| 50 | + unless msg is `undefined` |
| 51 | + msg.send output |
| 52 | + |
| 53 | + # run the update script |
| 54 | + update_aliases() |
| 55 | + |
| 56 | + # generalized spawn method |
| 57 | + execute_drush = (msg, drush_args) -> |
| 58 | + output = '' |
| 59 | + msg.send "This may take a moment..." |
| 60 | + drush_spawn = spawn("drush", drush_args) |
| 61 | + drush_spawn.stdout.on "data", (data) -> |
| 62 | + output += data |
| 63 | + drush_spawn.stderr.on "data", (data) -> |
| 64 | + output += data |
| 65 | + drush_spawn.on "exit", (code) -> |
| 66 | + output += "Command complete." |
| 67 | + msg.send output |
| 68 | + |
| 69 | + # the commands that we are allowing drush to execute |
| 70 | + # NOTE: If you decide to augment the commands here please carefully consider what you are opening to the people |
| 71 | + # interacting with hubot. |
| 72 | + allowed_commands = |
| 73 | + drush_sa: (msg, command) -> |
| 74 | + if command.args.indexOf('--update-aliases') is -1 |
| 75 | + msg.send "If this list is empty or has unexpected results update aliases ('drush sa --update-aliases')." |
| 76 | + msg.send "Aliases we have in memory:\n" + site_aliases.join("\n") |
| 77 | + else |
| 78 | + msg.send "Updating aliases..." |
| 79 | + update_aliases(msg) |
| 80 | + |
| 81 | + drush_cc: (msg, command) -> |
| 82 | + execute_drush(msg, [command.alias, "cc", "all"]) |
| 83 | + |
| 84 | + drush_rq: (msg, command) -> |
| 85 | + execute_drush(msg, [command.alias, "rq", "--severity=1"]) |
| 86 | + |
| 87 | + drush_pml: (msg, command) -> |
| 88 | + allowed_options = ["--status=enabled", "--status=disabled", "--no-core", "--core"] |
| 89 | + filtered_options = command.args.filter((elem) -> |
| 90 | + allowed_options.indexOf(elem) isnt -1 |
| 91 | + ) |
| 92 | + filtered_options.unshift(command.alias, "pml") |
| 93 | + execute_drush(msg, filtered_options) |
| 94 | + |
| 95 | + drush_uinf: (msg, command) -> |
| 96 | + user_search = command.args.shift() |
| 97 | + execute_drush(msg, [command.alias, "uinf", user_search]) |
| 98 | + |
| 99 | + drush_pmi: (msg, command) -> |
| 100 | + extension_search = command.args.shift() |
| 101 | + execute_drush(msg, [command.alias, "pmi", extension_search]) |
| 102 | + |
| 103 | + drush_ws: (msg, command) -> |
| 104 | + allowed_options = ["--full"] |
| 105 | + filtered_options = command.args.filter((elem) -> |
| 106 | + allowed_options.indexOf(elem) isnt -1 |
| 107 | + ) |
| 108 | + filtered_options.unshift(command.alias, "ws") |
| 109 | + execute_drush(msg, filtered_options) |
| 110 | + |
| 111 | + drush_vget: (msg, command) -> |
| 112 | + variable_search = command.args.shift() |
| 113 | + # forcing this to --exact to prevent channel flood from a huge search |
| 114 | + execute_drush(msg, [command.alias, "vget", variable_search, "--exact"]) |
| 115 | + |
| 116 | + # verify alias before firing the command, saves us time on waiting for an err from drush |
| 117 | + verify_alias = (check_alias) -> |
| 118 | + if site_aliases.indexOf(check_alias) is -1 |
| 119 | + false |
| 120 | + else |
| 121 | + true |
| 122 | + |
| 123 | + # parsing the user input after "drush " |
| 124 | + parse_command = (user_command) -> |
| 125 | + extra_args = user_command.split(' ') |
| 126 | + site_alias = extra_args.shift() |
| 127 | + command_suff = '' |
| 128 | + if site_alias.charAt(0) is "@" |
| 129 | + unless verify_alias(site_alias) is false |
| 130 | + command_suff = extra_args.shift() |
| 131 | + else |
| 132 | + `undefined` |
| 133 | + # Kind of gross but the site-alias command is the only one that does not need a site alias |
| 134 | + # so let's check before we fire up drush to fail. |
| 135 | + else unless site_alias is "sa" |
| 136 | + `undefined` |
| 137 | + else |
| 138 | + command_suff = site_alias |
| 139 | + site_alias = '' |
| 140 | + command = "drush_" + command_suff |
| 141 | + if typeof allowed_commands[command] is "function" |
| 142 | + return ( |
| 143 | + cmnd: command |
| 144 | + alias: site_alias |
| 145 | + args: extra_args |
| 146 | + ) |
| 147 | + else |
| 148 | + `undefined` |
| 149 | + |
| 150 | + # BEGIN public facing methods |
| 151 | + |
| 152 | + # The main method, fire this when we receive a "drush " command. |
| 153 | + execute: (msg) -> |
| 154 | + command = parse_command(msg.match[1]) |
| 155 | + unless command is `undefined` |
| 156 | + allowed_commands[command.cmnd](msg, command) |
| 157 | + else |
| 158 | + msg.send "'drush " + msg.match[1] + "' is and invalid command. Please try again." |
| 159 | + |
| 160 | +# Instantiate the drush interface |
| 161 | +drush = drush_interface() |
| 162 | + |
| 163 | +# Hook in with hobot |
| 164 | +module.exports = (robot) -> |
| 165 | + robot.respond /drush (.*)$/i, (msg) -> |
| 166 | + drush.execute(msg) |
0 commit comments