Skip to content

Commit 10caf8f

Browse files
committed
Removes corvus in favor of sentry and analytics client
Change-type: patch Signed-off-by: Otavio Jacobi
1 parent d5ba1ea commit 10caf8f

File tree

9 files changed

+606
-686
lines changed

9 files changed

+606
-686
lines changed

.github/actions/publish/action.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ runs:
172172
for target in ${TARGETS}; do
173173
electron-builder ${ELECTRON_BUILDER_OS} ${target} ${ARCHITECTURE_FLAGS} \
174174
--c.extraMetadata.analytics.sentry.token='${{ steps.sentry.outputs.dsn }}' \
175-
--c.extraMetadata.analytics.mixpanel.token='balena-etcher' \
175+
--c.extraMetadata.analytics.amplitude.token='balena-etcher' \
176176
--c.extraMetadata.packageType="${target}"
177177
178178
find dist -type f -maxdepth 1

docs/MAINTAINERS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Releasing
3131
- [Post release note to forums](https://forums.balena.io/c/etcher)
3232
- [Submit Windows binaries to Symantec for whitelisting](#submitting-binaries-to-symantec)
3333
- [Update the website](https://github.com/balena-io/etcher-homepage)
34-
- Wait 2-3 hours for analytics (Sentry, Mixpanel) to trickle in and check for elevated error rates, or regressions
34+
- Wait 2-3 hours for analytics (Sentry, Amplitude) to trickle in and check for elevated error rates, or regressions
3535
- If regressions arise; pull the release, and release a patched version, else:
3636
- [Upload deb & rpm packages to Bintray](#uploading-packages-to-bintray)
3737
- [Upload build artifacts to Amazon S3](#uploading-binaries-to-amazon-s3)
@@ -48,7 +48,7 @@ Make sure to set the analytics tokens when generating production release binarie
4848

4949
```bash
5050
export ANALYTICS_SENTRY_TOKEN="xxxxxx"
51-
export ANALYTICS_MIXPANEL_TOKEN="xxxxxx"
51+
export ANALYTICS_AMPLITUDE_TOKEN="xxxxxx"
5252
```
5353

5454
#### Linux

docs/MANUAL-TESTING.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,4 @@ Analytics
112112
- [ ] Disable analytics, open DevTools Network pane or a packet sniffer, and
113113
check that no request is sent
114114
- [ ] **Disable analytics, refresh application from DevTools (using Cmd-R or
115-
F5), and check that initial events are not sent to Mixpanel**
115+
F5), and check that initial events are not sent to Amplitude**

lib/gui/app/app.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,8 @@ driveScanner.start();
296296

297297
let popupExists = false;
298298

299+
analytics.initAnalytics();
300+
299301
window.addEventListener('beforeunload', async (event) => {
300302
if (!flashState.isFlashing() || popupExists) {
301303
analytics.logEvent('Close application', {

lib/gui/app/modules/analytics.ts

Lines changed: 46 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -15,108 +15,74 @@
1515
*/
1616

1717
import * as _ from 'lodash';
18-
import * as resinCorvus from 'resin-corvus/browser';
19-
20-
import * as packageJSON from '../../../../package.json';
21-
import { getConfig } from '../../../shared/utils';
18+
import { Client, createClient, createNoopClient } from 'analytics-client';
19+
import * as SentryRenderer from '@sentry/electron/renderer';
2220
import * as settings from '../models/settings';
2321
import { store } from '../models/store';
22+
import * as packageJSON from '../../../../package.json';
2423

25-
const DEFAULT_PROBABILITY = 0.1;
26-
27-
async function installCorvus(): Promise<void> {
28-
const sentryToken =
29-
(await settings.get('analyticsSentryToken')) ||
30-
_.get(packageJSON, ['analytics', 'sentry', 'token']);
31-
const mixpanelToken =
32-
(await settings.get('analyticsMixpanelToken')) ||
33-
_.get(packageJSON, ['analytics', 'mixpanel', 'token']);
34-
resinCorvus.install({
35-
services: {
36-
sentry: sentryToken,
37-
mixpanel: mixpanelToken,
38-
},
39-
options: {
40-
release: packageJSON.version,
41-
shouldReport: () => {
42-
return settings.getSync('errorReporting');
43-
},
44-
mixpanelDeferred: true,
45-
},
46-
});
47-
}
48-
49-
let mixpanelSample = DEFAULT_PROBABILITY;
50-
24+
let analyticsClient: Client;
5125
/**
5226
* @summary Init analytics configurations
5327
*/
54-
async function initConfig() {
55-
await installCorvus();
56-
let validatedConfig = null;
57-
try {
58-
const configUrl = await settings.get('configUrl');
59-
const config = await getConfig(configUrl);
60-
const mixpanel = _.get(config, ['analytics', 'mixpanel'], {});
61-
mixpanelSample = mixpanel.probability || DEFAULT_PROBABILITY;
62-
if (isClientEligible(mixpanelSample)) {
63-
validatedConfig = validateMixpanelConfig(mixpanel);
64-
}
65-
} catch (err) {
66-
resinCorvus.logException(err);
67-
}
68-
resinCorvus.setConfigs({
69-
mixpanel: validatedConfig,
70-
});
71-
}
72-
73-
initConfig();
28+
export const initAnalytics = _.once(() => {
29+
const dsn =
30+
settings.getSync('analyticsSentryToken') ||
31+
_.get(packageJSON, ['analytics', 'sentry', 'token']);
32+
SentryRenderer.init({ dsn });
7433

75-
/**
76-
* @summary Check that the client is eligible for analytics
77-
*/
78-
function isClientEligible(probability: number) {
79-
return Math.random() < probability;
80-
}
34+
const projectName =
35+
settings.getSync('analyticsAmplitudeToken') ||
36+
_.get(packageJSON, ['analytics', 'amplitude', 'token']);
8137

82-
/**
83-
* @summary Check that config has at least HTTP_PROTOCOL and api_host
84-
*/
85-
function validateMixpanelConfig(config: {
86-
api_host?: string;
87-
HTTP_PROTOCOL?: string;
88-
}) {
89-
const mixpanelConfig = {
90-
api_host: 'https://api.mixpanel.com',
38+
const clientConfig = {
39+
projectName,
40+
endpoint: 'data.balena-cloud',
41+
componentName: 'etcher',
42+
componentVersion: packageJSON.version,
9143
};
92-
if (config.HTTP_PROTOCOL !== undefined && config.api_host !== undefined) {
93-
mixpanelConfig.api_host = `${config.HTTP_PROTOCOL}://${config.api_host}`;
94-
}
95-
return mixpanelConfig;
96-
}
44+
analyticsClient = projectName
45+
? createClient(clientConfig)
46+
: createNoopClient();
47+
});
9748

98-
/**
99-
* @summary Log an event
100-
*
101-
* @description
102-
* This function sends the debug message to product analytics services.
103-
*/
104-
export function logEvent(message: string, data: _.Dictionary<any> = {}) {
49+
function reportAnalytics(message: string, data: _.Dictionary<any> = {}) {
10550
const { applicationSessionUuid, flashingWorkflowUuid } = store
10651
.getState()
10752
.toJS();
108-
resinCorvus.logEvent(message, {
53+
54+
analyticsClient.track(message, {
10955
...data,
110-
sample: mixpanelSample,
11156
applicationSessionUuid,
11257
flashingWorkflowUuid,
11358
});
11459
}
11560

61+
/**
62+
* @summary Log an event
63+
*
64+
* @description
65+
* This function sends the debug message to product analytics services.
66+
*/
67+
export async function logEvent(message: string, data: _.Dictionary<any> = {}) {
68+
const shouldReportAnalytics = await settings.get('errorReporting');
69+
if (shouldReportAnalytics) {
70+
initAnalytics();
71+
reportAnalytics(message, data);
72+
}
73+
}
74+
11675
/**
11776
* @summary Log an exception
11877
*
11978
* @description
12079
* This function logs an exception to error reporting services.
12180
*/
122-
export const logException = resinCorvus.logException;
81+
export function logException(error: any) {
82+
const shouldReportErrors = settings.getSync('errorReporting');
83+
if (shouldReportErrors) {
84+
initAnalytics();
85+
console.error(error);
86+
SentryRenderer.captureException(error);
87+
}
88+
}

lib/gui/etcher.ts

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,16 +20,18 @@ import { promises as fs } from 'fs';
2020
import { platform } from 'os';
2121
import * as path from 'path';
2222
import * as semver from 'semver';
23+
import * as _ from 'lodash';
2324

2425
import './app/i18n';
2526

2627
import { packageType, version } from '../../package.json';
2728
import * as EXIT_CODES from '../shared/exit-codes';
2829
import { delay, getConfig } from '../shared/utils';
2930
import * as settings from './app/models/settings';
30-
import { logException } from './app/modules/analytics';
3131
import { buildWindowMenu } from './menu';
3232
import * as i18n from 'i18next';
33+
import * as SentryMain from '@sentry/electron/main';
34+
import * as packageJSON from '../../package.json';
3335

3436
const customProtocol = 'etcher';
3537
const scheme = `${customProtocol}://`;
@@ -53,13 +55,21 @@ async function checkForUpdates(interval: number) {
5355
packageUpdated = true;
5456
}
5557
} catch (err) {
56-
logException(err);
58+
logMainProcessException(err);
5759
}
5860
}
5961
await delay(interval);
6062
}
6163
}
6264

65+
function logMainProcessException(error: any) {
66+
const shouldReportErrors = settings.getSync('errorReporting');
67+
if (shouldReportErrors) {
68+
console.error(error);
69+
SentryMain.captureException(error);
70+
}
71+
}
72+
6373
async function isFile(filePath: string): Promise<boolean> {
6474
try {
6575
const stat = await fs.stat(filePath);
@@ -94,6 +104,14 @@ async function getCommandLineURL(argv: string[]): Promise<string | undefined> {
94104
}
95105
}
96106

107+
const initSentryMain = _.once(() => {
108+
const dsn =
109+
settings.getSync('analyticsSentryToken') ||
110+
_.get(packageJSON, ['analytics', 'sentry', 'token']);
111+
112+
SentryMain.init({ dsn });
113+
});
114+
97115
const sourceSelectorReady = new Promise((resolve) => {
98116
electron.ipcMain.on('source-selector-ready', resolve);
99117
});
@@ -190,8 +208,9 @@ async function createMainWindow() {
190208
const page = mainWindow.webContents;
191209

192210
page.once('did-frame-finish-load', async () => {
211+
console.log('packageUpdatable', packageUpdatable);
193212
autoUpdater.on('error', (err) => {
194-
logException(err);
213+
logMainProcessException(err);
195214
});
196215
if (packageUpdatable) {
197216
try {
@@ -208,7 +227,7 @@ async function createMainWindow() {
208227
onlineConfig?.autoUpdates?.checkForUpdatesTimer ?? 300000;
209228
checkForUpdates(checkForUpdatesTimer);
210229
} catch (err) {
211-
logException(err);
230+
logMainProcessException(err);
212231
}
213232
}
214233
});
@@ -233,6 +252,7 @@ async function main(): Promise<void> {
233252
if (!electron.app.requestSingleInstanceLock()) {
234253
electron.app.quit();
235254
} else {
255+
initSentryMain();
236256
await electron.app.whenReady();
237257
const window = await createMainWindow();
238258
electron.app.on('second-instance', async (_event, argv) => {
@@ -256,7 +276,6 @@ async function main(): Promise<void> {
256276
});
257277
}
258278
}
259-
260279
main();
261280

262281
console.time('ready-to-show');

0 commit comments

Comments
 (0)