diff --git a/lib/app-oclif.ts b/lib/app-oclif.ts deleted file mode 100644 index f824b83a..00000000 --- a/lib/app-oclif.ts +++ /dev/null @@ -1,61 +0,0 @@ -/** - * @license - * Copyright 2019 Balena Ltd. - * - * 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. - */ - -import { Main } from '@oclif/command'; - -import { trackPromise } from './hooks/prerun/track'; - -class CustomMain extends Main { - protected _helpOverride(): boolean { - // Disable oclif's default handler for the 'version' command - if (['-v', '--version', 'version'].includes(this.argv[0])) { - return false; - } else { - return super._helpOverride(); - } - } -} - -import type { AppOptions } from './preparser'; - -/** - * oclif CLI entrypoint - */ -export async function run(command: string[], options: AppOptions) { - const runPromise = CustomMain.run(command).then( - () => { - if (!options.noFlush) { - return require('@oclif/command/flush'); - } - }, - (error) => { - // oclif sometimes exits with ExitError code 0 (not an error) - // (Avoid `error instanceof ExitError` here for the reasons explained - // in the CONTRIBUTING.md file regarding the `instanceof` operator.) - if (error.oclif?.exit === 0) { - return; - } else { - throw error; - } - }, - ); - try { - await Promise.all([trackPromise, runPromise]); - } catch (err) { - await (await import('./errors')).handleError(err); - } -} diff --git a/lib/app.ts b/lib/app.ts index 433aff03..a5897bb2 100644 --- a/lib/app.ts +++ b/lib/app.ts @@ -1,6 +1,6 @@ /** * @license - * Copyright 2019 Balena Ltd. + * Copyright 2019-2020 Balena Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,78 +15,138 @@ * limitations under the License. */ +import * as packageJSON from '../package.json'; +import { CliSettings } from './utils/bootstrap'; +import { onceAsync, stripIndent } from './utils/lazy'; + /** - * CLI entrypoint, but see also `bin/balena` and `bin/balena-dev` which - * call this function. + * Sentry.io setup + * @see https://docs.sentry.io/error-reporting/quickstart/?platform=node */ +export const setupSentry = onceAsync(async () => { + const config = await import('./config'); + const Sentry = await import('@sentry/node'); + Sentry.init({ + dsn: config.sentryDsn, + release: packageJSON.version, + }); + Sentry.configureScope((scope) => { + scope.setExtras({ + is_pkg: !!(process as any).pkg, + node_version: process.version, + platform: process.platform, + }); + }); + return Sentry.getCurrentHub(); +}); + +async function checkNodeVersion() { + const validNodeVersions = packageJSON.engines.node; + if (!(await import('semver')).satisfies(process.version, validNodeVersions)) { + console.warn(stripIndent` + ------------------------------------------------------------------------------ + Warning: Node version "${process.version}" does not match required versions "${validNodeVersions}". + This may cause unexpected behavior. To upgrade Node, visit: + https://nodejs.org/en/download/ + ------------------------------------------------------------------------------ + `); + } +} + +/** Setup balena-sdk options that are shared with imported packages */ +function setupBalenaSdkSharedOptions(settings: CliSettings) { + const BalenaSdk = require('balena-sdk') as typeof import('balena-sdk'); + BalenaSdk.setSharedOptions({ + apiUrl: settings.get('apiUrl'), + dataDirectory: settings.get('dataDirectory'), + }); +} + +/** + * Addresses the console warning: + * (node:49500) MaxListenersExceededWarning: Possible EventEmitter memory + * leak detected. 11 error listeners added. Use emitter.setMaxListeners() to + * increase limit + */ +export function setMaxListeners(maxListeners: number) { + require('events').EventEmitter.defaultMaxListeners = maxListeners; +} + +/** Selected CLI initialization steps */ +async function init() { + if (process.env.BALENARC_NO_SENTRY) { + console.error(`WARN: disabling Sentry.io error reporting`); + } else { + await setupSentry(); + } + checkNodeVersion(); + + const settings = new CliSettings(); + + // Proxy setup should be done early on, before loading balena-sdk + await (await import('./utils/proxy')).setupGlobalHttpProxy(settings); + + setupBalenaSdkSharedOptions(settings); + + // check for CLI updates once a day + (await import('./utils/update')).notify(); +} + +/** Execute the oclif parser and the CLI command. */ +async function oclifRun( + command: string[], + options: import('./preparser').AppOptions, +) { + const { CustomMain } = await import('./utils/oclif-utils'); + const runPromise = CustomMain.run(command).then( + () => { + if (!options.noFlush) { + return require('@oclif/command/flush'); + } + }, + (error) => { + // oclif sometimes exits with ExitError code 0 (not an error) + // (Avoid `error instanceof ExitError` here for the reasons explained + // in the CONTRIBUTING.md file regarding the `instanceof` operator.) + if (error.oclif?.exit === 0) { + return; + } else { + throw error; + } + }, + ); + const { trackPromise } = await import('./hooks/prerun/track'); + await Promise.all([trackPromise, runPromise]); +} + +/** CLI entrypoint. Called by the `bin/balena` and `bin/balena-dev` scripts. */ export async function run( cliArgs = process.argv, options: import('./preparser').AppOptions = {}, ) { - // DEBUG set to falsy for negative values else is truthy - process.env.DEBUG = ['0', 'no', 'false', '', undefined].includes( - process.env.DEBUG?.toLowerCase(), - ) - ? '' - : '1'; - - // The 'pkgExec' special/internal command provides a Node.js interpreter - // for use of the standalone zip package. See pkgExec function. - if (cliArgs.length > 3 && cliArgs[2] === 'pkgExec') { - return pkgExec(cliArgs[3], cliArgs.slice(4)); - } - - const { globalInit } = await import('./app-common'); - const { preparseArgs, checkDeletedCommand } = await import('./preparser'); - - // globalInit() must be called very early on (before other imports) because - // it sets up Sentry error reporting, global HTTP proxy settings, balena-sdk - // shared options, and performs node version requirement checks. - await globalInit(); - - // Look for commands that have been removed and if so, exit with a notice - checkDeletedCommand(cliArgs.slice(2)); - - const args = await preparseArgs(cliArgs); - await (await import('./app-oclif')).run(args, options); - - // Windows fix: reading from stdin prevents the process from exiting - process.stdin.pause(); -} - -/** - * Implements the 'pkgExec' command, used as a way to provide a Node.js - * interpreter for child_process.spawn()-like operations when the CLI is - * executing as a standalone zip package (built-in Node interpreter) and - * the system may not have a separate Node.js installation. A present use - * case is a patched version of the 'windosu' package that requires a - * Node.js interpreter to spawn a privileged child process. - * - * @param modFunc Path to a JS module that will be executed via require(). - * The modFunc argument may optionally contain a function name separated - * by '::', for example '::main' in: - * 'C:\\snapshot\\balena-cli\\node_modules\\windosu\\lib\\pipe.js::main' - * in which case that function is executed in the require'd module. - * @param args Optional arguments to passed through process.argv and as - * arguments to the function specified via modFunc. - */ -async function pkgExec(modFunc: string, args: string[]) { - const [modPath, funcName] = modFunc.split('::'); - let replacedModPath = modPath; - const match = modPath - .replace(/\\/g, '/') - .match(/\/snapshot\/balena-cli\/(.+)/); - if (match) { - replacedModPath = `../${match[1]}`; - } - process.argv = [process.argv[0], process.argv[1], ...args]; try { - const mod: any = await import(replacedModPath); - if (funcName) { - await mod[funcName](...args); + const { normalizeEnvVars, pkgExec } = await import('./utils/bootstrap'); + normalizeEnvVars(); + + // The 'pkgExec' special/internal command provides a Node.js interpreter + // for use of the standalone zip package. See pkgExec function. + if (cliArgs.length > 3 && cliArgs[2] === 'pkgExec') { + return pkgExec(cliArgs[3], cliArgs.slice(4)); } + + await init(); + + const { preparseArgs, checkDeletedCommand } = await import('./preparser'); + + // Look for commands that have been removed and if so, exit with a notice + checkDeletedCommand(cliArgs.slice(2)); + + const args = await preparseArgs(cliArgs); + await oclifRun(args, options); } catch (err) { - console.error(`Error executing pkgExec "${modFunc}" [${args.join()}]`); - console.error(err); + await (await import('./errors')).handleError(err); + } finally { + // Windows fix: reading from stdin prevents the process from exiting + process.stdin.pause(); } } diff --git a/lib/errors.ts b/lib/errors.ts index 66e108c0..f4da8aca 100644 --- a/lib/errors.ts +++ b/lib/errors.ts @@ -162,6 +162,18 @@ export const getSentry = async function () { return await import('@sentry/node'); }; +async function sentryCaptureException(error: Error) { + const Sentry = await getSentry(); + Sentry.captureException(error); + try { + await Sentry.close(1000); + } catch (e) { + if (process.env.DEBUG) { + console.error('Timeout reporting error to sentry.io'); + } + } +} + export async function handleError(error: Error) { // Set appropriate exitCode process.exitCode = @@ -189,15 +201,10 @@ export async function handleError(error: Error) { printErrorMessage(message.join('\n')); // Report "unexpected" errors via Sentry.io - const Sentry = await getSentry(); - Sentry.captureException(error); - try { - await Sentry.close(1000); - } catch (e) { - if (process.env.DEBUG) { - console.error('Timeout reporting error to sentry.io'); - } + if (!process.env.BALENARC_NO_SENTRY) { + await sentryCaptureException(error); } + // Unhandled/unexpected error: ensure that the process terminates. // The exit error code was set above through `process.exitCode`. process.exit(); diff --git a/lib/events.ts b/lib/events.ts index a12e2de1..b427465a 100644 --- a/lib/events.ts +++ b/lib/events.ts @@ -49,10 +49,13 @@ interface CachedUsername { */ export async function trackCommand(commandSignature: string) { try { - const Sentry = await import('@sentry/node'); - Sentry.configureScope((scope) => { - scope.setExtra('command', commandSignature); - }); + let Sentry: typeof import('@sentry/node'); + if (!process.env.BALENARC_NO_SENTRY) { + Sentry = await import('@sentry/node'); + Sentry.configureScope((scope) => { + scope.setExtra('command', commandSignature); + }); + } const settings = await import('balena-settings-client'); const balenaUrl = settings.get('balenaUrl') as string; @@ -90,12 +93,14 @@ export async function trackCommand(commandSignature: string) { const mixpanel = getMixpanel(balenaUrl); - Sentry.configureScope((scope) => { - scope.setUser({ - id: username, - username, + if (!process.env.BALENARC_NO_SENTRY) { + Sentry!.configureScope((scope) => { + scope.setUser({ + id: username, + username, + }); }); - }); + } // Don't actually call mixpanel.track() while running test cases if (!process.env.BALENA_CLI_TEST_TYPE) { await mixpanel.track(`[CLI] ${commandSignature}`, { diff --git a/lib/utils/bootstrap.ts b/lib/utils/bootstrap.ts new file mode 100644 index 00000000..f05106fe --- /dev/null +++ b/lib/utils/bootstrap.ts @@ -0,0 +1,103 @@ +/** + * @license + * Copyright 2019-2020 Balena Ltd. + * + * 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. + */ + +/* + * THIS MODULE SHOULD NOT IMPORT / REQUIRE ANYTHING AT THE GLOBAL LEVEL. + * It is meant to contain elementary helper functions or classes that + * can be used very early on during CLI startup, before anything else + * like Sentry error reporting, preparser, oclif parser and the like. + */ + +export class CliSettings { + public readonly settings: any; + constructor() { + this.settings = require('balena-settings-client') as typeof import('balena-settings-client'); + } + + public get(name: string): T { + return this.settings.get(name); + } + + /** + * Like settings.get(), but return `undefined` instead of throwing an + * error if the setting is not found / not defined. + */ + public getCatch(name: string): T | undefined { + try { + return this.settings.get(name); + } catch (err) { + if (!/Setting not found/i.test(err.message)) { + throw err; + } + } + } +} + +export function parseBoolEnvVar(varName: string): boolean { + return !['0', 'no', 'false', '', undefined].includes( + process.env[varName]?.toLowerCase(), + ); +} + +export function normalizeEnvVar(varName: string) { + process.env[varName] = parseBoolEnvVar(varName) ? '1' : ''; +} + +const bootstrapVars = ['DEBUG', 'BALENARC_NO_SENTRY']; + +export function normalizeEnvVars(varNames: string[] = bootstrapVars) { + for (const varName of varNames) { + normalizeEnvVar(varName); + } +} + +/** + * Implements the 'pkgExec' command, used as a way to provide a Node.js + * interpreter for child_process.spawn()-like operations when the CLI is + * executing as a standalone zip package (built-in Node interpreter) and + * the system may not have a separate Node.js installation. A present use + * case is a patched version of the 'windosu' package that requires a + * Node.js interpreter to spawn a privileged child process. + * + * @param modFunc Path to a JS module that will be executed via require(). + * The modFunc argument may optionally contain a function name separated + * by '::', for example '::main' in: + * 'C:\\snapshot\\balena-cli\\node_modules\\windosu\\lib\\pipe.js::main' + * in which case that function is executed in the require'd module. + * @param args Optional arguments to passed through process.argv and as + * arguments to the function specified via modFunc. + */ +export async function pkgExec(modFunc: string, args: string[]) { + const [modPath, funcName] = modFunc.split('::'); + let replacedModPath = modPath; + const match = modPath + .replace(/\\/g, '/') + .match(/\/snapshot\/balena-cli\/(.+)/); + if (match) { + replacedModPath = `../${match[1]}`; + } + process.argv = [process.argv[0], process.argv[1], ...args]; + try { + const mod: any = await import(replacedModPath); + if (funcName) { + await mod[funcName](...args); + } + } catch (err) { + console.error(`Error executing pkgExec "${modFunc}" [${args.join()}]`); + console.error(err); + } +} diff --git a/lib/utils/oclif-utils.ts b/lib/utils/oclif-utils.ts index aeee4650..de1f9717 100644 --- a/lib/utils/oclif-utils.ts +++ b/lib/utils/oclif-utils.ts @@ -15,6 +15,7 @@ * limitations under the License. */ +import { Main } from '@oclif/command'; import type * as Config from '@oclif/config'; /** @@ -48,6 +49,17 @@ export class CommandHelp { } } +export class CustomMain extends Main { + protected _helpOverride(): boolean { + // Disable oclif's default handler for the 'version' command + if (['-v', '--version', 'version'].includes(this.argv[0])) { + return false; + } else { + return super._helpOverride(); + } + } +} + /** Convert e.g. 'env add NAME [VALUE]' to 'env add [value]' */ export function capitanoizeOclifUsage( oclifUsage: string | string[] | undefined, diff --git a/lib/app-common.ts b/lib/utils/proxy.ts similarity index 64% rename from lib/app-common.ts rename to lib/utils/proxy.ts index 2ae011d4..ebc7aa72 100644 --- a/lib/app-common.ts +++ b/lib/utils/proxy.ts @@ -15,71 +15,11 @@ * limitations under the License. */ -import * as packageJSON from '../package.json'; -import { onceAsync, stripIndent } from './utils/lazy'; - -class CliSettings { - public readonly settings: any; - constructor() { - this.settings = require('balena-settings-client') as typeof import('balena-settings-client'); - } - - public get(name: string): T { - return this.settings.get(name); - } - - /** - * Like settings.get(), but return `undefined` instead of throwing an - * error if the setting is not found / not defined. - */ - public getCatch(name: string): T | undefined { - try { - return this.settings.get(name); - } catch (err) { - if (!/Setting not found/i.test(err.message)) { - throw err; - } - } - } -} - -/** - * Sentry.io setup - * @see https://docs.sentry.io/error-reporting/quickstart/?platform=node - */ -export const setupSentry = onceAsync(async () => { - const config = await import('./config'); - const Sentry = await import('@sentry/node'); - Sentry.init({ - dsn: config.sentryDsn, - release: packageJSON.version, - }); - Sentry.configureScope((scope) => { - scope.setExtras({ - is_pkg: !!(process as any).pkg, - node_version: process.version, - platform: process.platform, - }); - }); - return Sentry.getCurrentHub(); -}); - -async function checkNodeVersion() { - const validNodeVersions = packageJSON.engines.node; - if (!(await import('semver')).satisfies(process.version, validNodeVersions)) { - console.warn(stripIndent` - ------------------------------------------------------------------------------ - Warning: Node version "${process.version}" does not match required versions "${validNodeVersions}". - This may cause unexpected behavior. To upgrade Node, visit: - https://nodejs.org/en/download/ - ------------------------------------------------------------------------------ - `); - } -} - import type { Options as GlobalTunnelNgConfig } from 'global-tunnel-ng'; export type { GlobalTunnelNgConfig }; +import { CliSettings } from './bootstrap'; + type ProxyConfig = string | GlobalTunnelNgConfig; /** @@ -104,7 +44,7 @@ type ProxyConfig = string | GlobalTunnelNgConfig; * 'localhost' and '127.0.0.1' are always excluded. If NO_PROXY is not defined, * default exclusion patterns are added for all private IPv4 address ranges. */ -async function setupGlobalHttpProxy(settings: CliSettings) { +export async function setupGlobalHttpProxy(settings: CliSettings) { // `global-tunnel-ng` accepts lowercase variables with higher precedence // than uppercase variables, but `global-agent` does not accept lowercase. // Set uppercase versions for backwards compatibility. @@ -207,37 +147,3 @@ export function makeUrlFromTunnelNgConfig(cfg: GlobalTunnelNgConfig): string { } return url; } - -function setupBalenaSdkSharedOptions(settings: CliSettings) { - // We don't yet use balena-sdk directly everywhere, but we set up shared - // options correctly so we can do safely in submodules - const BalenaSdk = require('balena-sdk') as typeof import('balena-sdk'); - BalenaSdk.setSharedOptions({ - apiUrl: settings.get('apiUrl'), - dataDirectory: settings.get('dataDirectory'), - }); -} - -/** - * Addresses the console warning: - * (node:49500) MaxListenersExceededWarning: Possible EventEmitter memory - * leak detected. 11 error listeners added. Use emitter.setMaxListeners() to - * increase limit - */ -export function setMaxListeners(maxListeners: number) { - require('events').EventEmitter.defaultMaxListeners = maxListeners; -} - -export async function globalInit() { - await setupSentry(); - checkNodeVersion(); - - const settings = new CliSettings(); - - // Proxy setup should be done early on, before loading balena-sdk - await setupGlobalHttpProxy(settings); - setupBalenaSdkSharedOptions(settings); - - // check for CLI updates once a day - (await import('./utils/update')).notify(); -} diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 80ffda61..208938c0 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -1250,107 +1250,69 @@ "resolved": "https://registry.npmjs.org/@resin.io/valid-email/-/valid-email-0.1.0.tgz", "integrity": "sha1-DnUwmoQ8AUqAqhSC+WmQYvL6UV0=" }, - "@sentry/apm": { - "version": "5.21.1", - "resolved": "https://registry.npmjs.org/@sentry/apm/-/apm-5.21.1.tgz", - "integrity": "sha512-mxMOCpeXULbQCC/f9SwPqW+g12mk3nWRNjeAUm5dyiKHY13agtQBSSYs4ROEH190YxmwTZr3vxhlR2jNSdSZcg==", - "requires": { - "@sentry/browser": "5.21.1", - "@sentry/hub": "5.21.1", - "@sentry/minimal": "5.21.1", - "@sentry/types": "5.21.1", - "@sentry/utils": "5.21.1", - "tslib": "^1.9.3" - }, - "dependencies": { - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - } - } - }, - "@sentry/browser": { - "version": "5.21.1", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.21.1.tgz", - "integrity": "sha512-sUxsW545klZxJE4iBAYQ8SuVS85HTOGNmIIIZWFUogB5oW3O0L+nJluXEqf/pHU82LnjDIzqsWCYQ0cRUaeYow==", - "requires": { - "@sentry/core": "5.21.1", - "@sentry/types": "5.21.1", - "@sentry/utils": "5.21.1", - "tslib": "^1.9.3" - }, - "dependencies": { - "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" - } - } - }, "@sentry/core": { - "version": "5.21.1", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.21.1.tgz", - "integrity": "sha512-Luulwx3GLUiY0gmHOhU+4eSga28Ce8DwoBcRq9GkGuhPu9r80057d5urxrDLp/leIZBXVvpY7tvmSN/rMtvF9w==", + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.25.0.tgz", + "integrity": "sha512-hY6Zmo7t/RV+oZuvXHP6nyAj/QnZr2jW0e7EbL5YKMV8q0vlnjcE0LgqFXme726OJemoLk67z+sQOJic/Ztehg==", "requires": { - "@sentry/hub": "5.21.1", - "@sentry/minimal": "5.21.1", - "@sentry/types": "5.21.1", - "@sentry/utils": "5.21.1", + "@sentry/hub": "5.25.0", + "@sentry/minimal": "5.25.0", + "@sentry/types": "5.25.0", + "@sentry/utils": "5.25.0", "tslib": "^1.9.3" }, "dependencies": { "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.0.tgz", + "integrity": "sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw==" } } }, "@sentry/hub": { - "version": "5.21.1", - "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.21.1.tgz", - "integrity": "sha512-x5i9Ggi5ZYMhBYL5kyTu2fUJ6owjKH2tgJL3UExoZdRyZkbLAFZb+DtfSnteWgQ6wriGfgPD3r/hAIEdaomk2A==", + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.25.0.tgz", + "integrity": "sha512-kOlOiJV8wMX50lYpzMlOXBoH7MNG0Ho4RTusdZnXZBaASq5/ljngDJkLr6uylNjceZQP21wzipCQajsJMYB7EQ==", "requires": { - "@sentry/types": "5.21.1", - "@sentry/utils": "5.21.1", + "@sentry/types": "5.25.0", + "@sentry/utils": "5.25.0", "tslib": "^1.9.3" }, "dependencies": { "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.0.tgz", + "integrity": "sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw==" } } }, "@sentry/minimal": { - "version": "5.21.1", - "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.21.1.tgz", - "integrity": "sha512-OBVPASZ+mcXMKajvJon9RjEZ+ny3+VGhOI66acoP1hmYxKvji1OC2bYEuP1r4qtHxWVLAdV7qFj3EQ9ckErZmQ==", + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.25.0.tgz", + "integrity": "sha512-9JFKuW7U+1vPO86k3+XRtJyooiVZsVOsFFO4GulBzepi3a0ckNyPgyjUY1saLH+cEHx18hu8fGgajvI8ANUF2g==", "requires": { - "@sentry/hub": "5.21.1", - "@sentry/types": "5.21.1", + "@sentry/hub": "5.25.0", + "@sentry/types": "5.25.0", "tslib": "^1.9.3" }, "dependencies": { "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.0.tgz", + "integrity": "sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw==" } } }, "@sentry/node": { - "version": "5.21.1", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.21.1.tgz", - "integrity": "sha512-+QLqGz6+/gtShv0F16nI2+AuVEDZG2k9L25BVCNoysYzH1J1/QIKHsl7YF2trDMlWM4T7cbu5Fh8AhK6an+5/g==", + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.25.0.tgz", + "integrity": "sha512-zxoUVdAFTeK9kdEGY95TMs6g8Zx/P55HxG4gHD80BG/XIEvWiGPcGCLOspO4IdGqYXkGS74KfBOIXmmCawWwLg==", "requires": { - "@sentry/apm": "5.21.1", - "@sentry/core": "5.21.1", - "@sentry/hub": "5.21.1", - "@sentry/types": "5.21.1", - "@sentry/utils": "5.21.1", + "@sentry/core": "5.25.0", + "@sentry/hub": "5.25.0", + "@sentry/tracing": "5.25.0", + "@sentry/types": "5.25.0", + "@sentry/utils": "5.25.0", "cookie": "^0.4.1", "https-proxy-agent": "^5.0.0", "lru_map": "^0.3.3", @@ -1358,30 +1320,49 @@ }, "dependencies": { "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.0.tgz", + "integrity": "sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw==" } } }, - "@sentry/types": { - "version": "5.21.1", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.21.1.tgz", - "integrity": "sha512-hFN4aDduMpjj6vZSIIp+9kSr8MglcKO/UmbuUXN6hKLewhxt+Zj2wjXN7ulSs5OK5mjXP9QLA5YJvVQsl2//qw==" - }, - "@sentry/utils": { - "version": "5.21.1", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.21.1.tgz", - "integrity": "sha512-p5vPuc7+GfOmW8CXxWd0samS77Q00YrN8q5TC/ztF8nBhEF18GiMeWAdQnlSwt3iWal3q3gSSrbF4c9guIugng==", + "@sentry/tracing": { + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.25.0.tgz", + "integrity": "sha512-KcyHEGFpqSDubHrdWT/vF2hKkjw/ts6NpJ6tPDjBXUNz98BHdAyMKtLOFTCeJFply7/s5fyiAYu44M+M6IG3Bw==", "requires": { - "@sentry/types": "5.21.1", + "@sentry/hub": "5.25.0", + "@sentry/minimal": "5.25.0", + "@sentry/types": "5.25.0", + "@sentry/utils": "5.25.0", "tslib": "^1.9.3" }, "dependencies": { "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==" + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.0.tgz", + "integrity": "sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw==" + } + } + }, + "@sentry/types": { + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.25.0.tgz", + "integrity": "sha512-8M4PREbcar+15wrtEqcwfcU33SS+2wBSIOd/NrJPXJPTYxi49VypCN1mZBDyWkaK+I+AuQwI3XlRPCfsId3D1A==" + }, + "@sentry/utils": { + "version": "5.25.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.25.0.tgz", + "integrity": "sha512-Hz5spdIkMSRH5NR1YFOp5qbsY5Ud2lKhEQWlqxcVThMG5YNUc10aYv5ijL19v0YkrC2rqPjCRm7GrVtzOc7bXQ==", + "requires": { + "@sentry/types": "5.25.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.0.tgz", + "integrity": "sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw==" } } }, diff --git a/package.json b/package.json index 7628baa9..c4d6133e 100644 --- a/package.json +++ b/package.json @@ -197,7 +197,7 @@ "@balena/es-version": "^1.0.0", "@oclif/command": "^1.8.0", "@resin.io/valid-email": "^0.1.0", - "@sentry/node": "^5.21.1", + "@sentry/node": "^5.25.0", "@types/fast-levenshtein": "0.0.1", "@types/update-notifier": "^4.1.1", "@zeit/dockerignore": "0.0.3", diff --git a/tests/helpers.ts b/tests/helpers.ts index 4fbad73f..26830a39 100644 --- a/tests/helpers.ts +++ b/tests/helpers.ts @@ -23,7 +23,6 @@ import * as nock from 'nock'; import * as path from 'path'; import * as balenaCLI from '../build/app'; -import { setupSentry } from '../build/app-common'; const balenaExe = process.platform === 'win32' ? 'balena.exe' : 'balena'; const standalonePath = path.resolve(__dirname, '..', 'build-bin', balenaExe); @@ -287,7 +286,7 @@ export function fillTemplateArray( export async function switchSentry( enabled: boolean | undefined, ): Promise { - const sentryOpts = (await setupSentry()).getClient()?.getOptions(); + const sentryOpts = (await balenaCLI.setupSentry()).getClient()?.getOptions(); if (sentryOpts) { const sentryStatus = sentryOpts.enabled; sentryOpts.enabled = enabled; diff --git a/tests/app-common.spec.ts b/tests/proxy.spec.ts similarity index 97% rename from tests/app-common.spec.ts rename to tests/proxy.spec.ts index 25b73590..aa1d41d6 100644 --- a/tests/app-common.spec.ts +++ b/tests/proxy.spec.ts @@ -20,7 +20,7 @@ import { expect } from 'chai'; import { GlobalTunnelNgConfig, makeUrlFromTunnelNgConfig, -} from '../build/app-common'; +} from '../build/utils/proxy'; describe('makeUrlFromTunnelNgConfig() function', function () { it('should return a URL given a GlobalTunnelNgConfig object', () => {