mirror of
https://github.com/balena-io/balena-cli.git
synced 2025-01-23 12:58:55 +00:00
Merge pull request #1806 from balena-io/errors-improvements
Improve presentation of errors, help
This commit is contained in:
commit
c2561938c1
@ -73,8 +73,8 @@ const manuallySortedPrimaryCommands = [
|
|||||||
|
|
||||||
const general = function(_params, options, done) {
|
const general = function(_params, options, done) {
|
||||||
console.log('Usage: balena [COMMAND] [OPTIONS]\n');
|
console.log('Usage: balena [COMMAND] [OPTIONS]\n');
|
||||||
console.log(messages.reachingOut);
|
|
||||||
console.log('\nPrimary commands:\n');
|
console.log('Primary commands:\n');
|
||||||
|
|
||||||
// We do not want the wildcard command
|
// We do not want the wildcard command
|
||||||
// to be printed in the help screen.
|
// to be printed in the help screen.
|
||||||
@ -123,6 +123,9 @@ const general = function(_params, options, done) {
|
|||||||
console.log('\nGlobal Options:\n');
|
console.log('\nGlobal Options:\n');
|
||||||
print(parse(capitano.state.globalOptions).sort());
|
print(parse(capitano.state.globalOptions).sort());
|
||||||
}
|
}
|
||||||
|
console.log(indent('--debug\n'));
|
||||||
|
|
||||||
|
console.log(messages.help);
|
||||||
|
|
||||||
return done();
|
return done();
|
||||||
})
|
})
|
||||||
@ -152,6 +155,8 @@ const commandHelp = (params, _options, done) =>
|
|||||||
print(parse(command.options).sort());
|
print(parse(command.options).sort());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log();
|
||||||
|
|
||||||
return done();
|
return done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ export class NotLoggedInError extends ExpectedError {}
|
|||||||
export class InsufficientPrivilegesError extends ExpectedError {}
|
export class InsufficientPrivilegesError extends ExpectedError {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* instanceOf is a more reliable implemention of the plain `instanceof`
|
* instanceOf is a more reliable implementation of the plain `instanceof`
|
||||||
* typescript operator, for use with TypedError errors when the error
|
* typescript operator, for use with TypedError errors when the error
|
||||||
* classes may be defined in external packages/dependencies.
|
* classes may be defined in external packages/dependencies.
|
||||||
* Sample usage:
|
* Sample usage:
|
||||||
@ -126,56 +126,81 @@ const messages: {
|
|||||||
`,
|
`,
|
||||||
|
|
||||||
BalenaExpiredToken: () => stripIndent`
|
BalenaExpiredToken: () => stripIndent`
|
||||||
Looks like your session token is expired.
|
Looks like your session token has expired.
|
||||||
Please try logging in again with:
|
Please try logging in again with:
|
||||||
$ balena login`,
|
$ balena login`,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const EXPECTED_ERROR_REGEXES = [
|
||||||
|
/^BalenaApplicationNotFound:/, // balena-sdk
|
||||||
|
/^BalenaDeviceNotFound:/, // balena-sdk
|
||||||
|
/^Missing \w+$/, // Capitano, oclif parser: RequiredArgsError, RequiredFlagError
|
||||||
|
/^Unexpected arguments?:/, // oclif parser: UnexpectedArgsError
|
||||||
|
/to be one of/, // oclif parser: FlagInvalidOptionError, ArgInvalidOptionError
|
||||||
|
];
|
||||||
|
|
||||||
export async function handleError(error: any) {
|
export async function handleError(error: any) {
|
||||||
|
// Set appropriate exitCode
|
||||||
process.exitCode =
|
process.exitCode =
|
||||||
error.exitCode === 0
|
error.exitCode === 0
|
||||||
? 0
|
? 0
|
||||||
: parseInt(error.exitCode, 10) || process.exitCode || 1;
|
: parseInt(error.exitCode, 10) || process.exitCode || 1;
|
||||||
|
|
||||||
|
// Handle non-Error objects (probably strings)
|
||||||
if (!(error instanceof Error)) {
|
if (!(error instanceof Error)) {
|
||||||
printErrorMessage(String(error));
|
printErrorMessage(String(error));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prepare message
|
||||||
const message = [interpret(error)];
|
const message = [interpret(error)];
|
||||||
|
|
||||||
if (process.env.DEBUG && error.stack) {
|
if (error.stack) {
|
||||||
message.push('\n' + error.stack);
|
if (process.env.DEBUG) {
|
||||||
|
message.push('\n' + error.stack);
|
||||||
|
} else {
|
||||||
|
// Include first line of stacktrace
|
||||||
|
message.push('\n' + error.stack.split(`\n`)[0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
printErrorMessage(message.join('\n'));
|
|
||||||
|
|
||||||
const expectedErrorREs = [
|
// Expected?
|
||||||
/^BalenaApplicationNotFound:/, // balena-sdk
|
const isExpectedError =
|
||||||
/^BalenaDeviceNotFound:/, // balena-sdk
|
|
||||||
/^Missing \w+$/, // Capitano's command line parsing error
|
|
||||||
/^Unexpected arguments?:/, // oclif's command line parsing error
|
|
||||||
];
|
|
||||||
|
|
||||||
if (
|
|
||||||
error instanceof ExpectedError ||
|
error instanceof ExpectedError ||
|
||||||
expectedErrorREs.some(re => re.test(message[0]))
|
EXPECTED_ERROR_REGEXES.some(re => re.test(message[0]));
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Report "unexpected" errors via Sentry.io
|
// Output/report error
|
||||||
const Sentry = await import('@sentry/node');
|
if (isExpectedError) {
|
||||||
Sentry.captureException(error);
|
printExpectedErrorMessage(message.join('\n'));
|
||||||
await Sentry.close(1000);
|
} else {
|
||||||
// Unhandled/unexpected error: ensure that the process terminates.
|
printErrorMessage(message.join('\n'));
|
||||||
// The exit error code was set above through `process.exitCode`.
|
|
||||||
process.exit();
|
// Report "unexpected" errors via Sentry.io
|
||||||
|
const Sentry = await import('@sentry/node');
|
||||||
|
Sentry.captureException(error);
|
||||||
|
await Sentry.close(1000);
|
||||||
|
// Unhandled/unexpected error: ensure that the process terminates.
|
||||||
|
// The exit error code was set above through `process.exitCode`.
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function printErrorMessage(message: string) {
|
export function printErrorMessage(message: string) {
|
||||||
const chalk = getChalk();
|
const chalk = getChalk();
|
||||||
console.error(chalk.red(message));
|
|
||||||
console.error(chalk.red(`\n${getHelp}\n`));
|
// Only first line should be red
|
||||||
|
const messageLines = message.split('\n');
|
||||||
|
console.error(chalk.red(messageLines.shift()));
|
||||||
|
|
||||||
|
messageLines.forEach(line => {
|
||||||
|
console.error(line);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.error(`\n${getHelp}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function printExpectedErrorMessage(message: string) {
|
||||||
|
console.error(`${message}\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,19 +9,16 @@ create a new one at: https://github.com/balena-io/balena-cli/issues/\
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
const debugHint = `\
|
const debugHint = `\
|
||||||
Additional information may be available by setting a DEBUG=1 environment
|
Additional information may be available with the \`--debug\` flag.
|
||||||
variable: "set DEBUG=1" on a Windows command prompt, "$env:DEBUG = 1" on
|
|
||||||
powershell, or "export DEBUG=1" on Linux or macOS.\n
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const getHelp = `${DEBUG_MODE ? '' : debugHint}\
|
export const help = `\
|
||||||
If you need help, don't hesitate in contacting our support forums at
|
For help, visit our support forums: https://forums.balena.io
|
||||||
https://forums.balena.io
|
For bug reports or feature requests, see: https://github.com/balena-io/balena-cli/issues/
|
||||||
|
|
||||||
For CLI bug reports or feature requests, have a look at the GitHub issues or
|
|
||||||
create a new one at: https://github.com/balena-io/balena-cli/issues/\
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
export const getHelp = (DEBUG_MODE ? '' : debugHint) + help;
|
||||||
|
|
||||||
export const balenaAsciiArt = `\
|
export const balenaAsciiArt = `\
|
||||||
_ _
|
_ _
|
||||||
| |__ __ _ | | ____ _ __ __ _
|
| |__ __ _ | | ____ _ __ __ _
|
||||||
|
@ -4,12 +4,6 @@ import { cleanOutput, runCommand } from '../helpers';
|
|||||||
const SIMPLE_HELP = `
|
const SIMPLE_HELP = `
|
||||||
Usage: balena [COMMAND] [OPTIONS]
|
Usage: balena [COMMAND] [OPTIONS]
|
||||||
|
|
||||||
If you need help, or just want to say hi, don't hesitate in reaching out
|
|
||||||
through our discussion and support forums at https://forums.balena.io
|
|
||||||
|
|
||||||
For bug reports or feature requests, have a look at the GitHub issues or
|
|
||||||
create a new one at: https://github.com/balena-io/balena-cli/issues/
|
|
||||||
|
|
||||||
Primary commands:
|
Primary commands:
|
||||||
|
|
||||||
help [command...] show help
|
help [command...] show help
|
||||||
@ -84,33 +78,33 @@ Additional commands:
|
|||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const LIST_ADDITIONAL = `
|
||||||
|
Run \`balena help --verbose\` to list additional commands
|
||||||
|
`;
|
||||||
|
|
||||||
const GLOBAL_OPTIONS = `
|
const GLOBAL_OPTIONS = `
|
||||||
Global Options:
|
Global Options:
|
||||||
|
|
||||||
--help, -h
|
--help, -h
|
||||||
--version, -v
|
--version, -v
|
||||||
|
--debug
|
||||||
|
`;
|
||||||
|
|
||||||
|
const ONLINE_RESOURCES = `
|
||||||
|
For help, visit our support forums: https://forums.balena.io
|
||||||
|
For bug reports or feature requests, see: https://github.com/balena-io/balena-cli/issues/
|
||||||
`;
|
`;
|
||||||
|
|
||||||
describe('balena help', function() {
|
describe('balena help', function() {
|
||||||
it('should list primary command summaries', async () => {
|
it('should list primary command summaries', async () => {
|
||||||
const { out, err } = await runCommand('help');
|
const { out, err } = await runCommand('help');
|
||||||
|
|
||||||
console.log('ONE');
|
|
||||||
console.log(cleanOutput(out));
|
|
||||||
console.log(
|
|
||||||
cleanOutput([
|
|
||||||
SIMPLE_HELP,
|
|
||||||
'Run `balena help --verbose` to list additional commands',
|
|
||||||
GLOBAL_OPTIONS,
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
console.log();
|
|
||||||
|
|
||||||
expect(cleanOutput(out)).to.deep.equal(
|
expect(cleanOutput(out)).to.deep.equal(
|
||||||
cleanOutput([
|
cleanOutput([
|
||||||
SIMPLE_HELP,
|
SIMPLE_HELP,
|
||||||
'Run `balena help --verbose` to list additional commands',
|
LIST_ADDITIONAL,
|
||||||
GLOBAL_OPTIONS,
|
GLOBAL_OPTIONS,
|
||||||
|
ONLINE_RESOURCES,
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -121,7 +115,12 @@ describe('balena help', function() {
|
|||||||
const { out, err } = await runCommand('help -v');
|
const { out, err } = await runCommand('help -v');
|
||||||
|
|
||||||
expect(cleanOutput(out)).to.deep.equal(
|
expect(cleanOutput(out)).to.deep.equal(
|
||||||
cleanOutput([SIMPLE_HELP, ADDITIONAL_HELP, GLOBAL_OPTIONS]),
|
cleanOutput([
|
||||||
|
SIMPLE_HELP,
|
||||||
|
ADDITIONAL_HELP,
|
||||||
|
GLOBAL_OPTIONS,
|
||||||
|
ONLINE_RESOURCES,
|
||||||
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(err.join('')).to.equal('');
|
expect(err.join('')).to.equal('');
|
||||||
@ -135,8 +134,9 @@ describe('balena help', function() {
|
|||||||
expect(cleanOutput(out)).to.deep.equal(
|
expect(cleanOutput(out)).to.deep.equal(
|
||||||
cleanOutput([
|
cleanOutput([
|
||||||
SIMPLE_HELP,
|
SIMPLE_HELP,
|
||||||
'Run `balena help --verbose` to list additional commands',
|
LIST_ADDITIONAL,
|
||||||
GLOBAL_OPTIONS,
|
GLOBAL_OPTIONS,
|
||||||
|
ONLINE_RESOURCES,
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user