mirror of
https://github.com/balena-io/etcher.git
synced 2025-07-29 14:16:36 +00:00
Merge pull request #3891 from balena-io/removes-corvus
Removes corvus in favor of sentry and analytics client
This commit is contained in:
commit
7616c41564
30
.github/actions/publish/action.yml
vendored
30
.github/actions/publish/action.yml
vendored
@ -72,32 +72,6 @@ runs:
|
|||||||
chmod +x "$(dirname "$(which node)")/resinci-deploy" && which resinci-deploy
|
chmod +x "$(dirname "$(which node)")/resinci-deploy" && which resinci-deploy
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# FIXME: store sentry workflow is not documented
|
|
||||||
# https://github.com/product-os/resinci-deploy/blob/master/lib/sentry.ts
|
|
||||||
# https://github.com/getsentry/sentry-cli
|
|
||||||
# https://docs.sentry.io/api/projects/create-a-new-client-key/
|
|
||||||
- name: Generate Sentry DSN
|
|
||||||
id: sentry
|
|
||||||
shell: bash --noprofile --norc -eo pipefail -x {0}
|
|
||||||
run: |
|
|
||||||
set -ea
|
|
||||||
|
|
||||||
[[ '${{ inputs.VERBOSE }}' =~ on|On|Yes|yes|true|True ]] && set -x
|
|
||||||
|
|
||||||
branch="$(echo '${{ github.event.pull_request.head.ref }}' | sed 's/[^[:alnum:]]/-/g')"
|
|
||||||
|
|
||||||
stdout="$(resinci-deploy store sentry \
|
|
||||||
--branch="${branch}" \
|
|
||||||
--name="$(jq -r '.name' package.json)" \
|
|
||||||
--team="$(yq e '.sentry.team' repo.yml)" \
|
|
||||||
--org="$(yq e '.sentry.org' repo.yml)" \
|
|
||||||
--type="$(yq e '.sentry.type' repo.yml)")"
|
|
||||||
|
|
||||||
echo "dsn=$(echo "${stdout}" | tail -n 1)" >> $GITHUB_OUTPUT
|
|
||||||
|
|
||||||
env:
|
|
||||||
SENTRY_TOKEN: ${{ fromJSON(inputs.secrets).SENTRY_AUTH_TOKEN }}
|
|
||||||
|
|
||||||
# https://www.electron.build/code-signing.html
|
# https://www.electron.build/code-signing.html
|
||||||
# https://github.com/Apple-Actions/import-codesign-certs
|
# https://github.com/Apple-Actions/import-codesign-certs
|
||||||
- name: Import Apple code signing certificate
|
- name: Import Apple code signing certificate
|
||||||
@ -171,8 +145,8 @@ runs:
|
|||||||
|
|
||||||
for target in ${TARGETS}; do
|
for target in ${TARGETS}; do
|
||||||
electron-builder ${ELECTRON_BUILDER_OS} ${target} ${ARCHITECTURE_FLAGS} \
|
electron-builder ${ELECTRON_BUILDER_OS} ${target} ${ARCHITECTURE_FLAGS} \
|
||||||
--c.extraMetadata.analytics.sentry.token='${{ steps.sentry.outputs.dsn }}' \
|
--c.extraMetadata.analytics.sentry.token='https://739bbcfc0ba4481481138d3fc831136d@o95242.ingest.sentry.io/4504451487301632' \
|
||||||
--c.extraMetadata.analytics.mixpanel.token='balena-etcher' \
|
--c.extraMetadata.analytics.amplitude.token='balena-etcher' \
|
||||||
--c.extraMetadata.packageType="${target}"
|
--c.extraMetadata.packageType="${target}"
|
||||||
|
|
||||||
find dist -type f -maxdepth 1
|
find dist -type f -maxdepth 1
|
||||||
|
@ -31,7 +31,7 @@ Releasing
|
|||||||
- [Post release note to forums](https://forums.balena.io/c/etcher)
|
- [Post release note to forums](https://forums.balena.io/c/etcher)
|
||||||
- [Submit Windows binaries to Symantec for whitelisting](#submitting-binaries-to-symantec)
|
- [Submit Windows binaries to Symantec for whitelisting](#submitting-binaries-to-symantec)
|
||||||
- [Update the website](https://github.com/balena-io/etcher-homepage)
|
- [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:
|
- If regressions arise; pull the release, and release a patched version, else:
|
||||||
- [Upload deb & rpm packages to Bintray](#uploading-packages-to-bintray)
|
- [Upload deb & rpm packages to Bintray](#uploading-packages-to-bintray)
|
||||||
- [Upload build artifacts to Amazon S3](#uploading-binaries-to-amazon-s3)
|
- [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
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
export ANALYTICS_SENTRY_TOKEN="xxxxxx"
|
export ANALYTICS_SENTRY_TOKEN="xxxxxx"
|
||||||
export ANALYTICS_MIXPANEL_TOKEN="xxxxxx"
|
export ANALYTICS_AMPLITUDE_TOKEN="xxxxxx"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Linux
|
#### Linux
|
||||||
|
@ -112,4 +112,4 @@ Analytics
|
|||||||
- [ ] Disable analytics, open DevTools Network pane or a packet sniffer, and
|
- [ ] Disable analytics, open DevTools Network pane or a packet sniffer, and
|
||||||
check that no request is sent
|
check that no request is sent
|
||||||
- [ ] **Disable analytics, refresh application from DevTools (using Cmd-R or
|
- [ ] **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**
|
||||||
|
@ -296,6 +296,8 @@ driveScanner.start();
|
|||||||
|
|
||||||
let popupExists = false;
|
let popupExists = false;
|
||||||
|
|
||||||
|
analytics.initAnalytics();
|
||||||
|
|
||||||
window.addEventListener('beforeunload', async (event) => {
|
window.addEventListener('beforeunload', async (event) => {
|
||||||
if (!flashState.isFlashing() || popupExists) {
|
if (!flashState.isFlashing() || popupExists) {
|
||||||
analytics.logEvent('Close application', {
|
analytics.logEvent('Close application', {
|
||||||
|
@ -183,7 +183,12 @@ export class SafeWebview extends React.PureComponent<
|
|||||||
// only care about this event if it's a request for the main frame
|
// only care about this event if it's a request for the main frame
|
||||||
if (event.resourceType === 'mainFrame') {
|
if (event.resourceType === 'mainFrame') {
|
||||||
const HTTP_OK = 200;
|
const HTTP_OK = 200;
|
||||||
analytics.logEvent('SafeWebview loaded', { event });
|
const { webContents, ...webviewEvent } = event;
|
||||||
|
analytics.logEvent('SafeWebview loaded', {
|
||||||
|
...webviewEvent,
|
||||||
|
screen_height: webContents?.hostWebContents.browserWindowOptions.height,
|
||||||
|
screen_width: webContents?.hostWebContents.browserWindowOptions.width,
|
||||||
|
});
|
||||||
this.setState({
|
this.setState({
|
||||||
shouldShow: event.statusCode === HTTP_OK,
|
shouldShow: event.statusCode === HTTP_OK,
|
||||||
});
|
});
|
||||||
|
@ -15,84 +15,188 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as resinCorvus from 'resin-corvus/browser';
|
import { Client, createClient, createNoopClient } from 'analytics-client';
|
||||||
|
import * as SentryRenderer from '@sentry/electron/renderer';
|
||||||
import * as packageJSON from '../../../../package.json';
|
|
||||||
import { getConfig } from '../../../shared/utils';
|
|
||||||
import * as settings from '../models/settings';
|
import * as settings from '../models/settings';
|
||||||
import { store } from '../models/store';
|
import { store } from '../models/store';
|
||||||
|
import * as packageJSON from '../../../../package.json';
|
||||||
|
|
||||||
const DEFAULT_PROBABILITY = 0.1;
|
type AnalyticsPayload = _.Dictionary<any>;
|
||||||
|
|
||||||
async function installCorvus(): Promise<void> {
|
const clearUserPath = (filename: string): string => {
|
||||||
const sentryToken =
|
const generatedFile = filename.split('generated').reverse()[0];
|
||||||
(await settings.get('analyticsSentryToken')) ||
|
return generatedFile !== filename ? `generated${generatedFile}` : filename;
|
||||||
_.get(packageJSON, ['analytics', 'sentry', 'token']);
|
};
|
||||||
const mixpanelToken =
|
|
||||||
(await settings.get('analyticsMixpanelToken')) ||
|
export const anonymizeSentryData = (
|
||||||
_.get(packageJSON, ['analytics', 'mixpanel', 'token']);
|
event: SentryRenderer.Event,
|
||||||
resinCorvus.install({
|
): SentryRenderer.Event => {
|
||||||
services: {
|
event.exception?.values?.forEach((exception) => {
|
||||||
sentry: sentryToken,
|
exception.stacktrace?.frames?.forEach((frame) => {
|
||||||
mixpanel: mixpanelToken,
|
if (frame.filename) {
|
||||||
},
|
frame.filename = clearUserPath(frame.filename);
|
||||||
options: {
|
}
|
||||||
release: packageJSON.version,
|
});
|
||||||
shouldReport: () => {
|
|
||||||
return settings.getSync('errorReporting');
|
|
||||||
},
|
|
||||||
mixpanelDeferred: true,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
let mixpanelSample = DEFAULT_PROBABILITY;
|
event.breadcrumbs?.forEach((breadcrumb) => {
|
||||||
|
if (breadcrumb.data?.url) {
|
||||||
|
breadcrumb.data.url = clearUserPath(breadcrumb.data.url);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (event.request?.url) {
|
||||||
|
event.request.url = clearUserPath(event.request.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
return event;
|
||||||
|
};
|
||||||
|
|
||||||
|
const extractPathRegex = /(.*)(^|\s)(file\:\/\/)?(\w\:)?([\\\/].+)/;
|
||||||
|
const etcherSegmentMarkers = ['app.asar', 'Resources'];
|
||||||
|
|
||||||
|
export const anonymizePath = (input: string) => {
|
||||||
|
// First, extract a part of the value that matches a path pattern.
|
||||||
|
const match = extractPathRegex.exec(input);
|
||||||
|
if (match === null) {
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
const mainPart = match[5];
|
||||||
|
const space = match[2];
|
||||||
|
const beginning = match[1];
|
||||||
|
const uriPrefix = match[3] || '';
|
||||||
|
|
||||||
|
// We have to deal with both Windows and POSIX here.
|
||||||
|
// The path starts with its separator (we work with absolute paths).
|
||||||
|
const sep = mainPart[0];
|
||||||
|
const segments = mainPart.split(sep);
|
||||||
|
|
||||||
|
// Moving from the end, find the first marker and cut the path from there.
|
||||||
|
const startCutIndex = _.findLastIndex(segments, (segment) =>
|
||||||
|
etcherSegmentMarkers.includes(segment),
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
beginning +
|
||||||
|
space +
|
||||||
|
uriPrefix +
|
||||||
|
'[PERSONAL PATH]' +
|
||||||
|
sep +
|
||||||
|
segments.splice(startCutIndex).join(sep)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const safeAnonymizePath = (input: string) => {
|
||||||
|
try {
|
||||||
|
return anonymizePath(input);
|
||||||
|
} catch (e) {
|
||||||
|
return '[ANONYMIZE PATH FAILED]';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const sensitiveEtcherProperties = [
|
||||||
|
'error.description',
|
||||||
|
'error.message',
|
||||||
|
'error.stack',
|
||||||
|
'image',
|
||||||
|
'image.path',
|
||||||
|
'path',
|
||||||
|
];
|
||||||
|
|
||||||
|
export const anonymizeAnalyticsPayload = (
|
||||||
|
data: AnalyticsPayload,
|
||||||
|
): AnalyticsPayload => {
|
||||||
|
for (const prop of sensitiveEtcherProperties) {
|
||||||
|
const value = data[prop];
|
||||||
|
if (value != null) {
|
||||||
|
data[prop] = safeAnonymizePath(value.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
|
||||||
|
let analyticsClient: Client;
|
||||||
/**
|
/**
|
||||||
* @summary Init analytics configurations
|
* @summary Init analytics configurations
|
||||||
*/
|
*/
|
||||||
async function initConfig() {
|
export const initAnalytics = _.once(() => {
|
||||||
await installCorvus();
|
const dsn =
|
||||||
let validatedConfig = null;
|
settings.getSync('analyticsSentryToken') ||
|
||||||
try {
|
_.get(packageJSON, ['analytics', 'sentry', 'token']);
|
||||||
const configUrl = await settings.get('configUrl');
|
SentryRenderer.init({ dsn, beforeSend: anonymizeSentryData });
|
||||||
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);
|
|
||||||
}
|
|
||||||
resinCorvus.setConfigs({
|
|
||||||
mixpanel: validatedConfig,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
initConfig();
|
const projectName =
|
||||||
|
settings.getSync('analyticsAmplitudeToken') ||
|
||||||
|
_.get(packageJSON, ['analytics', 'amplitude', 'token']);
|
||||||
|
|
||||||
/**
|
const clientConfig = {
|
||||||
* @summary Check that the client is eligible for analytics
|
projectName,
|
||||||
*/
|
endpoint: 'data.balena-cloud.com',
|
||||||
function isClientEligible(probability: number) {
|
componentName: 'etcher',
|
||||||
return Math.random() < probability;
|
componentVersion: packageJSON.version,
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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',
|
|
||||||
};
|
};
|
||||||
if (config.HTTP_PROTOCOL !== undefined && config.api_host !== undefined) {
|
analyticsClient = projectName
|
||||||
mixpanelConfig.api_host = `${config.HTTP_PROTOCOL}://${config.api_host}`;
|
? createClient(clientConfig)
|
||||||
|
: createNoopClient();
|
||||||
|
});
|
||||||
|
|
||||||
|
const getCircularReplacer = () => {
|
||||||
|
const seen = new WeakSet();
|
||||||
|
return (key: any, value: any) => {
|
||||||
|
if (typeof value === 'object' && value !== null) {
|
||||||
|
if (seen.has(value)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
seen.add(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function flattenObject(obj: any) {
|
||||||
|
const toReturn: AnalyticsPayload = {};
|
||||||
|
|
||||||
|
for (const i in obj) {
|
||||||
|
if (!obj.hasOwnProperty(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(obj[i])) {
|
||||||
|
toReturn[i] = obj[i];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof obj[i] === 'object' && obj[i] !== null) {
|
||||||
|
const flatObject = flattenObject(obj[i]);
|
||||||
|
for (const x in flatObject) {
|
||||||
|
if (!flatObject.hasOwnProperty(x)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
toReturn[i.toLowerCase() + '.' + x.toLowerCase()] = flatObject[x];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toReturn[i] = obj[i];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return mixpanelConfig;
|
return toReturn;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatEvent(data: any): AnalyticsPayload {
|
||||||
|
const event = JSON.parse(JSON.stringify(data, getCircularReplacer()));
|
||||||
|
return anonymizeAnalyticsPayload(flattenObject(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
function reportAnalytics(message: string, data: AnalyticsPayload = {}) {
|
||||||
|
const { applicationSessionUuid, flashingWorkflowUuid } = store
|
||||||
|
.getState()
|
||||||
|
.toJS();
|
||||||
|
|
||||||
|
const event = formatEvent({
|
||||||
|
...data,
|
||||||
|
applicationSessionUuid,
|
||||||
|
flashingWorkflowUuid,
|
||||||
|
});
|
||||||
|
analyticsClient.track(message, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -101,16 +205,12 @@ function validateMixpanelConfig(config: {
|
|||||||
* @description
|
* @description
|
||||||
* This function sends the debug message to product analytics services.
|
* This function sends the debug message to product analytics services.
|
||||||
*/
|
*/
|
||||||
export function logEvent(message: string, data: _.Dictionary<any> = {}) {
|
export async function logEvent(message: string, data: AnalyticsPayload = {}) {
|
||||||
const { applicationSessionUuid, flashingWorkflowUuid } = store
|
const shouldReportAnalytics = await settings.get('errorReporting');
|
||||||
.getState()
|
if (shouldReportAnalytics) {
|
||||||
.toJS();
|
initAnalytics();
|
||||||
resinCorvus.logEvent(message, {
|
reportAnalytics(message, data);
|
||||||
...data,
|
}
|
||||||
sample: mixpanelSample,
|
|
||||||
applicationSessionUuid,
|
|
||||||
flashingWorkflowUuid,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -119,4 +219,11 @@ export function logEvent(message: string, data: _.Dictionary<any> = {}) {
|
|||||||
* @description
|
* @description
|
||||||
* This function logs an exception to error reporting services.
|
* This function logs an exception to error reporting services.
|
||||||
*/
|
*/
|
||||||
export const logException = resinCorvus.logException;
|
export function logException(error: any) {
|
||||||
|
const shouldReportErrors = settings.getSync('errorReporting');
|
||||||
|
if (shouldReportErrors) {
|
||||||
|
initAnalytics();
|
||||||
|
console.error(error);
|
||||||
|
SentryRenderer.captureException(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -20,6 +20,7 @@ import { promises as fs } from 'fs';
|
|||||||
import { platform } from 'os';
|
import { platform } from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
|
import * as _ from 'lodash';
|
||||||
|
|
||||||
import './app/i18n';
|
import './app/i18n';
|
||||||
|
|
||||||
@ -27,9 +28,11 @@ import { packageType, version } from '../../package.json';
|
|||||||
import * as EXIT_CODES from '../shared/exit-codes';
|
import * as EXIT_CODES from '../shared/exit-codes';
|
||||||
import { delay, getConfig } from '../shared/utils';
|
import { delay, getConfig } from '../shared/utils';
|
||||||
import * as settings from './app/models/settings';
|
import * as settings from './app/models/settings';
|
||||||
import { logException } from './app/modules/analytics';
|
|
||||||
import { buildWindowMenu } from './menu';
|
import { buildWindowMenu } from './menu';
|
||||||
import * as i18n from 'i18next';
|
import * as i18n from 'i18next';
|
||||||
|
import * as SentryMain from '@sentry/electron/main';
|
||||||
|
import * as packageJSON from '../../package.json';
|
||||||
|
import { anonymizeSentryData } from './app/modules/analytics';
|
||||||
|
|
||||||
const customProtocol = 'etcher';
|
const customProtocol = 'etcher';
|
||||||
const scheme = `${customProtocol}://`;
|
const scheme = `${customProtocol}://`;
|
||||||
@ -53,13 +56,21 @@ async function checkForUpdates(interval: number) {
|
|||||||
packageUpdated = true;
|
packageUpdated = true;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logException(err);
|
logMainProcessException(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await delay(interval);
|
await delay(interval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function logMainProcessException(error: any) {
|
||||||
|
const shouldReportErrors = settings.getSync('errorReporting');
|
||||||
|
if (shouldReportErrors) {
|
||||||
|
console.error(error);
|
||||||
|
SentryMain.captureException(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function isFile(filePath: string): Promise<boolean> {
|
async function isFile(filePath: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const stat = await fs.stat(filePath);
|
const stat = await fs.stat(filePath);
|
||||||
@ -94,6 +105,14 @@ async function getCommandLineURL(argv: string[]): Promise<string | undefined> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const initSentryMain = _.once(() => {
|
||||||
|
const dsn =
|
||||||
|
settings.getSync('analyticsSentryToken') ||
|
||||||
|
_.get(packageJSON, ['analytics', 'sentry', 'token']);
|
||||||
|
|
||||||
|
SentryMain.init({ dsn, beforeSend: anonymizeSentryData });
|
||||||
|
});
|
||||||
|
|
||||||
const sourceSelectorReady = new Promise((resolve) => {
|
const sourceSelectorReady = new Promise((resolve) => {
|
||||||
electron.ipcMain.on('source-selector-ready', resolve);
|
electron.ipcMain.on('source-selector-ready', resolve);
|
||||||
});
|
});
|
||||||
@ -190,8 +209,9 @@ async function createMainWindow() {
|
|||||||
const page = mainWindow.webContents;
|
const page = mainWindow.webContents;
|
||||||
|
|
||||||
page.once('did-frame-finish-load', async () => {
|
page.once('did-frame-finish-load', async () => {
|
||||||
|
console.log('packageUpdatable', packageUpdatable);
|
||||||
autoUpdater.on('error', (err) => {
|
autoUpdater.on('error', (err) => {
|
||||||
logException(err);
|
logMainProcessException(err);
|
||||||
});
|
});
|
||||||
if (packageUpdatable) {
|
if (packageUpdatable) {
|
||||||
try {
|
try {
|
||||||
@ -208,7 +228,7 @@ async function createMainWindow() {
|
|||||||
onlineConfig?.autoUpdates?.checkForUpdatesTimer ?? 300000;
|
onlineConfig?.autoUpdates?.checkForUpdatesTimer ?? 300000;
|
||||||
checkForUpdates(checkForUpdatesTimer);
|
checkForUpdates(checkForUpdatesTimer);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logException(err);
|
logMainProcessException(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -233,6 +253,7 @@ async function main(): Promise<void> {
|
|||||||
if (!electron.app.requestSingleInstanceLock()) {
|
if (!electron.app.requestSingleInstanceLock()) {
|
||||||
electron.app.quit();
|
electron.app.quit();
|
||||||
} else {
|
} else {
|
||||||
|
initSentryMain();
|
||||||
await electron.app.whenReady();
|
await electron.app.whenReady();
|
||||||
const window = await createMainWindow();
|
const window = await createMainWindow();
|
||||||
electron.app.on('second-instance', async (_event, argv) => {
|
electron.app.on('second-instance', async (_event, argv) => {
|
||||||
@ -256,7 +277,6 @@ async function main(): Promise<void> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
|
||||||
console.time('ready-to-show');
|
console.time('ready-to-show');
|
||||||
|
1122
package-lock.json
generated
1122
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -53,6 +53,7 @@
|
|||||||
"@balena/lint": "5.4.2",
|
"@balena/lint": "5.4.2",
|
||||||
"@balena/sudo-prompt": "9.2.1-workaround-windows-amperstand-in-username-0849e215b947987a643fe5763902aea201255534",
|
"@balena/sudo-prompt": "9.2.1-workaround-windows-amperstand-in-username-0849e215b947987a643fe5763902aea201255534",
|
||||||
"@fortawesome/fontawesome-free": "5.15.4",
|
"@fortawesome/fontawesome-free": "5.15.4",
|
||||||
|
"@sentry/electron": "^4.1.2",
|
||||||
"@svgr/webpack": "5.5.0",
|
"@svgr/webpack": "5.5.0",
|
||||||
"@types/chai": "4.3.4",
|
"@types/chai": "4.3.4",
|
||||||
"@types/copy-webpack-plugin": "6.4.3",
|
"@types/copy-webpack-plugin": "6.4.3",
|
||||||
@ -68,6 +69,7 @@
|
|||||||
"@types/terser-webpack-plugin": "5.0.4",
|
"@types/terser-webpack-plugin": "5.0.4",
|
||||||
"@types/tmp": "0.2.3",
|
"@types/tmp": "0.2.3",
|
||||||
"@types/webpack-node-externals": "2.5.3",
|
"@types/webpack-node-externals": "2.5.3",
|
||||||
|
"analytics-client": "^2.0.1",
|
||||||
"aws4-axios": "2.4.9",
|
"aws4-axios": "2.4.9",
|
||||||
"chai": "4.3.7",
|
"chai": "4.3.7",
|
||||||
"copy-webpack-plugin": "7.0.0",
|
"copy-webpack-plugin": "7.0.0",
|
||||||
@ -102,7 +104,6 @@
|
|||||||
"react-i18next": "11.18.6",
|
"react-i18next": "11.18.6",
|
||||||
"redux": "4.2.0",
|
"redux": "4.2.0",
|
||||||
"rendition": "19.3.2",
|
"rendition": "19.3.2",
|
||||||
"resin-corvus": "2.0.5",
|
|
||||||
"semver": "7.3.8",
|
"semver": "7.3.8",
|
||||||
"simple-progress-webpack-plugin": "1.1.2",
|
"simple-progress-webpack-plugin": "1.1.2",
|
||||||
"sinon": "9.2.4",
|
"sinon": "9.2.4",
|
||||||
|
@ -29,7 +29,7 @@ import * as PnpWebpackPlugin from 'pnp-webpack-plugin';
|
|||||||
import * as tsconfigRaw from './tsconfig.webpack.json';
|
import * as tsconfigRaw from './tsconfig.webpack.json';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Don't webpack package.json as mixpanel & sentry tokens
|
* Don't webpack package.json as sentry tokens
|
||||||
* will be inserted in it after webpacking
|
* will be inserted in it after webpacking
|
||||||
*/
|
*/
|
||||||
function externalPackageJson(packageJsonPath: string) {
|
function externalPackageJson(packageJsonPath: string) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user