balena-cli
Version:
The official balena Command Line Interface
216 lines (213 loc) • 8.41 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.printExpectedErrorMessage = exports.printErrorMessage = exports.getSentry = exports.SIGINTError = exports.NoPortsDefinedError = exports.InvalidPortMappingError = exports.NotAvailableInOfflineModeError = exports.InsufficientPrivilegesError = exports.NotLoggedInError = exports.ExpectedError = void 0;
exports.instanceOf = instanceOf;
exports.handleError = handleError;
const os = require("os");
const typed_error_1 = require("typed-error");
const lazy_1 = require("./utils/lazy");
const messages_1 = require("./utils/messages");
const bootstrap_1 = require("./utils/bootstrap");
class ExpectedError extends typed_error_1.TypedError {
}
exports.ExpectedError = ExpectedError;
class NotLoggedInError extends ExpectedError {
}
exports.NotLoggedInError = NotLoggedInError;
class InsufficientPrivilegesError extends ExpectedError {
}
exports.InsufficientPrivilegesError = InsufficientPrivilegesError;
class NotAvailableInOfflineModeError extends ExpectedError {
}
exports.NotAvailableInOfflineModeError = NotAvailableInOfflineModeError;
class InvalidPortMappingError extends ExpectedError {
constructor(mapping) {
super(`'${mapping}' is not a valid port mapping.`);
}
}
exports.InvalidPortMappingError = InvalidPortMappingError;
class NoPortsDefinedError extends ExpectedError {
constructor() {
super('No ports have been provided.');
}
}
exports.NoPortsDefinedError = NoPortsDefinedError;
class SIGINTError extends ExpectedError {
}
exports.SIGINTError = SIGINTError;
function instanceOf(err, klass) {
var _a;
if (err instanceof klass) {
return true;
}
const name = err.name || ((_a = err.constructor) === null || _a === void 0 ? void 0 : _a.name);
return name != null && name === klass.name;
}
function hasCode(error) {
return error.code != null;
}
function treatFailedBindingAsMissingModule(error) {
if (error.message.startsWith('Could not locate the bindings file.')) {
error.code = 'MODULE_NOT_FOUND';
}
}
function interpret(error) {
treatFailedBindingAsMissingModule(error);
if (hasCode(error)) {
const errorCodeHandler = messages[error.code];
const message = errorCodeHandler === null || errorCodeHandler === void 0 ? void 0 : errorCodeHandler(error);
if (message) {
return message;
}
if (typeof error.message === 'string' && error.message.length > 0) {
return `${error.code}: ${error.message}`;
}
}
return error.message;
}
function loadDataDirectory() {
try {
const settings = new bootstrap_1.CliSettings();
return settings.get('dataDirectory');
}
catch (_a) {
return os.platform() === 'win32'
? 'C:\\Users\\<user>\\_balena'
: '$HOME/.balena';
}
}
const messages = {
EISDIR: (error) => `File is a directory: ${error.path}`,
ENOENT: (error) => `No such file or directory: ${error.path}`,
ENOGIT: () => (0, lazy_1.stripIndent) `
Git is not installed on this system.
Head over to http://git-scm.com to install it and run this command again.`,
EPERM: () => (0, lazy_1.stripIndent) `
You don't have sufficient privileges to run this operation.
${os.platform() === 'win32'
? 'Run a new Command Prompt as administrator and try running this command again.'
: 'Try running this command again prefixing it with `sudo`.'}
If this is not the case, and you're trying to burn an SDCard, check that the write lock is not set.`,
EACCES: (e) => messages.EPERM(e),
BalenaSettingsPermissionError: () => {
const dataDirectory = loadDataDirectory();
return (0, lazy_1.stripIndent) `
Error reading data directory: "${dataDirectory}"
This error usually indicates that the user doesn't have permissions over that directory,
which can happen if balena CLI was executed as the root user.
${os.platform() === 'win32'
? `Try resetting the ownership by opening a new Command Prompt as administrator and running: \`takeown /f ${dataDirectory} /r\``
: `Try resetting the ownership by running: \`sudo chown -R $(whoami) ${dataDirectory}\``}
`;
},
ETIMEDOUT: () => 'Oops something went wrong, please check your connection and try again.',
MODULE_NOT_FOUND: () => (0, lazy_1.stripIndent) `
Part of the CLI could not be loaded. This typically means your CLI install is in a broken state.
${os.arch() === 'x64'
? 'You can normally fix this by uninstalling and reinstalling the CLI.'
: (0, lazy_1.stripIndent) `
You're using an unsupported architecture (${os.arch()}), so this is typically caused by missing native modules.
Reinstalling may help, but pay attention to errors in native module build steps en route.
`}
`,
BalenaExpiredToken: () => (0, lazy_1.stripIndent) `
Looks like the session token has expired.
Try logging in again with the "balena login" command.`,
BalenaInvalidDeviceType: (error) => {
if (typeof error.type === 'string' &&
error.type.startsWith('Incompatible ')) {
return error.type;
}
const slug = error.deviceTypeSlug ? `"${error.deviceTypeSlug}"` : 'slug';
return (0, lazy_1.stripIndent) `
Device type ${slug} not recognized. Perhaps misspelled?
Check available device types with "balena device-type list"`;
},
};
const EXPECTED_ERROR_REGEXES = [
/cannot also be provided when using/,
/^BalenaSettingsPermissionError/,
/^BalenaAmbiguousApplication/,
/^BalenaAmbiguousDevice/,
/^BalenaApplicationNotFound/,
/^BalenaDeviceNotFound/,
/^BalenaExpiredToken/,
/^BalenaInvalidDeviceType/,
/^BalenaError: Release asset/,
/^Release asset not found/,
/Cannot deactivate devices/i,
/Devices must be offline in order to be deactivated\.$/i,
/^BalenaOrganizationNotFound/,
/Request error: Unauthorized$/,
/^Missing \d+ required arg/,
/^Unexpected argument/,
/to be one of/,
/must also be provided when using/,
/^Expected an integer/,
/^Flag .* expects a value/,
/^Error parsing config file.*balenarc\.yml/,
];
const getSentry = async function () {
return await Promise.resolve().then(() => require('@sentry/node'));
};
exports.getSentry = getSentry;
async function sentryCaptureException(error) {
const Sentry = await (0, exports.getSentry)();
Sentry.captureException(error);
try {
await Sentry.close(1000);
}
catch (_a) {
if (process.env.DEBUG) {
console.error('[debug] Timeout reporting error to sentry.io');
}
}
}
async function handleError(error) {
var _a;
if (typeof error === 'string') {
error = new Error(error);
}
const truncatedCode = Math.trunc(error.exitCode);
process.exitCode =
error.exitCode === 0
? 0
: Number.isFinite(truncatedCode)
? truncatedCode
: ((_a = process.exitCode) !== null && _a !== void 0 ? _a : 1);
const message = [interpret(error)];
if (error.stack && process.env.DEBUG) {
message.push('\n' + error.stack);
}
const isExpectedError = error instanceof ExpectedError ||
EXPECTED_ERROR_REGEXES.some((re) => re.test(message[0])) ||
EXPECTED_ERROR_REGEXES.some((re) => re.test(error.code));
if (isExpectedError) {
(0, exports.printExpectedErrorMessage)(message.join('\n'));
}
else {
(0, exports.printErrorMessage)(message.join('\n'));
if (!process.env.BALENARC_NO_SENTRY) {
await sentryCaptureException(error);
}
}
if (error instanceof SIGINTError || !isExpectedError) {
process.exit();
}
}
const printErrorMessage = function (message) {
var _a;
const ux = (0, lazy_1.getCliUx)();
const messageLines = message.split('\n');
console.error(ux.colorize('red', (_a = messageLines.shift()) !== null && _a !== void 0 ? _a : ''));
messageLines.forEach((line) => {
console.error(line);
});
console.error(`\n${(0, messages_1.getHelp)()}\n`);
};
exports.printErrorMessage = printErrorMessage;
const printExpectedErrorMessage = function (message) {
console.error(`${message}\n`);
};
exports.printExpectedErrorMessage = printExpectedErrorMessage;
//# sourceMappingURL=errors.js.map