Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ on:
pull_request:
env:
ELECTRON_CACHE_DIR: ${{ github.workspace }}
FAILURE_LOG: true

jobs:
build:
Expand Down
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,21 @@

## Unreleased

## 4.0.0-beta.1

Updating the underlying Sentry JavaScript SDK's to v7 forces a major version bump due to minor breaking changes in user
facing APIs. Be sure to check out the [migration doc](./MIGRATION.md).

Upgrading to v7 of the Sentry JavaScript SDKs (#471):

- Minor internal changes due to API changes and deprecations
- Rewrite transports to use new functional API
- Simplify minidump submission since the underlying SDKs now support attachments

- feat: Add ability to explicitly control offline mode (#489)
- feat: Allow closing of SDK (#467)
- fix: Ensure environment is overridden for minidumps (#497)
- fix: Pass correct event to beforeSend (#481)

## 3.0.7

Expand Down
5 changes: 5 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# Upgrading from 3.x to 4.x

All the breaking changes in v4 are due to changes in the underlying Sentry JavaScript SDKs so be sure to check the
[JavaScript migration docs](https://github.com/getsentry/sentry-javascript/blob/master/MIGRATION.md#upgrading-from-6x-to-7x).

# Upgrading from 2.x to 3.x

v3 of the Electron SDK includes significant changes to simplify usage, improve maintainability and bundler support.
Expand Down
21 changes: 11 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,25 +53,26 @@
"fix:eslint": "eslint . --format stylish --fix",
"pretest": "yarn build",
"test": "cross-env TS_NODE_PROJECT=tsconfig.json xvfb-maybe electron-mocha --require ts-node/register/transpile-only --timeout 120000 ./test/unit/**/*.ts",
"pree2e": "rimraf test/e2e/dist/**/node_modules/@sentry/electron/** test/e2e/dist/**/yarn.lock && node scripts/clean-cache.js && yarn build && npm pack",
"pree2e": "rimraf test/e2e/dist/**/node_modules/@sentry/** test/e2e/dist/**/yarn.lock && node scripts/clean-cache.js && yarn build && npm pack",
"e2e": "cross-env TS_NODE_PROJECT=tsconfig.json xvfb-maybe mocha --require ts-node/register/transpile-only --retries 3 ./test/e2e/*.ts"
},
"dependencies": {
"@sentry/browser": "6.19.2",
"@sentry/core": "6.19.2",
"@sentry/node": "6.19.2",
"@sentry/types": "6.19.2",
"@sentry/utils": "6.19.2",
"@sentry/browser": "7.3.1",
"@sentry/core": "7.3.1",
"@sentry/hub": "7.3.1",
"@sentry/node": "7.3.1",
"@sentry/types": "7.3.1",
"@sentry/utils": "7.3.1",
"deepmerge": "^4.2.2",
"tslib": "^2.3.1"
},
"devDependencies": {
"@sentry-internal/eslint-config-sdk": "6.19.2",
"@sentry-internal/typescript": "6.19.2",
"@sentry/tracing": "6.19.2",
"@sentry-internal/eslint-config-sdk": "7.3.1",
"@sentry-internal/typescript": "7.3.1",
"@sentry/tracing": "7.3.1",
"@types/busboy": "^0.2.3",
"@types/chai": "^4.2.10",
"@types/chai-as-promised": "^7.1.0",
"@types/chai-as-promised": "^7.1.5",
"@types/chai-subset": "^1.3.3",
"@types/form-data": "^2.5.0",
"@types/koa": "^2.0.52",
Expand Down
55 changes: 16 additions & 39 deletions src/common/normalize.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Event, Exception, Stacktrace } from '@sentry/types';
import { Event } from '@sentry/types';

/**
* Normalizes URLs in exceptions and stacktraces so Sentry can fingerprint
Expand Down Expand Up @@ -34,15 +34,14 @@ export function normalizeUrl(url: string, basePath: string): string {
* @param event The event to normalize.
*/
export function normalizeEvent(event: Event, basePath: string): Event {
// Retrieve stack traces and normalize their URLs. Without this, grouping
// would not work due to user folders in file names.
const stacktrace = getStacktrace(event);
if (stacktrace && stacktrace.frames) {
stacktrace.frames.forEach((frame) => {
// Retrieve stack traces and normalize their paths. Without this, grouping
// would not work due to usernames in file paths.
for (const exception of event.exception?.values || []) {
for (const frame of exception.stacktrace?.frames || []) {
if (frame.filename) {
frame.filename = normalizeUrl(frame.filename, basePath);
}
});
}
}

if (event.transaction) {
Expand All @@ -54,48 +53,26 @@ export function normalizeEvent(event: Event, basePath: string): Event {
request.url = normalizeUrl(request.url, basePath);
}

event.contexts = {
...event.contexts,
runtime: {
name: 'Electron',
version: process.versions.electron,
},
};

// The user agent is parsed by Sentry and would overwrite certain context
// information, which we don't want. Generally remove it, since we know that
// we are browsing with Chrome.
if (request.headers) {
delete request.headers['User-Agent'];
}

// The Node SDK currently adds a default tag for server_name, which contains
// The Node SDK includes server_name, which contains
// the machine name of the computer running Electron. This is not useful
// information in this case.
const { tags = {} } = event;
delete tags.server_name;
delete event.server_name;
return event;
}

/**
* Returns a reference to the exception stack trace in the given event.
* @param event An event potentially containing stack traces.
*/
function getStacktrace(event: Event): Stacktrace | undefined {
const { stacktrace, exception } = event;

// Try the main event stack trace first
if (stacktrace) {
return stacktrace;
}

if (exception) {
// Raven Node adheres to the Event interface
// @ts-ignore: need to be able to index exception
if (exception[0]) {
// @ts-ignore: need to be able to index exception
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
return exception[0].stacktrace;
}

// Raven JS uses the full values interface, which has been removed
const raven = exception as any as { values: Exception[] };
if (raven.values && raven.values[0]) {
return raven.values[0].stacktrace;
}
}

return undefined;
}
11 changes: 7 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import { getIntegrations, removeEmptyIntegrations } from './integrations';
import { ElectronMainOptions } from './main';
import { BrowserOptions } from './renderer';

export {
export type {
Breadcrumb,
BreadcrumbHint,
Request,
SdkInfo,
Event,
EventHint,
EventStatus,
Exception,
Response,
Session,
// eslint-disable-next-line deprecation/deprecation
Severity,
SeverityLevel,
StackFrame,
Expand All @@ -29,6 +29,7 @@ export {
captureEvent,
captureMessage,
configureScope,
createTransport,
getHubFromCarrier,
getCurrentHub,
Hub,
Expand All @@ -42,12 +43,14 @@ export {
setTags,
setUser,
withScope,
FunctionToString,
InboundFilters,
} from '@sentry/core';

export const Integrations = getIntegrations();

// eslint-disable-next-line @typescript-eslint/no-empty-interface
export interface ElectronOptions extends ElectronMainOptions, BrowserOptions {
export interface ElectronOptions extends ElectronMainOptions, Omit<BrowserOptions, 'transportOptions' | 'transport'> {
//
}

Expand Down
8 changes: 4 additions & 4 deletions src/main/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,11 +295,11 @@ export function getDefaultEnvironment(): string {
* runtimes, limited device information, operating system context and defaults
* for the release and environment.
*/
async function _getEventDefaults(release?: string): Promise<Event> {
async function _getEventDefaults(release?: string, environment?: string): Promise<Event> {
return {
sdk: getSdkInfo(),
contexts: await getContexts(),
environment: getDefaultEnvironment(),
environment: environment || getDefaultEnvironment(),
release: release || getDefaultReleaseName(),
user: { ip_address: '{{auto}}' },
tags: {
Expand All @@ -322,12 +322,12 @@ let cachedDefaultsPromise: Promise<Event>;
* runtimes, limited device information, operating system context and defaults
* for the release and environment.
*/
export async function getEventDefaults(release?: string): Promise<Event> {
export async function getEventDefaults(release?: string, environment?: string): Promise<Event> {
// The event defaults are cached as long as the app is running. We create the
// promise here synchronously to avoid multiple events computing them at the
// same time.
if (!cachedDefaultsPromise) {
cachedDefaultsPromise = _getEventDefaults(release);
cachedDefaultsPromise = _getEventDefaults(release, environment);
}

return await cachedDefaultsPromise;
Expand Down
20 changes: 13 additions & 7 deletions src/main/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import { Integrations as NodeIntegrations } from '@sentry/node';

import * as ElectronMainIntegrations from './integrations';

export {
export type {
Breadcrumb,
BreadcrumbHint,
Request,
SdkInfo,
Event,
EventHint,
EventStatus,
Exception,
Response,
Session,
// eslint-disable-next-line deprecation/deprecation
Severity,
SeverityLevel,
StackFrame,
Expand All @@ -30,9 +30,11 @@ export {
captureEvent,
captureMessage,
configureScope,
createTransport,
getHubFromCarrier,
getCurrentHub,
Hub,
makeMain,
Scope,
startTransaction,
setContext,
Expand All @@ -42,13 +44,17 @@ export {
setTags,
setUser,
withScope,
FunctionToString,
InboundFilters,
} from '@sentry/core';

export { flush, close, NodeOptions, NodeBackend, NodeClient, lastEventId } from '@sentry/node';
export type { NodeOptions } from '@sentry/node';
export { flush, close, NodeClient, lastEventId } from '@sentry/node';

export { ElectronNetTransport } from './transports/electron-net';
export { ElectronOfflineNetTransport } from './transports/electron-offline-net';
export { makeElectronTransport } from './transports/electron-net';
export { makeElectronOfflineTransport } from './transports/electron-offline-net';
export const Integrations = { ...ElectronMainIntegrations, ...NodeIntegrations };

export { init, ElectronMainOptions, defaultIntegrations } from './sdk';
export type { ElectronMainOptions } from './sdk';
export { init, defaultIntegrations } from './sdk';
export { IPCMode } from '../common';
10 changes: 5 additions & 5 deletions src/main/integrations/child-process.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { addBreadcrumb, captureMessage, getCurrentHub } from '@sentry/core';
import { NodeClient } from '@sentry/node';
import { Integration, Severity } from '@sentry/types';
import { Integration, SeverityLevel } from '@sentry/types';

import { OrBool } from '../../common/types';
import { EXIT_REASONS, ExitReason, onChildProcessGone, onRendererProcessGone } from '../electron-normalize';
Expand All @@ -19,20 +19,20 @@ const DEFAULT_OPTIONS: ChildProcessOptions = {
};

/** Gets message and severity */
function getMessageAndSeverity(reason: ExitReason, proc?: string): { message: string; level: Severity } {
function getMessageAndSeverity(reason: ExitReason, proc?: string): { message: string; level: SeverityLevel } {
const message = `'${proc}' process exited with '${reason}'`;

switch (reason) {
case 'abnormal-exit':
case 'killed':
return { message, level: Severity.Warning };
return { message, level: 'warning' };
case 'crashed':
case 'oom':
case 'launch-failed':
case 'integrity-failure':
return { message, level: Severity.Critical };
return { message, level: 'fatal' };
default:
return { message, level: Severity.Debug };
return { message, level: 'debug' };
}
}

Expand Down
20 changes: 10 additions & 10 deletions src/main/integrations/electron-minidump.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getCurrentHub, Scope } from '@sentry/core';
import { NodeClient, NodeOptions } from '@sentry/node';
import { Event, Integration, Severity } from '@sentry/types';
import { forget, logger, makeDsn, SentryError } from '@sentry/utils';
import { Event, Integration } from '@sentry/types';
import { logger, makeDsn, SentryError } from '@sentry/utils';
import { app, crashReporter } from 'electron';

import { mergeEvents, normalizeEvent } from '../../common';
Expand Down Expand Up @@ -43,10 +43,10 @@ function getScope(options: NodeOptions): Event {
* @param dsn Dsn
*/
export function minidumpUrlFromDsn(dsn: string): string {
const { host, path, projectId, port, protocol, user } = makeDsn(dsn);
const { host, path, projectId, port, protocol, publicKey } = makeDsn(dsn);
return `${protocol}://${host}${port !== '' ? `:${port}` : ''}${
path !== '' ? `/${path}` : ''
}/api/${projectId}/minidump/?sentry_key=${user}`;
}/api/${projectId}/minidump/?sentry_key=${publicKey}`;
}

/** Sends minidumps via the Electron built-in uploader. */
Expand Down Expand Up @@ -98,7 +98,7 @@ export class ElectronMinidump implements Integration {
// Check if last crash report was likely to have been unreported in the last session
const previousSessionCrashed = unreportedDuringLastSession(crashReporter.getLastCrashReport()?.date);
// Check if a previous session was not closed
forget(checkPreviousSession(previousSessionCrashed));
checkPreviousSession(previousSessionCrashed).catch((error) => logger.error(error));
}

/**
Expand Down Expand Up @@ -145,23 +145,23 @@ export class ElectronMinidump implements Integration {
this._updateEpoch += 1;
const currentEpoch = this._updateEpoch;

forget(
this._getNativeUploaderEvent(scope).then((event) => {
this._getNativeUploaderEvent(scope)
.then((event) => {
if (currentEpoch !== this._updateEpoch) return;

// Update the extra parameters in the main process
const mainParams = this._getNativeUploaderExtraParams(event);
for (const key of Object.keys(mainParams)) {
crashReporter.addExtraParameter(key, mainParams[key]);
}
}),
);
})
.catch((error) => logger.error(error));
}

/** Builds up an event to send with the native Electron uploader */
private async _getNativeUploaderEvent(scope: Scope): Promise<Event> {
const event = mergeEvents(await getEventDefaults(this._customRelease), {
level: Severity.Fatal,
level: 'fatal',
platform: 'native',
tags: { 'event.environment': 'native', event_type: 'native' },
});
Expand Down
6 changes: 2 additions & 4 deletions src/main/integrations/main-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,8 @@ export class MainContext implements Integration {

addGlobalEventProcessor(async (event: Event) => {
const normalized = normalizeEvent(event, app.getAppPath());
const defaults = await getEventDefaults(options?.release);
const fullEvent = mergeEvents(defaults, normalized);

return fullEvent;
const defaults = await getEventDefaults(options?.release, options?.environment);
return mergeEvents(defaults, normalized);
});
}
}
Loading