mirror of
https://github.com/balena-io/etcher.git
synced 2025-04-22 06:17:20 +00:00

The `--robot` option of the CLI causes the program to output machine-parseable strings, which can be easily consumed by the GUI to update progress and other information. The problem is that if the CLI fails to parse its command line arguments when being called from the GUI for whatever reason, the `.fail()` Yargs handler will be called, which doesn't output error information in the usual "robot" format, causing the GUI to not understand the error message. Since the `.fail()` Yargs handler doesn't have access to the passed options, we moved the "robot" functionality to an environment variable, which we can easily check from there. As a bonus, this PR refactors the whole robot logic into `lib/shared/robot.js` and adds unit tests to it. See: https://github.com/resin-io/etcher/issues/986 Change-Type: major Changelog-Entry: Replace the `--robot` CLI option with an `ETCHER_CLI_ROBOT` environment variable. Signed-off-by: Juan Cruz Viotti <jviotti@openmailbox.org>
236 lines
5.0 KiB
JavaScript
236 lines
5.0 KiB
JavaScript
/*
|
|
* 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.
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const _ = require('lodash');
|
|
|
|
/**
|
|
* @summary Check whether we should emit parseable output
|
|
* @function
|
|
* @public
|
|
*
|
|
* @param {Object} environment - environment
|
|
* @returns {Boolean} whether we should emit parseable output
|
|
*
|
|
* @example
|
|
* if (robot.isEnabled(process.env)) {
|
|
* console.log('We should emit parseable output');
|
|
* }
|
|
*/
|
|
exports.isEnabled = (environment) => {
|
|
const value = _.get(environment, 'ETCHER_CLI_ROBOT', false);
|
|
return Boolean(value === 'false' ? false : value);
|
|
};
|
|
|
|
/**
|
|
* @summary Build a machine-parseable message
|
|
* @function
|
|
* @private
|
|
*
|
|
* @param {String} title - message title
|
|
* @param {Object} [data] - message data
|
|
* @returns {String} parseable message
|
|
*
|
|
* @example
|
|
* const message = robot.buildMessage('progress', {
|
|
* percentage: 50
|
|
* });
|
|
*
|
|
* console.log(message);
|
|
* > '{"command":"progress","data":{"percentage":50}}'
|
|
*/
|
|
exports.buildMessage = (title, data = {}) => {
|
|
if (!_.isPlainObject(data)) {
|
|
throw new Error(`Invalid data: ${data}`);
|
|
}
|
|
|
|
return JSON.stringify({
|
|
command: title,
|
|
data: data
|
|
});
|
|
};
|
|
|
|
/**
|
|
* @summary Parse a machine-parseable message
|
|
* @function
|
|
* @public
|
|
*
|
|
* @param {String} string - message string
|
|
* @returns {Object} parsed message
|
|
*
|
|
* @example
|
|
* const result = robot.parseMessage('{"command":"progress","data":{"foo":50}}');
|
|
* console.log(message);
|
|
* > {
|
|
* > command: 'progress',
|
|
* > data: {
|
|
* > foo: 50
|
|
* > }
|
|
* > }
|
|
*/
|
|
exports.parseMessage = (string) => {
|
|
let output;
|
|
|
|
try {
|
|
output = JSON.parse(string);
|
|
} catch (error) {
|
|
error.message = 'Invalid message';
|
|
error.description = `${string}, ${error.message}`;
|
|
throw error;
|
|
}
|
|
|
|
if (!output.command || !output.data) {
|
|
const error = new Error('Invalid message');
|
|
error.description = `No command or data: ${string}`;
|
|
throw error;
|
|
}
|
|
|
|
return output;
|
|
};
|
|
|
|
/**
|
|
* @summary Build a machine-parseable error message
|
|
* @function
|
|
* @private
|
|
*
|
|
* @param {(String|Error)} error - error
|
|
* @returns {String} parseable error message
|
|
*
|
|
* @example
|
|
* const error = new Error('foo');
|
|
* const errorMessage = robot.buildErrorMessage(error);
|
|
*
|
|
* console.log(error.command);
|
|
* > 'error'
|
|
*
|
|
* console.log(error.data.message);
|
|
* > 'foo'
|
|
*
|
|
* error.data.stacktrace === error.stack;
|
|
* > true
|
|
*/
|
|
exports.buildErrorMessage = (error) => {
|
|
if (_.isString(error)) {
|
|
error = new Error(error);
|
|
}
|
|
|
|
return exports.buildMessage('error', {
|
|
message: error.message,
|
|
description: error.description,
|
|
stacktrace: error.stack,
|
|
code: error.code
|
|
});
|
|
};
|
|
|
|
/**
|
|
* @summary Recompose an error message
|
|
* @function
|
|
* @public
|
|
*
|
|
* @param {String} message - error message
|
|
* @returns {Error} error object
|
|
*
|
|
* @example
|
|
* const message = robot.buildErrorMessage(new Error('foo'));
|
|
* const error = robot.recomposeErrorMessage(robot.parseMessage(message));
|
|
*
|
|
* error instanceof Error;
|
|
* > true
|
|
*
|
|
* console.log(error.message);
|
|
* > 'foo'
|
|
*/
|
|
exports.recomposeErrorMessage = (message) => {
|
|
const error = new Error(message.data.message);
|
|
_.assign(error, _.omit(message.data, 'stacktrace'));
|
|
error.stack = message.data.stacktrace;
|
|
return error;
|
|
};
|
|
|
|
/**
|
|
* @summary Get message command
|
|
* @function
|
|
* @public
|
|
*
|
|
* @param {Object} message - message
|
|
* @returns {String} command
|
|
*
|
|
* @example
|
|
* const command = robot.getCommand({
|
|
* command: 'foo',
|
|
* data: {}
|
|
* });
|
|
*
|
|
* console.log(command);
|
|
* > 'foo'
|
|
*/
|
|
exports.getCommand = (message) => {
|
|
return _.get(message, 'command');
|
|
};
|
|
|
|
/**
|
|
* @summary Get message data
|
|
* @function
|
|
* @public
|
|
*
|
|
* @param {Object} message - message
|
|
* @returns {Object} data
|
|
*
|
|
* @example
|
|
* const data = robot.getData({
|
|
* command: 'foo',
|
|
* data: {
|
|
* foo: 1
|
|
* }
|
|
* });
|
|
*
|
|
* console.log(data);
|
|
* > { foo: 1 }
|
|
*/
|
|
exports.getData = (message) => {
|
|
return _.get(message, 'data', {});
|
|
};
|
|
|
|
/**
|
|
* @summary Print an error in a machine-friendly way
|
|
* @function
|
|
* @public
|
|
*
|
|
* @param {(Error|String)} error - error
|
|
*
|
|
* @example
|
|
* robot.printError(new Error('This is an error'));
|
|
*/
|
|
exports.printError = (error) => {
|
|
console.error(exports.buildErrorMessage(error));
|
|
};
|
|
|
|
/**
|
|
* @summary Print a message in a machine-friendly way
|
|
* @function
|
|
* @public
|
|
*
|
|
* @param {String} message - message
|
|
* @param {Object} [data] - data
|
|
*
|
|
* @example
|
|
* robot.printMessage('progress', { percentage: 50 });
|
|
*/
|
|
exports.printMessage = (message, data) => {
|
|
console.log(exports.buildMessage(message, data));
|
|
};
|