├── test ├── b.txt ├── a.txt └── deploy.js ├── package.json ├── .gitignore ├── README.md ├── index.js ├── bin └── deploy └── upload.js /test/b.txt: -------------------------------------------------------------------------------- 1 | fdsaaf -------------------------------------------------------------------------------- /test/a.txt: -------------------------------------------------------------------------------- 1 | 2ddfdsfdsaa 2 | 3 | rerwfdsfs -------------------------------------------------------------------------------- /test/deploy.js: -------------------------------------------------------------------------------- 1 | module.exports = [ 2 | { 3 | receiver: 'http://127.0.0.1:8999/receiver', 4 | from: '/', 5 | to: '/Users/shouding/Downloads/' 6 | } 7 | ]; 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "deploy.js", 3 | "version": "0.0.4", 4 | "description": "...", 5 | "main": "index.js", 6 | "bin": { 7 | "deploy": "bin/deploy" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "keywords": [ 13 | "fis-plus", 14 | "fis", 15 | "jello", 16 | "yogurt" 17 | ], 18 | "author": "fansekey", 19 | "license": "MIT", 20 | "dependencies": { 21 | "debug": ">=2.6.9", 22 | "glob": "4.0.6", 23 | "mstring": "0.1.2", 24 | "rewatch": "0.4.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /.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 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Dependency directory 23 | # Deployed apps should consider commenting this line out: 24 | # see https://npmjs.org/doc/faq.html#Should-I-check-my-node_modules-folder-into-git 25 | node_modules 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### deploy.js 2 | 3 | ### install 4 | 5 | ``` 6 | npm install -g deploy.js 7 | ``` 8 | 9 | ### use 10 | 11 | ➜ deploy git:(master) ✗ deploy -h 12 | ``` 13 | Usage: 14 | deploy -r 15 | 16 | Options: 17 | -r, --root set root path 18 | -v, --version print the version of deploy 19 | -h, --help display this message 20 | 21 | Examples: 22 | deploy -r /home/user/dir 23 | ``` 24 | 25 | - 服务端部署 receiver 26 | 27 | 参见[fex-team/receiver](https://github.com/fex-team/receiver) 28 | 29 | - 想上传部署的目录下放置配置文件 `deploy.js` 30 | 31 | :deploy.js: 32 | ```javascript 33 | module.exports = [ 34 | { 35 | receiver: 'http://127.0.0.1:8999/receiver', 36 | from: '/', 37 | to: '/home/work/www' 38 | } 39 | ]; 40 | ``` 41 | 42 | > 具体配置可以参考FIS 2.x 的`deploy`配置方式 43 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * author: fansekey@gmail.com 3 | * create time: 2014-10-16 14:11:44 4 | */ 5 | 6 | var Rewatch = require('Rewatch'); 7 | var debug = require('debug')('deploy'); 8 | var util = require('util'); 9 | var glob = require('glob'); 10 | var fs = require('fs'); 11 | 12 | function Deploy(files, command, options) { 13 | var me = this; 14 | me.root = options['root'] || process.cwd(); 15 | Rewatch.apply(me, arguments); 16 | me.removeAllListeners('change'); 17 | me.on('change', function(o) { 18 | if (me.delay) { 19 | setTimeout(function() { 20 | me.execute(o.file); 21 | }, me.delay); 22 | } else { 23 | me.execute(o.file); 24 | } 25 | }); 26 | } 27 | 28 | util.inherits(Deploy, Rewatch); 29 | 30 | Deploy.prototype.watch = function(file) { 31 | var me = this; 32 | if (~file.indexOf('*')) { 33 | glob(file, function(err, files) { 34 | files.forEach(function(file) { 35 | me.watch(file); 36 | }); 37 | }); 38 | } else { 39 | // fs.watch is not reliable 40 | // https://github.com/joyent/node/issues/3172 41 | fs.watchFile(file, {interval: me.interval}, function() { 42 | me.emit('change', { 43 | file: file 44 | }); 45 | }); 46 | } 47 | }; 48 | 49 | Deploy.prototype.execute = function (file) { 50 | var me = this; 51 | var now = new Date(); 52 | var spawn = require('child_process').spawn; 53 | 54 | var commands = me._command.split(/\s+/); 55 | if (!me._time || now - me._time > me.interval) { 56 | me._time = now; 57 | if (me._child && me.signal) { 58 | me._child.kill(me.singal); 59 | } 60 | 61 | me._child = spawn(commands[0], commands.slice(1), { 62 | cwd: me.root, 63 | change_file: file 64 | }); 65 | me._child.stdout.pipe(process.stdout); 66 | me._child.stderr.pipe(process.stderr); 67 | } 68 | } 69 | 70 | 71 | 72 | module.exports = Deploy; 73 | -------------------------------------------------------------------------------- /bin/deploy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | function parseArgv() { 4 | var argv = process.argv; 5 | var opts = {}; 6 | for (var i = 0, len = argv.length; i < len; i+=2) { 7 | var cur = argv[i]; 8 | 9 | if (cur == '-h' || cur == '--help') { 10 | opts['help'] = true; 11 | } 12 | 13 | if (cur == '-v' || cur == '--version') { 14 | opts['version'] = true; 15 | } 16 | 17 | if (cur == '-r' || cur == '--root') { 18 | var path = require('path'); 19 | opts['root'] = path.resolve(argv[i+1]); 20 | } 21 | } 22 | return opts; 23 | } 24 | 25 | function printVersion() { 26 | console.log(require('../package.json').version); 27 | process.exit(); 28 | } 29 | 30 | function printHelp() { 31 | var mstring = require('mstring'); 32 | console.log(mstring(function () { 33 | /*** 34 | Usage: 35 | deploy -r 36 | 37 | Options: 38 | -r, --root set root path 39 | -v, --version print the version of deploy 40 | -h, --help display this message 41 | 42 | Examples: 43 | deploy -r /home/user/dir 44 | ***/ 45 | })); 46 | process.exit(); 47 | } 48 | 49 | (function main() { 50 | var opts = parseArgv(); 51 | if (opts['help']) { 52 | printHelp(); 53 | } 54 | 55 | if (opts['version']) { 56 | printVersion(); 57 | } 58 | 59 | if (!opts['root']) { 60 | printHelp(); 61 | } 62 | 63 | var spawn = require('child_process').spawn; 64 | var god = spawn('node', [__dirname + '/../upload.js'], { 65 | cwd: opts['root'] 66 | }); 67 | var str = ''; 68 | god.stdout.on('data', function (c) { 69 | str += c.toString(); 70 | }); 71 | god.stdout.on('end', function () { 72 | console.log(str); 73 | var Deploy = require('..'); 74 | var w = new Deploy( 75 | ['**'], 76 | 'node ' + __dirname + '/../upload.js', 77 | opts 78 | ); 79 | }); 80 | god.stderr.pipe(process.stderr); 81 | })(); 82 | -------------------------------------------------------------------------------- /upload.js: -------------------------------------------------------------------------------- 1 | /** 2 | * author: fansekey@gmail.com 3 | */ 4 | 5 | 'use strict'; 6 | 7 | var path = require('path'); 8 | var fs = require('fs'); 9 | var glob = require('glob'); 10 | var Url = require('url'); 11 | 12 | function now(){ 13 | var d = new Date(), str; 14 | str = [ 15 | d.getHours(), 16 | d.getMinutes(), 17 | d.getSeconds() 18 | ].join(':').replace(/\b\d\b/g, '0$&'); 19 | return str; 20 | }; 21 | 22 | 23 | function parseUrl(url, opt){ 24 | opt = opt || {}; 25 | url = Url.parse(url); 26 | var ssl = url.protocol === 'https:'; 27 | opt.host = opt.host 28 | || opt.hostname 29 | || ((ssl || url.protocol === 'http:') ? url.hostname : 'localhost'); 30 | opt.port = opt.port || (url.port || (ssl ? 443 : 80)); 31 | opt.path = opt.path || (url.pathname + (url.search ? url.search : '')); 32 | opt.method = opt.method || 'GET'; 33 | opt.agent = opt.agent || false; 34 | return opt; 35 | } 36 | 37 | function map(obj, callback, merge){ 38 | var index = 0; 39 | for(var key in obj){ 40 | if(obj.hasOwnProperty(key)){ 41 | if(merge){ 42 | callback[key] = obj[key]; 43 | } else if(callback(key, obj[key], index++)) { 44 | break; 45 | } 46 | } 47 | } 48 | } 49 | 50 | function upload(url, opt, data, content, subpath, callback){ 51 | if(typeof content === 'string'){ 52 | content = new Buffer(content, 'utf8'); 53 | } else if(!(content instanceof Buffer)){ 54 | throw new Error('unable to upload content [' + (typeof content) + ']'); 55 | } 56 | data = data || {}; 57 | var endl = '\r\n'; 58 | var boundary = '-----np' + Math.random(); 59 | var collect = []; 60 | map(data, function(key, value){ 61 | collect.push('--' + boundary + endl); 62 | collect.push('Content-Disposition: form-data; name="' + key + '"' + endl); 63 | collect.push(endl); 64 | collect.push(value + endl); 65 | }); 66 | collect.push('--' + boundary + endl); 67 | collect.push('Content-Disposition: form-data; name="file"; filename="' + subpath + '"' + endl); 68 | collect.push(endl); 69 | collect.push(content); 70 | collect.push('--' + boundary + '--' + endl); 71 | 72 | var length = 0; 73 | collect.forEach(function(ele){ 74 | length += ele.length; 75 | }); 76 | 77 | opt = opt || {}; 78 | opt.method = opt.method || 'POST'; 79 | opt.headers = { 80 | 'Content-Type': 'multipart/form-data; boundary=' + boundary, 81 | 'Content-Length': length 82 | }; 83 | opt = parseUrl(url, opt); 84 | var http = opt.protocol === 'https:' ? require('https') : require('http'); 85 | var req = http.request(opt, function(res){ 86 | var status = res.statusCode; 87 | var body = ''; 88 | res 89 | .on('data', function(chunk){ 90 | body += chunk; 91 | }) 92 | .on('end', function(){ 93 | if(status >= 200 && status < 300 || status === 304){ 94 | callback(null, body); 95 | } else { 96 | callback(status); 97 | } 98 | }) 99 | .on('error', function(err){ 100 | callback(err.message || err); 101 | }); 102 | }); 103 | collect.forEach(function(d){ 104 | req.write(d); 105 | if(d instanceof Buffer){ 106 | req.write(endl); 107 | } 108 | }); 109 | req.end(); 110 | } 111 | 112 | (function main() { 113 | var root = process.cwd(); 114 | 115 | var config = path.join(root, 'deploy.js'); 116 | var opts = require(config); 117 | 118 | for (var i = 0, len = opts.length; i < len; i++) { 119 | var node = opts[i]; 120 | var dir = path.join(root, node.from); 121 | glob(dir + '**', function (err, files) { 122 | if (err) { 123 | throw err; 124 | } 125 | for (var k = 0, ll = files.length; k < ll; k++) { 126 | var file = files[k]; 127 | var subpath = '/' + file.replace(dir, ''); 128 | var stat = fs.statSync(file); 129 | if (!stat.isFile()) { 130 | continue; 131 | } 132 | (function (file, subpath) { 133 | fs.readFile(file, function (err, content) { 134 | if (err) { 135 | throw err; 136 | } 137 | var to = path.join(node.to, subpath); 138 | upload( 139 | node['receiver'], 140 | {}, 141 | { 142 | to: to 143 | }, 144 | content, 145 | subpath, 146 | function (err, body) { 147 | if (err) { 148 | throw new Error(err); 149 | } 150 | console.log('[', now(), '] upload file ', file, ' > ', to, 'success'); 151 | } 152 | ); 153 | }); 154 | })(file, subpath); 155 | } 156 | }); 157 | } 158 | })(); 159 | --------------------------------------------------------------------------------