├── .gitignore ├── LICENSE ├── README.md ├── index.coffee ├── package.json └── src └── reload-scripts.coffee /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | 29 | .DS_Store* 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Vinta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hubot-reload-scripts 2 | 3 | [![](https://img.shields.io/npm/v/hubot-reload-scripts.svg?style=flat-square)](https://www.npmjs.com/package/hubot-reload-scripts) 4 | 5 | Reloads scripts without restart. Loads new scripts too. (a fork version that works perfectly) 6 | 7 | Fork from [the original](https://github.com/github/hubot-scripts/blob/master/src/scripts/reload.coffee) `reload.coffee` and @srobroek's [code](https://github.com/srobroek/hubot/blob/e543dff46fba9e435a352e6debe5cf210e40f860/src/robot.coffee). 8 | 9 | ## Installation 10 | 11 | In your hubot project repo, run: 12 | 13 | ``` bash 14 | npm install hubot-reload-scripts --save 15 | ``` 16 | 17 | Then add **hubot-reload-scripts** to your `external-scripts.json`: 18 | 19 | ``` json 20 | [ 21 | "hubot-reload-scripts" 22 | ] 23 | ``` 24 | 25 | ## Usage 26 | 27 | ``` 28 | user>> hubot reload 29 | hubot>> Reloaded all scripts 30 | ``` 31 | -------------------------------------------------------------------------------- /index.coffee: -------------------------------------------------------------------------------- 1 | fs = require 'fs' 2 | path = require 'path' 3 | 4 | module.exports = (robot, scripts) -> 5 | scriptsPath = path.resolve(__dirname, 'src') 6 | fs.exists scriptsPath, (exists) -> 7 | if exists 8 | for script in fs.readdirSync(scriptsPath) 9 | if scripts? and '*' not in scripts 10 | robot.loadFile(scriptsPath, script) if script in scripts 11 | else 12 | robot.loadFile(scriptsPath, script) 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hubot-reload-scripts", 3 | "description": "Reloads scripts without restart. Loads new scripts too. (a fork version that works perfectly with some more fixes)", 4 | "version": "0.1.4", 5 | "author": "Vinta Chen (http://vinta.ws/)", 6 | "license": "MIT", 7 | "keywords": "hubot, hubot reload, hubot load, hubot scripts, hubot scripting", 8 | "repository": { 9 | "type": "git", 10 | "url": "git://github.com/vinta/hubot-reload-scripts.git" 11 | }, 12 | "bugs": { 13 | "url": "https://github.com/vinta/hubot-reload-scripts/issues" 14 | }, 15 | "dependencies": {}, 16 | "peerDependencies": { 17 | "hubot": "2.x" 18 | }, 19 | "devDependencies": { 20 | "coffee-script": "1.9.x" 21 | }, 22 | "main": "index.coffee" 23 | } 24 | -------------------------------------------------------------------------------- /src/reload-scripts.coffee: -------------------------------------------------------------------------------- 1 | # Description: 2 | # Allows Hubot to (re)load scripts without restart 3 | # 4 | # Commands: 5 | # hubot reload - Reloads scripts without restart. Loads new scripts too. (a fork version that works perfectly) 6 | # 7 | # Author: 8 | # spajus 9 | # vinta 10 | # m-seldin 11 | 12 | Fs = require 'fs' 13 | Path = require 'path' 14 | 15 | oldCommands = null 16 | oldListeners = null 17 | 18 | module.exports = (robot) -> 19 | 20 | robot.respond /reload/i, id:'reload-scripts.reload', (msg) -> 21 | try 22 | oldCommands = robot.commands 23 | oldListeners = robot.listeners 24 | 25 | robot.commands = [] 26 | robot.listeners = [] 27 | 28 | reloadAllScripts msg, success, (err) -> 29 | msg.send err 30 | catch error 31 | console.log "Hubot reloader:", error 32 | msg.send "Could not reload all scripts: #{error}" 33 | 34 | success = (msg) -> 35 | # Cleanup old listeners and help 36 | for listener in oldListeners 37 | listener = {} 38 | oldListeners = null 39 | oldCommands = null 40 | msg.send "Reloaded all scripts" 41 | 42 | 43 | walkSync = (dir, filelist) -> 44 | #walk through given directory and collect files 45 | files = Fs.readdirSync(dir) 46 | filelist = filelist || [] 47 | for file in files 48 | fullPath = Path.join(dir,file) 49 | robot.logger.debug "Scanning file : #{fullPath}" 50 | 51 | if (Fs.statSync(fullPath).isDirectory()) 52 | filelist = walkSync(fullPath, filelist) 53 | else 54 | #add full path file to returning collection 55 | filelist.push(fullPath) 56 | return filelist 57 | 58 | # ref: https://github.com/srobroek/hubot/blob/e543dff46fba9e435a352e6debe5cf210e40f860/src/robot.coffee 59 | deleteScriptCache = (scriptsBaseDir) -> 60 | if Fs.existsSync(scriptsBaseDir) 61 | fileList = walkSync scriptsBaseDir 62 | 63 | for file in fileList.sort() 64 | robot.logger.debug "file: #{file}" 65 | if require.cache[require.resolve(file)] 66 | try 67 | cacheobj = require.resolve(file) 68 | console.log "Invalidate require cache for #{cacheobj}" 69 | delete require.cache[cacheobj] 70 | catch error 71 | console.log "Unable to invalidate #{cacheobj}: #{error.stack}" 72 | robot.logger.debug "Finished deleting script cache!" 73 | 74 | reloadAllScripts = (msg, success, error) -> 75 | robot = msg.robot 76 | robot.emit('reload_scripts') 77 | 78 | robot.logger.debug "Deleting script cache..." 79 | 80 | scriptsPath = Path.resolve ".", "scripts" 81 | deleteScriptCache scriptsPath 82 | robot.load scriptsPath 83 | 84 | scriptsPath = Path.resolve ".", "src", "scripts" 85 | deleteScriptCache scriptsPath 86 | robot.load scriptsPath 87 | 88 | robot.logger.debug "Loading hubot scripts..." 89 | 90 | hubotScripts = Path.resolve ".", "hubot-scripts.json" 91 | Fs.exists hubotScripts, (exists) -> 92 | if exists 93 | Fs.readFile hubotScripts, (err, data) -> 94 | if data.length > 0 95 | try 96 | scripts = JSON.parse data 97 | scriptsPath = Path.resolve "node_modules", "hubot-scripts", "src", "scripts" 98 | robot.loadHubotScripts scriptsPath, scripts 99 | catch err 100 | error "Error parsing JSON data from hubot-scripts.json: #{err}" 101 | return 102 | 103 | robot.logger.debug "Loading hubot external scripts..." 104 | 105 | robot.logger.debug "Deleting cache for apppulsemobile" 106 | deleteScriptCache Path.resolve ".","node_modules","hubot-apppulsemobile","src" 107 | 108 | externalScripts = Path.resolve ".", "external-scripts.json" 109 | Fs.exists externalScripts, (exists) -> 110 | if exists 111 | Fs.readFile externalScripts, (err, data) -> 112 | if data.length > 0 113 | try 114 | robot.logger.debug "DATA : #{data}" 115 | scripts = JSON.parse data 116 | 117 | if scripts instanceof Array 118 | for pkg in scripts 119 | scriptPath = Path.resolve ".","node_modules",pkg,"src" 120 | robot.logger.debug "Deleting cache for #{pkg}" 121 | robot.logger.debug "Path : #{scripts}" 122 | deleteScriptCache scriptPath 123 | catch err 124 | error "Error parsing JSON data from external-scripts.json: #{err}" 125 | robot.loadExternalScripts scripts 126 | return 127 | robot.logger.debug "step 5" 128 | 129 | success(msg) 130 | --------------------------------------------------------------------------------