diff --git a/app/skybreak/deploy.js b/app/skybreak/deploy.js index bec1cdf194..8bfb7dd10a 100644 --- a/app/skybreak/deploy.js +++ b/app/skybreak/deploy.js @@ -23,6 +23,59 @@ exports.parse_url = function (url) { return parsed; }; +// hash the password so we never send plaintext over the wire. Doesn't +// actually make us more secure, but it means we won't leak a user's +// password, which they might use on other sites too. +var transform_password = function (password) { + // XXX + return password; +}; + +// read a password from stdin. return it in a callback. +var read_password = function (callback) { + // Password prompt code adapted from + // https://github.com/visionmedia/commander.js/blob/master/lib/commander.js + + var buf = ''; + process.stdin.resume(); + tty.setRawMode(true); + + // keypress + process.stdin.on('keypress', function(c, key){ + if (key && 'enter' === key.name) { + console.log(); + process.stdin.removeAllListeners('keypress'); + tty.setRawMode(false); + + // if they just hit enter, prompt again. let's not do this. + // This means empty password is a valid password. + //if (!buf.trim().length) return self.password(str, mask, fn); + + callback(transform_password(buf)); + return; + } + + // deal with backspace + if (key && 'backspace' === key.name) { + buf = buf.substring(0, buf.length - 1); + return; + } + + // raw mode masks control-c. make sure users can get out. + if (key && key.ctrl && 'c' === key.name) { + console.log(); + process.stdin.removeAllListeners('keypress'); + tty.setRawMode(false); + + process.kill(process.pid, 'SIGINT'); + return; + } + + buf += c; + }); + +}; + // Check if a particular endpoint requires a password. If so, prompt for // it. @@ -51,48 +104,25 @@ exports.maybe_password = function (endpoint, callback) { return; } - // Password prompt code adapted from - // https://github.com/visionmedia/commander.js/blob/master/lib/commander.js - - var buf = ''; - process.stdin.resume(); - tty.setRawMode(true); process.stdout.write("Password: "); - - // keypress - process.stdin.on('keypress', function(c, key){ - if (key && 'enter' === key.name) { - console.log(); - process.stdin.removeAllListeners('keypress'); - tty.setRawMode(false); - - // if they just hit enter, prompt again. let's not do this. - //if (!buf.trim().length) return self.password(str, mask, fn); - - // XXX md5 hash the password! - - callback(buf); - return; - } - - // deal with backspace - if (key && 'backspace' === key.name) { - buf = buf.substring(0, buf.length - 1); - return; - } - - // raw mode masks control-c. make sure users can get out. - if (key && key.ctrl && 'c' === key.name) { - console.log(); - process.stdin.removeAllListeners('keypress'); - tty.setRawMode(false); - - process.kill(process.pid, 'SIGINT'); - return; - } - - buf += c; - }); + read_password(callback); }); }; + +// Prompts for a new password, asking you twice so you don't typo +// it. Keeps prompting you until you have two that match. +exports.get_new_password = function (callback) { + process.stdout.write("New Password: "); + read_password(function (p1) { + process.stdout.write("Confirm Password: "); + read_password(function (p2) { + if (p1 === p2) { + callback(p1); + return; + } + process.stdout.write("Passwords do not match! Try again.\n"); + exports.get_new_password(callback); + }); + }); +}; diff --git a/app/skybreak/skybreak.js b/app/skybreak/skybreak.js index 134e40d007..0210557a5f 100644 --- a/app/skybreak/skybreak.js +++ b/app/skybreak/skybreak.js @@ -491,18 +491,27 @@ Commands.push({ name: "deploy", help: "Deploy this project to Skybreak", func: function (argv) { - if (argv.help || argv._.length < 1 || argv._.length > 2) { - process.stdout.write( + var opt = require('optimist') + .boolean('password') + .alias('password', 'P') + .describe('password', 'set a password for the deployment') + .usage( "Usage: skybreak deploy \n" + "\n" + "Deploy the current code in your tree to the specified subdomain of\n" + -"skybreakplatform.com.\n"); +"skybreakplatform.com.\n" + ); + + new_argv = opt.argv; + + if (new_argv.help || new_argv._.length != 2) { + process.stdout.write(opt.help()); process.exit(1); } var deploy = require('./deploy'); - var url = deploy.parse_url(argv._[0]); + var url = deploy.parse_url(new_argv._[1]); if (!url.hostname) { process.stdout.write( @@ -518,16 +527,14 @@ Commands.push({ process.exit(1); } - deploy.maybe_password(url.hostname, function (password) { - - // XXX prompt for new password here + var app_dir = path.resolve(require_project("bundle")); + var build_dir = path.join(app_dir, '.skybreak/local/build_tar'); + var bundle_path = path.join(build_dir, 'bundle'); + var bundle_opts = { skip_dev_bundle: true }; + var do_deploy = function (password, set_password) { process.stdout.write('Deploying to ' + url.hostname + '. Bundling ... '); - var app_dir = path.resolve(require_project("bundle")); - var build_dir = path.join(app_dir, '.skybreak/local/build_tar'); - var bundle_path = path.join(build_dir, 'bundle'); - var bundle_opts = { skip_dev_bundle: true }; require('../lib/bundler.js').bundle(app_dir, bundle_path, bundle_opts); process.stdout.write('uploading ... '); @@ -541,9 +548,11 @@ Commands.push({ host: deploy.HOSTNAME, path: '/deploy/' + url.hostname }; - if (password) { - options.path += '?password=' + password; - } + var password_opts = {}; + if (password) password_opts.password = password; + if (set_password) password_opts.set_password = set_password; + if (password || set_password) + deploy_req_opts.path += "?" + require('querystring').stringify(password_opts); var http = require('http'); var deploy_req = http.request(deploy_req_opts, function (deploy_res) { @@ -579,6 +588,16 @@ Commands.push({ tar.on('exit', function (code) { deploy_req.end(); }); + }; + + deploy.maybe_password(url.hostname, function (password) { + if (new_argv.password) { + deploy.get_new_password(function (set_password) { + do_deploy(password, set_password); + }); + } else { + do_deploy(password); + } }); } });