From b77cb56cd04ba633f204640ec0b16e1edd18a993 Mon Sep 17 00:00:00 2001
From: Paulo Castro <paulo@balena.io>
Date: Wed, 4 Mar 2020 19:03:43 +0000
Subject: [PATCH 1/4] Fix occasionally missed command tracking request (oclif
 commands)

Change-type: patch
---
 lib/hooks/prerun/track.ts | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/lib/hooks/prerun/track.ts b/lib/hooks/prerun/track.ts
index 11722017..d5863ea4 100644
--- a/lib/hooks/prerun/track.ts
+++ b/lib/hooks/prerun/track.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.
@@ -16,8 +16,12 @@
  */
 import { Hook } from '@oclif/config';
 
+let trackResolve: (result: Promise<any>) => void;
+
 // note: trackPromise is subject to a Bluebird.timeout, defined in events.ts
-export let trackPromise: PromiseLike<void>;
+export const trackPromise = new Promise(resolve => {
+	trackResolve = resolve;
+});
 
 /**
  * This is an oclif 'prerun' hook. This hook runs after the command line is
@@ -38,7 +42,7 @@ const hook: Hook<'prerun'> = async function(options) {
 
 	// Intentionally do not await for the track promise here, in order to
 	// run the command tracking and the command itself in parallel.
-	trackPromise = events.trackCommand(cmdSignature);
+	trackResolve(events.trackCommand(cmdSignature));
 };
 
 export default hook;

From 36d3d1256ebd851e2edeab4380a97646126fb5ee Mon Sep 17 00:00:00 2001
From: Paulo Castro <paulo@balena.io>
Date: Thu, 5 Mar 2020 16:24:00 +0000
Subject: [PATCH 2/4] Don't send the full command line to Sentry.io

Resolves: #703
Change-type: patch
---
 lib/app-common.ts | 1 -
 1 file changed, 1 deletion(-)

diff --git a/lib/app-common.ts b/lib/app-common.ts
index baaa5b78..b38fe781 100644
--- a/lib/app-common.ts
+++ b/lib/app-common.ts
@@ -58,7 +58,6 @@ function setupRaven() {
 
 	Raven.setContext({
 		extra: {
-			args: process.argv,
 			node_version: process.version,
 		},
 	});

From d2df2c7b60e6024f3452c002184f7824f5b952f0 Mon Sep 17 00:00:00 2001
From: Paulo Castro <paulo@balena.io>
Date: Wed, 4 Mar 2020 16:31:54 +0000
Subject: [PATCH 3/4] Fix occasional "CLI prints 'null' and exits" (replace old
 Raven/Sentry SDK)

Resolves: #1523
Connects-to: #1333
Connects-to: #1193
Change-type: patch
---
 lib/app-common.ts   |  35 +++++-----
 lib/errors.ts       |  22 ++----
 lib/events.ts       |  61 +++++++++--------
 lib/global.d.ts     |   5 +-
 npm-shrinkwrap.json | 163 +++++++++++++++++++++++++++++++-------------
 package.json        |   6 +-
 6 files changed, 178 insertions(+), 114 deletions(-)

diff --git a/lib/app-common.ts b/lib/app-common.ts
index b38fe781..c59ec66e 100644
--- a/lib/app-common.ts
+++ b/lib/app-common.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,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();
 
diff --git a/lib/errors.ts b/lib/errors.ts
index d8c8daf4..a4e01a01 100644
--- a/lib/errors.ts
+++ b/lib/errors.ts
@@ -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();
 }
diff --git a/lib/events.ts b/lib/events.ts
index a5bd167d..5438de8b 100644
--- a/lib/events.ts
+++ b/lib/events.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.
@@ -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);
diff --git a/lib/global.d.ts b/lib/global.d.ts
index 5d702e1a..18a74982 100644
--- a/lib/global.d.ts
+++ b/lib/global.d.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.
@@ -20,6 +20,9 @@ interface Dictionary<T> {
 }
 
 declare module '*/package.json' {
+	export const engines: {
+		node: string;
+	};
 	export const name: string;
 	export const version: string;
 }
diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json
index f1b82a9e..c96556f0 100644
--- a/npm-shrinkwrap.json
+++ b/npm-shrinkwrap.json
@@ -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",
diff --git a/package.json b/package.json
index 0602091a..35bcb05b 100644
--- a/package.json
+++ b/package.json
@@ -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",

From 5a806543057338d617dbddcaf965bd46fe168347 Mon Sep 17 00:00:00 2001
From: Paulo Castro <paulo@balena.io>
Date: Thu, 5 Mar 2020 01:02:46 +0000
Subject: [PATCH 4/4] Avoid Sentry reporting of selected common "expected"
 errors

Change-type: patch
---
 lib/errors.ts             | 15 +++++++++++++--
 lib/utils/remote-build.ts |  8 ++++----
 2 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/lib/errors.ts b/lib/errors.ts
index a4e01a01..49af2d2c 100644
--- a/lib/errors.ts
+++ b/lib/errors.ts
@@ -17,8 +17,9 @@ limitations under the License.
 import { stripIndent } from 'common-tags';
 import * as _ from 'lodash';
 import * as os from 'os';
+import { TypedError } from 'typed-error';
 
-export class ExpectedError extends Error {}
+export class ExpectedError extends TypedError {}
 
 export class NotLoggedInError extends ExpectedError {}
 
@@ -115,7 +116,17 @@ export async function handleError(error: any) {
 	}
 	printErrorMessage(message.join('\n'));
 
-	if (error instanceof ExpectedError) {
+	const expectedErrorREs = [
+		/^BalenaApplicationNotFound:/, // balena-sdk
+		/^BalenaDeviceNotFound:/, // balena-sdk
+		/^Missing \w+$/, // Capitano's command line parsing error
+		/^Unexpected arguments?:/, // oclif's command line parsing error
+	];
+
+	if (
+		error instanceof ExpectedError ||
+		expectedErrorREs.some(re => re.test(message[0]))
+	) {
 		return;
 	}
 
diff --git a/lib/utils/remote-build.ts b/lib/utils/remote-build.ts
index b619092d..527163fb 100644
--- a/lib/utils/remote-build.ts
+++ b/lib/utils/remote-build.ts
@@ -22,12 +22,12 @@ import { RegistrySecrets } from 'resin-multibuild';
 import * as Stream from 'stream';
 import streamToPromise = require('stream-to-promise');
 import { Pack } from 'tar-stream';
-import { TypedError } from 'typed-error';
-import Logger = require('./logger');
 
+import { ExpectedError } from '../errors';
 import { exitWithExpectedError } from '../utils/patterns';
 import { tarDirectory } from './compose';
 import { getVisuals } from './lazy';
+import Logger = require('./logger');
 
 const globalLogger = Logger.getLogger();
 
@@ -77,7 +77,7 @@ interface HeadlessBuilderMessage {
 	releaseId?: number;
 }
 
-export class RemoteBuildFailedError extends TypedError {
+export class RemoteBuildFailedError extends ExpectedError {
 	public constructor(message = 'Remote build failed') {
 		super(message);
 	}
@@ -138,10 +138,10 @@ export async function startRemoteBuild(build: RemoteBuild): Promise<void> {
 			stream.on('end', resolve);
 			stream.on('error', reject);
 		}).then(() => {
+			globalLogger.outputDeferredMessages();
 			if (build.hadError) {
 				throw new RemoteBuildFailedError();
 			}
-			globalLogger.outputDeferredMessages();
 		});
 	}