Merge pull request #430 from resin-io/rdt-merge

Rdt merge
This commit is contained in:
Kostas Lekkas 2017-03-09 11:21:25 +00:00 committed by GitHub
commit 7af004501a
47 changed files with 1566 additions and 31 deletions

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io
@ -34,7 +34,6 @@ limitations under the License.
}
],
permission: 'user',
primary: true,
action: function(params, options, done) {
var patterns, resin;
resin = require('resin-sdk-preconfigured');

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io
@ -26,6 +26,7 @@ limitations under the License.
env: require('./environment-variables'),
keys: require('./keys'),
logs: require('./logs'),
local: require('./local'),
notes: require('./notes'),
help: require('./help'),
os: require('./os'),

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -0,0 +1,89 @@
// Generated by CoffeeScript 1.12.4
(function() {
var Docker, Promise, _, chalk, form;
Promise = require('bluebird');
_ = require('lodash');
Docker = require('docker-toolbelt');
form = require('resin-cli-form');
chalk = require('chalk');
module.exports = {
selectContainerFromDevice: Promise.method(function(deviceIp) {
var docker;
docker = new Docker({
host: deviceIp,
port: 2375
});
return docker.listContainersAsync({
all: true
}).then(function(containers) {
if (_.isEmpty(containers)) {
throw new Error("No containers found in " + deviceIp);
}
return form.ask({
message: 'Select a container',
type: 'list',
choices: _.map(containers, function(container) {
var containerName, containerStatus, shortContainerId;
containerName = container.Names[0] || 'Untitled';
shortContainerId = ('' + container.Id).substr(0, 11);
containerStatus = container.Status;
return {
name: containerName + " (" + shortContainerId + ") - " + containerStatus,
value: container.Id
};
})
});
});
}),
pipeContainerStream: Promise.method(function(arg) {
var container, deviceIp, docker, follow, name, outStream, ref;
deviceIp = arg.deviceIp, name = arg.name, outStream = arg.outStream, follow = (ref = arg.follow) != null ? ref : false;
docker = new Docker({
host: deviceIp,
port: 2375
});
container = docker.getContainer(name);
return container.inspectAsync().then(function(containerInfo) {
var ref1;
return containerInfo != null ? (ref1 = containerInfo.State) != null ? ref1.Running : void 0 : void 0;
}).then(function(isRunning) {
return container.attachAsync({
logs: !follow || !isRunning,
stream: follow && isRunning,
stdout: true,
stderr: true
});
}).then(function(containerStream) {
return containerStream.pipe(outStream);
})["catch"](function(err) {
err = '' + err.statusCode;
if (err === '404') {
return console.log(chalk.red.bold("Container '" + name + "' not found."));
}
throw err;
});
}),
getSubShellCommand: function(command) {
var os;
os = require('os');
if (os.platform() === 'win32') {
return {
program: 'cmd.exe',
args: ['/s', '/c', command]
};
} else {
return {
program: '/bin/sh',
args: ['-c', command]
};
}
}
};
}).call(this);

View File

@ -0,0 +1,133 @@
// Generated by CoffeeScript 1.12.4
/*
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
var CONFIGURATION_SCHEMA;
CONFIGURATION_SCHEMA = {
mapper: [
{
template: {
hostname: '{{hostname}}',
persistentLogging: '{{persistentLogging}}'
},
domain: [['config_json', 'hostname'], ['config_json', 'persistentLogging']]
}, {
template: {
wifi: {
ssid: '{{networkSsid}}'
},
'wifi-security': {
psk: '{{networkKey}}'
}
},
domain: [['system_connections', 'resin-sample', 'wifi'], ['system_connections', 'resin-sample', 'wifi-security']]
}
],
files: {
system_connections: {
fileset: true,
type: 'ini',
location: {
path: 'system-connections',
partition: {
primary: 1
}
}
},
config_json: {
type: 'json',
location: {
path: 'config.json',
partition: {
primary: 1
}
}
}
}
};
module.exports = {
signature: 'local configure <target>',
description: '(Re)configure a resinOS drive or image',
help: 'Use this command to configure or reconfigure a resinOS drive or image.\n\nExamples:\n\n $ resin local configure /dev/sdc\n $ resin local configure path/to/image.img',
root: true,
action: function(params, options, done) {
var Promise, _, denymount, inquirer, reconfix, umount;
_ = require('lodash');
Promise = require('bluebird');
umount = Promise.promisifyAll(require('umount'));
inquirer = require('inquirer');
reconfix = require('reconfix');
denymount = Promise.promisify(require('denymount'));
return umount.isMountedAsync(params.target).then(function(isMounted) {
if (!isMounted) {
return;
}
return umount.umountAsync(params.target);
}).then(function() {
return denymount(params.target, function(cb) {
return reconfix.readConfiguration(CONFIGURATION_SCHEMA, params.target).then(function(data) {
data.persistentLogging = data.persistentLogging || false;
return inquirer.prompt([
{
message: 'Network SSID',
type: 'input',
name: 'networkSsid',
"default": data.networkSsid
}, {
message: 'Network Key',
type: 'input',
name: 'networkKey',
"default": data.networkKey
}, {
message: 'Do you want to set advanced settings?',
type: 'confirm',
name: 'advancedSettings',
"default": false
}, {
message: 'Device Hostname',
type: 'input',
name: 'hostname',
"default": data.hostname,
when: function(answers) {
return answers.advancedSettings;
}
}, {
message: 'Do you want to enable persistent logging?',
type: 'confirm',
name: 'persistentLogging',
"default": data.persistentLogging,
when: function(answers) {
return answers.advancedSettings;
}
}
]).then(function(answers) {
return _.merge(data, answers);
});
}).then(function(answers) {
return reconfix.writeConfiguration(CONFIGURATION_SCHEMA, answers, params.target);
}).asCallback(cb);
});
}).then(function() {
return console.log('Done!');
}).asCallback(done);
}
};
}).call(this);

View File

@ -0,0 +1,129 @@
// Generated by CoffeeScript 1.12.4
/*
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the 'License');
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an 'AS IS' BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
module.exports = {
signature: 'local flash <image>',
description: 'Flash an image to a drive',
help: 'Use this command to flash a resinOS image to a drive.\n\nExamples:\n\n $ resin local flash path/to/resinos.img\n $ resin local flash path/to/resinos.img --drive /dev/disk2\n $ resin local flash path/to/resinos.img --drive /dev/disk2 --yes',
options: [
{
signature: 'yes',
boolean: true,
description: 'confirm non-interactively',
alias: 'y'
}, {
signature: 'drive',
parameter: 'drive',
description: 'drive',
alias: 'd'
}
],
root: true,
action: function(params, options, done) {
var Promise, _, chalk, drivelist, form, fs, imageWrite, os, umount, visuals;
_ = require('lodash');
os = require('os');
Promise = require('bluebird');
umount = Promise.promisifyAll(require('umount'));
fs = Promise.promisifyAll(require('fs'));
drivelist = Promise.promisifyAll(require('drivelist'));
chalk = require('chalk');
visuals = require('resin-cli-visuals');
form = require('resin-cli-form');
require('babel-register')({
only: /etcher-image-write|bmapflash/,
presets: ['es2015'],
compact: true
});
imageWrite = require('etcher-image-write');
return form.run([
{
message: 'Select drive',
type: 'drive',
name: 'drive'
}, {
message: 'This will erase the selected drive. Are you sure?',
type: 'confirm',
name: 'yes',
"default": false
}
], {
override: {
drive: options.drive,
yes: options.yes || void 0
}
}).then(function(answers) {
if (answers.yes !== true) {
console.log(chalk.red.bold('Aborted image flash'));
process.exit(0);
}
return drivelist.listAsync().then(function(drives) {
var selectedDrive;
selectedDrive = _.find(drives, {
device: answers.drive
});
if (selectedDrive == null) {
throw new Error("Drive not found: " + answers.drive);
}
return selectedDrive;
});
}).then(function(selectedDrive) {
var progressBars;
progressBars = {
write: new visuals.Progress('Flashing'),
check: new visuals.Progress('Validating')
};
return umount.umountAsync(selectedDrive.device).then(function() {
return Promise.props({
imageSize: fs.statAsync(params.image).get('size'),
imageStream: Promise.resolve(fs.createReadStream(params.image)),
driveFileDescriptor: fs.openAsync(selectedDrive.raw, 'rs+')
});
}).then(function(results) {
return imageWrite.write({
fd: results.driveFileDescriptor,
device: selectedDrive.raw,
size: selectedDrive.size
}, {
stream: results.imageStream,
size: results.imageSize
}, {
check: true
});
}).then(function(writer) {
return new Promise(function(resolve, reject) {
writer.on('progress', function(state) {
return progressBars[state.type].update(state);
});
writer.on('error', reject);
return writer.on('done', resolve);
});
}).then(function() {
var removedrive;
if ((os.platform() === 'win32') && (selectedDrive.mountpoint != null)) {
removedrive = Promise.promisifyAll(require('removedrive'));
return removedrive.ejectAsync(selectedDrive.mountpoint);
}
return umount.umountAsync(selectedDrive.device);
});
}).asCallback(done);
}
};
}).call(this);

View File

@ -0,0 +1,34 @@
// Generated by CoffeeScript 1.12.4
/*
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
exports.configure = require('./configure');
exports.flash = require('./flash');
exports.logs = require('./logs');
exports.promote = require('./promote');
exports.scan = require('./scan');
exports.ssh = require('./ssh');
exports.push = require('./push');
}).call(this);

View File

@ -0,0 +1,68 @@
// Generated by CoffeeScript 1.12.4
/*
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
module.exports = {
signature: 'local logs [deviceIp]',
description: 'Get or attach to logs of a running container on a resinOS device',
help: '\nExamples:\n\n $ resin local logs\n $ resin local logs -f\n $ resin local logs 192.168.1.10\n $ resin local logs 192.168.1.10 -f\n $ resin local logs 192.168.1.10 -f --app-name myapp',
options: [
{
signature: 'follow',
boolean: true,
description: 'follow log',
alias: 'f'
}, {
signature: 'app-name',
parameter: 'name',
description: 'name of container to get logs from',
alias: 'a'
}
],
action: function(params, options, done) {
var Promise, forms, pipeContainerStream, ref, selectContainerFromDevice;
Promise = require('bluebird');
forms = require('resin-sync').forms;
ref = require('./common'), selectContainerFromDevice = ref.selectContainerFromDevice, pipeContainerStream = ref.pipeContainerStream;
return Promise["try"](function() {
if (params.deviceIp == null) {
return forms.selectLocalResinOsDevice();
}
return params.deviceIp;
}).then((function(_this) {
return function(deviceIp) {
_this.deviceIp = deviceIp;
if (options['app-name'] == null) {
return selectContainerFromDevice(_this.deviceIp);
}
return options['app-name'];
};
})(this)).then((function(_this) {
return function(appName) {
return pipeContainerStream({
deviceIp: _this.deviceIp,
name: appName,
outStream: process.stdout,
follow: options['follow']
});
};
})(this));
}
};
}).call(this);

View File

@ -0,0 +1,64 @@
// Generated by CoffeeScript 1.12.4
/*
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
module.exports = {
signature: 'local promote [deviceIp]',
description: 'Promote a resinOS device',
help: 'Warning: \'resin promote\' requires an openssh-compatible client to be correctly\ninstalled in your shell environment. For more information (including Windows\nsupport) please check the README here: https://github.com/resin-io/resin-cli\n\nUse this command to promote your device.\n\nPromoting a device will provision it onto the Resin platform,\nconverting it from an unmanaged device to a managed device.\n\nExamples:\n\n $ resin local promote\n $ resin local promote --port 22222\n $ resin local promote --verbose',
options: [
{
signature: 'verbose',
boolean: true,
description: 'increase verbosity',
alias: 'v'
}, {
signature: 'port',
parameter: 'port',
description: 'ssh port number (default: 22222)',
alias: 'p'
}
],
action: function(params, options, done) {
var Promise, _, child_process, forms, getSubShellCommand, verbose;
child_process = require('child_process');
Promise = require('bluebird');
_ = require('lodash');
forms = require('resin-sync').forms;
getSubShellCommand = require('./common').getSubShellCommand;
if (options.port == null) {
options.port = 22222;
}
verbose = options.verbose ? '-vvv' : '';
return Promise["try"](function() {
return params.deviceIp != null ? params.deviceIp : params.deviceIp = forms.selectLocalResinOsDevice();
}).then(function(deviceIp) {
var command, subShellCommand;
_.assign(options, {
deviceIp: deviceIp
});
command = "ssh " + verbose + " -t -p " + options.port + " -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ControlMaster=no root@" + options.deviceIp + " -- \"resin-provision interactive\"";
subShellCommand = getSubShellCommand(command);
return child_process.spawn(subShellCommand.program, subShellCommand.args, {
stdio: 'inherit'
});
}).nodeify(done);
}
};
}).call(this);

View File

@ -0,0 +1,34 @@
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
var resinPush;
resinPush = require('resin-sync').capitano('resin-toolbox');
resinPush.signature = 'local push [deviceIp]';
resinPush.help = {
help: 'Warning: \'resin local push\' requires an openssh-compatible client and \'rsync\' to\nbe correctly installed in your shell environment. For more information (including\nWindows support) please check the README here: https://github.com/resin-io/resin-cli\n\nUse this command to push your local changes to a container on a LAN-accessible resinOS device on the fly.\n\nIf `Dockerfile` or any file in the \'build-triggers\' list is changed, a new container will be built and run on your device.\nIf not, changes will simply be synced with `rsync` into the application container.\n\nAfter every \'resin local push\' the updated settings will be saved in\n\'<source>/.resin-sync.yml\' and will be used in later invocations. You can\nalso change any option by editing \'.resin-sync.yml\' directly.\n\nHere is an example \'.resin-sync.yml\' :\n\n $ cat $PWD/.resin-sync.yml\n destination: \'/usr/src/app\'\n before: \'echo Hello\'\n after: \'echo Done\'\n ignore:\n - .git\n - node_modules/\n\nCommand line options have precedence over the ones saved in \'.resin-sync.yml\'.\n\nIf \'.gitignore\' is found in the source directory then all explicitly listed files will be\nexcluded when using rsync to update the container. You can choose to change this default behavior with the\n\'--skip-gitignore\' option.\n\nExamples:\n\n $ resin local push\n $ resin local push --app-name test-server --build-triggers package.json,requirements.txt\n $ resin local push --force-build\n $ resin local push --force-build --skip-logs\n $ resin local push --ignore lib/\n $ resin local push --verbose false\n $ resin local push 192.168.2.10 --source . --destination /usr/src/app\n $ resin local push 192.168.2.10 -s /home/user/myResinProject -d /usr/src/app --before \'echo Hello\' --after \'echo Done\''
};
resinPush.primary = true;
module.exports = resinPush;
}).call(this);

101
build/actions/local/scan.js Normal file
View File

@ -0,0 +1,101 @@
// Generated by CoffeeScript 1.12.4
/*
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
var dockerInfoProperties, dockerVersionProperties;
dockerInfoProperties = ['Containers', 'ContainersRunning', 'ContainersPaused', 'ContainersStopped', 'Images', 'Driver', 'SystemTime', 'KernelVersion', 'OperatingSystem', 'Architecture'];
dockerVersionProperties = ['Version', 'ApiVersion'];
module.exports = {
signature: 'local scan',
description: 'Scan for resinOS devices in your local network',
help: '\nExamples:\n\n $ resin local scan\n $ resin local scan --timeout 120\n $ resin local scan --verbose',
options: [
{
signature: 'verbose',
boolean: true,
description: 'Display full info',
alias: 'v'
}, {
signature: 'timeout',
parameter: 'timeout',
description: 'Scan timeout in seconds',
alias: 't'
}
],
primary: true,
action: function(params, options, done) {
var Docker, Promise, SpinnerPromise, _, discover, prettyjson;
Promise = require('bluebird');
_ = require('lodash');
prettyjson = require('prettyjson');
Docker = require('docker-toolbelt');
discover = require('resin-sync').discover;
SpinnerPromise = require('resin-cli-visuals').SpinnerPromise;
if (options.timeout != null) {
options.timeout *= 1000;
}
return Promise["try"](function() {
return new SpinnerPromise({
promise: discover.discoverLocalResinOsDevices(options.timeout),
startMessage: 'Scanning for local resinOS devices..',
stopMessage: 'Reporting scan results'
});
}).tap(function(devices) {
if (_.isEmpty(devices)) {
throw new Error('Could not find any resinOS devices in the local network');
}
}).map(function(arg) {
var address, docker, host;
host = arg.host, address = arg.address;
docker = new Docker({
host: address,
port: 2375
});
return Promise.props({
dockerInfo: docker.infoAsync().catchReturn('Could not get Docker info'),
dockerVersion: docker.versionAsync().catchReturn('Could not get Docker version')
}).then(function(arg1) {
var dockerInfo, dockerVersion;
dockerInfo = arg1.dockerInfo, dockerVersion = arg1.dockerVersion;
if (!options.verbose) {
if (_.isObject(dockerInfo)) {
dockerInfo = _.pick(dockerInfo, dockerInfoProperties);
}
if (_.isObject(dockerVersion)) {
dockerVersion = _.pick(dockerVersion, dockerVersionProperties);
}
}
return {
host: host,
address: address,
dockerInfo: dockerInfo,
dockerVersion: dockerVersion
};
});
}).then(function(devicesInfo) {
return console.log(prettyjson.render(devicesInfo, {
noColor: true
}));
}).nodeify(done);
}
};
}).call(this);

View File

@ -0,0 +1,93 @@
// Generated by CoffeeScript 1.12.4
/*
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function() {
module.exports = {
signature: 'local ssh [deviceIp]',
description: 'Get a shell into a resinOS device',
help: 'Warning: \'resin local ssh\' requires an openssh-compatible client to be correctly\ninstalled in your shell environment. For more information (including Windows\nsupport) please check the README here: https://github.com/resin-io/resin-cli\n\nUse this command to get a shell into the running application container of\nyour device.\n\nThe \'--host\' option will get you a shell into the Host OS of the resinOS device.\nNo option will return a list of containers to enter or you can explicitly select\none by passing its name to the --container option\n\nExamples:\n\n $ resin local ssh\n $ resin local ssh --host\n $ resin local ssh --container chaotic_water\n $ resin local ssh --container chaotic_water --port 22222\n $ resin local ssh --verbose',
options: [
{
signature: 'verbose',
boolean: true,
description: 'increase verbosity',
alias: 'v'
}, {
signature: 'host',
boolean: true,
description: 'get a shell into the host OS',
alias: 's'
}, {
signature: 'container',
parameter: 'container',
"default": null,
description: 'name of container to access',
alias: 'c'
}, {
signature: 'port',
parameter: 'port',
description: 'ssh port number (default: 22222)',
alias: 'p'
}
],
action: function(params, options, done) {
var Promise, _, child_process, forms, getSubShellCommand, ref, selectContainerFromDevice, verbose;
child_process = require('child_process');
Promise = require('bluebird');
_ = require('lodash');
forms = require('resin-sync').forms;
ref = require('./common'), selectContainerFromDevice = ref.selectContainerFromDevice, getSubShellCommand = ref.getSubShellCommand;
if (options.host === true && (options.container != null)) {
throw new Error('Please pass either --host or --container option');
}
if (options.port == null) {
options.port = 22222;
}
verbose = options.verbose ? '-vvv' : '';
return Promise["try"](function() {
if (params.deviceIp == null) {
return forms.selectLocalResinOsDevice();
}
return params.deviceIp;
}).then(function(deviceIp) {
_.assign(options, {
deviceIp: deviceIp
});
if (options.host) {
return;
}
if (options.container == null) {
return selectContainerFromDevice(deviceIp);
}
return options.container;
}).then(function(container) {
var command, shellCmd, subShellCommand;
command = "ssh " + verbose + " -t -p " + options.port + " -o LogLevel=ERROR -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ControlMaster=no root@" + options.deviceIp;
if (!options.host) {
shellCmd = '/bin/sh -c $"\'if [ -e /bin/bash ]; then exec /bin/bash; else exec /bin/sh; fi\'"';
command += " docker exec -ti " + container + " " + shellCmd;
}
subShellCommand = getSubShellCommand(command);
return child_process.spawn(subShellCommand.program, subShellCommand.args, {
stdio: 'inherit'
});
}).nodeify(done);
}
};
}).call(this);

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io
@ -156,6 +156,20 @@ limitations under the License.
capitano.command(actions.ssh);
capitano.command(actions.local.configure);
capitano.command(actions.local.flash);
capitano.command(actions.local.logs);
capitano.command(actions.local.promote);
capitano.command(actions.local.push);
capitano.command(actions.local.ssh);
capitano.command(actions.local.scan);
update.notify();
plugins.register(/^resin-plugin-(.+)$/).then(function() {

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
(function() {
var Mixpanel, Promise, _, capitanoState, packageJSON, resin;

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
(function() {
exports.gettingStarted = 'Run the following command to get a device started with Resin.io\n\n $ resin quickstart';

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.12.2
// Generated by CoffeeScript 1.12.4
/*
Copyright 2016 Resin.io

View File

@ -43,7 +43,6 @@ exports.create =
}
]
permission: 'user'
primary: true
action: (params, options, done) ->
resin = require('resin-sdk-preconfigured')
patterns = require('../utils/patterns')

View File

@ -23,6 +23,7 @@ module.exports =
env: require('./environment-variables')
keys: require('./keys')
logs: require('./logs')
local: require('./local')
notes: require('./notes')
help: require('./help')
os: require('./os')

View File

@ -0,0 +1,69 @@
Promise = require('bluebird')
_ = require('lodash')
Docker = require('docker-toolbelt')
form = require('resin-cli-form')
chalk = require('chalk')
module.exports =
selectContainerFromDevice: Promise.method (deviceIp) ->
docker = new Docker(host: deviceIp, port: 2375)
# List all containers, including those not running
docker.listContainersAsync(all: true)
.then (containers) ->
if _.isEmpty(containers)
throw new Error("No containers found in #{deviceIp}")
return form.ask
message: 'Select a container'
type: 'list'
choices: _.map containers, (container) ->
containerName = container.Names[0] or 'Untitled'
shortContainerId = ('' + container.Id).substr(0, 11)
containerStatus = container.Status
return {
name: "#{containerName} (#{shortContainerId}) - #{containerStatus}"
value: container.Id
}
pipeContainerStream: Promise.method ({ deviceIp, name, outStream, follow = false }) ->
docker = new Docker(host: deviceIp, port: 2375)
container = docker.getContainer(name)
container.inspectAsync()
.then (containerInfo) ->
return containerInfo?.State?.Running
.then (isRunning) ->
container.attachAsync
logs: not follow or not isRunning
stream: follow and isRunning
stdout: true
stderr: true
.then (containerStream) ->
containerStream.pipe(outStream)
.catch (err) ->
err = '' + err.statusCode
if err is '404'
return console.log(chalk.red.bold("Container '#{name}' not found."))
throw err
# A function to reliably execute a command
# in all supported operating systems, including
# different Windows environments like `cmd.exe`
# and `Cygwin` should be encapsulated in a
# re-usable package.
getSubShellCommand: (command) ->
os = require('os')
if os.platform() is 'win32'
return {
program: 'cmd.exe'
args: [ '/s', '/c', command ]
}
else
return {
program: '/bin/sh'
args: [ '-c', command ]
}

View File

@ -0,0 +1,128 @@
###
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
CONFIGURATION_SCHEMA =
mapper: [
{
template:
hostname: '{{hostname}}'
persistentLogging: '{{persistentLogging}}'
domain: [
[ 'config_json', 'hostname' ]
[ 'config_json', 'persistentLogging' ]
]
}
{
template:
wifi:
ssid: '{{networkSsid}}'
'wifi-security':
psk: '{{networkKey}}'
domain: [
[ 'system_connections', 'resin-sample', 'wifi' ]
[ 'system_connections', 'resin-sample', 'wifi-security' ]
]
}
]
files:
system_connections:
fileset: true
type: 'ini'
location:
path: 'system-connections'
partition:
primary: 1
config_json:
type: 'json'
location:
path: 'config.json'
partition:
primary: 1
module.exports =
signature: 'local configure <target>'
description: '(Re)configure a resinOS drive or image'
help: '''
Use this command to configure or reconfigure a resinOS drive or image.
Examples:
$ resin local configure /dev/sdc
$ resin local configure path/to/image.img
'''
root: true
action: (params, options, done) ->
_ = require('lodash')
Promise = require('bluebird')
umount = Promise.promisifyAll(require('umount'))
inquirer = require('inquirer')
reconfix = require('reconfix')
denymount = Promise.promisify(require('denymount'))
umount.isMountedAsync(params.target).then (isMounted) ->
return if not isMounted
umount.umountAsync(params.target)
.then ->
denymount params.target, (cb) ->
reconfix.readConfiguration(CONFIGURATION_SCHEMA, params.target).then (data) ->
# `persistentLogging` can be `undefined`, so we want
# to make sure that case defaults to `false`
data.persistentLogging = data.persistentLogging or false
inquirer.prompt([
{
message: 'Network SSID'
type: 'input'
name: 'networkSsid'
default: data.networkSsid
}
{
message: 'Network Key'
type: 'input'
name: 'networkKey'
default: data.networkKey
}
{
message: 'Do you want to set advanced settings?'
type: 'confirm'
name: 'advancedSettings'
default: false
}
{
message: 'Device Hostname'
type: 'input'
name: 'hostname'
default: data.hostname,
when: (answers) ->
answers.advancedSettings
}
{
message: 'Do you want to enable persistent logging?'
type: 'confirm'
name: 'persistentLogging'
default: data.persistentLogging
when: (answers) ->
answers.advancedSettings
}
]).then (answers) ->
return _.merge(data, answers)
.then (answers) ->
reconfix.writeConfiguration(CONFIGURATION_SCHEMA, answers, params.target)
.asCallback(cb)
.then ->
console.log('Done!')
.asCallback(done)

View File

@ -0,0 +1,126 @@
###
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the 'License');
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an 'AS IS' BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
module.exports =
signature: 'local flash <image>'
description: 'Flash an image to a drive'
help: '''
Use this command to flash a resinOS image to a drive.
Examples:
$ resin local flash path/to/resinos.img
$ resin local flash path/to/resinos.img --drive /dev/disk2
$ resin local flash path/to/resinos.img --drive /dev/disk2 --yes
'''
options: [
signature: 'yes'
boolean: true
description: 'confirm non-interactively'
alias: 'y'
,
signature: 'drive'
parameter: 'drive'
description: 'drive'
alias: 'd'
]
root: true
action: (params, options, done) ->
_ = require('lodash')
os = require('os')
Promise = require('bluebird')
umount = Promise.promisifyAll(require('umount'))
fs = Promise.promisifyAll(require('fs'))
drivelist = Promise.promisifyAll(require('drivelist'))
chalk = require('chalk')
visuals = require('resin-cli-visuals')
form = require('resin-cli-form')
# XXX: Find a better ES6 module loading story/contract between resin.io modules
require('babel-register')({
only: /etcher-image-write|bmapflash/
presets: ['es2015']
compact: true
})
imageWrite = require('etcher-image-write')
form.run [
{
message: 'Select drive'
type: 'drive'
name: 'drive'
},
{
message: 'This will erase the selected drive. Are you sure?'
type: 'confirm'
name: 'yes'
default: false
}
],
override:
drive: options.drive
# If `options.yes` is `false`, pass `undefined`,
# otherwise the question will not be asked because
# `false` is a defined value.
yes: options.yes || undefined
.then (answers) ->
if answers.yes isnt true
console.log(chalk.red.bold('Aborted image flash'))
process.exit(0)
drivelist.listAsync().then (drives) ->
selectedDrive = _.find(drives, device: answers.drive)
if not selectedDrive?
throw new Error("Drive not found: #{answers.drive}")
return selectedDrive
.then (selectedDrive) ->
progressBars =
write: new visuals.Progress('Flashing')
check: new visuals.Progress('Validating')
umount.umountAsync(selectedDrive.device).then ->
Promise.props
imageSize: fs.statAsync(params.image).get('size'),
imageStream: Promise.resolve(fs.createReadStream(params.image))
driveFileDescriptor: fs.openAsync(selectedDrive.raw, 'rs+')
.then (results) ->
imageWrite.write
fd: results.driveFileDescriptor
device: selectedDrive.raw
size: selectedDrive.size
,
stream: results.imageStream,
size: results.imageSize
,
check: true
.then (writer) ->
new Promise (resolve, reject) ->
writer.on 'progress', (state) ->
progressBars[state.type].update(state)
writer.on('error', reject)
writer.on('done', resolve)
.then ->
if (os.platform() is 'win32') and selectedDrive.mountpoint?
removedrive = Promise.promisifyAll(require('removedrive'))
return removedrive.ejectAsync(selectedDrive.mountpoint)
return umount.umountAsync(selectedDrive.device)
.asCallback(done)

View File

@ -0,0 +1,23 @@
###
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
exports.configure = require('./configure')
exports.flash = require('./flash')
exports.logs = require('./logs')
exports.promote = require('./promote')
exports.scan = require('./scan')
exports.ssh = require('./ssh')
exports.push = require('./push')

View File

@ -0,0 +1,65 @@
###
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
# A function to reliably execute a command
# in all supported operating systems, including
# different Windows environments like `cmd.exe`
# and `Cygwin` should be encapsulated in a
# re-usable package.
#
module.exports =
signature: 'local logs [deviceIp]'
description: 'Get or attach to logs of a running container on a resinOS device'
help: '''
Examples:
$ resin local logs
$ resin local logs -f
$ resin local logs 192.168.1.10
$ resin local logs 192.168.1.10 -f
$ resin local logs 192.168.1.10 -f --app-name myapp
'''
options: [
signature: 'follow'
boolean: true
description: 'follow log'
alias: 'f'
,
signature: 'app-name'
parameter: 'name'
description: 'name of container to get logs from'
alias: 'a'
]
action: (params, options, done) ->
Promise = require('bluebird')
{ forms } = require('resin-sync')
{ selectContainerFromDevice, pipeContainerStream } = require('./common')
Promise.try ->
if not params.deviceIp?
return forms.selectLocalResinOsDevice()
return params.deviceIp
.then (@deviceIp) =>
if not options['app-name']?
return selectContainerFromDevice(@deviceIp)
return options['app-name']
.then (appName) =>
pipeContainerStream
deviceIp: @deviceIp
name: appName
outStream: process.stdout
follow: options['follow']

View File

@ -0,0 +1,78 @@
###
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
module.exports =
signature: 'local promote [deviceIp]'
description: 'Promote a resinOS device'
help: '''
Warning: 'resin promote' requires an openssh-compatible client to be correctly
installed in your shell environment. For more information (including Windows
support) please check the README here: https://github.com/resin-io/resin-cli
Use this command to promote your device.
Promoting a device will provision it onto the Resin platform,
converting it from an unmanaged device to a managed device.
Examples:
$ resin local promote
$ resin local promote --port 22222
$ resin local promote --verbose
'''
options: [
signature: 'verbose'
boolean: true
description: 'increase verbosity'
alias: 'v'
,
signature: 'port'
parameter: 'port'
description: 'ssh port number (default: 22222)'
alias: 'p'
]
action: (params, options, done) ->
child_process = require('child_process')
Promise = require 'bluebird'
_ = require('lodash')
{ forms } = require('resin-sync')
{ getSubShellCommand } = require('./common')
options.port ?= 22222
verbose = if options.verbose then '-vvv' else ''
Promise.try ->
return params.deviceIp ?= forms.selectLocalResinOsDevice()
.then (deviceIp) ->
_.assign(options, { deviceIp })
command = "ssh \
#{verbose} \
-t \
-p #{options.port} \
-o LogLevel=ERROR \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
-o ControlMaster=no \
root@#{options.deviceIp} \
-- \"resin-provision interactive\""
subShellCommand = getSubShellCommand(command)
child_process.spawn subShellCommand.program, subShellCommand.args,
stdio: 'inherit'
.nodeify(done)

View File

@ -0,0 +1,69 @@
###
Copyright 2016 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
# Loads '.resin-sync.yml' configuration from 'source' directory.
# Returns the configuration object on success
#
resinPush = require('resin-sync').capitano('resin-toolbox')
# TODO: This is a temporary workaround to reuse the existing `rdt push`
# capitano frontend in `resin local push`.
resinPush.signature = 'local push [deviceIp]'
resinPush.help =
help: '''
Warning: 'resin local push' requires an openssh-compatible client and 'rsync' to
be correctly installed in your shell environment. For more information (including
Windows support) please check the README here: https://github.com/resin-io/resin-cli
Use this command to push your local changes to a container on a LAN-accessible resinOS device on the fly.
If `Dockerfile` or any file in the 'build-triggers' list is changed, a new container will be built and run on your device.
If not, changes will simply be synced with `rsync` into the application container.
After every 'resin local push' the updated settings will be saved in
'<source>/.resin-sync.yml' and will be used in later invocations. You can
also change any option by editing '.resin-sync.yml' directly.
Here is an example '.resin-sync.yml' :
$ cat $PWD/.resin-sync.yml
destination: '/usr/src/app'
before: 'echo Hello'
after: 'echo Done'
ignore:
- .git
- node_modules/
Command line options have precedence over the ones saved in '.resin-sync.yml'.
If '.gitignore' is found in the source directory then all explicitly listed files will be
excluded when using rsync to update the container. You can choose to change this default behavior with the
'--skip-gitignore' option.
Examples:
$ resin local push
$ resin local push --app-name test-server --build-triggers package.json,requirements.txt
$ resin local push --force-build
$ resin local push --force-build --skip-logs
$ resin local push --ignore lib/
$ resin local push --verbose false
$ resin local push 192.168.2.10 --source . --destination /usr/src/app
$ resin local push 192.168.2.10 -s /home/user/myResinProject -d /usr/src/app --before 'echo Hello' --after 'echo Done'
'''
resinPush.primary = true
module.exports = resinPush

View File

@ -0,0 +1,92 @@
###
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
dockerInfoProperties = [
'Containers'
'ContainersRunning'
'ContainersPaused'
'ContainersStopped'
'Images'
'Driver'
'SystemTime'
'KernelVersion'
'OperatingSystem'
'Architecture'
]
dockerVersionProperties = [
'Version'
'ApiVersion'
]
module.exports =
signature: 'local scan'
description: 'Scan for resinOS devices in your local network'
help: '''
Examples:
$ resin local scan
$ resin local scan --timeout 120
$ resin local scan --verbose
'''
options: [
signature: 'verbose'
boolean: true
description: 'Display full info'
alias: 'v'
,
signature: 'timeout'
parameter: 'timeout'
description: 'Scan timeout in seconds'
alias: 't'
]
primary: true
action: (params, options, done) ->
Promise = require('bluebird')
_ = require('lodash')
prettyjson = require('prettyjson')
Docker = require('docker-toolbelt')
{ discover } = require('resin-sync')
{ SpinnerPromise } = require('resin-cli-visuals')
if options.timeout?
options.timeout *= 1000
Promise.try ->
new SpinnerPromise
promise: discover.discoverLocalResinOsDevices(options.timeout)
startMessage: 'Scanning for local resinOS devices..'
stopMessage: 'Reporting scan results'
.tap (devices) ->
if _.isEmpty(devices)
throw new Error('Could not find any resinOS devices in the local network')
.map ({ host, address }) ->
docker = new Docker(host: address, port: 2375)
Promise.props
dockerInfo: docker.infoAsync().catchReturn('Could not get Docker info')
dockerVersion: docker.versionAsync().catchReturn('Could not get Docker version')
.then ({ dockerInfo, dockerVersion }) ->
if not options.verbose
dockerInfo = _.pick(dockerInfo, dockerInfoProperties) if _.isObject(dockerInfo)
dockerVersion = _.pick(dockerVersion, dockerVersionProperties) if _.isObject(dockerVersion)
return { host, address, dockerInfo, dockerVersion }
.then (devicesInfo) ->
console.log(prettyjson.render(devicesInfo, noColor: true))
.nodeify(done)

View File

@ -0,0 +1,109 @@
###
Copyright 2017 Resin.io
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
###
module.exports =
signature: 'local ssh [deviceIp]'
description: 'Get a shell into a resinOS device'
help: '''
Warning: 'resin local ssh' requires an openssh-compatible client to be correctly
installed in your shell environment. For more information (including Windows
support) please check the README here: https://github.com/resin-io/resin-cli
Use this command to get a shell into the running application container of
your device.
The '--host' option will get you a shell into the Host OS of the resinOS device.
No option will return a list of containers to enter or you can explicitly select
one by passing its name to the --container option
Examples:
$ resin local ssh
$ resin local ssh --host
$ resin local ssh --container chaotic_water
$ resin local ssh --container chaotic_water --port 22222
$ resin local ssh --verbose
'''
options: [
signature: 'verbose'
boolean: true
description: 'increase verbosity'
alias: 'v'
,
signature: 'host'
boolean: true
description: 'get a shell into the host OS'
alias: 's'
,
signature: 'container'
parameter: 'container'
default: null
description: 'name of container to access'
alias: 'c'
,
signature: 'port'
parameter: 'port'
description: 'ssh port number (default: 22222)'
alias: 'p'
]
action: (params, options, done) ->
child_process = require('child_process')
Promise = require 'bluebird'
_ = require('lodash')
{ forms } = require('resin-sync')
{ selectContainerFromDevice, getSubShellCommand } = require('./common')
if (options.host is true and options.container?)
throw new Error('Please pass either --host or --container option')
if not options.port?
options.port = 22222
verbose = if options.verbose then '-vvv' else ''
Promise.try ->
if not params.deviceIp?
return forms.selectLocalResinOsDevice()
return params.deviceIp
.then (deviceIp) ->
_.assign(options, { deviceIp })
return if options.host
if not options.container?
return selectContainerFromDevice(deviceIp)
return options.container
.then (container) ->
command = "ssh \
#{verbose} \
-t \
-p #{options.port} \
-o LogLevel=ERROR \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
-o ControlMaster=no \
root@#{options.deviceIp}"
if not options.host
shellCmd = '''/bin/sh -c $"'if [ -e /bin/bash ]; then exec /bin/bash; else exec /bin/sh; fi'"'''
command += " docker exec -ti #{container} #{shellCmd}"
subShellCommand = getSubShellCommand(command)
child_process.spawn subShellCommand.program, subShellCommand.args,
stdio: 'inherit'
.nodeify(done)

View File

@ -124,6 +124,15 @@ capitano.command(actions.sync)
# ---------- SSH Module ----------
capitano.command(actions.ssh)
# ---------- Local ResinOS Module ----------
capitano.command(actions.local.configure)
capitano.command(actions.local.flash)
capitano.command(actions.local.logs)
capitano.command(actions.local.promote)
capitano.command(actions.local.push)
capitano.command(actions.local.ssh)
capitano.command(actions.local.scan)
update.notify()
plugins.register(/^resin-plugin-(.+)$/).then ->

View File

@ -31,11 +31,18 @@
"gulp-shell": "^0.5.2"
},
"dependencies": {
"babel-preset-es2015": "^6.16.0",
"babel-register": "^6.16.3",
"bluebird": "^3.3.3",
"capitano": "~1.7.0",
"chalk": "^1.1.1",
"chalk": "^1.1.3",
"coffee-script": "~1.12.2",
"columnify": "^1.5.2",
"denymount": "^2.2.0",
"docker-toolbelt": "^1.3.3",
"drivelist": "^5.0.16",
"etcher-image-write": "^9.0.1",
"inquirer": "^3.0.6",
"is-root": "^1.0.0",
"js-yaml": "^3.7.0",
"lodash": "^3.10.0",
@ -44,10 +51,11 @@
"nplugm": "^3.0.0",
"president": "^2.0.1",
"prettyjson": "^1.1.3",
"reconfix": "0.0.3",
"resin-cli-auth": "^1.0.0",
"resin-cli-errors": "^1.2.0",
"resin-cli-form": "^1.4.0",
"resin-cli-visuals": "^1.2.2",
"resin-cli-form": "^1.4.1",
"resin-cli-visuals": "^1.3.0",
"resin-config-json": "^1.0.0",
"resin-device-config": "^3.0.0",
"resin-device-init": "^2.1.0",
@ -59,7 +67,7 @@
"rimraf": "^2.4.3",
"rindle": "^1.0.0",
"tmp": "^0.0.31",
"umount": "^1.1.1",
"umount": "^1.1.5",
"underscore.string": "^3.1.1",
"unzip2": "^0.2.5",
"update-notifier": "^0.6.1",