From 7b65f27f9005426faf1865de11dff65f12c7b959 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Wed, 29 Jul 2026 15:49:44 +0200 Subject: [PATCH 1/6] e2e --- .../test-applications/astro-7-orchestrion/astro.config.mjs | 6 ------ .../test-applications/astro-7-orchestrion/package.json | 1 - 2 files changed, 7 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/astro.config.mjs b/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/astro.config.mjs index bca1420e1124..3eecaa7d420b 100644 --- a/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/astro.config.mjs +++ b/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/astro.config.mjs @@ -1,6 +1,5 @@ import node from '@astrojs/node'; import sentry from '@sentry/astro'; -import { sentryOrchestrionPlugin } from '@sentry/server-utils/orchestrion/vite'; // @ts-check import { defineConfig } from 'astro/config'; @@ -18,9 +17,4 @@ export default defineConfig({ adapter: node({ mode: 'standalone', }), - vite: { - // Run the orchestrion code transform on the Vite SSR bundle so instrumented - // DB drivers get `diagnostics_channel` publishers injected at build time. - plugins: [sentryOrchestrionPlugin()], - }, }); diff --git a/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/package.json b/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/package.json index 5d39c4e4b7c8..cfa13769990a 100644 --- a/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/package.json +++ b/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/package.json @@ -18,7 +18,6 @@ "@sentry-internal/test-utils": "link:../../../test-utils", "@sentry/astro": "file:../../packed/sentry-astro-packed.tgz", "@sentry/node": "file:../../packed/sentry-node-packed.tgz", - "@sentry/server-utils": "file:../../packed/sentry-server-utils-packed.tgz", "astro": "^7.0.6", "ioredis": "5.10.1", "mysql": "^2.18.1" From f72aa25356f71cebc2f6b74947855975fda3de86 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Wed, 29 Jul 2026 15:50:46 +0200 Subject: [PATCH 2/6] sdk --- packages/astro/package.json | 1 + packages/astro/src/integration/cloudflare.ts | 2 +- packages/astro/src/integration/index.ts | 12 ++ packages/astro/test/integration/index.test.ts | 133 ++++++++++++++++-- 4 files changed, 139 insertions(+), 9 deletions(-) diff --git a/packages/astro/package.json b/packages/astro/package.json index 621ae4b6a381..6341d5ae3ab9 100644 --- a/packages/astro/package.json +++ b/packages/astro/package.json @@ -60,6 +60,7 @@ "@sentry/core": "10.67.0", "@sentry/conventions": "^0.16.0", "@sentry/node": "10.67.0", + "@sentry/server-utils": "10.67.0", "@sentry/bundler-plugins": "10.67.0" }, "devDependencies": { diff --git a/packages/astro/src/integration/cloudflare.ts b/packages/astro/src/integration/cloudflare.ts index 2d6d02ba3ed0..93311a735248 100644 --- a/packages/astro/src/integration/cloudflare.ts +++ b/packages/astro/src/integration/cloudflare.ts @@ -4,7 +4,7 @@ import { builtinModules } from 'module'; // Derived from Astro's own config type rather than imported from `vite` directly: Astro bundles its // own Vite version, which differs across the Astro majors we support. A plugin typed against any // single Vite version is not assignable to `updateConfig({ vite: { plugins } })` for the others. -type VitePlugin = Extract['plugins']>[number], { name: string }>; +export type VitePlugin = Extract['plugins']>[number], { name: string }>; // Build a set of all Node.js built-in module names, including both // bare names (e.g. "fs") and "node:" prefixed names (e.g. "node:fs"). diff --git a/packages/astro/src/integration/index.ts b/packages/astro/src/integration/index.ts index 7845308a7536..02867e6adce3 100644 --- a/packages/astro/src/integration/index.ts +++ b/packages/astro/src/integration/index.ts @@ -1,8 +1,10 @@ import { sentryVitePlugin } from '@sentry/bundler-plugins/vite'; +import { sentryOrchestrionPlugin } from '@sentry/server-utils/orchestrion/vite'; import type { AstroConfig, AstroIntegration, AstroIntegrationLogger } from 'astro'; import * as fs from 'fs'; import { createRequire } from 'module'; import * as path from 'path'; +import type { VitePlugin } from './cloudflare'; import { sentryCloudflareNodeWarningPlugin, sentryCloudflareVitePlugin } from './cloudflare'; import { buildClientSnippet, buildSdkInitFileImportSnippet, buildServerSnippet } from './snippets'; import type { SentryOptions } from './types'; @@ -34,6 +36,7 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => { sourcemaps, // todo(v11): Extract `release` build time option here - cannot be done currently, because it conflicts with the `DeprecatedRuntimeOptions` type // release, + buildTimeInstrumentation, bundleSizeOptimizations, applicationKey, unstable_sentryVitePluginOptions, @@ -167,6 +170,15 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => { const isCloudflare = config?.adapter?.name?.startsWith('@astrojs/cloudflare'); const isCloudflareWorkers = isCloudflare && !isCloudflarePages(); + // TODO: Cloudflare/workerd needs different wiring — skipped for now. + if (sdkEnabled.server && !isCloudflare) { + updateConfig({ + vite: { + plugins: [sentryOrchestrionPlugin({ buildTimeInstrumentation }) as VitePlugin], + }, + }); + } + if (isCloudflare) { try { const _require = createRequire(`${process.cwd()}/`); diff --git a/packages/astro/test/integration/index.test.ts b/packages/astro/test/integration/index.test.ts index 7012140f92bf..99e19b0f0879 100644 --- a/packages/astro/test/integration/index.test.ts +++ b/packages/astro/test/integration/index.test.ts @@ -10,6 +10,26 @@ vi.mock('@sentry/bundler-plugins/vite', () => ({ sentryVitePlugin: vi.fn(args => sentryVitePluginSpy(args)), })); +// Stub the orchestrion plugin so these stay pure wiring tests (no apm code transformer pulled in). +// Mirror the real plugin's contract: `buildTimeInstrumentation: false` yields the inert variant. +const orchestrionVite = vi.fn((options?: { buildTimeInstrumentation?: boolean }) => ({ + name: options?.buildTimeInstrumentation === false ? 'sentry-orchestrion-disabled' : 'sentry-orchestrion-vite', +})); +vi.mock('@sentry/server-utils/orchestrion/vite', () => ({ + sentryOrchestrionPlugin: (options?: { buildTimeInstrumentation?: boolean }) => orchestrionVite(options), +})); + +// The cloudflare adapter path resolves `@sentry/cloudflare` via `createRequire` and calls +// `process.exit(1)` when it's missing. Stub the resolver so it always "finds" the package, +// keeping these tests hermetic regardless of what's installed in `node_modules`. +vi.mock('module', async requireActual => { + const actual = await requireActual(); + return { + ...actual, + createRequire: () => ({ resolve: () => '@sentry/cloudflare' }), + }; +}); + process.env = { ...process.env, SENTRY_AUTH_TOKEN: 'my-token', @@ -23,7 +43,7 @@ const config = { } as AstroConfig; const baseConfigHookObject = { - logger: { warn: vi.fn(), info: vi.fn() }, + logger: { warn: vi.fn(), info: vi.fn(), error: vi.fn() }, addMiddleware: vi.fn(), }; @@ -46,7 +66,8 @@ describe('sentryAstro integration', () => { // @ts-expect-error - the hook exists and we only need to pass what we actually use await integration.hooks['astro:config:setup']({ ...baseConfigHookObject, updateConfig, injectScript, config }); - expect(updateConfig).toHaveBeenCalledTimes(1); + // one call for the sourcemaps vite plugin, one for the orchestrion plugin + expect(updateConfig).toHaveBeenCalledTimes(2); expect(updateConfig).toHaveBeenCalledWith({ vite: { build: { @@ -55,6 +76,11 @@ describe('sentryAstro integration', () => { plugins: ['sentryVitePlugin'], }, }); + expect(updateConfig).toHaveBeenCalledWith({ + vite: { + plugins: [{ name: 'sentry-orchestrion-vite' }], + }, + }); expect(sentryVitePluginSpy).toHaveBeenCalledTimes(1); expect(sentryVitePluginSpy).toHaveBeenCalledWith( @@ -294,7 +320,13 @@ describe('sentryAstro integration', () => { // @ts-expect-error - the hook exists and we only need to pass what we actually use await integration.hooks['astro:config:setup']({ ...baseConfigHookObject, updateConfig, injectScript, config }); - expect(updateConfig).toHaveBeenCalledTimes(0); + // only the orchestrion plugin is wired, no sourcemaps plugin + expect(updateConfig).toHaveBeenCalledTimes(1); + expect(updateConfig).toHaveBeenCalledWith({ + vite: { + plugins: [{ name: 'sentry-orchestrion-vite' }], + }, + }); expect(sentryVitePluginSpy).toHaveBeenCalledTimes(0); }); @@ -307,7 +339,13 @@ describe('sentryAstro integration', () => { // @ts-expect-error - the hook exists and we only need to pass what we actually use await integration.hooks['astro:config:setup']({ ...baseConfigHookObject, updateConfig, injectScript, config }); - expect(updateConfig).toHaveBeenCalledTimes(0); + // only the orchestrion plugin is wired, no sourcemaps plugin + expect(updateConfig).toHaveBeenCalledTimes(1); + expect(updateConfig).toHaveBeenCalledWith({ + vite: { + plugins: [{ name: 'sentry-orchestrion-vite' }], + }, + }); expect(sentryVitePluginSpy).toHaveBeenCalledTimes(0); }); @@ -318,11 +356,12 @@ describe('sentryAstro integration', () => { // @ts-expect-error - the hook exists and we only need to pass what we actually use await integration.hooks['astro:config:setup']({ ...baseConfigHookObject, updateConfig, injectScript, config }); - expect(updateConfig).toHaveBeenCalledTimes(1); + // one call for the sourcemaps vite plugin, one for the orchestrion plugin + expect(updateConfig).toHaveBeenCalledTimes(2); expect(sentryVitePluginSpy).toHaveBeenCalledTimes(1); }); - it("doesn't add the Vite plugin in dev mode", async () => { + it("doesn't add the sourcemaps Vite plugin in dev mode", async () => { const integration = sentryAstro({ sourceMapsUploadOptions: { enabled: true }, }); @@ -337,7 +376,13 @@ describe('sentryAstro integration', () => { command: 'dev', }); - expect(updateConfig).toHaveBeenCalledTimes(0); + // the sourcemaps plugin is skipped in dev, but the orchestrion plugin is still wired + expect(updateConfig).toHaveBeenCalledTimes(1); + expect(updateConfig).toHaveBeenCalledWith({ + vite: { + plugins: [{ name: 'sentry-orchestrion-vite' }], + }, + }); expect(sentryVitePluginSpy).toHaveBeenCalledTimes(0); }); @@ -348,12 +393,84 @@ describe('sentryAstro integration', () => { expect(integration.hooks['astro:config:setup']).toBeDefined(); // @ts-expect-error - the hook exists and we only need to pass what we actually use - await integration.hooks['astro:config:setup']({ updateConfig, injectScript, config }); + await integration.hooks['astro:config:setup']({ ...baseConfigHookObject, updateConfig, injectScript, config }); + // neither the sourcemaps nor the orchestrion plugin should be wired expect(updateConfig).toHaveBeenCalledTimes(0); + expect(orchestrionVite).not.toHaveBeenCalled(); expect(sentryVitePluginSpy).toHaveBeenCalledTimes(0); }); + it('adds the orchestrion plugin by default', async () => { + const integration = sentryAstro({}); + + expect(integration.hooks['astro:config:setup']).toBeDefined(); + // @ts-expect-error - the hook exists and we only need to pass what we actually use + await integration.hooks['astro:config:setup']({ ...baseConfigHookObject, updateConfig, injectScript, config }); + + expect(orchestrionVite).toHaveBeenCalledWith({ buildTimeInstrumentation: undefined }); + expect(updateConfig).toHaveBeenCalledWith({ + vite: { + plugins: [{ name: 'sentry-orchestrion-vite' }], + }, + }); + }); + + it('adds an inert orchestrion plugin when `buildTimeInstrumentation` is `false`', async () => { + const integration = sentryAstro({ buildTimeInstrumentation: false }); + + expect(integration.hooks['astro:config:setup']).toBeDefined(); + // @ts-expect-error - the hook exists and we only need to pass what we actually use + await integration.hooks['astro:config:setup']({ ...baseConfigHookObject, updateConfig, injectScript, config }); + + expect(orchestrionVite).toHaveBeenCalledWith({ buildTimeInstrumentation: false }); + expect(updateConfig).toHaveBeenCalledWith({ + vite: { + plugins: [{ name: 'sentry-orchestrion-disabled' }], + }, + }); + }); + + it("doesn't add the orchestrion plugin for the cloudflare adapter", async () => { + const integration = sentryAstro({}); + + const cloudflareConfig = { ...config, adapter: { name: '@astrojs/cloudflare' } } as AstroConfig; + + expect(integration.hooks['astro:config:setup']).toBeDefined(); + // @ts-expect-error - the hook exists and we only need to pass what we actually use + await integration.hooks['astro:config:setup']({ + ...baseConfigHookObject, + updateConfig, + injectScript, + config: cloudflareConfig, + }); + + expect(orchestrionVite).not.toHaveBeenCalled(); + expect(updateConfig).not.toHaveBeenCalledWith({ + vite: { + plugins: [{ name: 'sentry-orchestrion-vite' }], + }, + }); + }); + + it("doesn't warn about deprecated options when `buildTimeInstrumentation` is set", async () => { + const integration = sentryAstro({ buildTimeInstrumentation: false }); + + const logger = { warn: vi.fn(), info: vi.fn() }; + + expect(integration.hooks['astro:config:setup']).toBeDefined(); + // @ts-expect-error - the hook exists and we only need to pass what we actually use + await integration.hooks['astro:config:setup']({ + ...baseConfigHookObject, + updateConfig, + injectScript, + config, + logger, + }); + + expect(logger.warn).not.toHaveBeenCalledWith(expect.stringContaining('buildTimeInstrumentation')); + }); + it.each([{}, { enabled: true }])('injects client and server init scripts', async options => { const integration = sentryAstro(options); From b172e5efbbec80990e6c66726aa8455c432df39a Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Thu, 30 Jul 2026 10:21:06 +0200 Subject: [PATCH 3/6] test(astro): Fix type-check failure in orchestrion deprecation test The multi-line hook-call argument pushed the `AstroIntegrationLogger` type mismatch onto the `logger` property line, past the single `@ts-expect-error` covering the call. Collapse to a single-line call (matching the sibling deprecation test) so the suppression covers the whole argument. Co-Authored-By: Claude Opus 4.8 --- packages/astro/test/integration/index.test.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/astro/test/integration/index.test.ts b/packages/astro/test/integration/index.test.ts index 99e19b0f0879..68ef1dd5e33a 100644 --- a/packages/astro/test/integration/index.test.ts +++ b/packages/astro/test/integration/index.test.ts @@ -460,13 +460,7 @@ describe('sentryAstro integration', () => { expect(integration.hooks['astro:config:setup']).toBeDefined(); // @ts-expect-error - the hook exists and we only need to pass what we actually use - await integration.hooks['astro:config:setup']({ - ...baseConfigHookObject, - updateConfig, - injectScript, - config, - logger, - }); + await integration.hooks['astro:config:setup']({ updateConfig, injectScript, config, logger }); expect(logger.warn).not.toHaveBeenCalledWith(expect.stringContaining('buildTimeInstrumentation')); }); From 82cdcd2d39caf46684b7845ba96d4c0ec994d1ce Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Thu, 30 Jul 2026 10:33:57 +0200 Subject: [PATCH 4/6] docs(astro): Restore orchestrion wiring rationale comment lost in merge Co-Authored-By: Claude Opus 4.8 --- packages/astro/src/integration/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/astro/src/integration/index.ts b/packages/astro/src/integration/index.ts index 02867e6adce3..32593a0d2c32 100644 --- a/packages/astro/src/integration/index.ts +++ b/packages/astro/src/integration/index.ts @@ -170,6 +170,10 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => { const isCloudflare = config?.adapter?.name?.startsWith('@astrojs/cloudflare'); const isCloudflareWorkers = isCloudflare && !isCloudflarePages(); + // Wire up the orchestrion code transform so instrumented server-side dependencies (e.g. + // `mysql`, `ioredis`) get `diagnostics_channel` publishers injected into the SSR bundle at + // build time, with no manual plugin setup. The plugin opts out internally when + // `buildTimeInstrumentation` is `false`. // TODO: Cloudflare/workerd needs different wiring — skipped for now. if (sdkEnabled.server && !isCloudflare) { updateConfig({ From f0e65b40d6273915ada765b89d6bc87594661864 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Thu, 30 Jul 2026 11:32:04 +0200 Subject: [PATCH 5/6] test(e2e/astro): Run orchestrion e2e in dev mode too Add `test:dev` (TEST_ENV=development, `astro dev`) alongside the existing production run and make `test:assert` run both, mirroring the nextjs e2e apps. This exercises the auto-wired orchestrion transform in Vite's dev SSR path, not just the production bundle. Co-Authored-By: Claude Opus 4.8 --- .../test-applications/astro-7-orchestrion/package.json | 4 +++- .../astro-7-orchestrion/playwright.config.mjs | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/package.json b/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/package.json index cfa13769990a..9aab06eb717c 100644 --- a/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/package.json +++ b/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/package.json @@ -9,7 +9,9 @@ "astro": "astro", "start": "node ./dist/server/entry.mjs", "test:build": "pnpm install && pnpm build", - "test:assert": "TEST_ENV=production playwright test" + "test:prod": "TEST_ENV=production playwright test", + "test:dev": "TEST_ENV=development playwright test", + "test:assert": "pnpm test:prod && pnpm test:dev" }, "//": "Need to use ioredis 5.10.1 because that's the last version before they support tracing channels", "dependencies": { diff --git a/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/playwright.config.mjs index 047fc701fa50..469a395dd1d4 100644 --- a/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/playwright.config.mjs +++ b/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/playwright.config.mjs @@ -1,7 +1,13 @@ import { getPlaywrightConfig } from '@sentry-internal/test-utils'; +const testEnv = process.env.TEST_ENV; + +if (!testEnv) { + throw new Error('No test env defined'); +} + const config = getPlaywrightConfig({ - startCommand: 'pnpm start', + startCommand: testEnv === 'development' ? 'pnpm dev --port 3030' : 'pnpm start', }); export default { From 43b4301acd845f72176d6d4adec3f81efe521f81 Mon Sep 17 00:00:00 2001 From: Charly Gomez Date: Thu, 30 Jul 2026 11:49:48 +0200 Subject: [PATCH 6/6] Revert "test(e2e/astro): Run orchestrion e2e in dev mode too" This reverts commit f0e65b40d6273915ada765b89d6bc87594661864. --- .../test-applications/astro-7-orchestrion/package.json | 4 +--- .../astro-7-orchestrion/playwright.config.mjs | 8 +------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/package.json b/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/package.json index 9aab06eb717c..cfa13769990a 100644 --- a/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/package.json +++ b/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/package.json @@ -9,9 +9,7 @@ "astro": "astro", "start": "node ./dist/server/entry.mjs", "test:build": "pnpm install && pnpm build", - "test:prod": "TEST_ENV=production playwright test", - "test:dev": "TEST_ENV=development playwright test", - "test:assert": "pnpm test:prod && pnpm test:dev" + "test:assert": "TEST_ENV=production playwright test" }, "//": "Need to use ioredis 5.10.1 because that's the last version before they support tracing channels", "dependencies": { diff --git a/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/playwright.config.mjs b/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/playwright.config.mjs index 469a395dd1d4..047fc701fa50 100644 --- a/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/playwright.config.mjs +++ b/dev-packages/e2e-tests/test-applications/astro-7-orchestrion/playwright.config.mjs @@ -1,13 +1,7 @@ import { getPlaywrightConfig } from '@sentry-internal/test-utils'; -const testEnv = process.env.TEST_ENV; - -if (!testEnv) { - throw new Error('No test env defined'); -} - const config = getPlaywrightConfig({ - startCommand: testEnv === 'development' ? 'pnpm dev --port 3030' : 'pnpm start', + startCommand: 'pnpm start', }); export default {