balena-cli/lib/actions/device.coffee
Juan Cruz Viotti 210680c9c9 Lazy load command actions dependencies
In my system (MBPr 13), printing the current version takes over 2
seconds:

```sh
$ time ./bin/resin version
2.4.0
./bin/resin version  1.37s user 0.19s system 73% cpu 2.130 total
```

The CLI takes almost all of these time to parse the dependency tree
before returning control over the actually called command.

To mitigate this problem, we only require the NPM dependencies a command
requires when executing such command, and thus prevent dependencies from
being required and parsed unnecessary.

After this improvement, printing the original example (`resin version`)
returns in less than a second (2x improvement):

```sh
$ time ./bin/resin version
2.4.0
./bin/resin version  0.88s user 0.09s system 102% cpu 0.938 total
```
2015-12-07 11:48:54 -03:00

292 lines
8.0 KiB
CoffeeScript

commandOptions = require('./command-options')
exports.list =
signature: 'devices'
description: 'list all devices'
help: '''
Use this command to list all devices that belong to you.
You can filter the devices by application by using the `--application` option.
Examples:
$ resin devices
$ resin devices --application MyApp
$ resin devices --app MyApp
$ resin devices -a MyApp
'''
options: [ commandOptions.optionalApplication ]
permission: 'user'
primary: true
action: (params, options, done) ->
Promise = require('bluebird')
resin = require('resin-sdk')
visuals = require('resin-cli-visuals')
Promise.try ->
if options.application?
return resin.models.device.getAllByApplication(options.application)
return resin.models.device.getAll()
.tap (devices) ->
console.log visuals.table.horizontal devices, [
'id'
'uuid'
'name'
'device_type'
'application_name'
'status'
]
.nodeify(done)
exports.info =
signature: 'device <uuid>'
description: 'list a single device'
help: '''
Use this command to show information about a single device.
Examples:
$ resin device 7cf02a62a3a84440b1bb5579a3d57469148943278630b17e7fc6c4f7b465c9
'''
permission: 'user'
primary: true
action: (params, options, done) ->
resin = require('resin-sdk')
visuals = require('resin-cli-visuals')
events = require('resin-cli-events')
resin.models.device.get(params.uuid).then (device) ->
# TODO: We should outsource this logic and probably
# other last_seen edge cases to either Resin CLI Visuals
# or have it parsed appropriately in the SDK.
device.last_seen ?= 'Not seen'
console.log visuals.table.vertical device, [
"$#{device.name}$"
'id'
'device_type'
'is_online'
'ip_address'
'application_name'
'status'
'last_seen'
'uuid'
'commit'
'supervisor_version'
'is_web_accessible'
'note'
]
events.send('device.open', device: device.uuid)
.nodeify(done)
exports.register =
signature: 'device register <application>'
description: 'register a device'
help: '''
Use this command to register a device to an application.
Examples:
$ resin device register MyApp
'''
permission: 'user'
options: [
signature: 'uuid'
description: 'custom uuid'
parameter: 'uuid'
alias: 'u'
]
action: (params, options, done) ->
Promise = require('bluebird')
resin = require('resin-sdk')
resin.models.application.get(params.application).then (application) ->
Promise.try ->
return options.uuid or resin.models.device.generateUUID()
.then (uuid) ->
console.info("Registering to #{application.app_name}: #{uuid}")
return resin.models.device.register(application.app_name, uuid)
.get('uuid')
.nodeify(done)
exports.remove =
signature: 'device rm <uuid>'
description: 'remove a device'
help: '''
Use this command to remove a device from resin.io.
Notice this command asks for confirmation interactively.
You can avoid this by passing the `--yes` boolean option.
Examples:
$ resin device rm 7cf02a62a3a84440b1bb5579a3d57469148943278630b17e7fc6c4f7b465c9
$ resin device rm 7cf02a62a3a84440b1bb5579a3d57469148943278630b17e7fc6c4f7b465c9 --yes
'''
options: [ commandOptions.yes ]
permission: 'user'
action: (params, options, done) ->
resin = require('resin-sdk')
events = require('resin-cli-events')
patterns = require('../utils/patterns')
patterns.confirm(options.yes, 'Are you sure you want to delete the device?').then ->
resin.models.device.remove(params.uuid)
.tap ->
events.send('device.delete', device: params.uuid)
.nodeify(done)
exports.identify =
signature: 'device identify <uuid>'
description: 'identify a device with a UUID'
help: '''
Use this command to identify a device.
In the Raspberry Pi, the ACT led is blinked several times.
Examples:
$ resin device identify 23c73a12e3527df55c60b9ce647640c1b7da1b32d71e6a39849ac0f00db828
'''
permission: 'user'
action: (params, options, done) ->
resin = require('resin-sdk')
resin.models.device.identify(params.uuid).nodeify(done)
exports.rename =
signature: 'device rename <uuid> [newName]'
description: 'rename a resin device'
help: '''
Use this command to rename a device.
If you omit the name, you'll get asked for it interactively.
Examples:
$ resin device rename 7cf02a62a3a84440b1bb5579a3d57469148943278630b17e7fc6c4f7b465c9 MyPi
$ resin device rename 7cf02a62a3a84440b1bb5579a3d57469148943278630b17e7fc6c4f7b465c9
'''
permission: 'user'
action: (params, options, done) ->
Promise = require('bluebird')
_ = require('lodash')
resin = require('resin-sdk')
events = require('resin-cli-events')
form = require('resin-cli-form')
Promise.try ->
return params.newName if not _.isEmpty(params.newName)
form.ask
message: 'How do you want to name this device?'
type: 'input'
.then(_.partial(resin.models.device.rename, params.uuid))
.tap ->
events.send('device.rename', device: params.uuid)
.nodeify(done)
exports.move =
signature: 'device move <uuid>'
description: 'move a device to another application'
help: '''
Use this command to move a device to another application you own.
If you omit the application, you'll get asked for it interactively.
Examples:
$ resin device move 7cf02a62a3a84440b1bb5579a3d57469148943278630b17e7fc6c4f7b465c9
$ resin device move 7cf02a62a3a84440b1bb5579a3d57469148943278630b17e7fc6c4f7b465c9 --application MyNewApp
'''
permission: 'user'
options: [ commandOptions.optionalApplication ]
action: (params, options, done) ->
resin = require('resin-sdk')
_ = require('lodash')
patterns = require('../utils/patterns')
resin.models.device.get(params.uuid).then (device) ->
return options.application or patterns.selectApplication (application) ->
return _.all [
application.device_type is device.device_type
device.application_name isnt application.app_name
]
.tap (application) ->
return resin.models.device.move(params.uuid, application)
.then (application) ->
console.info("#{params.uuid} was moved to #{application}")
.nodeify(done)
exports.init =
signature: 'device init'
description: 'initialise a device with resin os'
help: '''
Use this command to download the OS image of a certain application and write it to an SD Card.
Notice this command may ask for confirmation interactively.
You can avoid this by passing the `--yes` boolean option.
Examples:
$ resin device init
$ resin device init --application MyApp
'''
options: [
commandOptions.optionalApplication
commandOptions.yes
{
signature: 'advanced'
description: 'enable advanced configuration'
boolean: true
alias: 'v'
}
]
permission: 'user'
primary: true
action: (params, options, done) ->
Promise = require('bluebird')
capitano = Promise.promisifyAll(require('capitano'))
rimraf = Promise.promisify(require('rimraf'))
tmp = Promise.promisifyAll(require('tmp'))
tmp.setGracefulCleanup()
resin = require('resin-sdk')
helpers = require('../utils/helpers')
patterns = require('../utils/patterns')
Promise.try ->
return options.application if options.application?
return patterns.selectApplication()
.then(resin.models.application.get)
.then (application) ->
download = ->
tmp.tmpNameAsync().then (temporalPath) ->
capitano.runAsync("os download #{application.device_type} --output #{temporalPath}")
.disposer (temporalPath) ->
return rimraf(temporalPath)
Promise.using download(), (temporalPath) ->
capitano.runAsync("device register #{application.app_name}")
.then(resin.models.device.get)
.tap (device) ->
configure = "os configure #{temporalPath} #{device.uuid}"
configure += ' --advanced' if options.advanced
capitano.runAsync(configure).then ->
message = '''
Initializing a device requires administration permissions
given that we need to access raw devices directly.
'''
helpers.sudo([ 'os', 'initialize', temporalPath, '--type', application.device_type ], message)
.then (device) ->
console.log('Done')
return device.uuid
.nodeify(done)