mirror of
https://github.com/balena-io/etcher.git
synced 2025-11-09 10:28:32 +00:00
Compare commits
1 Commits
v1.14.0
...
aethernet/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec7e0b745e |
37
.github/actions/publish/action.yml
vendored
37
.github/actions/publish/action.yml
vendored
@@ -72,6 +72,29 @@ runs:
|
|||||||
chmod +x "$(dirname "$(which node)")/resinci-deploy" && which resinci-deploy
|
chmod +x "$(dirname "$(which node)")/resinci-deploy" && which resinci-deploy
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Upload sourcemaps to sentry
|
||||||
|
- 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
|
||||||
@@ -145,8 +168,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='https://739bbcfc0ba4481481138d3fc831136d@o95242.ingest.sentry.io/4504451487301632' \
|
--c.extraMetadata.analytics.sentry.token='${{ steps.sentry.outputs.dsn }}' \
|
||||||
--c.extraMetadata.analytics.amplitude.token='balena-etcher' \
|
--c.extraMetadata.analytics.mixpanel.token='balena-etcher' \
|
||||||
--c.extraMetadata.packageType="${target}"
|
--c.extraMetadata.packageType="${target}"
|
||||||
|
|
||||||
find dist -type f -maxdepth 1
|
find dist -type f -maxdepth 1
|
||||||
@@ -180,6 +203,16 @@ runs:
|
|||||||
-name "latest*.yml" \
|
-name "latest*.yml" \
|
||||||
-exec yq -i e .stagingPercentage=\"$percentage\" {} \;
|
-exec yq -i e .stagingPercentage=\"$percentage\" {} \;
|
||||||
|
|
||||||
|
- name: Upload sourcemap to Sentry
|
||||||
|
shell: bash --noprofile --norc -eo pipefail -x {0}
|
||||||
|
run: |
|
||||||
|
VERSION=${{ steps.package_release.outputs.version }} npm run uploadSourcemap
|
||||||
|
env:
|
||||||
|
SENTRY_AUTH_TOKEN: ${{ fromJSON(inputs.secrets).SENTRY_AUTH_TOKEN }}
|
||||||
|
npm_config_SENTRY_ORG: balenaEtcher
|
||||||
|
npm_config_SENTRY_PROJECT: balenaetcher
|
||||||
|
npm_config_SENTRY_VERSION: ${{ steps.package_release.outputs.version }}
|
||||||
|
|
||||||
- name: Upload artifacts
|
- name: Upload artifacts
|
||||||
uses: actions/upload-artifact@v3
|
uses: actions/upload-artifact@v3
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -1,57 +1,3 @@
|
|||||||
- commits:
|
|
||||||
- subject: Anonymizes all paths before sending
|
|
||||||
hash: 86d43a536f7c9aa6b450a9f2f90341e07364208e
|
|
||||||
body: ""
|
|
||||||
footer:
|
|
||||||
Change-type: patch
|
|
||||||
change-type: patch
|
|
||||||
author: Otávio Jacobi
|
|
||||||
nested: []
|
|
||||||
- subject: "patch: Sentry fix path"
|
|
||||||
hash: 6c417e35a13873cd95d25f42a819de3750cdf65d
|
|
||||||
body: ""
|
|
||||||
footer: {}
|
|
||||||
author: Edwin Joassart
|
|
||||||
nested: []
|
|
||||||
- subject: Remove personal path on etcher
|
|
||||||
hash: 2b728d3c521b76177a2c019b4891627272f35aac
|
|
||||||
body: ""
|
|
||||||
footer:
|
|
||||||
Change-type: minor
|
|
||||||
change-type: minor
|
|
||||||
author: Otávio Jacobi
|
|
||||||
nested: []
|
|
||||||
- subject: Unifying sentry reports in a single project
|
|
||||||
hash: f3f7ecb852503d4d97dbe6a78bf920ca177bddd1
|
|
||||||
body: ""
|
|
||||||
footer:
|
|
||||||
Change-type: patch
|
|
||||||
change-type: patch
|
|
||||||
author: Edwin Joassart
|
|
||||||
nested: []
|
|
||||||
- subject: Removes corvus in favor of sentry and analytics client
|
|
||||||
hash: 41fca03c98d4a72bd8c3842d7e6b9d41f65336f9
|
|
||||||
body: ""
|
|
||||||
footer:
|
|
||||||
Change-type: patch
|
|
||||||
change-type: patch
|
|
||||||
Signed-off-by: Otavio Jacobi
|
|
||||||
signed-off-by: Otavio Jacobi
|
|
||||||
author: Otávio Jacobi
|
|
||||||
nested: []
|
|
||||||
- subject: Removes corvus in favor of sentry and analytics client
|
|
||||||
hash: 10caf8f1b6a174762192b13ce7bb4eaa71e90fcc
|
|
||||||
body: ""
|
|
||||||
footer:
|
|
||||||
Change-type: patch
|
|
||||||
change-type: patch
|
|
||||||
Signed-off-by: Otavio Jacobi
|
|
||||||
signed-off-by: Otavio Jacobi
|
|
||||||
author: Otávio Jacobi
|
|
||||||
nested: []
|
|
||||||
version: 1.14.0
|
|
||||||
title: ""
|
|
||||||
date: 2023-01-16T11:23:54.866Z
|
|
||||||
- commits:
|
- commits:
|
||||||
- subject: Adding EtcherPro device serial number to the Settings modal
|
- subject: Adding EtcherPro device serial number to the Settings modal
|
||||||
hash: d25eda9a7d6bf89284b630b2d55cbb0a7e3a9432
|
hash: d25eda9a7d6bf89284b630b2d55cbb0a7e3a9432
|
||||||
|
|||||||
10
CHANGELOG.md
10
CHANGELOG.md
@@ -3,16 +3,6 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
# v1.14.0
|
|
||||||
## (2023-01-16)
|
|
||||||
|
|
||||||
* Anonymizes all paths before sending [Otávio Jacobi]
|
|
||||||
* patch: Sentry fix path [Edwin Joassart]
|
|
||||||
* Remove personal path on etcher [Otávio Jacobi]
|
|
||||||
* Unifying sentry reports in a single project [Edwin Joassart]
|
|
||||||
* Removes corvus in favor of sentry and analytics client [Otávio Jacobi]
|
|
||||||
* Removes corvus in favor of sentry and analytics client [Otávio Jacobi]
|
|
||||||
|
|
||||||
# v1.13.4
|
# v1.13.4
|
||||||
## (2023-01-12)
|
## (2023-01-12)
|
||||||
|
|
||||||
|
|||||||
@@ -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, Amplitude) to trickle in and check for elevated error rates, or regressions
|
- Wait 2-3 hours for analytics (Sentry, Mixpanel) 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_AMPLITUDE_TOKEN="xxxxxx"
|
export ANALYTICS_MIXPANEL_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 Amplitude**
|
F5), and check that initial events are not sent to Mixpanel**
|
||||||
|
|||||||
@@ -296,8 +296,6 @@ 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,12 +183,7 @@ 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;
|
||||||
const { webContents, ...webviewEvent } = event;
|
analytics.logEvent('SafeWebview loaded', { 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,188 +15,84 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import { Client, createClient, createNoopClient } from 'analytics-client';
|
import * as resinCorvus from 'resin-corvus/browser';
|
||||||
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';
|
|
||||||
|
|
||||||
type AnalyticsPayload = _.Dictionary<any>;
|
const DEFAULT_PROBABILITY = 0.1;
|
||||||
|
|
||||||
const clearUserPath = (filename: string): string => {
|
async function installCorvus(): Promise<void> {
|
||||||
const generatedFile = filename.split('generated').reverse()[0];
|
const sentryToken =
|
||||||
return generatedFile !== filename ? `generated${generatedFile}` : filename;
|
(await settings.get('analyticsSentryToken')) ||
|
||||||
};
|
_.get(packageJSON, ['analytics', 'sentry', 'token']);
|
||||||
|
const mixpanelToken =
|
||||||
export const anonymizeSentryData = (
|
(await settings.get('analyticsMixpanelToken')) ||
|
||||||
event: SentryRenderer.Event,
|
_.get(packageJSON, ['analytics', 'mixpanel', 'token']);
|
||||||
): SentryRenderer.Event => {
|
resinCorvus.install({
|
||||||
event.exception?.values?.forEach((exception) => {
|
services: {
|
||||||
exception.stacktrace?.frames?.forEach((frame) => {
|
sentry: sentryToken,
|
||||||
if (frame.filename) {
|
mixpanel: mixpanelToken,
|
||||||
frame.filename = clearUserPath(frame.filename);
|
},
|
||||||
}
|
options: {
|
||||||
});
|
release: packageJSON.version,
|
||||||
|
shouldReport: () => {
|
||||||
|
return settings.getSync('errorReporting');
|
||||||
|
},
|
||||||
|
mixpanelDeferred: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
event.breadcrumbs?.forEach((breadcrumb) => {
|
let mixpanelSample = DEFAULT_PROBABILITY;
|
||||||
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
|
||||||
*/
|
*/
|
||||||
export const initAnalytics = _.once(() => {
|
async function initConfig() {
|
||||||
const dsn =
|
await installCorvus();
|
||||||
settings.getSync('analyticsSentryToken') ||
|
let validatedConfig = null;
|
||||||
_.get(packageJSON, ['analytics', 'sentry', 'token']);
|
try {
|
||||||
SentryRenderer.init({ dsn, beforeSend: anonymizeSentryData });
|
const configUrl = await settings.get('configUrl');
|
||||||
|
const config = await getConfig(configUrl);
|
||||||
const projectName =
|
const mixpanel = _.get(config, ['analytics', 'mixpanel'], {});
|
||||||
settings.getSync('analyticsAmplitudeToken') ||
|
mixpanelSample = mixpanel.probability || DEFAULT_PROBABILITY;
|
||||||
_.get(packageJSON, ['analytics', 'amplitude', 'token']);
|
if (isClientEligible(mixpanelSample)) {
|
||||||
|
validatedConfig = validateMixpanelConfig(mixpanel);
|
||||||
const clientConfig = {
|
|
||||||
projectName,
|
|
||||||
endpoint: 'data.balena-cloud.com',
|
|
||||||
componentName: 'etcher',
|
|
||||||
componentVersion: packageJSON.version,
|
|
||||||
};
|
|
||||||
analyticsClient = projectName
|
|
||||||
? 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];
|
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
resinCorvus.logException(err);
|
||||||
}
|
}
|
||||||
return toReturn;
|
resinCorvus.setConfigs({
|
||||||
}
|
mixpanel: validatedConfig,
|
||||||
|
|
||||||
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);
|
}
|
||||||
|
|
||||||
|
initConfig();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @summary Check that the client is eligible for analytics
|
||||||
|
*/
|
||||||
|
function isClientEligible(probability: number) {
|
||||||
|
return Math.random() < probability;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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) {
|
||||||
|
mixpanelConfig.api_host = `${config.HTTP_PROTOCOL}://${config.api_host}`;
|
||||||
|
}
|
||||||
|
return mixpanelConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -205,12 +101,16 @@ function reportAnalytics(message: string, data: AnalyticsPayload = {}) {
|
|||||||
* @description
|
* @description
|
||||||
* This function sends the debug message to product analytics services.
|
* This function sends the debug message to product analytics services.
|
||||||
*/
|
*/
|
||||||
export async function logEvent(message: string, data: AnalyticsPayload = {}) {
|
export function logEvent(message: string, data: _.Dictionary<any> = {}) {
|
||||||
const shouldReportAnalytics = await settings.get('errorReporting');
|
const { applicationSessionUuid, flashingWorkflowUuid } = store
|
||||||
if (shouldReportAnalytics) {
|
.getState()
|
||||||
initAnalytics();
|
.toJS();
|
||||||
reportAnalytics(message, data);
|
resinCorvus.logEvent(message, {
|
||||||
}
|
...data,
|
||||||
|
sample: mixpanelSample,
|
||||||
|
applicationSessionUuid,
|
||||||
|
flashingWorkflowUuid,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -219,11 +119,4 @@ export async function logEvent(message: string, data: AnalyticsPayload = {}) {
|
|||||||
* @description
|
* @description
|
||||||
* This function logs an exception to error reporting services.
|
* This function logs an exception to error reporting services.
|
||||||
*/
|
*/
|
||||||
export function logException(error: any) {
|
export const logException = resinCorvus.logException;
|
||||||
const shouldReportErrors = settings.getSync('errorReporting');
|
|
||||||
if (shouldReportErrors) {
|
|
||||||
initAnalytics();
|
|
||||||
console.error(error);
|
|
||||||
SentryRenderer.captureException(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ 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';
|
||||||
|
|
||||||
@@ -28,11 +27,9 @@ 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}://`;
|
||||||
@@ -56,21 +53,13 @@ async function checkForUpdates(interval: number) {
|
|||||||
packageUpdated = true;
|
packageUpdated = true;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logMainProcessException(err);
|
logException(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);
|
||||||
@@ -105,14 +94,6 @@ 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);
|
||||||
});
|
});
|
||||||
@@ -209,9 +190,8 @@ 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) => {
|
||||||
logMainProcessException(err);
|
logException(err);
|
||||||
});
|
});
|
||||||
if (packageUpdatable) {
|
if (packageUpdatable) {
|
||||||
try {
|
try {
|
||||||
@@ -228,7 +208,7 @@ async function createMainWindow() {
|
|||||||
onlineConfig?.autoUpdates?.checkForUpdatesTimer ?? 300000;
|
onlineConfig?.autoUpdates?.checkForUpdatesTimer ?? 300000;
|
||||||
checkForUpdates(checkForUpdatesTimer);
|
checkForUpdates(checkForUpdatesTimer);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logMainProcessException(err);
|
logException(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -253,7 +233,6 @@ 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) => {
|
||||||
@@ -277,6 +256,7 @@ async function main(): Promise<void> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
main();
|
main();
|
||||||
|
|
||||||
console.time('ready-to-show');
|
console.time('ready-to-show');
|
||||||
|
|||||||
1083
package-lock.json
generated
1083
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,7 @@
|
|||||||
"name": "balena-etcher",
|
"name": "balena-etcher",
|
||||||
"private": true,
|
"private": true,
|
||||||
"displayName": "balenaEtcher",
|
"displayName": "balenaEtcher",
|
||||||
"version": "1.14.0",
|
"version": "1.13.4",
|
||||||
"packageType": "local",
|
"packageType": "local",
|
||||||
"main": "generated/etcher.js",
|
"main": "generated/etcher.js",
|
||||||
"description": "Flash OS images to SD cards and USB drives, safely and easily.",
|
"description": "Flash OS images to SD cards and USB drives, safely and easily.",
|
||||||
@@ -31,6 +31,7 @@
|
|||||||
"test-spectron": "mocha --recursive --reporter spec --require ts-node/register/transpile-only --require-main tests/gui/allow-renderer-process-reuse.ts tests/spectron/runner.spec.ts",
|
"test-spectron": "mocha --recursive --reporter spec --require ts-node/register/transpile-only --require-main tests/gui/allow-renderer-process-reuse.ts tests/spectron/runner.spec.ts",
|
||||||
"test-windows": "npm run lint && npm run test-gui && npm run test-shared && npm run test-spectron && npm run sanity-checks",
|
"test-windows": "npm run lint && npm run test-gui && npm run test-shared && npm run test-spectron && npm run sanity-checks",
|
||||||
"test": "echo npm run test-{linux,windows,macos}",
|
"test": "echo npm run test-{linux,windows,macos}",
|
||||||
|
"uploadSourcemap": "sentry-cli releases files $npm_config_SENTRY_VERSION upload-sourcemaps ./generated/*.js.map --org $npm_config_SENTRY_ORG --project $npm_config_SENTRY_PROJECT",
|
||||||
"watch": "webpack serve --no-optimization-minimize --config ./webpack.dev.config.ts",
|
"watch": "webpack serve --no-optimization-minimize --config ./webpack.dev.config.ts",
|
||||||
"webpack": "webpack"
|
"webpack": "webpack"
|
||||||
},
|
},
|
||||||
@@ -53,7 +54,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",
|
"@sentry/cli": "^2.11.0",
|
||||||
"@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",
|
||||||
@@ -69,7 +70,6 @@
|
|||||||
"@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",
|
||||||
@@ -104,6 +104,7 @@
|
|||||||
"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",
|
||||||
@@ -127,6 +128,6 @@
|
|||||||
"node": ">=14 < 16"
|
"node": ">=14 < 16"
|
||||||
},
|
},
|
||||||
"versionist": {
|
"versionist": {
|
||||||
"publishedAt": "2023-01-16T11:23:55.599Z"
|
"publishedAt": "2023-01-12T15:10:50.986Z"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 sentry tokens
|
* Don't webpack package.json as mixpanel & sentry tokens
|
||||||
* will be inserted in it after webpacking
|
* will be inserted in it after webpacking
|
||||||
*/
|
*/
|
||||||
function externalPackageJson(packageJsonPath: string) {
|
function externalPackageJson(packageJsonPath: string) {
|
||||||
@@ -455,6 +455,7 @@ const guiConfig = {
|
|||||||
const mainConfig = {
|
const mainConfig = {
|
||||||
...commonConfig,
|
...commonConfig,
|
||||||
target: 'electron-main',
|
target: 'electron-main',
|
||||||
|
devtool: 'source-map',
|
||||||
node: {
|
node: {
|
||||||
__dirname: false,
|
__dirname: false,
|
||||||
__filename: true,
|
__filename: true,
|
||||||
|
|||||||
Reference in New Issue
Block a user