Fix occasional "CLI prints 'null' and exits" (replace old Raven/Sentry SDK)

Resolves: #1523
Connects-to: #1333
Connects-to: #1193
Change-type: patch
This commit is contained in:
Paulo Castro 2020-03-04 16:31:54 +00:00
parent 36d3d1256e
commit d2df2c7b60
6 changed files with 178 additions and 114 deletions

View File

@ -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,6 +15,8 @@
* limitations under the License.
*/
import * as packageJSON from '../package.json';
class CliSettings {
public readonly settings: any;
constructor() {
@ -42,29 +44,26 @@ class CliSettings {
/**
* Sentry.io setup
* @see https://docs.sentry.io/clients/node/
* @see https://docs.sentry.io/error-reporting/quickstart/?platform=node
*/
function setupRaven() {
const Raven = require('raven');
Raven.disableConsoleAlerts();
Raven.config(require('./config').sentryDsn, {
captureUnhandledRejections: true,
autoBreadcrumbs: true,
release: require('../package.json').version,
}).install(function(_logged: any, error: Error) {
console.error(error);
process.exit(1);
async function setupSentry() {
const config = await import('./config');
const Sentry = await import('@sentry/node');
Sentry.init({
dsn: config.sentryDsn,
release: packageJSON.version,
});
Raven.setContext({
extra: {
Sentry.configureScope(scope => {
scope.setExtras({
is_pkg: !!(process as any).pkg,
node_version: process.version,
},
platform: process.platform,
});
});
}
function checkNodeVersion() {
const validNodeVersions = require('../package.json').engines.node;
const validNodeVersions = packageJSON.engines.node;
if (!require('semver').satisfies(process.version, validNodeVersions)) {
const { stripIndent } = require('common-tags');
console.warn(stripIndent`
@ -243,7 +242,7 @@ export function setMaxListeners(maxListeners: number) {
}
export async function globalInit() {
setupRaven();
await setupSentry();
checkNodeVersion();
configureBluebird();

View File

@ -1,5 +1,5 @@
/*
Copyright 2016-2019 Balena
Copyright 2016-2020 Balena
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -14,21 +14,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import * as Bluebird from 'bluebird';
import { stripIndent } from 'common-tags';
import * as _ from 'lodash';
import * as os from 'os';
import * as Raven from 'raven';
export class ExpectedError extends Error {}
export class NotLoggedInError extends ExpectedError {}
const captureException = Bluebird.promisify<string, Error>(
Raven.captureException,
{ context: Raven },
);
function hasCode(error: any): error is Error & { code: string } {
return error.code != null;
}
@ -127,11 +120,10 @@ export async function handleError(error: any) {
}
// Report "unexpected" errors via Sentry.io
await captureException(error)
.timeout(1000)
.catch(function() {
// Ignore any errors (from error logging, or timeouts)
})
// exit with the process.exitCode set earlier
.finally(() => process.exit());
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();
}

View File

@ -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.
@ -14,12 +14,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import Promise = require('bluebird');
import _ = require('lodash');
import Mixpanel = require('mixpanel');
import Raven = require('raven');
import * as Sentry from '@sentry/node';
import * as Bluebird from 'bluebird';
import * as _ from 'lodash';
import * as Mixpanel from 'mixpanel';
import packageJSON = require('../package.json');
import * as packageJSON from '../package.json';
import { getBalenaSdk } from './utils/lazy';
const getMixpanel = _.once<any>(() => {
@ -31,36 +31,43 @@ const getMixpanel = _.once<any>(() => {
});
});
/**
* Mixpanel.com analytics tracking (information on balena CLI usage).
*
* @param commandSignature A string like, for example:
* "push <applicationOrDevice>"
* That's literally so: "applicationOrDevice" is NOT replaced with the actual
* application ID or device ID. The purpose is to find out the most / least
* used command verbs, so we can focus our development effort where it is most
* beneficial to end users.
*
* The username and command signature are also added as extra context
* information in Sentry.io error reporting, for CLI debugging purposes
* (mainly unexpected/unhandled exceptions -- see also `lib/errors.ts`).
*/
export function trackCommand(commandSignature: string) {
const balena = getBalenaSdk();
return Promise.props({
return Bluebird.props({
balenaUrl: balena.settings.get('balenaUrl'),
username: balena.auth.whoami().catchReturn(undefined),
mixpanel: getMixpanel(),
})
.then(({ username, balenaUrl, mixpanel }) => {
return Promise.try(() => {
Raven.mergeContext({
user: {
id: username,
username,
},
});
// commandSignature is a string like, for example:
// "push <applicationOrDevice>"
// That's literally so: "applicationOrDevice" is NOT replaced with
// the actual application ID or device ID. The purpose is find out the
// most / least used command verbs, so we can focus our development
// effort where it is most beneficial to end users.
return mixpanel.track(`[CLI] ${commandSignature}`, {
distinct_id: username,
version: packageJSON.version,
node: process.version,
arch: process.arch,
balenaUrl, // e.g. 'balena-cloud.com' or 'balena-staging.com'
platform: process.platform,
Sentry.configureScope(scope => {
scope.setExtra('command', commandSignature);
scope.setUser({
id: username,
username,
});
});
return mixpanel.track(`[CLI] ${commandSignature}`, {
distinct_id: username,
version: packageJSON.version,
node: process.version,
arch: process.arch,
balenaUrl, // e.g. 'balena-cloud.com' or 'balena-staging.com'
platform: process.platform,
});
})
.timeout(100)
.catchReturn(undefined);

5
lib/global.d.ts vendored
View File

@ -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.
@ -20,6 +20,9 @@ interface Dictionary<T> {
}
declare module '*/package.json' {
export const engines: {
node: string;
};
export const name: string;
export const version: string;
}

163
npm-shrinkwrap.json generated
View File

@ -553,6 +553,113 @@
"resolved": "https://registry.npmjs.org/@resin.io/valid-email/-/valid-email-0.1.0.tgz",
"integrity": "sha1-DnUwmoQ8AUqAqhSC+WmQYvL6UV0="
},
"@sentry/apm": {
"version": "5.13.2",
"resolved": "https://registry.npmjs.org/@sentry/apm/-/apm-5.13.2.tgz",
"integrity": "sha512-Pv6PRVkcmmYYIT422gXm968F8YQyf5uN1RSHOFBjWsxI3Ke/uRgeEdIVKPDo78GklBfETyRN6GyLEZ555jRe6g==",
"requires": {
"@sentry/browser": "5.13.2",
"@sentry/hub": "5.13.2",
"@sentry/minimal": "5.13.2",
"@sentry/types": "5.13.2",
"@sentry/utils": "5.13.2",
"tslib": "^1.9.3"
}
},
"@sentry/browser": {
"version": "5.13.2",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.13.2.tgz",
"integrity": "sha512-4MeauHs8Rf1c2FF6n84wrvA4LexEL1K/Tg3r+1vigItiqyyyYBx1sPjHGZeKeilgBi+6IEV5O8sy30QIrA/NsQ==",
"requires": {
"@sentry/core": "5.13.2",
"@sentry/types": "5.13.2",
"@sentry/utils": "5.13.2",
"tslib": "^1.9.3"
}
},
"@sentry/core": {
"version": "5.13.2",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.13.2.tgz",
"integrity": "sha512-iB7CQSt9e0EJhSmcNOCjzJ/u7E7qYJ3mI3h44GO83n7VOmxBXKSvtUl9FpKFypbWrsdrDz8HihLgAZZoMLWpPA==",
"requires": {
"@sentry/hub": "5.13.2",
"@sentry/minimal": "5.13.2",
"@sentry/types": "5.13.2",
"@sentry/utils": "5.13.2",
"tslib": "^1.9.3"
}
},
"@sentry/hub": {
"version": "5.13.2",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.13.2.tgz",
"integrity": "sha512-/U7yq3DTuRz8SRpZVKAaenW9sD2F5wbj12kDVPxPnGspyqhy0wBWKs9j0YJfBiDXMKOwp3HX964O3ygtwjnfAw==",
"requires": {
"@sentry/types": "5.13.2",
"@sentry/utils": "5.13.2",
"tslib": "^1.9.3"
}
},
"@sentry/minimal": {
"version": "5.13.2",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.13.2.tgz",
"integrity": "sha512-VV0eA3HgrnN3mac1XVPpSCLukYsU+QxegbmpnZ8UL8eIQSZ/ZikYxagDNlZbdnmXHUpOEUeag2gxVntSCo5UcA==",
"requires": {
"@sentry/hub": "5.13.2",
"@sentry/types": "5.13.2",
"tslib": "^1.9.3"
}
},
"@sentry/node": {
"version": "5.13.2",
"resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.13.2.tgz",
"integrity": "sha512-LwNOUvc0+28jYfI0o4HmkDTEYdY3dWvSCnL5zggO12buon7Wc+jirXZbEQAx84HlXu7sGSjtKCTzUQOphv7sPw==",
"requires": {
"@sentry/apm": "5.13.2",
"@sentry/core": "5.13.2",
"@sentry/hub": "5.13.2",
"@sentry/types": "5.13.2",
"@sentry/utils": "5.13.2",
"cookie": "^0.3.1",
"https-proxy-agent": "^4.0.0",
"lru_map": "^0.3.3",
"tslib": "^1.9.3"
},
"dependencies": {
"agent-base": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-5.1.1.tgz",
"integrity": "sha512-TMeqbNl2fMW0nMjTEPOwe3J/PRFP4vqeoNuQMG0HlMrtm5QxKqdvAkZ1pRBQ/ulIyDD5Yq0nJ7YbdD8ey0TO3g=="
},
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
},
"https-proxy-agent": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-4.0.0.tgz",
"integrity": "sha512-zoDhWrkR3of1l9QAL8/scJZyLu8j/gBkcwcaQOZh7Gyh/+uJQzGVETdgT30akuwkpL8HTRfssqI3BZuV18teDg==",
"requires": {
"agent-base": "5",
"debug": "4"
}
}
}
},
"@sentry/types": {
"version": "5.13.2",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.13.2.tgz",
"integrity": "sha512-mgAEQyc77PYBnAjnslSXUz6aKgDlunlg2c2qSK/ivKlEkTgTWWW/dE76++qVdrqM8SupnqQoiXyPDL0wUNdB3g=="
},
"@sentry/utils": {
"version": "5.13.2",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.13.2.tgz",
"integrity": "sha512-LwPQl6WRMKEnd16kg35HS3yE+VhBc8vN4+BBIlrgs7X0aoT+AbEd/sQLMisDgxNboCF44Ho3RCKtztiPb9blqg==",
"requires": {
"@sentry/types": "5.13.2",
"tslib": "^1.9.3"
}
},
"@sinonjs/commons": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.0.tgz",
@ -905,15 +1012,6 @@
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==",
"dev": true
},
"@types/raven": {
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/@types/raven/-/raven-2.5.3.tgz",
"integrity": "sha512-k6vxiX5I6/GEqJS9mMYPVIgMJf/X26n09NfzuqBpdcEp684RIwpdrwCgSyJGuy8EaSG1wc2rFP7xVEAixPzw7Q==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/request": {
"version": "2.48.4",
"resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.4.tgz",
@ -2850,11 +2948,6 @@
"resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
"integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="
},
"charenc": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz",
"integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc="
},
"check-error": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
@ -3630,11 +3723,6 @@
}
}
},
"crypt": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz",
"integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs="
},
"crypto-random-string": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-1.0.0.tgz",
@ -8523,6 +8611,11 @@
"es5-ext": "~0.10.2"
}
},
"lru_map": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz",
"integrity": "sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0="
},
"lzma-native": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lzma-native/-/lzma-native-4.0.6.tgz",
@ -8697,16 +8790,6 @@
"chs": "^1.1.0"
}
},
"md5": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
"integrity": "sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=",
"requires": {
"charenc": "~0.0.1",
"crypt": "~0.0.1",
"is-buffer": "~1.1.1"
}
},
"media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@ -14814,25 +14897,6 @@
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="
},
"raven": {
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/raven/-/raven-2.6.4.tgz",
"integrity": "sha512-6PQdfC4+DQSFncowthLf+B6Hr0JpPsFBgTVYTAOq7tCmx/kR4SXbeawtPch20+3QfUcQDoJBLjWW1ybvZ4kXTw==",
"requires": {
"cookie": "0.3.1",
"md5": "^2.2.1",
"stack-trace": "0.0.10",
"timed-out": "4.0.1",
"uuid": "3.3.2"
},
"dependencies": {
"cookie": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
"integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s="
}
}
},
"raw-body": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz",
@ -16884,7 +16948,8 @@
"stack-trace": {
"version": "0.0.10",
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
"integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA="
"integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=",
"dev": true
},
"static-extend": {
"version": "0.1.2",

View File

@ -25,8 +25,7 @@
"scripts": [
"node_modules/balena-sync/build/capitano/*.js",
"node_modules/balena-sync/build/sync/*.js",
"node_modules/resin-compose-parse/build/schemas/*.json",
"node_modules/raven/lib/instrumentation/*.js"
"node_modules/resin-compose-parse/build/schemas/*.json"
],
"assets": [
"build/**/*.js",
@ -121,7 +120,6 @@
"@types/nock": "^11.0.7",
"@types/node": "^10.17.17",
"@types/prettyjson": "0.0.29",
"@types/raven": "^2.5.3",
"@types/request": "^2.48.4",
"@types/rewire": "^2.5.28",
"@types/rimraf": "^2.0.3",
@ -160,6 +158,7 @@
"@oclif/command": "^1.5.19",
"@oclif/errors": "^1.2.2",
"@resin.io/valid-email": "^0.1.0",
"@sentry/node": "^5.13.1",
"@zeit/dockerignore": "0.0.3",
"JSONStream": "^1.0.3",
"ansi-escapes": "^2.0.0",
@ -221,7 +220,6 @@
"patch-package": "6.1.2",
"prettyjson": "^1.1.3",
"progress-stream": "^2.0.0",
"raven": "^2.5.0",
"reconfix": "^0.1.0",
"request": "^2.88.2",
"resin-cli-form": "^2.0.1",