Skip to content

Commit e13faa5

Browse files
mydeaclaude
authored andcommitted
feat(ember)!: Update to v2 addon format (#23252)
Rebased continuation of #19229 (original author @aklkv) onto current `develop`, with merge conflicts resolved and the failing tests fixed. Migrates `@sentry/ember` from the legacy v1 addon format to the [Ember v2 addon format](https://rfcs.emberjs.com/id/0507-embroider-addons), so the package works with both classic Ember builds and Embroider-optimized builds and no longer depends on `@embroider/macros` at runtime. See #19229 for the full description of the migration. ## Why a new branch The original PR was ~1900 commits behind `develop` and conflicting. Rather than resolve the same conflicts twice across its two commits, I squashed them into one and rebased against the final state once. The subtle part: the migration renamed `packages/ember/addon/` → `src/`. Git therefore saw develop's later behavioral changes to those files as edits to *deleted* files and did **not** surface them as conflicts. Several develop-side changes had to be ported into the new `src/` files by hand: - **Span ops** (#22669, #23086) — route hooks now emit `op: 'function'` with a `code.function.name` attribute; the runloop uses `ui.task`; the transition span uses `router`. `instrumentRoutePerformance.ts` still carried the old `ui.ember.route.*` ops and had to be updated. - **URL attributes** (#22095, #22415) — `url.path` / `url.full` / `url.template` on router spans, reconciled onto the PR's restructured `instrumentEmberAppInstanceForPerformance.ts`. ## Build/tooling reconciliation - Re-added the nested `typescript: ~5.8.0` devDependency pin. `develop` upgraded to TypeScript 7 (the native compiler, which drops `typescript/lib/tsc`), and glint's declaration build needs the classic JS compiler — the same stop-gap `develop` already applies to ember (see #19435). Without the pin the declaration build fails with `ERR_PACKAGE_PATH_NOT_EXPORTED`. - Bumped `@sentry/browser` / `@sentry/core` from the PR's stale `10.53.1` to `10.67.0` and added `@sentry/conventions` (now imported by the ported instrumentation). - Removed the PR's `import/no-unresolved` oxlint rule (doesn't exist in this repo's oxlint 1.75) and wrapped `URL_FULL` in `filterCollectedUrl()` for the `sdk/no-unfiltered-url-attributes` rule, which now applies since the code lives under `src/**`. ## Test fixes The originally-failing tests came down to three things: - **Span-op port** above — fixed the `captures correct spans for navigation` assertions. - **Missing `traceLifecycle: 'static'`** in the two new e2e apps (`ember-strict-resolver`, `ember-vite`). `develop` made span-streaming the default and disables it in the ember test apps (#22588); the new apps predated that, so their performance tests hung waiting for transaction events that never arrived under streaming. - **Stale assertions** in `ember-strict-resolver`'s `sentry-performance.test.ts`, updated from the old `ui.ember.*` op schema to the new `router` / `function` / `ui.task` ops. All four ember e2e apps pass (`ember-classic` 6/6, `ember-embroider` 6/6, `ember-strict-resolver` 10/10, `ember-vite` 5/5), along with the ember unit tests, lint, and build. Supersedes #19229. --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent c56f617 commit e13faa5

167 files changed

Lines changed: 4651 additions & 6800 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

MIGRATION.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,80 @@ The experimental opt-in this replaces was removed:
659659
+ sentryCloudflareVitePlugin();
660660
```
661661

662+
### `@sentry/ember` is now a v2 addon with manual setup
663+
664+
Affected SDKs: `@sentry/ember`.
665+
666+
`@sentry/ember` is now a [v2 (Embroider) addon](https://rfcs.emberjs.com/id/0507-embroider-v2-package-format/), so it builds cleanly under Embroider and Vite in addition to classic builds. Because v2 addons cannot auto-configure the host app, Sentry is no longer wired up from `config/environment.js` and no longer registers its own initializer. You now call `Sentry.init()` yourself and opt into performance instrumentation explicitly. A full walkthrough lives in [`packages/ember/UPGRADE.md`](./packages/ember/UPGRADE.md).
667+
668+
**1. Initialize Sentry in `app/app.ts` instead of `config/environment.js`.** Remove the `'@sentry/ember'` block from `config/environment.js` and call `init()` before your `Application` class:
669+
670+
```ts
671+
// config/environment.js
672+
ENV.sentryDsn = process.env.E2E_TEST_DSN;
673+
```
674+
675+
```typescript
676+
// app/app.ts
677+
import Application from '@ember/application';
678+
import Resolver from 'ember-resolver';
679+
import loadInitializers from 'ember-load-initializers';
680+
import config from 'my-app/config/environment';
681+
import * as Sentry from '@sentry/ember';
682+
683+
Sentry.init({
684+
dsn: config.sentryDsn,
685+
tracesSampleRate: 1.0,
686+
// all @sentry/browser options are supported
687+
});
688+
689+
export default class App extends Application {
690+
modulePrefix = config.modulePrefix;
691+
podModulePrefix = config.podModulePrefix;
692+
Resolver = Resolver;
693+
}
694+
695+
loadInitializers(App, config.modulePrefix);
696+
```
697+
698+
The former `@sentry/ember` config keys map onto arguments you now pass directly: `sentry` options become `Sentry.init()` options, and the `disable*` performance flags move to `instrumentAppInstancePerformance()` (see below). `disablePerformance` no longer exists as a single switch — omit the instance-initializer entirely to disable performance instrumentation.
699+
700+
**2. Opt into performance instrumentation with an instance-initializer.** Automatic performance instrumentation is gone; add it yourself:
701+
702+
```typescript
703+
// app/instance-initializers/sentry-performance.ts
704+
import type ApplicationInstance from '@ember/application/instance';
705+
import { instrumentAppInstancePerformance } from '@sentry/ember';
706+
707+
export function initialize(appInstance: ApplicationInstance): void {
708+
instrumentAppInstancePerformance(appInstance, {
709+
// former config/environment flags live here now, e.g.:
710+
// disableRunloopPerformance: false,
711+
// disableInstrumentComponents: false,
712+
});
713+
}
714+
715+
export default { initialize };
716+
```
717+
718+
FastBoot is detected automatically, so client-side instrumentation is skipped during server rendering with no extra configuration.
719+
720+
**3. `instrumentRoutePerformance` is unchanged.** Wrapping individual routes works exactly as before:
721+
722+
```typescript
723+
// app/routes/posts.ts
724+
import Route from '@ember/routing/route';
725+
import { instrumentRoutePerformance } from '@sentry/ember';
726+
727+
class PostsRoute extends Route {
728+
async model() {
729+
return this.store.findAll('post');
730+
}
731+
}
732+
733+
export default instrumentRoutePerformance(PostsRoute);
734+
```
735+
662736
## 3. Removed APIs
663737

664738
### `@sentry/core` / All SDKs

dev-packages/e2e-tests/test-applications/ember-classic/app/app.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ import Resolver from 'ember-resolver';
66
import config from './config/environment';
77

88
Sentry.init({
9+
dsn: config.sentryDsn,
910
traceLifecycle: 'static',
11+
tracesSampleRate: 1,
1012
replaysSessionSampleRate: 1,
1113
replaysOnErrorSampleRate: 1,
14+
tracePropagationTargets: ['localhost', 'doesntexist.example'],
1215
tunnel: `http://localhost:3031/`, // proxy server
1316
});
1417

dev-packages/e2e-tests/test-applications/ember-classic/app/config/environment.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ declare const config: {
1111
podModulePrefix: string;
1212
locationType: 'history' | 'hash' | 'none' | 'auto';
1313
rootURL: string;
14+
sentryDsn: string;
1415
APP: Record<string, unknown>;
1516
};
1617

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type ApplicationInstance from '@ember/application/instance';
2+
import { instrumentAppInstancePerformance } from '@sentry/ember';
3+
4+
export function initialize(appInstance: ApplicationInstance): void {
5+
instrumentAppInstancePerformance(appInstance, {
6+
minimumRunloopQueueDuration: 0,
7+
minimumComponentRenderDuration: 0,
8+
});
9+
}
10+
11+
export default {
12+
initialize,
13+
};

dev-packages/e2e-tests/test-applications/ember-classic/config/environment.js

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,22 +19,7 @@ module.exports = function (environment) {
1919
},
2020
};
2121

22-
ENV['@sentry/ember'] = {
23-
sentry: {
24-
tracesSampleRate: 1,
25-
dsn: process.env.E2E_TEST_DSN,
26-
tracePropagationTargets: ['localhost', 'doesntexist.example'],
27-
browserTracingOptions: {
28-
_experiments: {
29-
// This lead to some flaky tests, as that is sometimes logged
30-
enableLongTask: false,
31-
},
32-
},
33-
},
34-
ignoreEmberOnErrorWarning: true,
35-
minimumRunloopQueueDuration: 0,
36-
minimumComponentRenderDuration: 0,
37-
};
22+
ENV.sentryDsn = process.env.E2E_TEST_DSN;
3823

3924
if (environment === 'development') {
4025
// ENV.APP.LOG_RESOLVER = true;

dev-packages/e2e-tests/test-applications/ember-classic/tests/errors.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ test('sends an error', async ({ page }) => {
3131

3232
test('assigns the correct transaction value after a navigation', async ({ page }) => {
3333
const pageloadTxnPromise = waitForTransaction('ember-classic', async transactionEvent => {
34-
return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload';
34+
return !!transactionEvent.transaction && transactionEvent.contexts?.trace?.op === 'pageload';
3535
});
3636

3737
const errorPromise = waitForError('ember-classic', async errorEvent => {

dev-packages/e2e-tests/test-applications/ember-classic/tests/performance.test.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { waitForTransaction } from '@sentry-internal/test-utils';
33

44
test('sends a pageload transaction with a parameterized URL', async ({ page }) => {
55
const transactionPromise = waitForTransaction('ember-classic', async transactionEvent => {
6-
return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload';
6+
return !!transactionEvent.transaction && transactionEvent.contexts?.trace?.op === 'pageload';
77
});
88

99
await page.goto(`/`);
@@ -33,11 +33,11 @@ test('sends a pageload transaction with a parameterized URL', async ({ page }) =
3333

3434
test('sends a navigation transaction with a parameterized URL', async ({ page }) => {
3535
const pageloadTxnPromise = waitForTransaction('ember-classic', async transactionEvent => {
36-
return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload';
36+
return !!transactionEvent.transaction && transactionEvent.contexts?.trace?.op === 'pageload';
3737
});
3838

3939
const navigationTxnPromise = waitForTransaction('ember-classic', async transactionEvent => {
40-
return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'navigation';
40+
return !!transactionEvent.transaction && transactionEvent.contexts?.trace?.op === 'navigation';
4141
});
4242

4343
await page.goto(`/`);
@@ -68,11 +68,11 @@ test('sends a navigation transaction with a parameterized URL', async ({ page })
6868

6969
test('sends a navigation transaction even if the pageload span is still active', async ({ page }) => {
7070
const pageloadTxnPromise = waitForTransaction('ember-classic', async transactionEvent => {
71-
return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload';
71+
return !!transactionEvent.transaction && transactionEvent.contexts?.trace?.op === 'pageload';
7272
});
7373

7474
const navigationTxnPromise = waitForTransaction('ember-classic', async transactionEvent => {
75-
return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'navigation';
75+
return !!transactionEvent.transaction && transactionEvent.contexts?.trace?.op === 'navigation';
7676
});
7777

7878
await page.goto(`/`);
@@ -127,11 +127,11 @@ test('sends a navigation transaction even if the pageload span is still active',
127127

128128
test('captures correct spans for navigation', async ({ page }) => {
129129
const pageloadTxnPromise = waitForTransaction('ember-classic', async transactionEvent => {
130-
return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'pageload';
130+
return !!transactionEvent.transaction && transactionEvent.contexts?.trace?.op === 'pageload';
131131
});
132132

133133
const navigationTxnPromise = waitForTransaction('ember-classic', async transactionEvent => {
134-
return !!transactionEvent?.transaction && transactionEvent.contexts?.trace?.op === 'navigation';
134+
return !!transactionEvent.transaction && transactionEvent.contexts?.trace?.op === 'navigation';
135135
});
136136

137137
await page.goto(`/tracing`);

dev-packages/e2e-tests/test-applications/ember-embroider/app/app.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,12 @@ import loadInitializers from 'ember-load-initializers';
55
import Resolver from 'ember-resolver';
66

77
Sentry.init({
8+
dsn: config.sentryDsn,
89
traceLifecycle: 'static',
10+
tracesSampleRate: 1,
911
replaysSessionSampleRate: 1,
1012
replaysOnErrorSampleRate: 1,
13+
tracePropagationTargets: ['localhost', 'doesntexist.example'],
1114
tunnel: `http://localhost:3031/`, // proxy server
1215
});
1316
export default class App extends Application {

dev-packages/e2e-tests/test-applications/ember-embroider/app/config/environment.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ declare const config: {
1111
podModulePrefix: string;
1212
locationType: 'history' | 'hash' | 'none' | 'auto';
1313
rootURL: string;
14+
sentryDsn: string;
1415
APP: Record<string, unknown>;
1516
};
1617

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import type ApplicationInstance from '@ember/application/instance';
2+
import { instrumentAppInstancePerformance } from '@sentry/ember';
3+
4+
export function initialize(appInstance: ApplicationInstance): void {
5+
instrumentAppInstancePerformance(appInstance, {
6+
minimumRunloopQueueDuration: 0,
7+
minimumComponentRenderDuration: 0,
8+
});
9+
}
10+
11+
export default {
12+
initialize,
13+
};

0 commit comments

Comments
 (0)