mirror of
https://github.com/balena-io/balena-cli.git
synced 2025-04-07 11:26:41 +00:00
Merge pull request #461 from resin-io/452-fix-permissions
isolate the sudo-runnable command
This commit is contained in:
commit
dede4bb329
@ -49,10 +49,9 @@ exports.login = {
|
||||
],
|
||||
primary: true,
|
||||
action: function(params, options, done) {
|
||||
var Promise, _, auth, capitano, form, login, messages, patterns, resin;
|
||||
var Promise, _, auth, form, login, messages, patterns, resin;
|
||||
_ = require('lodash');
|
||||
Promise = require('bluebird');
|
||||
capitano = Promise.promisifyAll(require('capitano'));
|
||||
resin = require('resin-sdk-preconfigured');
|
||||
auth = require('resin-cli-auth');
|
||||
form = require('resin-cli-form');
|
||||
@ -77,8 +76,10 @@ exports.login = {
|
||||
return auth.login();
|
||||
}
|
||||
return patterns.askLoginType().then(function(loginType) {
|
||||
var capitanoRunAsync;
|
||||
if (loginType === 'register') {
|
||||
return capitano.runAsync('signup');
|
||||
capitanoRunAsync = Promise.promisify(require('capitano').run);
|
||||
return capitanoRunAsync('signup');
|
||||
}
|
||||
options[loginType] = true;
|
||||
return login(options);
|
||||
@ -110,7 +111,7 @@ exports.logout = {
|
||||
exports.signup = {
|
||||
signature: 'signup',
|
||||
description: 'signup to resin.io',
|
||||
help: 'Use this command to signup for a resin.io account.\n\nIf signup is successful, you\'ll be logged in to your new user automatically.\n\nExamples:\n\n $ resin signup\n Email: me@mycompany.com\n Username: johndoe\n Password: ***********\n\n $ resin whoami\n johndoe',
|
||||
help: 'Use this command to signup for a resin.io account.\n\nIf signup is successful, you\'ll be logged in to your new user automatically.\n\nExamples:\n\n $ resin signup\n Email: johndoe@acme.com\n Password: ***********\n\n $ resin whoami\n johndoe',
|
||||
action: function(params, options, done) {
|
||||
var form, resin, validation;
|
||||
resin = require('resin-sdk-preconfigured');
|
||||
@ -124,10 +125,6 @@ exports.signup = {
|
||||
name: 'email',
|
||||
type: 'input',
|
||||
validate: validation.validateEmail
|
||||
}, {
|
||||
message: 'Username:',
|
||||
name: 'username',
|
||||
type: 'input'
|
||||
}, {
|
||||
message: 'Password:',
|
||||
name: 'password',
|
||||
|
@ -40,15 +40,15 @@ exports.read = {
|
||||
permission: 'user',
|
||||
root: true,
|
||||
action: function(params, options, done) {
|
||||
var Promise, config, prettyjson, umount, visuals;
|
||||
var Promise, config, prettyjson, umountAsync, visuals;
|
||||
Promise = require('bluebird');
|
||||
config = require('resin-config-json');
|
||||
visuals = require('resin-cli-visuals');
|
||||
umount = Promise.promisifyAll(require('umount'));
|
||||
umountAsync = Promise.promisify(require('umount').umount);
|
||||
prettyjson = require('prettyjson');
|
||||
return Promise["try"](function() {
|
||||
return options.drive || visuals.drive('Select the device drive');
|
||||
}).tap(umount.umountAsync).then(function(drive) {
|
||||
}).tap(umountAsync).then(function(drive) {
|
||||
return config.read(drive, options.type);
|
||||
}).tap(function(configJSON) {
|
||||
return console.info(prettyjson.render(configJSON));
|
||||
@ -77,21 +77,21 @@ exports.write = {
|
||||
permission: 'user',
|
||||
root: true,
|
||||
action: function(params, options, done) {
|
||||
var Promise, _, config, umount, visuals;
|
||||
var Promise, _, config, umountAsync, visuals;
|
||||
Promise = require('bluebird');
|
||||
_ = require('lodash');
|
||||
config = require('resin-config-json');
|
||||
visuals = require('resin-cli-visuals');
|
||||
umount = Promise.promisifyAll(require('umount'));
|
||||
umountAsync = Promise.promisify(require('umount').umount);
|
||||
return Promise["try"](function() {
|
||||
return options.drive || visuals.drive('Select the device drive');
|
||||
}).tap(umount.umountAsync).then(function(drive) {
|
||||
}).tap(umountAsync).then(function(drive) {
|
||||
return config.read(drive, options.type).then(function(configJSON) {
|
||||
console.info("Setting " + params.key + " to " + params.value);
|
||||
_.set(configJSON, params.key, params.value);
|
||||
return configJSON;
|
||||
}).tap(function() {
|
||||
return umount.umountAsync(drive);
|
||||
return umountAsync(drive);
|
||||
}).then(function(configJSON) {
|
||||
return config.write(drive, options.type, configJSON);
|
||||
});
|
||||
@ -122,16 +122,16 @@ exports.inject = {
|
||||
permission: 'user',
|
||||
root: true,
|
||||
action: function(params, options, done) {
|
||||
var Promise, config, fs, umount, visuals;
|
||||
var Promise, config, readFileAsync, umountAsync, visuals;
|
||||
Promise = require('bluebird');
|
||||
config = require('resin-config-json');
|
||||
visuals = require('resin-cli-visuals');
|
||||
umount = Promise.promisifyAll(require('umount'));
|
||||
fs = Promise.promisifyAll(require('fs'));
|
||||
umountAsync = Promise.promisify(require('umount').umount);
|
||||
readFileAsync = Promise.promisify(require('fs').readFile);
|
||||
return Promise["try"](function() {
|
||||
return options.drive || visuals.drive('Select the device drive');
|
||||
}).tap(umount.umountAsync).then(function(drive) {
|
||||
return fs.readFileAsync(params.file, 'utf8').then(JSON.parse).then(function(configJSON) {
|
||||
}).tap(umountAsync).then(function(drive) {
|
||||
return readFileAsync(params.file, 'utf8').then(JSON.parse).then(function(configJSON) {
|
||||
return config.write(drive, options.type, configJSON);
|
||||
});
|
||||
}).tap(function() {
|
||||
@ -166,24 +166,24 @@ exports.reconfigure = {
|
||||
permission: 'user',
|
||||
root: true,
|
||||
action: function(params, options, done) {
|
||||
var Promise, capitano, config, umount, visuals;
|
||||
var Promise, capitanoRunAsync, config, umountAsync, visuals;
|
||||
Promise = require('bluebird');
|
||||
config = require('resin-config-json');
|
||||
visuals = require('resin-cli-visuals');
|
||||
capitano = Promise.promisifyAll(require('capitano'));
|
||||
umount = Promise.promisifyAll(require('umount'));
|
||||
capitanoRunAsync = Promise.promisify(require('capitano').run);
|
||||
umountAsync = Promise.promisify(require('umount').umount);
|
||||
return Promise["try"](function() {
|
||||
return options.drive || visuals.drive('Select the device drive');
|
||||
}).tap(umount.umountAsync).then(function(drive) {
|
||||
}).tap(umountAsync).then(function(drive) {
|
||||
return config.read(drive, options.type).get('uuid').tap(function() {
|
||||
return umount.umountAsync(drive);
|
||||
return umountAsync(drive);
|
||||
}).then(function(uuid) {
|
||||
var configureCommand;
|
||||
configureCommand = "os configure " + drive + " " + uuid;
|
||||
if (options.advanced) {
|
||||
configureCommand += ' --advanced';
|
||||
}
|
||||
return capitano.runAsync(configureCommand);
|
||||
return capitanoRunAsync(configureCommand);
|
||||
});
|
||||
}).then(function() {
|
||||
return console.info('Done');
|
||||
@ -205,9 +205,9 @@ exports.generate = {
|
||||
],
|
||||
permission: 'user',
|
||||
action: function(params, options, done) {
|
||||
var Promise, _, deviceConfig, form, fs, prettyjson, resin;
|
||||
var Promise, _, deviceConfig, form, prettyjson, resin, writeFileAsync;
|
||||
Promise = require('bluebird');
|
||||
fs = Promise.promisifyAll(require('fs'));
|
||||
writeFileAsync = Promise.promisify(require('fs').writeFile);
|
||||
resin = require('resin-sdk-preconfigured');
|
||||
_ = require('lodash');
|
||||
form = require('resin-cli-form');
|
||||
@ -230,7 +230,7 @@ exports.generate = {
|
||||
});
|
||||
}).then(function(config) {
|
||||
if (options.output != null) {
|
||||
return fs.writeFileAsync(options.output, JSON.stringify(config));
|
||||
return writeFileAsync(options.output, JSON.stringify(config));
|
||||
}
|
||||
return console.log(prettyjson.render(config));
|
||||
}).nodeify(done);
|
||||
|
@ -274,11 +274,12 @@ exports.init = {
|
||||
],
|
||||
permission: 'user',
|
||||
action: function(params, options, done) {
|
||||
var Promise, capitano, helpers, patterns, resin, rimraf, tmp;
|
||||
var Promise, capitanoRunAsync, helpers, patterns, resin, rimraf, tmp, tmpNameAsync;
|
||||
Promise = require('bluebird');
|
||||
capitano = Promise.promisifyAll(require('capitano'));
|
||||
capitanoRunAsync = Promise.promisify(require('capitano').run);
|
||||
rimraf = Promise.promisify(require('rimraf'));
|
||||
tmp = Promise.promisifyAll(require('tmp'));
|
||||
tmp = require('tmp');
|
||||
tmpNameAsync = Promise.promisify(tmp.tmpName);
|
||||
tmp.setGracefulCleanup();
|
||||
resin = require('resin-sdk-preconfigured');
|
||||
helpers = require('../utils/helpers');
|
||||
@ -291,23 +292,23 @@ exports.init = {
|
||||
}).then(resin.models.application.get).then(function(application) {
|
||||
var download;
|
||||
download = function() {
|
||||
return tmp.tmpNameAsync().then(function(temporalPath) {
|
||||
return capitano.runAsync("os download " + application.device_type + " --output " + temporalPath + " --version default");
|
||||
}).disposer(function(temporalPath) {
|
||||
return rimraf(temporalPath);
|
||||
return tmpNameAsync().then(function(tempPath) {
|
||||
return capitanoRunAsync("os download " + application.device_type + " --output '" + tempPath + "' --version default");
|
||||
}).disposer(function(tempPath) {
|
||||
return rimraf(tempPath);
|
||||
});
|
||||
};
|
||||
return Promise.using(download(), function(temporalPath) {
|
||||
return capitano.runAsync("device register " + application.app_name).then(resin.models.device.get).tap(function(device) {
|
||||
var configure;
|
||||
configure = "os configure " + temporalPath + " " + device.uuid;
|
||||
return Promise.using(download(), function(tempPath) {
|
||||
return capitanoRunAsync("device register " + application.app_name).then(resin.models.device.get).tap(function(device) {
|
||||
var configureCommand;
|
||||
configureCommand = "os configure '" + tempPath + "' " + device.uuid;
|
||||
if (options.advanced) {
|
||||
configure += ' --advanced';
|
||||
configureCommand += ' --advanced';
|
||||
}
|
||||
return capitano.runAsync(configure).then(function() {
|
||||
var message;
|
||||
message = 'Initializing a device requires administrative permissions\ngiven that we need to access raw devices directly.\n';
|
||||
return helpers.sudo(['os', 'initialize', temporalPath, '--type', application.device_type], message);
|
||||
return capitanoRunAsync(configureCommand).then(function() {
|
||||
var osInitCommand;
|
||||
osInitCommand = "os initialize '" + tempPath + "' --type " + application.device_type;
|
||||
return capitanoRunAsync(osInitCommand);
|
||||
})["catch"](function(error) {
|
||||
return resin.models.device.remove(device.uuid)["finally"](function() {
|
||||
throw error;
|
||||
|
@ -60,12 +60,13 @@ general = function(params, options, done) {
|
||||
console.log(messages.reachingOut);
|
||||
console.log('\nPrimary commands:\n');
|
||||
commands = _.reject(capitano.state.commands, function(command) {
|
||||
return command.isWildcard();
|
||||
return command.hidden || command.isWildcard();
|
||||
});
|
||||
groupedCommands = _.groupBy(commands, function(command) {
|
||||
if (command.plugin) {
|
||||
return 'plugins';
|
||||
} else if (command.primary) {
|
||||
}
|
||||
if (command.primary) {
|
||||
return 'primary';
|
||||
}
|
||||
return 'secondary';
|
||||
|
@ -31,5 +31,6 @@ module.exports = {
|
||||
settings: require('./settings'),
|
||||
config: require('./config'),
|
||||
sync: require('./sync'),
|
||||
ssh: require('./ssh')
|
||||
ssh: require('./ssh'),
|
||||
internal: require('./internal')
|
||||
};
|
||||
|
35
build/actions/internal.js
Normal file
35
build/actions/internal.js
Normal file
@ -0,0 +1,35 @@
|
||||
// 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.
|
||||
*/
|
||||
exports.osInit = {
|
||||
signature: 'internal osinit <image> <type> <config>',
|
||||
description: 'do actual init of the device with the preconfigured os image',
|
||||
help: 'Don\'t use this command directly! Use `resin os initialize <image>` instead.',
|
||||
hidden: true,
|
||||
root: true,
|
||||
action: function(params, options, done) {
|
||||
var Promise, helpers, init;
|
||||
Promise = require('bluebird');
|
||||
init = require('resin-device-init');
|
||||
helpers = require('../utils/helpers');
|
||||
return Promise["try"](function() {
|
||||
var config;
|
||||
config = JSON.parse(params.config);
|
||||
return init.initialize(params.image, params.type, config);
|
||||
}).then(helpers.osProgressHandler).nodeify(done);
|
||||
}
|
||||
};
|
@ -72,15 +72,15 @@ exports.add = {
|
||||
help: 'Use this command to associate a new SSH key with your account.\n\nIf `path` is omitted, the command will attempt\nto read the SSH key from stdin.\n\nExamples:\n\n $ resin key add Main ~/.ssh/id_rsa.pub\n $ cat ~/.ssh/id_rsa.pub | resin key add Main',
|
||||
permission: 'user',
|
||||
action: function(params, options, done) {
|
||||
var Promise, _, capitano, fs, resin;
|
||||
var Promise, _, capitano, readFileAsync, resin;
|
||||
_ = require('lodash');
|
||||
Promise = require('bluebird');
|
||||
fs = Promise.promisifyAll(require('fs'));
|
||||
readFileAsync = Promise.promisify(require('fs').readFile);
|
||||
capitano = require('capitano');
|
||||
resin = require('resin-sdk-preconfigured');
|
||||
return Promise["try"](function() {
|
||||
if (params.path != null) {
|
||||
return fs.readFileAsync(params.path, {
|
||||
return readFileAsync(params.path, {
|
||||
encoding: 'utf8'
|
||||
});
|
||||
}
|
||||
|
@ -66,18 +66,20 @@ module.exports = {
|
||||
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;
|
||||
var Promise, _, denymount, inquirer, isMountedAsync, reconfix, umount, umountAsync;
|
||||
_ = require('lodash');
|
||||
Promise = require('bluebird');
|
||||
umount = Promise.promisifyAll(require('umount'));
|
||||
umount = require('umount');
|
||||
umountAsync = Promise.promisify(umount.umount);
|
||||
isMountedAsync = Promise.promisify(umount.isMounted);
|
||||
inquirer = require('inquirer');
|
||||
reconfix = require('reconfix');
|
||||
denymount = Promise.promisify(require('denymount'));
|
||||
return umount.isMountedAsync(params.target).then(function(isMounted) {
|
||||
return isMountedAsync(params.target).then(function(isMounted) {
|
||||
if (!isMounted) {
|
||||
return;
|
||||
}
|
||||
return umount.umountAsync(params.target);
|
||||
return umountAsync(params.target);
|
||||
}).then(function() {
|
||||
return denymount(params.target, function(cb) {
|
||||
return reconfix.readConfiguration(CONFIGURATION_SCHEMA, params.target).then(function(data) {
|
||||
|
@ -34,13 +34,13 @@ module.exports = {
|
||||
],
|
||||
root: true,
|
||||
action: function(params, options, done) {
|
||||
var Promise, _, chalk, drivelist, form, fs, imageWrite, os, umount, visuals;
|
||||
var Promise, _, chalk, driveListAsync, form, fs, imageWrite, os, umountAsync, visuals;
|
||||
_ = require('lodash');
|
||||
os = require('os');
|
||||
Promise = require('bluebird');
|
||||
umount = Promise.promisifyAll(require('umount'));
|
||||
umountAsync = Promise.promisify(require('umount').umount);
|
||||
fs = Promise.promisifyAll(require('fs'));
|
||||
drivelist = Promise.promisifyAll(require('drivelist'));
|
||||
driveListAsync = Promise.promisify(require('drivelist').list);
|
||||
chalk = require('chalk');
|
||||
visuals = require('resin-cli-visuals');
|
||||
form = require('resin-cli-form');
|
||||
@ -71,7 +71,7 @@ module.exports = {
|
||||
console.log(chalk.red.bold('Aborted image flash'));
|
||||
process.exit(0);
|
||||
}
|
||||
return drivelist.listAsync().then(function(drives) {
|
||||
return driveListAsync().then(function(drives) {
|
||||
var selectedDrive;
|
||||
selectedDrive = _.find(drives, {
|
||||
device: answers.drive
|
||||
@ -87,7 +87,7 @@ module.exports = {
|
||||
write: new visuals.Progress('Flashing'),
|
||||
check: new visuals.Progress('Validating')
|
||||
};
|
||||
return umount.umountAsync(selectedDrive.device).then(function() {
|
||||
return umountAsync(selectedDrive.device).then(function() {
|
||||
return Promise.props({
|
||||
imageSize: fs.statAsync(params.image).get('size'),
|
||||
imageStream: Promise.resolve(fs.createReadStream(params.image)),
|
||||
@ -113,12 +113,12 @@ module.exports = {
|
||||
return writer.on('done', resolve);
|
||||
});
|
||||
}).then(function() {
|
||||
var removedrive;
|
||||
var ejectAsync;
|
||||
if ((os.platform() === 'win32') && (selectedDrive.mountpoint != null)) {
|
||||
removedrive = Promise.promisifyAll(require('removedrive'));
|
||||
return removedrive.ejectAsync(selectedDrive.mountpoint);
|
||||
ejectAsync = Promise.promisify(require('removedrive').eject);
|
||||
return ejectAsync(selectedDrive.mountpoint);
|
||||
}
|
||||
return umount.umountAsync(selectedDrive.device);
|
||||
return umountAsync(selectedDrive.device);
|
||||
});
|
||||
}).asCallback(done);
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ 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.
|
||||
*/
|
||||
var commandOptions, formatVersion, resolveVersion, stepHandler;
|
||||
var commandOptions, formatVersion, initWarningMessage, resolveVersion;
|
||||
|
||||
commandOptions = require('./command-options');
|
||||
|
||||
@ -120,25 +120,6 @@ exports.download = {
|
||||
}
|
||||
};
|
||||
|
||||
stepHandler = function(step) {
|
||||
var _, bar, helpers, rindle, visuals;
|
||||
_ = require('lodash');
|
||||
rindle = require('rindle');
|
||||
visuals = require('resin-cli-visuals');
|
||||
helpers = require('../utils/helpers');
|
||||
step.on('stdout', _.bind(process.stdout.write, process.stdout));
|
||||
step.on('stderr', _.bind(process.stderr.write, process.stderr));
|
||||
step.on('state', function(state) {
|
||||
if (state.operation.command === 'burn') {
|
||||
return;
|
||||
}
|
||||
return console.log(helpers.stateToString(state));
|
||||
});
|
||||
bar = new visuals.Progress('Writing Device OS');
|
||||
step.on('burn', _.bind(bar.update, bar));
|
||||
return rindle.wait(step);
|
||||
};
|
||||
|
||||
exports.configure = {
|
||||
signature: 'os configure <image> <uuid>',
|
||||
description: 'configure an os image',
|
||||
@ -176,16 +157,18 @@ exports.configure = {
|
||||
override: override
|
||||
});
|
||||
}).then(function(answers) {
|
||||
return init.configure(params.image, params.uuid, answers).then(stepHandler);
|
||||
return init.configure(params.image, params.uuid, answers).then(helpers.osProgressHandler);
|
||||
});
|
||||
}).nodeify(done);
|
||||
}
|
||||
};
|
||||
|
||||
initWarningMessage = 'Note: Initializing the device may ask for administrative permissions\nbecause we need to access the raw devices directly.';
|
||||
|
||||
exports.initialize = {
|
||||
signature: 'os initialize <image>',
|
||||
description: 'initialize an os image',
|
||||
help: 'Use this command to initialize a previously configured operating system image.\n\nExamples:\n\n $ resin os initialize ../path/rpi.img --type \'raspberry-pi\'',
|
||||
help: "Use this command to initialize a device with previously configured operating system image.\n\n" + initWarningMessage + "\n\nExamples:\n\n $ resin os initialize ../path/rpi.img --type 'raspberry-pi'",
|
||||
permission: 'user',
|
||||
options: [
|
||||
commandOptions.yes, {
|
||||
@ -201,16 +184,14 @@ exports.initialize = {
|
||||
alias: 'd'
|
||||
}
|
||||
],
|
||||
root: true,
|
||||
action: function(params, options, done) {
|
||||
var Promise, form, helpers, init, patterns, umount;
|
||||
var Promise, form, helpers, patterns, umountAsync;
|
||||
Promise = require('bluebird');
|
||||
umount = Promise.promisifyAll(require('umount'));
|
||||
umountAsync = Promise.promisify(require('umount').umount);
|
||||
form = require('resin-cli-form');
|
||||
init = require('resin-device-init');
|
||||
patterns = require('../utils/patterns');
|
||||
helpers = require('../utils/helpers');
|
||||
console.info('Initializing device');
|
||||
console.info("Initializing device\n\n" + initWarningMessage);
|
||||
return helpers.getManifest(params.image, options.type).then(function(manifest) {
|
||||
var ref;
|
||||
return (ref = manifest.initialization) != null ? ref.options : void 0;
|
||||
@ -226,14 +207,14 @@ exports.initialize = {
|
||||
return;
|
||||
}
|
||||
message = "This will erase " + answers.drive + ". Are you sure?";
|
||||
return patterns.confirm(options.yes, message)["return"](answers.drive).then(umount.umountAsync);
|
||||
return patterns.confirm(options.yes, message)["return"](answers.drive).then(umountAsync);
|
||||
}).tap(function(answers) {
|
||||
return init.initialize(params.image, options.type, answers).then(stepHandler);
|
||||
return helpers.sudo(['internal', 'osinit', params.image, options.type, JSON.stringify(answers)]);
|
||||
}).then(function(answers) {
|
||||
if (answers.drive == null) {
|
||||
return;
|
||||
}
|
||||
return umount.umountAsync(answers.drive).tap(function() {
|
||||
return umountAsync(answers.drive).tap(function() {
|
||||
return console.info("You can safely remove " + answers.drive + " now");
|
||||
});
|
||||
}).nodeify(done);
|
||||
|
35
build/actions/util.js
Normal file
35
build/actions/util.js
Normal file
@ -0,0 +1,35 @@
|
||||
// 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.
|
||||
*/
|
||||
exports.osInit = {
|
||||
signature: 'util osinit <image> <type> <config>',
|
||||
description: 'do actual init of the device with the preconfigured os image',
|
||||
help: 'Don\'t use this command directly! Use `resin os initialize <image>` instead.',
|
||||
hidden: true,
|
||||
root: true,
|
||||
action: function(params, options, done) {
|
||||
var Promise, helpers, init;
|
||||
Promise = require('bluebird');
|
||||
init = require('resin-device-init');
|
||||
helpers = require('../utils/helpers');
|
||||
return Promise["try"](function() {
|
||||
var config;
|
||||
config = JSON.parse(params.config);
|
||||
return init.initialize(params.image, params.type, config);
|
||||
}).then(helpers.osProgressHandler).nodeify(done);
|
||||
}
|
||||
};
|
@ -21,9 +21,9 @@ exports.wizard = {
|
||||
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 $ resin quickstart\n $ resin quickstart MyApp',
|
||||
primary: true,
|
||||
action: function(params, options, done) {
|
||||
var Promise, capitano, patterns, resin;
|
||||
var Promise, capitanoRunAsync, patterns, resin;
|
||||
Promise = require('bluebird');
|
||||
capitano = Promise.promisifyAll(require('capitano'));
|
||||
capitanoRunAsync = Promise.promisify(require('capitano').run);
|
||||
resin = require('resin-sdk-preconfigured');
|
||||
patterns = require('../utils/patterns');
|
||||
return resin.auth.isLoggedIn().then(function(isLoggedIn) {
|
||||
@ -32,7 +32,7 @@ exports.wizard = {
|
||||
}
|
||||
console.info('Looks like you\'re not logged in yet!');
|
||||
console.info('Lets go through a quick wizard to get you started.\n');
|
||||
return capitano.runAsync('login');
|
||||
return capitanoRunAsync('login');
|
||||
}).then(function() {
|
||||
if (params.name != null) {
|
||||
return;
|
||||
@ -42,15 +42,15 @@ exports.wizard = {
|
||||
if (hasApplication) {
|
||||
return applicationName;
|
||||
}
|
||||
return capitano.runAsync("app create " + applicationName);
|
||||
return capitanoRunAsync("app create " + applicationName);
|
||||
});
|
||||
}).then(function(applicationName) {
|
||||
return params.name = applicationName;
|
||||
});
|
||||
}).then(function() {
|
||||
return capitano.runAsync("device init --application " + params.name);
|
||||
return capitanoRunAsync("device init --application " + params.name);
|
||||
}).tap(patterns.awaitDevice).then(function(uuid) {
|
||||
return capitano.runAsync("device " + uuid);
|
||||
return capitanoRunAsync("device " + uuid);
|
||||
}).then(function() {
|
||||
return resin.models.application.get(params.name);
|
||||
}).then(function(application) {
|
||||
|
12
build/app.js
12
build/app.js
@ -15,7 +15,7 @@ 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.
|
||||
*/
|
||||
var Promise, Raven, _, actions, capitano, errors, events, plugins, resin, update;
|
||||
var Promise, Raven, _, actions, capitano, capitanoExecuteAsync, errors, events, plugins, resin, update;
|
||||
|
||||
Raven = require('raven');
|
||||
|
||||
@ -30,7 +30,9 @@ _ = require('lodash');
|
||||
|
||||
Promise = require('bluebird');
|
||||
|
||||
capitano = Promise.promisifyAll(require('capitano'));
|
||||
capitano = require('capitano');
|
||||
|
||||
capitanoExecuteAsync = Promise.promisify(capitano.execute);
|
||||
|
||||
resin = require('resin-sdk-preconfigured');
|
||||
|
||||
@ -179,6 +181,8 @@ capitano.command(actions.local.scan);
|
||||
|
||||
capitano.command(actions.local.stop);
|
||||
|
||||
capitano.command(actions.internal.osInit);
|
||||
|
||||
update.notify();
|
||||
|
||||
plugins.register(/^resin-plugin-(.+)$/).then(function() {
|
||||
@ -187,10 +191,10 @@ plugins.register(/^resin-plugin-(.+)$/).then(function() {
|
||||
return events.trackCommand(cli).then(function() {
|
||||
var ref, ref1;
|
||||
if ((ref = cli.global) != null ? ref.help : void 0) {
|
||||
return capitano.executeAsync({
|
||||
return capitanoExecuteAsync({
|
||||
command: "help " + ((ref1 = cli.command) != null ? ref1 : '')
|
||||
});
|
||||
}
|
||||
return capitano.executeAsync(cli);
|
||||
return capitanoExecuteAsync(cli);
|
||||
});
|
||||
})["catch"](errors.handle);
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Generated by CoffeeScript 1.12.4
|
||||
var Mixpanel, Promise, _, capitanoState, packageJSON, resin;
|
||||
var Mixpanel, Promise, _, packageJSON, resin;
|
||||
|
||||
_ = require('lodash');
|
||||
|
||||
@ -9,8 +9,6 @@ Promise = require('bluebird');
|
||||
|
||||
resin = require('resin-sdk-preconfigured');
|
||||
|
||||
capitanoState = Promise.promisifyAll(require('capitano').state);
|
||||
|
||||
packageJSON = require('../package.json');
|
||||
|
||||
exports.getLoggerInstance = _.memoize(function() {
|
||||
@ -18,12 +16,14 @@ exports.getLoggerInstance = _.memoize(function() {
|
||||
});
|
||||
|
||||
exports.trackCommand = function(capitanoCommand) {
|
||||
var capitanoStateGetMatchCommandAsync;
|
||||
capitanoStateGetMatchCommandAsync = Promise.promisify(require('capitano').state.getMatchCommand);
|
||||
return Promise.props({
|
||||
resinUrl: resin.settings.get('resinUrl'),
|
||||
username: resin.auth.whoami(),
|
||||
mixpanel: exports.getLoggerInstance()
|
||||
}).then(function(data) {
|
||||
return capitanoState.getMatchCommandAsync(capitanoCommand.command).then(function(command) {
|
||||
return capitanoStateGetMatchCommandAsync(capitanoCommand.command).then(function(command) {
|
||||
return data.mixpanel.track("[CLI] " + (command.signature.toString()), {
|
||||
distinct_id: data.username,
|
||||
argv: process.argv.join(' '),
|
||||
|
@ -15,37 +15,23 @@ 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.
|
||||
*/
|
||||
var Promise, _, capitano, chalk, imagefs, os, president, resin, rindle;
|
||||
var Promise;
|
||||
|
||||
Promise = require('bluebird');
|
||||
|
||||
capitano = Promise.promisifyAll(require('capitano'));
|
||||
|
||||
_ = require('lodash');
|
||||
|
||||
_.str = require('underscore.string');
|
||||
|
||||
president = Promise.promisifyAll(require('president'));
|
||||
|
||||
resin = require('resin-sdk-preconfigured');
|
||||
|
||||
imagefs = require('resin-image-fs');
|
||||
|
||||
rindle = require('rindle');
|
||||
|
||||
os = require('os');
|
||||
|
||||
chalk = require('chalk');
|
||||
|
||||
exports.getGroupDefaults = function(group) {
|
||||
var _;
|
||||
_ = require('lodash');
|
||||
return _.chain(group).get('options').map(function(question) {
|
||||
return [question.name, question["default"]];
|
||||
}).object().value();
|
||||
};
|
||||
|
||||
exports.stateToString = function(state) {
|
||||
var percentage, result;
|
||||
percentage = _.str.lpad(state.percentage, 3, '0') + '%';
|
||||
var _str, chalk, percentage, result;
|
||||
_str = require('underscore.string');
|
||||
chalk = require('chalk');
|
||||
percentage = _str.lpad(state.percentage, 3, '0') + '%';
|
||||
result = (chalk.blue(percentage)) + " " + (chalk.cyan(state.operation.command));
|
||||
switch (state.operation.command) {
|
||||
case 'copy':
|
||||
@ -59,16 +45,23 @@ exports.stateToString = function(state) {
|
||||
}
|
||||
};
|
||||
|
||||
exports.sudo = function(command, message) {
|
||||
command = _.union(_.take(process.argv, 2), command);
|
||||
console.log(message);
|
||||
exports.sudo = function(command) {
|
||||
var _, os, presidentExecuteAsync;
|
||||
_ = require('lodash');
|
||||
os = require('os');
|
||||
if (os.platform() !== 'win32') {
|
||||
console.log('Type your computer password to continue');
|
||||
console.log('If asked please type your computer password to continue');
|
||||
}
|
||||
return president.executeAsync(command);
|
||||
command = _.union(_.take(process.argv, 2), command);
|
||||
presidentExecuteAsync = Promise.promisify(require('president').execute);
|
||||
return presidentExecuteAsync(command);
|
||||
};
|
||||
|
||||
exports.getManifest = function(image, deviceType) {
|
||||
var imagefs, resin, rindle;
|
||||
rindle = require('rindle');
|
||||
imagefs = require('resin-image-fs');
|
||||
resin = require('resin-sdk-preconfigured');
|
||||
return imagefs.read({
|
||||
image: image,
|
||||
partition: {
|
||||
@ -79,3 +72,25 @@ exports.getManifest = function(image, deviceType) {
|
||||
return resin.models.device.getManifestBySlug(deviceType);
|
||||
});
|
||||
};
|
||||
|
||||
exports.osProgressHandler = function(step) {
|
||||
var progressBars, rindle, visuals;
|
||||
rindle = require('rindle');
|
||||
visuals = require('resin-cli-visuals');
|
||||
step.on('stdout', process.stdout.write.bind(process.stdout));
|
||||
step.on('stderr', process.stderr.write.bind(process.stderr));
|
||||
step.on('state', function(state) {
|
||||
if (state.operation.command === 'burn') {
|
||||
return;
|
||||
}
|
||||
return console.log(exports.stateToString(state));
|
||||
});
|
||||
progressBars = {
|
||||
write: new visuals.Progress('Writing Device OS'),
|
||||
check: new visuals.Progress('Validating Device OS')
|
||||
};
|
||||
step.on('burn', function(state) {
|
||||
return progressBars[state.type].update(state);
|
||||
});
|
||||
return rindle.wait(step);
|
||||
};
|
||||
|
@ -240,8 +240,7 @@ If signup is successful, you'll be logged in to your new user automatically.
|
||||
Examples:
|
||||
|
||||
$ resin signup
|
||||
Email: me@mycompany.com
|
||||
Username: johndoe
|
||||
Email: johndoe@acme.com
|
||||
Password: ***********
|
||||
|
||||
$ resin whoami
|
||||
@ -830,7 +829,10 @@ show advanced commands
|
||||
|
||||
## os initialize <image>
|
||||
|
||||
Use this command to initialize a previously configured operating system image.
|
||||
Use this command to initialize a device with previously configured operating system image.
|
||||
|
||||
Note: Initializing the device may ask for administrative permissions
|
||||
because we need to access the raw devices directly.
|
||||
|
||||
Examples:
|
||||
|
||||
|
@ -73,7 +73,6 @@ exports.login =
|
||||
action: (params, options, done) ->
|
||||
_ = require('lodash')
|
||||
Promise = require('bluebird')
|
||||
capitano = Promise.promisifyAll(require('capitano'))
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
auth = require('resin-cli-auth')
|
||||
form = require('resin-cli-form')
|
||||
@ -98,7 +97,8 @@ exports.login =
|
||||
return patterns.askLoginType().then (loginType) ->
|
||||
|
||||
if loginType is 'register'
|
||||
return capitano.runAsync('signup')
|
||||
capitanoRunAsync = Promise.promisify(require('capitano').run)
|
||||
return capitanoRunAsync('signup')
|
||||
|
||||
options[loginType] = true
|
||||
return login(options)
|
||||
@ -150,8 +150,7 @@ exports.signup =
|
||||
Examples:
|
||||
|
||||
$ resin signup
|
||||
Email: me@mycompany.com
|
||||
Username: johndoe
|
||||
Email: johndoe@acme.com
|
||||
Password: ***********
|
||||
|
||||
$ resin whoami
|
||||
@ -170,10 +169,6 @@ exports.signup =
|
||||
name: 'email'
|
||||
type: 'input'
|
||||
validate: validation.validateEmail
|
||||
,
|
||||
message: 'Username:'
|
||||
name: 'username'
|
||||
type: 'input'
|
||||
,
|
||||
message: 'Password:'
|
||||
name: 'password'
|
||||
|
@ -48,12 +48,12 @@ exports.read =
|
||||
Promise = require('bluebird')
|
||||
config = require('resin-config-json')
|
||||
visuals = require('resin-cli-visuals')
|
||||
umount = Promise.promisifyAll(require('umount'))
|
||||
umountAsync = Promise.promisify(require('umount').umount)
|
||||
prettyjson = require('prettyjson')
|
||||
|
||||
Promise.try ->
|
||||
return options.drive or visuals.drive('Select the device drive')
|
||||
.tap(umount.umountAsync)
|
||||
.tap(umountAsync)
|
||||
.then (drive) ->
|
||||
return config.read(drive, options.type)
|
||||
.tap (configJSON) ->
|
||||
@ -94,18 +94,18 @@ exports.write =
|
||||
_ = require('lodash')
|
||||
config = require('resin-config-json')
|
||||
visuals = require('resin-cli-visuals')
|
||||
umount = Promise.promisifyAll(require('umount'))
|
||||
umountAsync = Promise.promisify(require('umount').umount)
|
||||
|
||||
Promise.try ->
|
||||
return options.drive or visuals.drive('Select the device drive')
|
||||
.tap(umount.umountAsync)
|
||||
.tap(umountAsync)
|
||||
.then (drive) ->
|
||||
config.read(drive, options.type).then (configJSON) ->
|
||||
console.info("Setting #{params.key} to #{params.value}")
|
||||
_.set(configJSON, params.key, params.value)
|
||||
return configJSON
|
||||
.tap ->
|
||||
return umount.umountAsync(drive)
|
||||
return umountAsync(drive)
|
||||
.then (configJSON) ->
|
||||
return config.write(drive, options.type, configJSON)
|
||||
.tap ->
|
||||
@ -144,14 +144,14 @@ exports.inject =
|
||||
Promise = require('bluebird')
|
||||
config = require('resin-config-json')
|
||||
visuals = require('resin-cli-visuals')
|
||||
umount = Promise.promisifyAll(require('umount'))
|
||||
fs = Promise.promisifyAll(require('fs'))
|
||||
umountAsync = Promise.promisify(require('umount').umount)
|
||||
readFileAsync = Promise.promisify(require('fs').readFile)
|
||||
|
||||
Promise.try ->
|
||||
return options.drive or visuals.drive('Select the device drive')
|
||||
.tap(umount.umountAsync)
|
||||
.tap(umountAsync)
|
||||
.then (drive) ->
|
||||
fs.readFileAsync(params.file, 'utf8').then(JSON.parse).then (configJSON) ->
|
||||
readFileAsync(params.file, 'utf8').then(JSON.parse).then (configJSON) ->
|
||||
return config.write(drive, options.type, configJSON)
|
||||
.tap ->
|
||||
console.info('Done')
|
||||
@ -196,21 +196,21 @@ exports.reconfigure =
|
||||
Promise = require('bluebird')
|
||||
config = require('resin-config-json')
|
||||
visuals = require('resin-cli-visuals')
|
||||
capitano = Promise.promisifyAll(require('capitano'))
|
||||
umount = Promise.promisifyAll(require('umount'))
|
||||
capitanoRunAsync = Promise.promisify(require('capitano').run)
|
||||
umountAsync = Promise.promisify(require('umount').umount)
|
||||
|
||||
Promise.try ->
|
||||
return options.drive or visuals.drive('Select the device drive')
|
||||
.tap(umount.umountAsync)
|
||||
.tap(umountAsync)
|
||||
.then (drive) ->
|
||||
config.read(drive, options.type).get('uuid')
|
||||
.tap ->
|
||||
umount.umountAsync(drive)
|
||||
umountAsync(drive)
|
||||
.then (uuid) ->
|
||||
configureCommand = "os configure #{drive} #{uuid}"
|
||||
if options.advanced
|
||||
configureCommand += ' --advanced'
|
||||
return capitano.runAsync(configureCommand)
|
||||
return capitanoRunAsync(configureCommand)
|
||||
.then ->
|
||||
console.info('Done')
|
||||
.nodeify(done)
|
||||
@ -241,7 +241,7 @@ exports.generate =
|
||||
permission: 'user'
|
||||
action: (params, options, done) ->
|
||||
Promise = require('bluebird')
|
||||
fs = Promise.promisifyAll(require('fs'))
|
||||
writeFileAsync = Promise.promisify(require('fs').writeFile)
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
_ = require('lodash')
|
||||
form = require('resin-cli-form')
|
||||
@ -271,7 +271,7 @@ exports.generate =
|
||||
return deviceConfig.getByApplication(resource.app_name, answers)
|
||||
.then (config) ->
|
||||
if options.output?
|
||||
return fs.writeFileAsync(options.output, JSON.stringify(config))
|
||||
return writeFileAsync(options.output, JSON.stringify(config))
|
||||
|
||||
console.log(prettyjson.render(config))
|
||||
.nodeify(done)
|
||||
|
@ -376,9 +376,10 @@ exports.init =
|
||||
permission: 'user'
|
||||
action: (params, options, done) ->
|
||||
Promise = require('bluebird')
|
||||
capitano = Promise.promisifyAll(require('capitano'))
|
||||
capitanoRunAsync = Promise.promisify(require('capitano').run)
|
||||
rimraf = Promise.promisify(require('rimraf'))
|
||||
tmp = Promise.promisifyAll(require('tmp'))
|
||||
tmp = require('tmp')
|
||||
tmpNameAsync = Promise.promisify(tmp.tmpName)
|
||||
tmp.setGracefulCleanup()
|
||||
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
@ -392,27 +393,23 @@ exports.init =
|
||||
.then (application) ->
|
||||
|
||||
download = ->
|
||||
tmp.tmpNameAsync().then (temporalPath) ->
|
||||
tmpNameAsync().then (tempPath) ->
|
||||
# TODO: allow version selection
|
||||
capitano.runAsync("os download #{application.device_type} --output #{temporalPath} --version default")
|
||||
.disposer (temporalPath) ->
|
||||
return rimraf(temporalPath)
|
||||
capitanoRunAsync("os download #{application.device_type} --output '#{tempPath}' --version default")
|
||||
.disposer (tempPath) ->
|
||||
return rimraf(tempPath)
|
||||
|
||||
Promise.using download(), (temporalPath) ->
|
||||
capitano.runAsync("device register #{application.app_name}")
|
||||
Promise.using download(), (tempPath) ->
|
||||
capitanoRunAsync("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 administrative permissions
|
||||
given that we need to access raw devices directly.
|
||||
|
||||
'''
|
||||
|
||||
helpers.sudo([ 'os', 'initialize', temporalPath, '--type', application.device_type ], message)
|
||||
|
||||
configureCommand = "os configure '#{tempPath}' #{device.uuid}"
|
||||
if options.advanced
|
||||
configureCommand += ' --advanced'
|
||||
capitanoRunAsync(configureCommand)
|
||||
.then ->
|
||||
osInitCommand = "os initialize '#{tempPath}' --type #{application.device_type}"
|
||||
capitanoRunAsync(osInitCommand)
|
||||
# Make sure the device resource is removed if there is an
|
||||
# error when configuring or initializing a device image
|
||||
.catch (error) ->
|
||||
|
@ -54,12 +54,12 @@ general = (params, options, done) ->
|
||||
# We do not want the wildcard command
|
||||
# to be printed in the help screen.
|
||||
commands = _.reject capitano.state.commands, (command) ->
|
||||
return command.isWildcard()
|
||||
return command.hidden or command.isWildcard()
|
||||
|
||||
groupedCommands = _.groupBy commands, (command) ->
|
||||
if command.plugin
|
||||
return 'plugins'
|
||||
else if command.primary
|
||||
if command.primary
|
||||
return 'primary'
|
||||
return 'secondary'
|
||||
|
||||
|
@ -31,3 +31,4 @@ module.exports =
|
||||
config: require('./config')
|
||||
sync: require('./sync')
|
||||
ssh: require('./ssh')
|
||||
internal: require('./internal')
|
||||
|
37
lib/actions/internal.coffee
Normal file
37
lib/actions/internal.coffee
Normal file
@ -0,0 +1,37 @@
|
||||
###
|
||||
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.
|
||||
###
|
||||
|
||||
# These are internal commands we want to be runnable from the outside
|
||||
# One use-case for this is spawning the minimal operation with root priviledges
|
||||
|
||||
exports.osInit =
|
||||
signature: 'internal osinit <image> <type> <config>'
|
||||
description: 'do actual init of the device with the preconfigured os image'
|
||||
help: '''
|
||||
Don't use this command directly! Use `resin os initialize <image>` instead.
|
||||
'''
|
||||
hidden: true
|
||||
root: true
|
||||
action: (params, options, done) ->
|
||||
Promise = require('bluebird')
|
||||
init = require('resin-device-init')
|
||||
helpers = require('../utils/helpers')
|
||||
|
||||
return Promise.try ->
|
||||
config = JSON.parse(params.config)
|
||||
init.initialize(params.image, params.type, config)
|
||||
.then(helpers.osProgressHandler)
|
||||
.nodeify(done)
|
@ -107,13 +107,14 @@ exports.add =
|
||||
action: (params, options, done) ->
|
||||
_ = require('lodash')
|
||||
Promise = require('bluebird')
|
||||
fs = Promise.promisifyAll(require('fs'))
|
||||
readFileAsync = Promise.promisify(require('fs').readFile)
|
||||
capitano = require('capitano')
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
|
||||
Promise.try ->
|
||||
return fs.readFileAsync(params.path, encoding: 'utf8') if params.path?
|
||||
return readFileAsync(params.path, encoding: 'utf8') if params.path?
|
||||
|
||||
# TODO: should this be promisified for consistency?
|
||||
Promise.fromNode (callback) ->
|
||||
capitano.utils.getStdin (data) ->
|
||||
return callback(null, data)
|
||||
|
@ -67,14 +67,16 @@ module.exports =
|
||||
action: (params, options, done) ->
|
||||
_ = require('lodash')
|
||||
Promise = require('bluebird')
|
||||
umount = Promise.promisifyAll(require('umount'))
|
||||
umount = require('umount')
|
||||
umountAsync = Promise.promisify(umount.umount)
|
||||
isMountedAsync = Promise.promisify(umount.isMounted)
|
||||
inquirer = require('inquirer')
|
||||
reconfix = require('reconfix')
|
||||
denymount = Promise.promisify(require('denymount'))
|
||||
|
||||
umount.isMountedAsync(params.target).then (isMounted) ->
|
||||
isMountedAsync(params.target).then (isMounted) ->
|
||||
return if not isMounted
|
||||
umount.umountAsync(params.target)
|
||||
umountAsync(params.target)
|
||||
.then ->
|
||||
denymount params.target, (cb) ->
|
||||
reconfix.readConfiguration(CONFIGURATION_SCHEMA, params.target).then (data) ->
|
||||
|
@ -43,15 +43,15 @@ module.exports =
|
||||
_ = require('lodash')
|
||||
os = require('os')
|
||||
Promise = require('bluebird')
|
||||
umount = Promise.promisifyAll(require('umount'))
|
||||
umountAsync = Promise.promisify(require('umount').umount)
|
||||
fs = Promise.promisifyAll(require('fs'))
|
||||
drivelist = Promise.promisifyAll(require('drivelist'))
|
||||
driveListAsync = Promise.promisify(require('drivelist').list)
|
||||
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
|
||||
# TODO: Find a better ES6 module loading story/contract between resin.io modules
|
||||
require('babel-register')({
|
||||
only: /etcher-image-write|bmapflash/
|
||||
presets: ['es2015']
|
||||
@ -79,12 +79,14 @@ module.exports =
|
||||
# otherwise the question will not be asked because
|
||||
# `false` is a defined value.
|
||||
yes: options.yes || undefined
|
||||
|
||||
# TODO: dedupe with the resin-device-operations
|
||||
.then (answers) ->
|
||||
if answers.yes isnt true
|
||||
console.log(chalk.red.bold('Aborted image flash'))
|
||||
process.exit(0)
|
||||
|
||||
drivelist.listAsync().then (drives) ->
|
||||
driveListAsync().then (drives) ->
|
||||
selectedDrive = _.find(drives, device: answers.drive)
|
||||
|
||||
if not selectedDrive?
|
||||
@ -96,7 +98,7 @@ module.exports =
|
||||
write: new visuals.Progress('Flashing')
|
||||
check: new visuals.Progress('Validating')
|
||||
|
||||
umount.umountAsync(selectedDrive.device).then ->
|
||||
umountAsync(selectedDrive.device).then ->
|
||||
Promise.props
|
||||
imageSize: fs.statAsync(params.image).get('size'),
|
||||
imageStream: Promise.resolve(fs.createReadStream(params.image))
|
||||
@ -119,8 +121,8 @@ module.exports =
|
||||
writer.on('done', resolve)
|
||||
.then ->
|
||||
if (os.platform() is 'win32') and selectedDrive.mountpoint?
|
||||
removedrive = Promise.promisifyAll(require('removedrive'))
|
||||
return removedrive.ejectAsync(selectedDrive.mountpoint)
|
||||
ejectAsync = Promise.promisify(require('removedrive').eject)
|
||||
return ejectAsync(selectedDrive.mountpoint)
|
||||
|
||||
return umount.umountAsync(selectedDrive.device)
|
||||
return umountAsync(selectedDrive.device)
|
||||
.asCallback(done)
|
||||
|
@ -133,25 +133,6 @@ exports.download =
|
||||
console.info('The image was downloaded successfully')
|
||||
.nodeify(done)
|
||||
|
||||
stepHandler = (step) ->
|
||||
_ = require('lodash')
|
||||
rindle = require('rindle')
|
||||
visuals = require('resin-cli-visuals')
|
||||
helpers = require('../utils/helpers')
|
||||
|
||||
step.on('stdout', _.bind(process.stdout.write, process.stdout))
|
||||
step.on('stderr', _.bind(process.stderr.write, process.stderr))
|
||||
|
||||
step.on 'state', (state) ->
|
||||
return if state.operation.command is 'burn'
|
||||
console.log(helpers.stateToString(state))
|
||||
|
||||
bar = new visuals.Progress('Writing Device OS')
|
||||
|
||||
step.on('burn', _.bind(bar.update, bar))
|
||||
|
||||
return rindle.wait(step)
|
||||
|
||||
exports.configure =
|
||||
signature: 'os configure <image> <uuid>'
|
||||
description: 'configure an os image'
|
||||
@ -192,19 +173,26 @@ exports.configure =
|
||||
|
||||
return form.run(questions, { override })
|
||||
.then (answers) ->
|
||||
init.configure(params.image, params.uuid, answers).then(stepHandler)
|
||||
init.configure(params.image, params.uuid, answers).then(helpers.osProgressHandler)
|
||||
.nodeify(done)
|
||||
|
||||
initWarningMessage = '''
|
||||
Note: Initializing the device may ask for administrative permissions
|
||||
because we need to access the raw devices directly.
|
||||
'''
|
||||
|
||||
exports.initialize =
|
||||
signature: 'os initialize <image>'
|
||||
description: 'initialize an os image'
|
||||
help: '''
|
||||
Use this command to initialize a previously configured operating system image.
|
||||
help: """
|
||||
Use this command to initialize a device with previously configured operating system image.
|
||||
|
||||
#{initWarningMessage}
|
||||
|
||||
Examples:
|
||||
|
||||
$ resin os initialize ../path/rpi.img --type 'raspberry-pi'
|
||||
'''
|
||||
"""
|
||||
permission: 'user'
|
||||
options: [
|
||||
commandOptions.yes
|
||||
@ -222,16 +210,18 @@ exports.initialize =
|
||||
alias: 'd'
|
||||
}
|
||||
]
|
||||
root: true
|
||||
action: (params, options, done) ->
|
||||
Promise = require('bluebird')
|
||||
umount = Promise.promisifyAll(require('umount'))
|
||||
umountAsync = Promise.promisify(require('umount').umount)
|
||||
form = require('resin-cli-form')
|
||||
init = require('resin-device-init')
|
||||
patterns = require('../utils/patterns')
|
||||
helpers = require('../utils/helpers')
|
||||
|
||||
console.info('Initializing device')
|
||||
console.info("""
|
||||
Initializing device
|
||||
|
||||
#{initWarningMessage}
|
||||
""")
|
||||
helpers.getManifest(params.image, options.type)
|
||||
.then (manifest) ->
|
||||
return manifest.initialization?.options
|
||||
@ -244,11 +234,33 @@ exports.initialize =
|
||||
message = "This will erase #{answers.drive}. Are you sure?"
|
||||
patterns.confirm(options.yes, message)
|
||||
.return(answers.drive)
|
||||
.then(umount.umountAsync)
|
||||
.then(umountAsync)
|
||||
.tap (answers) ->
|
||||
return init.initialize(params.image, options.type, answers).then(stepHandler)
|
||||
return helpers.sudo([
|
||||
'internal'
|
||||
'osinit'
|
||||
params.image
|
||||
options.type
|
||||
JSON.stringify(answers)
|
||||
])
|
||||
.then (answers) ->
|
||||
return if not answers.drive?
|
||||
umount.umountAsync(answers.drive).tap ->
|
||||
|
||||
# TODO: resin local makes use of ejectAsync, see below
|
||||
# DO we need this / should we do that here?
|
||||
|
||||
# getDrive = (drive) ->
|
||||
# driveListAsync().then (drives) ->
|
||||
# selectedDrive = _.find(drives, device: drive)
|
||||
|
||||
# if not selectedDrive?
|
||||
# throw new Error("Drive not found: #{drive}")
|
||||
|
||||
# return selectedDrive
|
||||
# if (os.platform() is 'win32') and selectedDrive.mountpoint?
|
||||
# ejectAsync = Promise.promisify(require('removedrive').eject)
|
||||
# return ejectAsync(selectedDrive.mountpoint)
|
||||
|
||||
umountAsync(answers.drive).tap ->
|
||||
console.info("You can safely remove #{answers.drive} now")
|
||||
.nodeify(done)
|
||||
|
@ -35,7 +35,7 @@ exports.wizard =
|
||||
primary: true
|
||||
action: (params, options, done) ->
|
||||
Promise = require('bluebird')
|
||||
capitano = Promise.promisifyAll(require('capitano'))
|
||||
capitanoRunAsync = Promise.promisify(require('capitano').run)
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
patterns = require('../utils/patterns')
|
||||
|
||||
@ -43,20 +43,20 @@ exports.wizard =
|
||||
return if isLoggedIn
|
||||
console.info('Looks like you\'re not logged in yet!')
|
||||
console.info('Lets go through a quick wizard to get you started.\n')
|
||||
return capitano.runAsync('login')
|
||||
return capitanoRunAsync('login')
|
||||
.then ->
|
||||
return if params.name?
|
||||
patterns.selectOrCreateApplication().tap (applicationName) ->
|
||||
resin.models.application.has(applicationName).then (hasApplication) ->
|
||||
return applicationName if hasApplication
|
||||
capitano.runAsync("app create #{applicationName}")
|
||||
capitanoRunAsync("app create #{applicationName}")
|
||||
.then (applicationName) ->
|
||||
params.name = applicationName
|
||||
.then ->
|
||||
return capitano.runAsync("device init --application #{params.name}")
|
||||
return capitanoRunAsync("device init --application #{params.name}")
|
||||
.tap(patterns.awaitDevice)
|
||||
.then (uuid) ->
|
||||
return capitano.runAsync("device #{uuid}")
|
||||
return capitanoRunAsync("device #{uuid}")
|
||||
.then ->
|
||||
return resin.models.application.get(params.name)
|
||||
.then (application) ->
|
||||
|
@ -24,7 +24,8 @@ Raven.config(
|
||||
|
||||
_ = require('lodash')
|
||||
Promise = require('bluebird')
|
||||
capitano = Promise.promisifyAll(require('capitano'))
|
||||
capitano = require('capitano')
|
||||
capitanoExecuteAsync = Promise.promisify(capitano.execute)
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
actions = require('./actions')
|
||||
errors = require('./errors')
|
||||
@ -142,6 +143,9 @@ capitano.command(actions.local.ssh)
|
||||
capitano.command(actions.local.scan)
|
||||
capitano.command(actions.local.stop)
|
||||
|
||||
# ---------- Internal utils ----------
|
||||
capitano.command(actions.internal.osInit)
|
||||
|
||||
update.notify()
|
||||
|
||||
plugins.register(/^resin-plugin-(.+)$/).then ->
|
||||
@ -149,7 +153,7 @@ plugins.register(/^resin-plugin-(.+)$/).then ->
|
||||
|
||||
events.trackCommand(cli).then ->
|
||||
if cli.global?.help
|
||||
return capitano.executeAsync(command: "help #{cli.command ? ''}")
|
||||
capitano.executeAsync(cli)
|
||||
return capitanoExecuteAsync(command: "help #{cli.command ? ''}")
|
||||
capitanoExecuteAsync(cli)
|
||||
|
||||
.catch(errors.handle)
|
||||
|
@ -2,19 +2,20 @@ _ = require('lodash')
|
||||
Mixpanel = require('mixpanel')
|
||||
Promise = require('bluebird')
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
capitanoState = Promise.promisifyAll(require('capitano').state)
|
||||
packageJSON = require('../package.json')
|
||||
|
||||
exports.getLoggerInstance = _.memoize ->
|
||||
return resin.models.config.getMixpanelToken().then(Mixpanel.init)
|
||||
|
||||
exports.trackCommand = (capitanoCommand) ->
|
||||
capitanoStateGetMatchCommandAsync = Promise.promisify(require('capitano').state.getMatchCommand)
|
||||
|
||||
return Promise.props
|
||||
resinUrl: resin.settings.get('resinUrl')
|
||||
username: resin.auth.whoami()
|
||||
mixpanel: exports.getLoggerInstance()
|
||||
.then (data) ->
|
||||
return capitanoState.getMatchCommandAsync(capitanoCommand.command).then (command) ->
|
||||
return capitanoStateGetMatchCommandAsync(capitanoCommand.command).then (command) ->
|
||||
data.mixpanel.track "[CLI] #{command.signature.toString()}",
|
||||
distinct_id: data.username
|
||||
argv: process.argv.join(' ')
|
||||
|
@ -15,17 +15,10 @@ limitations under the License.
|
||||
###
|
||||
|
||||
Promise = require('bluebird')
|
||||
capitano = Promise.promisifyAll(require('capitano'))
|
||||
_ = require('lodash')
|
||||
_.str = require('underscore.string')
|
||||
president = Promise.promisifyAll(require('president'))
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
imagefs = require('resin-image-fs')
|
||||
rindle = require('rindle')
|
||||
os = require('os')
|
||||
chalk = require('chalk')
|
||||
|
||||
exports.getGroupDefaults = (group) ->
|
||||
_ = require('lodash')
|
||||
|
||||
return _.chain(group)
|
||||
.get('options')
|
||||
.map (question) ->
|
||||
@ -34,7 +27,10 @@ exports.getGroupDefaults = (group) ->
|
||||
.value()
|
||||
|
||||
exports.stateToString = (state) ->
|
||||
percentage = _.str.lpad(state.percentage, 3, '0') + '%'
|
||||
_str = require('underscore.string')
|
||||
chalk = require('chalk')
|
||||
|
||||
percentage = _str.lpad(state.percentage, 3, '0') + '%'
|
||||
result = "#{chalk.blue(percentage)} #{chalk.cyan(state.operation.command)}"
|
||||
|
||||
switch state.operation.command
|
||||
@ -47,16 +43,21 @@ exports.stateToString = (state) ->
|
||||
else
|
||||
throw new Error("Unsupported operation: #{state.operation.type}")
|
||||
|
||||
exports.sudo = (command, message) ->
|
||||
command = _.union(_.take(process.argv, 2), command)
|
||||
console.log(message)
|
||||
exports.sudo = (command) ->
|
||||
_ = require('lodash')
|
||||
os = require('os')
|
||||
|
||||
if os.platform() isnt 'win32'
|
||||
console.log('Type your computer password to continue')
|
||||
console.log('If asked please type your computer password to continue')
|
||||
|
||||
return president.executeAsync(command)
|
||||
command = _.union(_.take(process.argv, 2), command)
|
||||
presidentExecuteAsync = Promise.promisify(require('president').execute)
|
||||
return presidentExecuteAsync(command)
|
||||
|
||||
exports.getManifest = (image, deviceType) ->
|
||||
rindle = require('rindle')
|
||||
imagefs = require('resin-image-fs')
|
||||
resin = require('resin-sdk-preconfigured')
|
||||
|
||||
# Attempt to read manifest from the first
|
||||
# partition, but fallback to the API if
|
||||
@ -70,3 +71,23 @@ exports.getManifest = (image, deviceType) ->
|
||||
.then(JSON.parse)
|
||||
.catch ->
|
||||
resin.models.device.getManifestBySlug(deviceType)
|
||||
|
||||
exports.osProgressHandler = (step) ->
|
||||
rindle = require('rindle')
|
||||
visuals = require('resin-cli-visuals')
|
||||
|
||||
step.on('stdout', process.stdout.write.bind(process.stdout))
|
||||
step.on('stderr', process.stderr.write.bind(process.stderr))
|
||||
|
||||
step.on 'state', (state) ->
|
||||
return if state.operation.command is 'burn'
|
||||
console.log(exports.stateToString(state))
|
||||
|
||||
progressBars =
|
||||
write: new visuals.Progress('Writing Device OS')
|
||||
check: new visuals.Progress('Validating Device OS')
|
||||
|
||||
step.on 'burn', (state) ->
|
||||
progressBars[state.type].update(state)
|
||||
|
||||
return rindle.wait(step)
|
||||
|
@ -61,7 +61,7 @@
|
||||
"resin-cli-visuals": "^1.3.0",
|
||||
"resin-config-json": "^1.0.0",
|
||||
"resin-device-config": "^3.0.0",
|
||||
"resin-device-init": "^2.1.0",
|
||||
"resin-device-init": "^2.2.0",
|
||||
"resin-image-fs": "^2.1.2",
|
||||
"resin-image-manager": "^4.1.0",
|
||||
"resin-sdk-preconfigured": "^0.1.1",
|
||||
@ -75,5 +75,8 @@
|
||||
"unzip2": "^0.2.5",
|
||||
"update-notifier": "^0.6.1",
|
||||
"valid-email": "^0.0.2"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"removedrive": "^1.0.0"
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user