mirror of
https://github.com/balena-io/balena-cli.git
synced 2024-12-23 23:42:24 +00:00
Merge pull request #1791 from balena-io/errors-refactor
Errors refactor
This commit is contained in:
commit
3b53b75626
@ -87,6 +87,7 @@ Examples:
|
|||||||
const balena = getBalenaSdk();
|
const balena = getBalenaSdk();
|
||||||
const patterns = await import('../utils/patterns');
|
const patterns = await import('../utils/patterns');
|
||||||
const messages = await import('../utils/messages');
|
const messages = await import('../utils/messages');
|
||||||
|
const { exitWithExpectedError } = await import('../errors');
|
||||||
|
|
||||||
const doLogin = async (loginOptions: Options): Promise<void> => {
|
const doLogin = async (loginOptions: Options): Promise<void> => {
|
||||||
if (loginOptions.token != null) {
|
if (loginOptions.token != null) {
|
||||||
@ -103,7 +104,7 @@ Examples:
|
|||||||
}
|
}
|
||||||
await balena.auth.loginWithToken(token);
|
await balena.auth.loginWithToken(token);
|
||||||
if (!(await balena.auth.whoami())) {
|
if (!(await balena.auth.whoami())) {
|
||||||
patterns.exitWithExpectedError('Token authentication failed');
|
exitWithExpectedError('Token authentication failed');
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
} else if (loginOptions.credentials) {
|
} else if (loginOptions.credentials) {
|
||||||
@ -120,7 +121,7 @@ Examples:
|
|||||||
const signupUrl = 'https://dashboard.balena-cloud.com/signup';
|
const signupUrl = 'https://dashboard.balena-cloud.com/signup';
|
||||||
const open = await import('open');
|
const open = await import('open');
|
||||||
open(signupUrl, { wait: false });
|
open(signupUrl, { wait: false });
|
||||||
return patterns.exitWithExpectedError(`Please sign up at ${signupUrl}`);
|
return exitWithExpectedError(`Please sign up at ${signupUrl}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
loginOptions[loginType] = true;
|
loginOptions[loginType] = true;
|
||||||
|
@ -322,7 +322,7 @@ Examples:
|
|||||||
generateApplicationConfig,
|
generateApplicationConfig,
|
||||||
} = require('../utils/config');
|
} = require('../utils/config');
|
||||||
const helpers = require('../utils/helpers');
|
const helpers = require('../utils/helpers');
|
||||||
const { exitWithExpectedError } = require('../utils/patterns');
|
const { exitWithExpectedError } = require('../errors');
|
||||||
|
|
||||||
if (options.device == null && options.application == null) {
|
if (options.device == null && options.application == null) {
|
||||||
exitWithExpectedError(`\
|
exitWithExpectedError(`\
|
||||||
|
@ -20,7 +20,7 @@ import * as capitano from 'capitano';
|
|||||||
import * as columnify from 'columnify';
|
import * as columnify from 'columnify';
|
||||||
import * as messages from '../utils/messages';
|
import * as messages from '../utils/messages';
|
||||||
import { getManualSortCompareFunction } from '../utils/helpers';
|
import { getManualSortCompareFunction } from '../utils/helpers';
|
||||||
import { exitWithExpectedError } from '../utils/patterns';
|
import { exitWithExpectedError } from '../errors';
|
||||||
import { getOclifHelpLinePairs } from './help_ts';
|
import { getOclifHelpLinePairs } from './help_ts';
|
||||||
|
|
||||||
const parse = object =>
|
const parse = object =>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import * as Promise from 'bluebird';
|
import * as Promise from 'bluebird';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as dockerUtils from '../../utils/docker';
|
import * as dockerUtils from '../../utils/docker';
|
||||||
import { exitWithExpectedError } from '../../utils/patterns';
|
import { exitWithExpectedError } from '../../errors';
|
||||||
import { getChalk } from '../../utils/lazy';
|
import { getChalk } from '../../utils/lazy';
|
||||||
|
|
||||||
export const dockerPort = 2375;
|
export const dockerPort = 2375;
|
||||||
|
@ -91,9 +91,8 @@ export const logs: CommandDefinition<
|
|||||||
'../utils/device/logs'
|
'../utils/device/logs'
|
||||||
);
|
);
|
||||||
const { validateIPAddress } = await import('../utils/validation');
|
const { validateIPAddress } = await import('../utils/validation');
|
||||||
const { exitIfNotLoggedIn, exitWithExpectedError } = await import(
|
const { checkLoggedIn } = await import('../utils/patterns');
|
||||||
'../utils/patterns'
|
const { exitWithExpectedError } = await import('../errors');
|
||||||
);
|
|
||||||
const Logger = await import('../utils/logger');
|
const Logger = await import('../utils/logger');
|
||||||
|
|
||||||
const logger = Logger.getLogger();
|
const logger = Logger.getLogger();
|
||||||
@ -152,7 +151,7 @@ export const logs: CommandDefinition<
|
|||||||
servicesToDisplay,
|
servicesToDisplay,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
await exitIfNotLoggedIn();
|
await checkLoggedIn();
|
||||||
if (options.tail) {
|
if (options.tail) {
|
||||||
return balena.logs
|
return balena.logs
|
||||||
.subscribe(params.uuidOrDevice, { count: 100 })
|
.subscribe(params.uuidOrDevice, { count: 100 })
|
||||||
|
@ -87,7 +87,7 @@ const getApplicationsWithSuccessfulBuilds = function(deviceType) {
|
|||||||
const selectApplication = function(deviceType) {
|
const selectApplication = function(deviceType) {
|
||||||
const visuals = getVisuals();
|
const visuals = getVisuals();
|
||||||
const form = require('resin-cli-form');
|
const form = require('resin-cli-form');
|
||||||
const { exitWithExpectedError } = require('../utils/patterns');
|
const { exitWithExpectedError } = require('../errors');
|
||||||
|
|
||||||
const applicationInfoSpinner = new visuals.Spinner(
|
const applicationInfoSpinner = new visuals.Spinner(
|
||||||
'Downloading list of applications and releases.',
|
'Downloading list of applications and releases.',
|
||||||
@ -116,7 +116,7 @@ const selectApplication = function(deviceType) {
|
|||||||
|
|
||||||
const selectApplicationCommit = function(releases) {
|
const selectApplicationCommit = function(releases) {
|
||||||
const form = require('resin-cli-form');
|
const form = require('resin-cli-form');
|
||||||
const { exitWithExpectedError } = require('../utils/patterns');
|
const { exitWithExpectedError } = require('../errors');
|
||||||
|
|
||||||
if (releases.length === 0) {
|
if (releases.length === 0) {
|
||||||
exitWithExpectedError('This application has no successful releases.');
|
exitWithExpectedError('This application has no successful releases.');
|
||||||
@ -263,7 +263,7 @@ Examples:
|
|||||||
const balenaPreload = require('balena-preload');
|
const balenaPreload = require('balena-preload');
|
||||||
const visuals = getVisuals();
|
const visuals = getVisuals();
|
||||||
const nodeCleanup = require('node-cleanup');
|
const nodeCleanup = require('node-cleanup');
|
||||||
const { exitWithExpectedError } = require('../utils/patterns');
|
const { exitWithExpectedError } = require('../errors');
|
||||||
|
|
||||||
const progressBars = {};
|
const progressBars = {};
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ export const list: CommandDefinition<
|
|||||||
const _ = await import('lodash');
|
const _ = await import('lodash');
|
||||||
const balena = getBalenaSdk();
|
const balena = getBalenaSdk();
|
||||||
|
|
||||||
const { exitWithExpectedError } = await import('../utils/patterns');
|
const { exitWithExpectedError } = await import('../errors');
|
||||||
|
|
||||||
return Bluebird.try<ApplicationTag[] | DeviceTag[] | ReleaseTag[]>(
|
return Bluebird.try<ApplicationTag[] | DeviceTag[] | ReleaseTag[]>(
|
||||||
async () => {
|
async () => {
|
||||||
@ -161,7 +161,7 @@ export const set: CommandDefinition<
|
|||||||
const _ = await import('lodash');
|
const _ = await import('lodash');
|
||||||
const balena = getBalenaSdk();
|
const balena = getBalenaSdk();
|
||||||
|
|
||||||
const { exitWithExpectedError } = await import('../utils/patterns');
|
const { exitWithExpectedError } = await import('../errors');
|
||||||
|
|
||||||
if (_.isEmpty(params.tagKey)) {
|
if (_.isEmpty(params.tagKey)) {
|
||||||
return exitWithExpectedError('No tag key was provided');
|
return exitWithExpectedError('No tag key was provided');
|
||||||
@ -250,7 +250,7 @@ export const remove: CommandDefinition<
|
|||||||
async action(params, options) {
|
async action(params, options) {
|
||||||
const _ = await import('lodash');
|
const _ = await import('lodash');
|
||||||
const balena = getBalenaSdk();
|
const balena = getBalenaSdk();
|
||||||
const { exitWithExpectedError } = await import('../utils/patterns');
|
const { exitWithExpectedError } = await import('../errors');
|
||||||
|
|
||||||
if (_.isEmpty(params.tagKey)) {
|
if (_.isEmpty(params.tagKey)) {
|
||||||
return exitWithExpectedError('No tag key was provided');
|
return exitWithExpectedError('No tag key was provided');
|
||||||
|
@ -22,7 +22,7 @@ import * as events from './events';
|
|||||||
|
|
||||||
capitano.permission('user', done =>
|
capitano.permission('user', done =>
|
||||||
require('./utils/patterns')
|
require('./utils/patterns')
|
||||||
.exitIfNotLoggedIn()
|
.checkLoggedIn()
|
||||||
.then(done, done),
|
.then(done, done),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
Copyright 2016-2020 Balena
|
Copyright 2016-2020 Balena Ltd.
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -18,6 +18,8 @@ import { stripIndent } from 'common-tags';
|
|||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import { TypedError } from 'typed-error';
|
import { TypedError } from 'typed-error';
|
||||||
|
import { getChalk } from './utils/lazy';
|
||||||
|
import { getHelp } from './utils/messages';
|
||||||
|
|
||||||
export class ExpectedError extends TypedError {}
|
export class ExpectedError extends TypedError {}
|
||||||
|
|
||||||
@ -130,8 +132,6 @@ const messages: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export async function handleError(error: any) {
|
export async function handleError(error: any) {
|
||||||
const { printErrorMessage } = await import('./utils/patterns');
|
|
||||||
|
|
||||||
process.exitCode =
|
process.exitCode =
|
||||||
error.exitCode === 0
|
error.exitCode === 0
|
||||||
? 0
|
? 0
|
||||||
@ -145,7 +145,7 @@ export async function handleError(error: any) {
|
|||||||
const message = [interpret(error)];
|
const message = [interpret(error)];
|
||||||
|
|
||||||
if (process.env.DEBUG && error.stack) {
|
if (process.env.DEBUG && error.stack) {
|
||||||
message.push(error.stack);
|
message.push('\n' + error.stack);
|
||||||
}
|
}
|
||||||
printErrorMessage(message.join('\n'));
|
printErrorMessage(message.join('\n'));
|
||||||
|
|
||||||
@ -171,3 +171,29 @@ export async function handleError(error: any) {
|
|||||||
// The exit error code was set above through `process.exitCode`.
|
// The exit error code was set above through `process.exitCode`.
|
||||||
process.exit();
|
process.exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function printErrorMessage(message: string) {
|
||||||
|
const chalk = getChalk();
|
||||||
|
console.error(chalk.red(message));
|
||||||
|
console.error(chalk.red(`\n${getHelp}\n`));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print a friendly error message and exit the CLI with an error code, BYPASSING
|
||||||
|
* error reporting through Sentry.io's platform (raven.Raven.captureException).
|
||||||
|
* Note that lib/errors.ts provides top-level error handling code to catch any
|
||||||
|
* otherwise uncaught errors, AND to report them through Sentry.io. But many
|
||||||
|
* "expected" errors (say, a JSON parsing error in a file provided by the user)
|
||||||
|
* don't warrant reporting through Sentry.io. For such mundane errors, catch
|
||||||
|
* them and call this function.
|
||||||
|
*
|
||||||
|
* DEPRECATED: Use `throw new ExpectedError(<message>)` instead.
|
||||||
|
*/
|
||||||
|
export function exitWithExpectedError(message: string | Error): never {
|
||||||
|
if (message instanceof Error) {
|
||||||
|
({ message } = message);
|
||||||
|
}
|
||||||
|
|
||||||
|
printErrorMessage(message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
import { stripIndent } from 'common-tags';
|
import { stripIndent } from 'common-tags';
|
||||||
|
|
||||||
import { exitWithExpectedError } from './utils/patterns';
|
import { exitWithExpectedError } from './errors';
|
||||||
|
|
||||||
export interface AppOptions {
|
export interface AppOptions {
|
||||||
// Prevent the default behavior of flushing stdout after running a command
|
// Prevent the default behavior of flushing stdout after running a command
|
||||||
|
@ -74,7 +74,7 @@ async function environmentFromInput(
|
|||||||
serviceNames: string[],
|
serviceNames: string[],
|
||||||
logger: Logger,
|
logger: Logger,
|
||||||
): Promise<ParsedEnvironment> {
|
): Promise<ParsedEnvironment> {
|
||||||
const { exitWithExpectedError } = await import('../patterns');
|
const { exitWithExpectedError } = await import('../../errors');
|
||||||
// A normal environment variable regex, with an added part
|
// A normal environment variable regex, with an added part
|
||||||
// to find a colon followed servicename at the start
|
// to find a colon followed servicename at the start
|
||||||
const varRegex = /^(?:([^\s:]+):)?([^\s]+?)=(.*)$/;
|
const varRegex = /^(?:([^\s:]+):)?([^\s]+?)=(.*)$/;
|
||||||
@ -121,7 +121,7 @@ async function environmentFromInput(
|
|||||||
|
|
||||||
export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
|
export async function deployToDevice(opts: DeviceDeployOptions): Promise<void> {
|
||||||
const { tarDirectory } = await import('../compose');
|
const { tarDirectory } = await import('../compose');
|
||||||
const { exitWithExpectedError } = await import('../patterns');
|
const { exitWithExpectedError } = await import('../../errors');
|
||||||
const { displayDeviceLogs } = await import('./logs');
|
const { displayDeviceLogs } = await import('./logs');
|
||||||
|
|
||||||
const api = new DeviceAPI(globalLogger, opts.deviceHost);
|
const api = new DeviceAPI(globalLogger, opts.deviceHost);
|
||||||
@ -574,7 +574,7 @@ export function generateTargetState(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function inspectBuildResults(images: LocalImage[]): Promise<void> {
|
async function inspectBuildResults(images: LocalImage[]): Promise<void> {
|
||||||
const { exitWithExpectedError } = await import('../patterns');
|
const { exitWithExpectedError } = await import('../../errors');
|
||||||
|
|
||||||
const failures: LocalPushErrors.BuildFailure[] = [];
|
const failures: LocalPushErrors.BuildFailure[] = [];
|
||||||
|
|
||||||
|
@ -270,7 +270,7 @@ export const createClient = function(opts) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var ensureDockerSeemsAccessible = function(docker) {
|
var ensureDockerSeemsAccessible = function(docker) {
|
||||||
const { exitWithExpectedError } = require('./patterns');
|
const { exitWithExpectedError } = require('../errors');
|
||||||
return docker
|
return docker
|
||||||
.ping()
|
.ping()
|
||||||
.catch(() =>
|
.catch(() =>
|
||||||
|
@ -20,9 +20,8 @@ import { stripIndent } from 'common-tags';
|
|||||||
import _ = require('lodash');
|
import _ = require('lodash');
|
||||||
import _form = require('resin-cli-form');
|
import _form = require('resin-cli-form');
|
||||||
|
|
||||||
import { instanceOf, NotLoggedInError } from '../errors';
|
import { exitWithExpectedError, instanceOf, NotLoggedInError } from '../errors';
|
||||||
import { getBalenaSdk, getChalk, getVisuals } from './lazy';
|
import { getBalenaSdk, getVisuals } from './lazy';
|
||||||
import messages = require('./messages');
|
|
||||||
import validation = require('./validation');
|
import validation = require('./validation');
|
||||||
|
|
||||||
const getForm = _.once((): typeof _form => require('resin-cli-form'));
|
const getForm = _.once((): typeof _form => require('resin-cli-form'));
|
||||||
@ -88,22 +87,6 @@ export async function checkLoggedIn(): Promise<void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if logged in, and call `exitWithExpectedError()` if not.
|
|
||||||
* DEPRECATED: Use checkLoggedIn() instead.
|
|
||||||
*/
|
|
||||||
export async function exitIfNotLoggedIn(): Promise<void> {
|
|
||||||
try {
|
|
||||||
await checkLoggedIn();
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof NotLoggedInError) {
|
|
||||||
exitWithExpectedError(error);
|
|
||||||
} else {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function askLoginType() {
|
export function askLoginType() {
|
||||||
return getForm().ask<'web' | 'credentials' | 'token' | 'register'>({
|
return getForm().ask<'web' | 'credentials' | 'token' | 'register'>({
|
||||||
message: 'How would you like to login?',
|
message: 'How would you like to login?',
|
||||||
@ -435,29 +418,3 @@ export function selectFromList<T>(
|
|||||||
})),
|
})),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function printErrorMessage(message: string) {
|
|
||||||
const chalk = getChalk();
|
|
||||||
console.error(chalk.red(message));
|
|
||||||
console.error(chalk.red(`\n${messages.getHelp}\n`));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Print a friendly error message and exit the CLI with an error code, BYPASSING
|
|
||||||
* error reporting through Sentry.io's platform (raven.Raven.captureException).
|
|
||||||
* Note that lib/errors.ts provides top-level error handling code to catch any
|
|
||||||
* otherwise uncaught errors, AND to report them through Sentry.io. But many
|
|
||||||
* "expected" errors (say, a JSON parsing error in a file provided by the user)
|
|
||||||
* don't warrant reporting through Sentry.io. For such mundane errors, catch
|
|
||||||
* them and call this function.
|
|
||||||
*
|
|
||||||
* DEPRECATED: Use `throw new ExpectedError(<message>)` instead.
|
|
||||||
*/
|
|
||||||
export function exitWithExpectedError(message: string | Error): never {
|
|
||||||
if (message instanceof Error) {
|
|
||||||
({ message } = message);
|
|
||||||
}
|
|
||||||
|
|
||||||
printErrorMessage(message);
|
|
||||||
process.exit(1);
|
|
||||||
}
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
import * as BalenaSdk from 'balena-sdk';
|
import * as BalenaSdk from 'balena-sdk';
|
||||||
import { stripIndent } from 'common-tags';
|
import { stripIndent } from 'common-tags';
|
||||||
|
|
||||||
import { ExpectedError } from '../errors';
|
import { ExpectedError, printErrorMessage } from '../errors';
|
||||||
import { getVisuals } from './lazy';
|
import { getVisuals } from './lazy';
|
||||||
import Logger = require('./logger');
|
import Logger = require('./logger');
|
||||||
import { exec, execBuffered, getDeviceOsRelease } from './ssh';
|
import { exec, execBuffered, getDeviceOsRelease } from './ssh';
|
||||||
@ -325,7 +325,6 @@ async function createApplication(
|
|||||||
): Promise<BalenaSdk.Application> {
|
): Promise<BalenaSdk.Application> {
|
||||||
const form = await import('resin-cli-form');
|
const form = await import('resin-cli-form');
|
||||||
const validation = await import('./validation');
|
const validation = await import('./validation');
|
||||||
const patterns = await import('./patterns');
|
|
||||||
|
|
||||||
let username = await sdk.auth.whoami();
|
let username = await sdk.auth.whoami();
|
||||||
if (!username) {
|
if (!username) {
|
||||||
@ -352,7 +351,9 @@ async function createApplication(
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
patterns.printErrorMessage(
|
// TODO: This is the only example in the codebase where `printErrorMessage()`
|
||||||
|
// is called directly. Consider refactoring.
|
||||||
|
printErrorMessage(
|
||||||
'You already have an application with that name; please choose another.',
|
'You already have an application with that name; please choose another.',
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
|
@ -24,7 +24,7 @@ import streamToPromise = require('stream-to-promise');
|
|||||||
import { Pack } from 'tar-stream';
|
import { Pack } from 'tar-stream';
|
||||||
|
|
||||||
import { ExpectedError } from '../errors';
|
import { ExpectedError } from '../errors';
|
||||||
import { exitWithExpectedError } from '../utils/patterns';
|
import { exitWithExpectedError } from '../errors';
|
||||||
import { tarDirectory } from './compose';
|
import { tarDirectory } from './compose';
|
||||||
import { getVisuals } from './lazy';
|
import { getVisuals } from './lazy';
|
||||||
import Logger = require('./logger');
|
import Logger = require('./logger');
|
||||||
|
Loading…
Reference in New Issue
Block a user