Implement Quickstart command

This commit is contained in:
Juan Cruz Viotti 2015-05-07 12:40:12 -03:00 committed by mike
parent d24b871964
commit dc030f4cd1
11 changed files with 320 additions and 18 deletions

View File

@ -41,12 +41,10 @@
if (options.type != null) { if (options.type != null) {
return callback(null, options.type); return callback(null, options.type);
} }
return resin.models.device.getSupportedDeviceTypes().then(function(supportedDeviceTypes) { return form.ask({
return form.ask({ message: 'Device Type',
message: 'Device Type', type: 'list',
type: 'list', choices: ['Raspberry Pi', 'Raspberry Pi 2', 'BeagleBone Black']
choices: supportedDeviceTypes
});
}).nodeify(callback); }).nodeify(callback);
}, function(type, callback) { }, function(type, callback) {
options.type = type; options.type = type;

View File

@ -1,5 +1,5 @@
(function() { (function() {
var _, async, capitano, commandOptions, deviceConfig, drivelist, form, fse, image, inject, manager, path, pine, registerDevice, resin, tmp, vcs, visuals; var _, async, capitano, commandOptions, deviceConfig, drivelist, form, fse, htmlToText, image, inject, manager, os, path, pine, registerDevice, resin, tmp, vcs, visuals;
fse = require('fs-extra'); fse = require('fs-extra');
@ -35,6 +35,10 @@
drivelist = require('drivelist'); drivelist = require('drivelist');
htmlToText = require('html-to-text');
os = require('os');
tmp.setGracefulCleanup(); tmp.setGracefulCleanup();
commandOptions = require('./command-options'); commandOptions = require('./command-options');
@ -291,6 +295,7 @@
} }
}, callback); }, callback);
}, function(results, callback) { }, function(results, callback) {
params.manifest = results.manifest;
console.info('Associating the device'); console.info('Associating the device');
return registerDevice.register(pine, results.config, function(error, device) { return registerDevice.register(pine, results.config, function(error, device) {
if (error != null) { if (error != null) {
@ -310,7 +315,7 @@
} }
bar = new visuals.Progress('Downloading Device OS'); bar = new visuals.Progress('Downloading Device OS');
spinner = new visuals.Spinner('Downloading Device OS (size unknown)'); spinner = new visuals.Spinner('Downloading Device OS (size unknown)');
return manager.configure(results.manifest, results.config, function(error, imagePath, removeCallback) { return manager.configure(params.manifest, results.config, function(error, imagePath, removeCallback) {
spinner.stop(); spinner.stop();
return callback(error, imagePath, removeCallback); return callback(error, imagePath, removeCallback);
}, function(state) { }, function(state) {
@ -322,6 +327,7 @@
}); });
}, function(configuredImagePath, removeCallback, callback) { }, function(configuredImagePath, removeCallback, callback) {
var bar; var bar;
console.info('The base image was cached to improve initialization time of similar devices');
console.info('Attempting to write operating system image to drive'); console.info('Attempting to write operating system image to drive');
bar = new visuals.Progress('Writing Device OS'); bar = new visuals.Progress('Writing Device OS');
return image.write({ return image.write({
@ -342,6 +348,28 @@
}, function(device, callback) { }, function(device, callback) {
console.info("Device created: " + device.name); console.info("Device created: " + device.name);
return callback(null, device.name); return callback(null, device.name);
}, function(deviceName, callback) {
var instructions, osSpecificInstructions, platform, platformHash;
if (params.manifest.instructions == null) {
instructions = '';
}
if (_.isArray(params.manifest.instructions)) {
instructions = htmlToText.fromString(params.manifest.instructions.join('\n'));
}
platformHash = {
darwin: 'osx',
linux: 'linux',
win32: 'windows'
};
platform = platformHash[os.platform()];
osSpecificInstructions = params.manifest.instructions[platform];
if (osSpecificInstructions == null) {
instructions = '';
} else {
instructions = htmlToText.fromString(osSpecificInstructions.join('\n'));
}
console.log('\n' + instructions);
return callback(null, params.uuid);
} }
], done); ], done);
} }

View File

@ -1,5 +1,6 @@
(function() { (function() {
module.exports = { module.exports = {
wizard: require('./wizard'),
app: require('./app'), app: require('./app'),
info: require('./info'), info: require('./info'),
auth: require('./auth'), auth: require('./auth'),

110
build/actions/wizard.js Normal file
View File

@ -0,0 +1,110 @@
(function() {
var Promise, _, async, capitano, form, mkdirp, path, resin, userHome, visuals;
_ = require('lodash-contrib');
Promise = require('bluebird');
capitano = Promise.promisifyAll(require('capitano'));
path = require('path');
mkdirp = require('mkdirp');
userHome = require('user-home');
visuals = require('resin-cli-visuals');
async = require('async');
resin = require('resin-sdk');
form = require('resin-cli-form');
exports.wizard = {
signature: 'quickstart [name]',
description: 'getting started with resin.io',
help: 'Use this command to run a friendly wizard to get started with resin.io.\n\nThe wizard will guide you through:\n\n - Create an application.\n - Initialise an SDCard with the resin.io operating system.\n - Associate an existing project directory with your resin.io application.\n - Push your project to your devices.\n\nExamples:\n\n $ sudo resin quickstart\n $ sudo resin quickstart MyApp',
root: true,
permission: 'user',
action: function(params, options, done) {
return async.waterfall([
function(callback) {
if (params.name != null) {
return callback();
}
return async.waterfall([
function(callback) {
return resin.models.application.hasAny().nodeify(callback);
}, function(hasAnyApplications, callback) {
if (!hasAnyApplications) {
return callback(null, null);
}
return async.waterfall([
function(callback) {
return resin.models.application.getAll().nodeify(callback);
}, function(applications, callback) {
applications = _.pluck(applications, 'app_name');
applications.unshift({
name: 'Create a new application',
value: null
});
return form.ask({
message: 'Select an application',
type: 'list',
choices: applications
}).nodeify(callback);
}
], callback);
}, function(application, callback) {
if (application != null) {
return callback(null, application);
}
return form.ask({
message: 'Choose a Name for your new application',
type: 'input'
}).then(function(applicationName) {
return capitano.runAsync("app create " + applicationName)["return"](applicationName);
}).nodeify(callback);
}, function(applicationName, callback) {
params.name = applicationName;
return callback();
}
], callback);
}, function(callback) {
return capitano.run("device init --application " + params.name, callback);
}, function(deviceUuid, callback) {
params.uuid = deviceUuid;
return resin.models.device.getName(params.uuid).then(function(deviceName) {
params.deviceName = deviceName;
console.log("Waiting for " + params.deviceName + " to connect to resin...");
return capitano.runAsync("device await " + params.uuid)["return"](callback);
}).nodeify(callback);
}, function(callback) {
console.log("The device " + params.deviceName + " successfully connected to resin!");
console.log('');
return capitano.run("device " + params.uuid, callback);
}, function(callback) {
console.log('Your device is ready, lets start pushing some code!');
return form.ask({
message: 'Please choose a directory for your code',
type: 'input',
"default": path.join(userHome, 'ResinProjects', params.name)
}).nodeify(callback);
}, function(directoryName, callback) {
params.directory = directoryName;
return mkdirp(directoryName, callback);
}, function(made, callback) {
console.log("Associating " + params.name + " with " + params.directory + "...");
process.chdir(params.directory);
return capitano.run("app associate " + params.name + " --project " + params.directory, callback);
}, function(remoteUrl, callback) {
console.log("Resin git remote added: " + remoteUrl);
console.log('Please type "git push resin master" into your project directory now!');
return callback();
}
], done);
}
};
}).call(this);

View File

@ -65,6 +65,8 @@
capitano.command(actions.help.help); capitano.command(actions.help.help);
capitano.command(actions.wizard.wizard);
capitano.command(actions.auth.login); capitano.command(actions.auth.login);
capitano.command(actions.auth.logout); capitano.command(actions.auth.logout);

View File

@ -45,11 +45,17 @@ exports.create =
return callback(new Error('You already have an application with that name!')) return callback(new Error('You already have an application with that name!'))
return callback(null, options.type) if options.type? return callback(null, options.type) if options.type?
resin.models.device.getSupportedDeviceTypes().then (supportedDeviceTypes) -> form.ask
form.ask message: 'Device Type'
message: 'Device Type' type: 'list'
type: 'list' choices: [
choices: supportedDeviceTypes
# Lock to specific devices until we support
# the rest with device specs.
'Raspberry Pi'
'Raspberry Pi 2'
'BeagleBone Black'
]
.nodeify(callback) .nodeify(callback)
(type, callback) -> (type, callback) ->

View File

@ -15,6 +15,8 @@ tmp = require('tmp')
deviceConfig = require('resin-device-config') deviceConfig = require('resin-device-config')
form = require('resin-cli-form') form = require('resin-cli-form')
drivelist = require('drivelist') drivelist = require('drivelist')
htmlToText = require('html-to-text')
os = require('os')
# Cleanup the temporary files even when an uncaught exception occurs # Cleanup the temporary files even when an uncaught exception occurs
tmp.setGracefulCleanup() tmp.setGracefulCleanup()
@ -365,6 +367,7 @@ exports.init =
, callback , callback
(results, callback) -> (results, callback) ->
params.manifest = results.manifest
console.info('Associating the device') console.info('Associating the device')
registerDevice.register pine, results.config, (error, device) -> registerDevice.register pine, results.config, (error, device) ->
@ -388,7 +391,7 @@ exports.init =
bar = new visuals.Progress('Downloading Device OS') bar = new visuals.Progress('Downloading Device OS')
spinner = new visuals.Spinner('Downloading Device OS (size unknown)') spinner = new visuals.Spinner('Downloading Device OS (size unknown)')
manager.configure results.manifest, results.config, (error, imagePath, removeCallback) -> manager.configure params.manifest, results.config, (error, imagePath, removeCallback) ->
spinner.stop() spinner.stop()
return callback(error, imagePath, removeCallback) return callback(error, imagePath, removeCallback)
, (state) -> , (state) ->
@ -398,6 +401,8 @@ exports.init =
spinner.start() spinner.start()
(configuredImagePath, removeCallback, callback) -> (configuredImagePath, removeCallback, callback) ->
console.info('The base image was cached to improve initialization time of similar devices')
console.info('Attempting to write operating system image to drive') console.info('Attempting to write operating system image to drive')
bar = new visuals.Progress('Writing Device OS') bar = new visuals.Progress('Writing Device OS')
@ -420,4 +425,26 @@ exports.init =
console.info("Device created: #{device.name}") console.info("Device created: #{device.name}")
return callback(null, device.name) return callback(null, device.name)
(deviceName, callback) ->
instructions = '' if not params.manifest.instructions?
if _.isArray(params.manifest.instructions)
instructions = htmlToText.fromString(params.manifest.instructions.join('\n'))
platformHash =
darwin: 'osx'
linux: 'linux'
win32: 'windows'
platform = platformHash[os.platform()]
osSpecificInstructions = params.manifest.instructions[platform]
if not osSpecificInstructions?
instructions = ''
else
instructions = htmlToText.fromString(osSpecificInstructions.join('\n'))
console.log('\n' + instructions)
return callback(null, params.uuid)
], done ], done

View File

@ -1,4 +1,5 @@
module.exports = module.exports =
wizard: require('./wizard')
app: require('./app') app: require('./app')
info: require('./info') info: require('./info')
auth: require('./auth') auth: require('./auth')

122
lib/actions/wizard.coffee Normal file
View File

@ -0,0 +1,122 @@
_ = require('lodash-contrib')
Promise = require('bluebird')
capitano = Promise.promisifyAll(require('capitano'))
path = require('path')
mkdirp = require('mkdirp')
userHome = require('user-home')
visuals = require('resin-cli-visuals')
async = require('async')
resin = require('resin-sdk')
form = require('resin-cli-form')
exports.wizard =
signature: 'quickstart [name]'
description: 'getting started with resin.io'
help: '''
Use this command to run a friendly wizard to get started with resin.io.
The wizard will guide you through:
- Create an application.
- Initialise an SDCard with the resin.io operating system.
- Associate an existing project directory with your resin.io application.
- Push your project to your devices.
Examples:
$ sudo resin quickstart
$ sudo resin quickstart MyApp
'''
root: true
permission: 'user'
action: (params, options, done) ->
async.waterfall [
(callback) ->
return callback() if params.name?
# TODO: Move this whole routine to Resin CLI Visuals
async.waterfall [
(callback) ->
resin.models.application.hasAny().nodeify(callback)
(hasAnyApplications, callback) ->
return callback(null, null) if not hasAnyApplications
async.waterfall [
(callback) ->
resin.models.application.getAll().nodeify(callback)
(applications, callback) ->
applications = _.pluck(applications, 'app_name')
applications.unshift
name: 'Create a new application'
value: null
form.ask
message: 'Select an application'
type: 'list'
choices: applications
.nodeify(callback)
], callback
(application, callback) ->
return callback(null, application) if application?
form.ask
message: 'Choose a Name for your new application'
type: 'input'
.then (applicationName) ->
capitano.runAsync("app create #{applicationName}").return(applicationName)
.nodeify(callback)
(applicationName, callback) ->
params.name = applicationName
return callback()
], callback
(callback) ->
capitano.run("device init --application #{params.name}", callback)
(deviceUuid, callback) ->
params.uuid = deviceUuid
resin.models.device.getName(params.uuid).then (deviceName) ->
params.deviceName = deviceName
console.log("Waiting for #{params.deviceName} to connect to resin...")
capitano.runAsync("device await #{params.uuid}").return(callback)
.nodeify(callback)
(callback) ->
console.log("The device #{params.deviceName} successfully connected to resin!")
console.log('')
capitano.run("device #{params.uuid}", callback)
(callback) ->
console.log('Your device is ready, lets start pushing some code!')
form.ask
message: 'Please choose a directory for your code'
type: 'input'
# TODO: Move this to resin-settings-client.
default: path.join(userHome, 'ResinProjects', params.name)
.nodeify(callback)
(directoryName, callback) ->
params.directory = directoryName
mkdirp(directoryName, callback)
(made, callback) ->
console.log("Associating #{params.name} with #{params.directory}...")
process.chdir(params.directory)
capitano.run("app associate #{params.name} --project #{params.directory}", callback)
(remoteUrl, callback) ->
console.log("Resin git remote added: #{remoteUrl}")
console.log('Please type "git push resin master" into your project directory now!')
return callback()
], done

View File

@ -51,6 +51,9 @@ capitano.command(actions.info.config)
# ---------- Help Module ---------- # ---------- Help Module ----------
capitano.command(actions.help.help) capitano.command(actions.help.help)
# ---------- Wizard Module ----------
capitano.command(actions.wizard.wizard)
# ---------- Auth Module ---------- # ---------- Auth Module ----------
capitano.command(actions.auth.login) capitano.command(actions.auth.login)
capitano.command(actions.auth.logout) capitano.command(actions.auth.logout)

View File

@ -42,11 +42,13 @@
}, },
"dependencies": { "dependencies": {
"async": "^1.3.0", "async": "^1.3.0",
"capitano": "~1.6.0", "bluebird": "^2.9.34",
"capitano": "~1.6.1",
"coffee-script": "^1.9.3", "coffee-script": "^1.9.3",
"conf.js": "^0.1.1", "conf.js": "^0.1.1",
"drivelist": "^1.2.2", "drivelist": "^1.2.2",
"fs-extra": "^0.22.1", "fs-extra": "^0.22.1",
"html-to-text": "^1.3.1",
"lodash": "^3.10.0", "lodash": "^3.10.0",
"lodash-contrib": "^393.0.1", "lodash-contrib": "^393.0.1",
"mkdirp": "~0.5.0", "mkdirp": "~0.5.0",
@ -57,8 +59,8 @@
"resin-cli-visuals": "^1.0.0", "resin-cli-visuals": "^1.0.0",
"resin-config-inject": "^2.0.0", "resin-config-inject": "^2.0.0",
"resin-device-config": "^1.0.0", "resin-device-config": "^1.0.0",
"resin-image": "^1.1.3", "resin-image": "^1.1.4",
"resin-image-manager": "^1.1.0", "resin-image-manager": "^2.0.0",
"resin-pine": "^1.3.0", "resin-pine": "^1.3.0",
"resin-register-device": "^1.0.1", "resin-register-device": "^1.0.1",
"resin-sdk": "^2.2.0", "resin-sdk": "^2.2.0",
@ -67,6 +69,8 @@
"selfupdate": "^1.1.0", "selfupdate": "^1.1.0",
"through2": "^2.0.0", "through2": "^2.0.0",
"tmp": "0.0.26", "tmp": "0.0.26",
"underscore.string": "^3.1.1" "underscore.string": "^3.1.1",
"update-notifier": "^0.3.1",
"user-home": "^2.0.0"
} }
} }