Skip to content

Commit

Permalink
Removes corvus in favor of sentry and analytics client
Browse files Browse the repository at this point in the history
Change-type: patch
Signed-off-by: Otavio Jacobi
  • Loading branch information
otaviojacobi committed Dec 22, 2022
1 parent 2ae6764 commit 9e157d8
Show file tree
Hide file tree
Showing 9 changed files with 487 additions and 575 deletions.
2 changes: 1 addition & 1 deletion .github/actions/publish/action.yml
Expand Up @@ -172,7 +172,7 @@ runs:
for target in ${TARGETS}; do
electron-builder ${ELECTRON_BUILDER_OS} ${target} ${ARCHITECTURE_FLAGS} \
--c.extraMetadata.analytics.sentry.token='${{ steps.sentry.outputs.dsn }}' \
--c.extraMetadata.analytics.mixpanel.token='balena-etcher' \
--c.extraMetadata.analytics.amplitude.token='balena-etcher' \
--c.extraMetadata.packageType="${target}"
find dist -type f -maxdepth 1
Expand Down
4 changes: 2 additions & 2 deletions docs/MAINTAINERS.md
Expand Up @@ -31,7 +31,7 @@ Releasing
- [Post release note to forums](https://forums.balena.io/c/etcher)
- [Submit Windows binaries to Symantec for whitelisting](#submitting-binaries-to-symantec)
- [Update the website](https://github.com/balena-io/etcher-homepage)
- Wait 2-3 hours for analytics (Sentry, Mixpanel) to trickle in and check for elevated error rates, or regressions
- Wait 2-3 hours for analytics (Sentry, amplitude) to trickle in and check for elevated error rates, or regressions
- If regressions arise; pull the release, and release a patched version, else:
- [Upload deb & rpm packages to Bintray](#uploading-packages-to-bintray)
- [Upload build artifacts to Amazon S3](#uploading-binaries-to-amazon-s3)
Expand All @@ -48,7 +48,7 @@ Make sure to set the analytics tokens when generating production release binarie

```bash
export ANALYTICS_SENTRY_TOKEN="xxxxxx"
export ANALYTICS_MIXPANEL_TOKEN="xxxxxx"
export ANALYTICS_AMPLITUDE_TOKEN="xxxxxx"
```

#### Linux
Expand Down
2 changes: 1 addition & 1 deletion docs/MANUAL-TESTING.md
Expand Up @@ -112,4 +112,4 @@ Analytics
- [ ] Disable analytics, open DevTools Network pane or a packet sniffer, and
check that no request is sent
- [ ] **Disable analytics, refresh application from DevTools (using Cmd-R or
F5), and check that initial events are not sent to Mixpanel**
F5), and check that initial events are not sent to Amplitude**
2 changes: 2 additions & 0 deletions lib/gui/app/app.ts
Expand Up @@ -296,6 +296,8 @@ driveScanner.start();

let popupExists = false;

analytics.initAnalytics();

window.addEventListener('beforeunload', async (event) => {
if (!flashState.isFlashing() || popupExists) {
analytics.logEvent('Close application', {
Expand Down
125 changes: 46 additions & 79 deletions lib/gui/app/modules/analytics.ts
Expand Up @@ -15,108 +15,75 @@
*/

import * as _ from 'lodash';
import * as resinCorvus from 'resin-corvus/browser';

import * as packageJSON from '../../../../package.json';
import { getConfig } from '../../../shared/utils';
import { Client, createClient, createNoopClient } from 'analytics-client';
import * as SentryRenderer from '@sentry/electron/renderer';
import * as settings from '../models/settings';
import { store } from '../models/store';
import * as packageJSON from '../../../../package.json';
import fetch from 'electron-fetch';

const DEFAULT_PROBABILITY = 0.1;

async function installCorvus(): Promise<void> {
const sentryToken =
(await settings.get('analyticsSentryToken')) ||
_.get(packageJSON, ['analytics', 'sentry', 'token']);
const mixpanelToken =
(await settings.get('analyticsMixpanelToken')) ||
_.get(packageJSON, ['analytics', 'mixpanel', 'token']);
resinCorvus.install({
services: {
sentry: sentryToken,
mixpanel: mixpanelToken,
},
options: {
release: packageJSON.version,
shouldReport: () => {
return settings.getSync('errorReporting');
},
mixpanelDeferred: true,
},
});
}

let mixpanelSample = DEFAULT_PROBABILITY;

let analyticsClient: Client;
/**
* @summary Init analytics configurations
*/
async function initConfig() {
await installCorvus();
let validatedConfig = null;
try {
const configUrl = await settings.get('configUrl');
const config = await getConfig(configUrl);
const mixpanel = _.get(config, ['analytics', 'mixpanel'], {});
mixpanelSample = mixpanel.probability || DEFAULT_PROBABILITY;
if (isClientEligible(mixpanelSample)) {
validatedConfig = validateMixpanelConfig(mixpanel);
}
} catch (err) {
resinCorvus.logException(err);
export const initAnalytics = _.once(async () => {
const dsn =
(await settings.get('analyticsSentryToken')) ||
_.get(packageJSON, ['analytics', 'sentry', 'token']);
if (dsn) {
SentryRenderer.init({ dsn });
}
resinCorvus.setConfigs({
mixpanel: validatedConfig,
});
}

initConfig();

/**
* @summary Check that the client is eligible for analytics
*/
function isClientEligible(probability: number) {
return Math.random() < probability;
}
const projectName =
(await settings.get('analyticsAmplitudeToken')) ||
_.get(packageJSON, ['analytics', 'amplitude', 'token']);

/**
* @summary Check that config has at least HTTP_PROTOCOL and api_host
*/
function validateMixpanelConfig(config: {
api_host?: string;
HTTP_PROTOCOL?: string;
}) {
const mixpanelConfig = {
api_host: 'https://api.mixpanel.com',
(global as any).fetch = fetch;
const clientConfig = {
projectName,
endpoint: 'data.balena-staging.com',
componentName: 'etcher',
componentVersion: packageJSON.version,
};
if (config.HTTP_PROTOCOL !== undefined && config.api_host !== undefined) {
mixpanelConfig.api_host = `${config.HTTP_PROTOCOL}://${config.api_host}`;
}
return mixpanelConfig;
}
analyticsClient = projectName
? createClient(clientConfig)
: createNoopClient();
});

/**
* @summary Log an event
*
* @description
* This function sends the debug message to product analytics services.
*/
export function logEvent(message: string, data: _.Dictionary<any> = {}) {
function reportAnalytics(message: string, data: _.Dictionary<any> = {}) {
const { applicationSessionUuid, flashingWorkflowUuid } = store
.getState()
.toJS();
resinCorvus.logEvent(message, {

analyticsClient.track(message, {
...data,
sample: mixpanelSample,
applicationSessionUuid,
flashingWorkflowUuid,
});
}

/**
* @summary Log an event
*
* @description
* This function sends the debug message to product analytics services.
*/
export async function logEvent(message: string, data: _.Dictionary<any> = {}) {
await initAnalytics();
const shouldReportAnalytics = await settings.get('errorReporting');
if (shouldReportAnalytics) {
reportAnalytics(message, data);
}
}

/**
* @summary Log an exception
*
* @description
* This function logs an exception to error reporting services.
*/
export const logException = resinCorvus.logException;
export async function logException(error: any) {
await initAnalytics();
console.error(error);
SentryRenderer.captureException(error);
}
13 changes: 13 additions & 0 deletions lib/gui/etcher.ts
Expand Up @@ -20,6 +20,7 @@ import { promises as fs } from 'fs';
import { platform } from 'os';
import * as path from 'path';
import * as semver from 'semver';
import * as _ from 'lodash';

import './app/i18n';

Expand All @@ -30,6 +31,8 @@ import * as settings from './app/models/settings';
import { logException } from './app/modules/analytics';
import { buildWindowMenu } from './menu';
import * as i18n from 'i18next';
import * as SentryMain from '@sentry/electron/main';
import * as packageJSON from '../../package.json';

const customProtocol = 'etcher';
const scheme = `${customProtocol}://`;
Expand Down Expand Up @@ -94,6 +97,14 @@ async function getCommandLineURL(argv: string[]): Promise<string | undefined> {
}
}

const initSentryMain = _.once(async () => {
const dsn =
(await settings.get('analyticsSentryToken')) ||
_.get(packageJSON, ['analytics', 'sentry', 'token']);

SentryMain.init({ dsn });
});

const sourceSelectorReady = new Promise((resolve) => {
electron.ipcMain.on('source-selector-ready', resolve);
});
Expand Down Expand Up @@ -190,6 +201,7 @@ async function createMainWindow() {
const page = mainWindow.webContents;

page.once('did-frame-finish-load', async () => {
console.log('packageUpdatable', packageUpdatable);
autoUpdater.on('error', (err) => {
logException(err);
});
Expand Down Expand Up @@ -230,6 +242,7 @@ electron.app.on('before-quit', () => {
});

async function main(): Promise<void> {
await initSentryMain();
if (!electron.app.requestSingleInstanceLock()) {
electron.app.quit();
} else {
Expand Down

0 comments on commit 9e157d8

Please sign in to comment.