diff --git a/.cursor/BUGBOT.md b/.cursor/BUGBOT.md index 7091a161f177..afbd17c05ff9 100644 --- a/.cursor/BUGBOT.md +++ b/.cursor/BUGBOT.md @@ -103,6 +103,12 @@ Keep reviews high-signal. Prefer actionable, high-confidence findings over specu - Only waiting for a request, after an action is performed. Instead, start waiting, perform action, await request promise. - Race conditions when waiting on multiple requests. Ensure that waiting checks are unique enough and don't depend on a hard order when there's a chance that telemetry can be sent in arbitrary order. - Timeouts or sleeps in tests. Instead suggest concrete events or other signals to wait on. +- Flag cold module loading inside timed test callbacks or their helpers when + it can safely run during collection or setup. It can exhaust the timeout + before the behavior under test runs. Prefer static imports, narrower + modules, or setup hooks while preserving isolation and mock order. + Preserve imports whose evaluation timing is itself being tested. + Only flag concrete avoidable loading, not dynamic imports in general. - Flag usage of `getFirstEnvelope*`, `getMultipleEnvelope*` or related test helpers in E2E tests. These are NOT reliable anymore. Instead suggest helpers like `waitForTransaction`, `waitForError`, `waitForSpans`, etc. - Flag any new or modified `docker-compose.yml` under `dev-packages/node-integration-tests/suites/` where a service does not define a `healthcheck:`. The runner uses `docker compose up --wait` and relies on healthchecks to know when services are actually ready; without one the test will race the service's startup. - Flag new Cloudflare tests that wrap the worker by hand with `withSentry`, `instrumentDurableObjectWithSentry`, `instrumentAgentWithSentry` or `instrumentWorkflowWithSentry`. This covers new suites under `dev-packages/cloudflare-integration-tests/suites/`, new E2E apps under `dev-packages/e2e-tests/test-applications/` that run on Cloudflare, and new Cloudflare variants of existing E2E apps (an entry in `sentryTest.variants` of the app's `package.json`, for example `hono-4 (cloudflare)`). App names do not always start with `cloudflare-`. Recommend the Sentry Cloudflare Vite plugin instead: `plugins: [cloudflare(), sentryCloudflareVitePlugin()]` in `vite.config.*` (`sentryCloudflareVitePlugin` from `@sentry/cloudflare/vite`), and the SDK options in an `instrument.server.ts` next to the worker entry that default-exports `defineCloudflareOptions(...)`. Do not flag tests whose purpose is to test one of these wrappers, or apps that do not build with Vite (for example Next.js on OpenNext). @@ -112,6 +118,7 @@ Keep reviews high-signal. Prefer actionable, high-confidence findings over specu - When any `setTimeout` or `setInterval` timers are started in a code path that can end up in server runtime packages (e.g. `@sentry/core` or `@sentry/node`), flag if neither `timeout.unref()` nor `safeUnref()` are called. Not unref'ing a timer can keep CLI-like applications or node scripts from exiting immediately, due to the process waiting on timers started by the SDK. - Flag Node-only imports (`fs`, `path`, `child_process`, `os`, `net`, `http`, `https`, `node:*`, etc.) in code paths that ship to non-Node runtimes (`@sentry/browser`, `@sentry/cloudflare`, `@sentry/deno`, `@sentry/bun`, or shared code in `@sentry/core` / `@sentry/browser-utils` reachable from those entry points). When the dependency is unavoidable, isolate it behind a runtime check, dynamic `import()`, or a Node-only entry point — never at the top level of a cross-runtime module. +- Flag a new package, or a new `exports` entry in a `package.json` under `packages/`, that can load on a server runtime (Node, Bun, Deno, Cloudflare Workers, Vercel Edge) when its package has no entry in `CASES` or `BUILD_TIME_CASES` in `dev-packages/bundler-tests/tests/runtime-entries.test.ts`. That test checks that server runtime entries do not reach build-time tooling (bundler plugins, Vite, Babel, WASM). For packages it already covers, the test fails on its own when an export is not classified, so do not flag those. Recommend adding the package to `devDependencies` in `dev-packages/bundler-tests/package.json`, and each export to `CASES` (runtime), `BUILD_TIME_CASES` (build-time), or `CLIENT_ENTRIES` (browser). Do not flag browser-only packages. ## What NOT to Flag diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml index 2a4b331fd3f6..5f51f918c34b 100644 --- a/.github/actions/install-dependencies/action.yml +++ b/.github/actions/install-dependencies/action.yml @@ -1,64 +1,30 @@ name: 'Install pnpm dependencies' -description: 'Installs pnpm dependencies into node_modules folders on the Namespace cache volume.' +description: 'Installs pnpm dependencies and caches them.' + +outputs: + cache_key: + description: 'The dependency cache key' + value: ${{ steps.compute_lockfile_hash.outputs.hash }} runs: using: 'composite' steps: - - name: List dependency paths - id: paths - run: | - { - echo 'paths<> "$GITHUB_OUTPUT" - shell: bash - - - name: Mount dependency cache - uses: ./.github/actions/mount-pnpm-store - with: - path: ${{ steps.paths.outputs.paths }} - - name: Install pnpm uses: ./.github/actions/install-pnpm - # When the node_modules layout changes, `pnpm install` keeps the `.bin` scripts of the workspace packages, which - # then point into a folder that the new layout does not have. Branches that change `nodeLinker` share this cache - # volume, so start from empty node_modules folders when the layout on the volume differs, or when an earlier - # install already left such broken `.bin` scripts behind. - - name: Remove node_modules of another layout - run: | - mapfile -t dirs < <(grep '/node_modules$' <<< "$DEPENDENCY_PATHS") - expected=$(pnpm config get node-linker) - if [[ "$expected" == undefined ]]; then - expected=isolated - fi - actual=$(node -p 'try { JSON.parse(require("fs").readFileSync("node_modules/.modules.yaml", "utf8")).nodeLinker ?? "" } catch { "" }') - if [[ -n "$actual" && "$actual" != "$expected" ]]; then - reason="The node_modules folders use the $actual layout, but nodeLinker is $expected." - else - broken=$(node "$GITHUB_ACTION_PATH/find-broken-bin.mjs" "${dirs[@]}") - if [[ -n "$broken" ]]; then - reason="The target of $broken does not exist." - fi - fi - if [[ -n "$reason" ]]; then - echo "$reason Removing the node_modules folders." - for dir in "${dirs[@]}"; do - if [[ -d "$dir" ]]; then - # The folders are mounted from the cache volume, so empty them instead of removing them - find "$dir/" -mindepth 1 -maxdepth 1 -exec rm -rf {} + - fi - done - fi - env: - DEPENDENCY_PATHS: ${{ steps.paths.outputs.paths }} + - name: Compute dependency cache key + id: compute_lockfile_hash + run: node ./scripts/dependency-hash-key.js >> "$GITHUB_OUTPUT" shell: bash - # The cache volume can hold node_modules of another commit or branch. This is a no-op when they match - # pnpm-lock.yaml. + - name: Check dependency cache + uses: actions/cache@v6.1.0 + id: cache_dependencies + with: + path: ${{ env.CACHED_DEPENDENCY_PATHS }} + key: ${{ steps.compute_lockfile_hash.outputs.hash }} + - name: Install dependencies + if: steps.cache_dependencies.outputs.cache-hit != 'true' run: pnpm install --frozen-lockfile shell: bash diff --git a/.github/actions/install-dependencies/find-broken-bin.mjs b/.github/actions/install-dependencies/find-broken-bin.mjs deleted file mode 100644 index 24aa8753cac5..000000000000 --- a/.github/actions/install-dependencies/find-broken-bin.mjs +++ /dev/null @@ -1,34 +0,0 @@ -// Prints the first `.bin` entry in the given node_modules folders whose target does not exist. -import { existsSync, lstatSync, readdirSync, readFileSync } from 'node:fs'; -import { join, resolve } from 'node:path'; - -// The pnpm shell shim runs the target with `exec "$basedir_abs/"` (`$basedir` in older versions) -const SHIM_TARGET = /^\s*exec \S+\s+"\$basedir(?:_abs)?\/([^"]+)"/m; - -for (const dir of process.argv.slice(2)) { - const binDir = join(dir, '.bin'); - let names; - try { - names = readdirSync(binDir); - } catch { - continue; - } - - for (const name of names) { - const bin = join(binDir, name); - const stat = lstatSync(bin); - let target; - if (stat.isSymbolicLink()) { - target = bin; - } else if (stat.isFile() && stat.size < 64 * 1024) { - const match = readFileSync(bin, 'utf8').match(SHIM_TARGET); - target = match && resolve(binDir, match[1]); - } - - if (target && !existsSync(target)) { - // eslint-disable-next-line no-console - console.log(bin); - process.exit(0); - } - } -} diff --git a/.github/actions/install-playwright/action.yml b/.github/actions/install-playwright/action.yml index 378e77666093..ca6c8a2d9583 100644 --- a/.github/actions/install-playwright/action.yml +++ b/.github/actions/install-playwright/action.yml @@ -1,5 +1,5 @@ name: 'Install Playwright dependencies' -description: 'Installs Playwright browsers and system dependencies, keeping both on the Namespace cache volume.' +description: 'Installs Playwright dependencies and caches them.' inputs: browsers: description: 'What browsers to install.' @@ -26,28 +26,43 @@ runs: shell: bash working-directory: ${{ inputs.cwd }} - - name: Mount Playwright cache - uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0 + - name: Restore cached playwright binaries + uses: actions/cache/restore@v6.1.0 + id: playwright-cache if: steps.playwright-version.outputs.version != '' with: - cache: | - playwright - apt + path: | + ~/.cache/ms-playwright + # Bump the iteration when bumping runner images to use a new cache + key: playwright-${{ runner.os }}-iteration-1-${{ steps.playwright-version.outputs.version }} - # `--with-deps` names packages that the runner image already has (glib, mesa, nss, ...), and `apt-get install` would - # upgrade them to the latest Ubuntu update. That is half of the download on a cold cache, and the image versions work. - - name: Keep installed apt packages - if: steps.playwright-version.outputs.version != '' - run: echo 'APT::Get::Upgrade "false";' | sudo tee /etc/apt/apt.conf.d/99-no-upgrade > /dev/null + # We always install all browsers, if uncached + # `npx` runs outside the repo, so it can't pick up a `playwright` that a parallel `pnpm install` is still linking. + - name: Install Playwright dependencies (uncached) + env: + PLAYWRIGHT_VERSION: ${{ steps.playwright-version.outputs.version }} + run: npx -y "playwright@$PLAYWRIGHT_VERSION" install chromium webkit firefox --with-deps + if: steps.playwright-version.outputs.version != '' && steps.playwright-cache.outputs.cache-hit != 'true' shell: bash + working-directory: ${{ runner.temp }} - # Downloads only the browsers and apt packages that are not on the cache volume yet. `npx` runs outside the repo, - # so it can't pick up a `playwright` that a parallel `pnpm install` is still linking. - - name: Install Playwright - if: steps.playwright-version.outputs.version != '' + - name: Install Playwright system dependencies only (cached) env: PLAYWRIGHT_BROWSERS: ${{ inputs.browsers || 'chromium webkit firefox' }} PLAYWRIGHT_VERSION: ${{ steps.playwright-version.outputs.version }} - run: npx -y "playwright@$PLAYWRIGHT_VERSION" install --with-deps $PLAYWRIGHT_BROWSERS + run: npx -y "playwright@$PLAYWRIGHT_VERSION" install-deps $PLAYWRIGHT_BROWSERS + if: steps.playwright-version.outputs.version != '' && steps.playwright-cache.outputs.cache-hit == 'true' shell: bash working-directory: ${{ runner.temp }} + + # Only store cache on develop branch + - name: Store cached playwright binaries + uses: actions/cache/save@v6.1.0 + if: + steps.playwright-version.outputs.version != '' && github.event_name == 'push' && github.ref == + 'refs/heads/develop' + with: + path: | + ~/.cache/ms-playwright + # Bump the iteration when bumping runner images to use a new cache + key: playwright-${{ runner.os }}-iteration-1-${{ steps.playwright-version.outputs.version }} diff --git a/.github/actions/mount-pnpm-store/action.yml b/.github/actions/mount-pnpm-store/action.yml deleted file mode 100644 index fed4e40c1b73..000000000000 --- a/.github/actions/mount-pnpm-store/action.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: 'Mount pnpm store' -description: - 'Mounts the pnpm store, the pnpm and npm caches and the given paths from the Namespace cache volume. Use it before - `install-pnpm`, so `install-pnpm` takes pnpm from the cache volume.' - -inputs: - path: - description: 'Paths to mount from the cache volume in addition to the pnpm store and caches, one per line.' - required: false - default: '' - -runs: - using: 'composite' - steps: - # Replaces the `pnpm` mode of nscloud-cache-action until it works without pnpm on PATH. The mode runs `pnpm store - # path`, so it only works after `install-pnpm`, when `pnpm self-update` has already downloaded pnpm. - # The cache directory holds `lockfile-verified.jsonl`. With it, `pnpm install` skips the supply-chain check of a - # lockfile that was already verified, which otherwise fetches the metadata of every package from the registry. - - name: Set pnpm store and cache directories - run: | - echo "PNPM_CONFIG_STORE_DIR=$HOME/.cache/pnpm-store" >> "$GITHUB_ENV" - echo "PNPM_CONFIG_CACHE_DIR=$HOME/.cache/pnpm" >> "$GITHUB_ENV" - shell: bash - - # The `npm` mode keeps the npm cache of the `npm ci` in `pnpm/action-setup`, which installs the bootstrap pnpm - - name: Mount cache paths - uses: namespacelabs/nscloud-cache-action@1124a6f3ce44e5cf84cc22111530961f4d2a15f9 # v1.7.0 - with: - cache: npm - path: | - ~/.cache/pnpm-store - ~/.cache/pnpm - ${{ inputs.path }} diff --git a/.github/actions/prepare-e2e-test/action.yml b/.github/actions/prepare-e2e-test/action.yml index 4a4c8a012026..6af9d8ce37a6 100644 --- a/.github/actions/prepare-e2e-test/action.yml +++ b/.github/actions/prepare-e2e-test/action.yml @@ -46,9 +46,6 @@ runs: uses: denoland/setup-deno@v2.0.5 with: deno-version: ${{ inputs.deno-version || 'v2.8.3' }} - # Keeps the pnpm store on the cache volume for the `pnpm install` of the test application - - name: Mount pnpm store - uses: ./.github/actions/mount-pnpm-store - name: Install pnpm uses: ./.github/actions/install-pnpm diff --git a/.github/actions/restore-cache/action.yml b/.github/actions/restore-cache/action.yml index c647c74405b9..daa5943f9877 100644 --- a/.github/actions/restore-cache/action.yml +++ b/.github/actions/restore-cache/action.yml @@ -1,13 +1,30 @@ name: 'Restore dependency & build cache' -description: 'Installs dependencies from the Namespace cache volume and restores the build output.' +description: 'Restore the dependency & build cache.' + +inputs: + dependency_cache_key: + description: 'The dependency cache key' + required: true runs: using: 'composite' steps: + - name: Install pnpm + uses: ./.github/actions/install-pnpm + + - name: Check dependency cache + id: dep-cache + uses: actions/cache/restore@v6.1.0 + with: + path: ${{ env.CACHED_DEPENDENCY_PATHS }} + key: ${{ inputs.dependency_cache_key }} + - name: Restore build artifacts uses: actions/download-artifact@v7 with: name: build-output - name: Install dependencies - uses: ./.github/actions/install-dependencies + if: steps.dep-cache.outputs.cache-hit != 'true' + run: pnpm install --frozen-lockfile + shell: bash diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f0fddbffa61e..87a7ed005b20 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -11,30 +11,120 @@ updates: - package-ecosystem: 'npm' directory: '/' schedule: - interval: 'weekly' + interval: 'daily' ignore: - - dependency-name: '@opentelemetry/instrumentation' - - dependency-name: '@opentelemetry/instrumentation-*' - dependency-name: 'typescript' - # 2.110.0 raised the engine requirement to Node >=22, while this repo runs Node 20. - - dependency-name: '@supabase/supabase-js' - versions: ['>=2.110.0'] + # Keep shared Prisma dependencies on v6; v7 has a dedicated integration suite. + - dependency-name: 'prisma' + update-types: ['version-update:semver-major'] + - dependency-name: '@prisma/client' + update-types: ['version-update:semver-major'] + # Major upgrades of SDK targets and coupled tooling require an explicit compatibility decision. + - dependency-name: '@angular/*' + update-types: ['version-update:semver-major'] + - dependency-name: '@angular-devkit/*' + update-types: ['version-update:semver-major'] + - dependency-name: 'ng-packagr' + update-types: ['version-update:semver-major'] + - dependency-name: 'astro' + update-types: ['version-update:semver-major'] + - dependency-name: 'ember-source' + update-types: ['version-update:semver-major'] + - dependency-name: 'effect' + update-types: ['version-update:semver-major'] + - dependency-name: 'next' + update-types: ['version-update:semver-major'] + - dependency-name: 'nuxt' + update-types: ['version-update:semver-major'] + - dependency-name: '@nuxt/kit' + update-types: ['version-update:semver-major'] + - dependency-name: '@nuxt/schema' + update-types: ['version-update:semver-major'] + - dependency-name: '@nuxt/nitro-server' + update-types: ['version-update:semver-major'] + - dependency-name: 'react' + update-types: ['version-update:semver-major'] + - dependency-name: 'react-dom' + update-types: ['version-update:semver-major'] + - dependency-name: '@types/react' + update-types: ['version-update:semver-major'] + - dependency-name: '@types/express' + update-types: ['version-update:semver-major'] + - dependency-name: 'react-router' + update-types: ['version-update:semver-major'] + - dependency-name: '@react-router/*' + update-types: ['version-update:semver-major'] + - dependency-name: '@remix-run/node' + update-types: ['version-update:semver-major'] + - dependency-name: '@remix-run/react' + update-types: ['version-update:semver-major'] + - dependency-name: '@remix-run/server-runtime' + update-types: ['version-update:semver-major'] + - dependency-name: '@remix-run/router' + update-types: ['version-update:semver-major'] + - dependency-name: '@solidjs/router' + update-types: ['version-update:semver-major'] + - dependency-name: '@solidjs/start' + update-types: ['version-update:semver-major'] + - dependency-name: 'svelte' + update-types: ['version-update:semver-major'] + - dependency-name: '@sveltejs/kit' + update-types: ['version-update:semver-major'] + - dependency-name: 'vue' + update-types: ['version-update:semver-major'] + - dependency-name: '@nestjs/common' + update-types: ['version-update:semver-major'] + - dependency-name: '@nestjs/core' + update-types: ['version-update:semver-major'] + - dependency-name: 'hono' + update-types: ['version-update:semver-major'] + - dependency-name: 'nitro' + update-types: ['version-update:semver-major'] + - dependency-name: 'nitropack' + update-types: ['version-update:semver-major'] + - dependency-name: 'h3' + update-types: ['version-update:semver-major'] + - dependency-name: '@cloudflare/workers-types' + update-types: ['version-update:semver-major'] + - dependency-name: 'wrangler' + update-types: ['version-update:semver-major'] + - dependency-name: 'bun-types' + update-types: ['version-update:semver-major'] + - dependency-name: '@types/node' + update-types: ['version-update:semver-major'] + - dependency-name: '@edge-runtime/types' + update-types: ['version-update:semver-major'] + - dependency-name: '@opentelemetry/api' + update-types: ['version-update:semver-major'] + - dependency-name: 'vite' + update-types: ['version-update:semver-major'] + - dependency-name: 'webpack' + update-types: ['version-update:semver-major'] + # These are also peer dependencies. The `increase` strategy raises the peer range too, + # which drops support for older versions (a breaking change). + - dependency-name: '@tanstack/vue-router' + - dependency-name: '@tanstack/solid-router' + - dependency-name: 'solid-js' + - dependency-name: 'elysia' + # node-integration-tests pins langchain v0 to test the lowest supported versions. + # Dependabot uses one version per workspace, so it would also change the v1 pins in cloudflare-integration-tests. + - dependency-name: 'langchain' + - dependency-name: '@langchain/*' groups: - opentelemetry: + dependencies: patterns: - - '@opentelemetry/*' - remix: - patterns: - - '@remix-run/*' - react-router: - patterns: - - 'react-router' - - 'react-router-dom' - - '@react-router/*' + - '*' versioning-strategy: increase commit-message: prefix: feat prefix-development: chore include: scope exclude-paths: + - 'dev-packages/browser-integration-tests/**' + - 'dev-packages/bundler-plugin-integration-tests/**' + - 'dev-packages/bundler-tests/**' + - 'dev-packages/bun-integration-tests/**' + - 'dev-packages/node-integration-tests/**' + - 'dev-packages/deno-integration-tests/**' + - 'dev-packages/cloudflare-integration-tests/**' - 'dev-packages/e2e-tests/test-applications/**' diff --git a/.github/workflows/auto-fix-issue.yml b/.github/workflows/auto-fix-issue.yml index 8feb478c6edb..f495e8bc8e6b 100644 --- a/.github/workflows/auto-fix-issue.yml +++ b/.github/workflows/auto-fix-issue.yml @@ -23,7 +23,7 @@ concurrency: jobs: auto-fix-issue: - runs-on: namespace-profile-sentry-javascript + runs-on: ubuntu-latest environment: ci-triage permissions: # Required to create a new branch and commit the fix @@ -77,7 +77,7 @@ jobs: - name: Try to fix the issue with Claude id: triage - uses: anthropics/claude-code-action@8ce9314fa9a404564fa7e954cd84f25bcba2b829 # v1 + uses: anthropics/claude-code-action@d535f5172dec02b204866b393d6b20b9b2aaf98d # v1 env: ANTHROPIC_BASE_URL: https://openrouter.ai/api with: diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml index 38802d1c0ad6..ab50a76d818b 100644 --- a/.github/workflows/auto-release.yml +++ b/.github/workflows/auto-release.yml @@ -13,7 +13,7 @@ permissions: jobs: release: - runs-on: namespace-profile-sentry-javascript-small + runs-on: ubuntu-24.04 name: 'Prepare a new version' steps: @@ -55,7 +55,7 @@ jobs: node-version-file: 'package.json' - name: Prepare release - uses: getsentry/craft@25028d0646040cc0a669ae3314cbf884f4c4347a # v2.31.2 + uses: getsentry/craft@3f1f02d50b08b8c509a28d2cc2abf594e9a28011 # v3.5.0 if: github.event.pull_request.merged == true && steps.version-regex.outputs.match != '' && steps.get_version.outputs.version != '' diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f6d386c40b43..9c408c806846 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,6 +29,12 @@ concurrency: env: HEAD_COMMIT: ${{ github.event.inputs.commit || github.sha }} + CACHED_DEPENDENCY_PATHS: | + ${{ github.workspace }}/node_modules + ${{ github.workspace }}/packages/*/node_modules + ${{ github.workspace }}/dev-packages/*/node_modules + ~/.cache/mongodb-binaries/ + # build-output artifact paths are computed in job_build (step output from # pnpm run ci:print-build-artifact-paths — Nx merged outputs for build:transpile, and build:types). @@ -52,11 +58,8 @@ env: # This option both controls the `v8-compile-cache-lib` and `v8-compile-cache` packages. DISABLE_V8_COMPILE_CACHE: '1' -# Jobs that install the monorepo dependencies share one Namespace cache volume through `overrides.cache-tag`, E2E jobs -# share another one (`-e2e`). The optional Cloudflare integration tests replace the installed wrangler with the latest -# one, so they use a third one (`-latest-wrangler`). Jobs that do not use a cache volume have no cache tag. Pull request -# jobs get their own volumes (`-pr`), so they cannot change the node_modules that develop and release builds install -# from. +# Jobs that need the monorepo dependencies restore them from the GitHub Actions cache (see `restore-cache`), +# keyed by the lockfile hash. E2E jobs install their test application separately and do not use that cache. jobs: job_get_metadata: uses: ./.github/workflows/ci-metadata.yml @@ -69,9 +72,7 @@ jobs: job_build: name: Build needs: job_get_metadata - runs-on: - namespace-profile-sentry-javascript-large;overrides.cache-tag=sentry-javascript${{ github.event_name == - 'pull_request' && '-pr' || '' }} + runs-on: ubuntu-24.04 timeout-minutes: 15 if: | needs.job_get_metadata.outputs.changed_any_code == 'true' || @@ -97,6 +98,7 @@ jobs: - name: Install Dependencies uses: ./.github/actions/install-dependencies + id: install_dependencies - name: Check for Affected Nx Projects uses: ./.github/actions/nx-affected-list @@ -140,7 +142,7 @@ jobs: - name: Determine which test applications should be run id: matrix run: - pnpm --silent run ci:build-matrix --base=${{ (github.event_name == 'pull_request' && + pnpm --silent run ci:build-matrix --shards=2 --base=${{ (github.event_name == 'pull_request' && needs.job_get_metadata.outputs.changed_ci != 'true' && github.event.pull_request.base.sha) || '' }} >> $GITHUB_OUTPUT working-directory: dev-packages/e2e-tests @@ -154,6 +156,7 @@ jobs: working-directory: dev-packages/e2e-tests outputs: + dependency_cache_key: ${{ steps.install_dependencies.outputs.cache_key }} changed_node_integration: ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, '@sentry-internal/node-integration-tests') }} @@ -184,19 +187,19 @@ jobs: changed_aws_serverless: ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, '@sentry/aws-serverless') }} - e2e-matrix: ${{ steps.matrix.outputs.matrix }} + e2e-matrix-1: ${{ steps.matrix.outputs.matrix-1 }} + e2e-matrix-2: ${{ steps.matrix.outputs.matrix-2 }} e2e-matrix-optional: ${{ steps.matrix-optional.outputs.matrix }} job_build_layer: name: Build Lambda layer needs: [job_get_metadata, job_build] if: - needs.job_build.outputs.changed_aws_serverless == 'true' || contains(needs.job_build.outputs.e2e-matrix, - 'aws-serverless') || github.event_name != 'pull_request' + needs.job_build.outputs.changed_aws_serverless == 'true' || contains(needs.job_build.outputs.e2e-matrix-1, + 'aws-serverless') || contains(needs.job_build.outputs.e2e-matrix-2, 'aws-serverless') || github.event_name != + 'pull_request' timeout-minutes: 10 - runs-on: - namespace-profile-sentry-javascript;overrides.cache-tag=sentry-javascript${{ github.event_name == 'pull_request' - && '-pr' || '' }} + runs-on: ubuntu-24.04 steps: - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v7 @@ -216,6 +219,8 @@ jobs: - name: Restore caches uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Build Lambda layer run: pnpm run build:layer @@ -233,9 +238,7 @@ jobs: name: Build bundles needs: [job_get_metadata, job_build] timeout-minutes: 10 - runs-on: - namespace-profile-sentry-javascript;overrides.cache-tag=sentry-javascript${{ github.event_name == 'pull_request' - && '-pr' || '' }} + runs-on: ubuntu-24.04 steps: - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v7 @@ -255,6 +258,8 @@ jobs: - name: Restore caches uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Build bundles run: pnpm run build:bundle @@ -271,7 +276,7 @@ jobs: job_check_branches: name: Check PR branches needs: job_get_metadata - runs-on: namespace-profile-sentry-javascript-small + runs-on: ubuntu-24.04 if: github.event_name == 'pull_request' permissions: pull-requests: write @@ -287,9 +292,7 @@ jobs: name: Size Check needs: [job_get_metadata, job_build, job_build_bundles] timeout-minutes: 15 - runs-on: - namespace-profile-sentry-javascript;overrides.cache-tag=sentry-javascript${{ github.event_name == 'pull_request' - && '-pr' || '' }} + runs-on: ubuntu-24.04 if: github.event_name == 'pull_request' || needs.job_get_metadata.outputs.is_base_branch == 'true' || needs.job_get_metadata.outputs.is_release == 'true' @@ -304,6 +307,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Restore build bundle artifacts uses: actions/download-artifact@v8 with: @@ -322,9 +327,7 @@ jobs: # inter-package dependencies resolve cleanly. needs: [job_get_metadata, job_build] timeout-minutes: 10 - runs-on: - namespace-profile-sentry-javascript-large;overrides.cache-tag=sentry-javascript${{ github.event_name == - 'pull_request' && '-pr' || '' }} + runs-on: ubuntu-24.04 steps: - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v7 @@ -336,6 +339,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - parallel: - name: Lint source files run: pnpm run lint @@ -364,7 +369,7 @@ jobs: needs: [job_get_metadata] # `pnpm dedupe --check` fetches metadata for every package from npm, so this job can take 8+ min on PRs. timeout-minutes: 20 - runs-on: namespace-profile-sentry-javascript + runs-on: ubuntu-24.04 if: | needs.job_get_metadata.outputs.changed_deps == 'true' || needs.job_get_metadata.outputs.is_base_branch == 'true' || @@ -395,9 +400,7 @@ jobs: name: Check file formatting needs: [job_get_metadata] timeout-minutes: 10 - runs-on: - namespace-profile-sentry-javascript;overrides.cache-tag=sentry-javascript${{ github.event_name == 'pull_request' - && '-pr' || '' }} + runs-on: ubuntu-24.04 steps: - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v7 @@ -418,9 +421,7 @@ jobs: job_artifacts: name: Upload Artifacts needs: [job_get_metadata, job_build, job_build_layer, job_build_bundles, job_build_tarballs] - runs-on: - namespace-profile-sentry-javascript;overrides.cache-tag=sentry-javascript${{ github.event_name == 'pull_request' - && '-pr' || '' }} + runs-on: ubuntu-24.04 # Build artifacts are only needed for releasing workflow. if: needs.job_get_metadata.outputs.is_release == 'true' steps: @@ -434,6 +435,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Restore tarball artifacts uses: actions/download-artifact@v8 @@ -472,9 +475,7 @@ jobs: name: Browser Unit Tests needs: [job_get_metadata, job_build] timeout-minutes: 10 - runs-on: - namespace-profile-sentry-javascript-large;overrides.cache-tag=sentry-javascript${{ github.event_name == - 'pull_request' && '-pr' || '' }} + runs-on: ubuntu-24.04 steps: - name: Check out base commit (${{ github.event.pull_request.base.sha }}) uses: actions/checkout@v7 @@ -492,6 +493,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Run affected tests run: pnpm run test:pr:browser --base=${{ github.event.pull_request.base.sha }} @@ -506,9 +509,7 @@ jobs: needs: [job_get_metadata, job_build] if: needs.job_build.outputs.changed_bun == 'true' || github.event_name != 'pull_request' timeout-minutes: 10 - runs-on: - namespace-profile-sentry-javascript;overrides.cache-tag=sentry-javascript${{ github.event_name == 'pull_request' - && '-pr' || '' }} + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: @@ -529,6 +530,8 @@ jobs: bun-version: ${{ matrix.bun }} - name: Restore caches uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Run tests run: | pnpm run test:ci:bun @@ -538,9 +541,7 @@ jobs: needs: [job_get_metadata, job_build] if: needs.job_build.outputs.changed_deno == 'true' || github.event_name != 'pull_request' timeout-minutes: 10 - runs-on: - namespace-profile-sentry-javascript;overrides.cache-tag=sentry-javascript${{ github.event_name == 'pull_request' - && '-pr' || '' }} + runs-on: ubuntu-24.04 steps: - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v7 @@ -553,9 +554,11 @@ jobs: - name: Set up Deno uses: denoland/setup-deno@v2.0.5 with: - deno-version: ${{ matrix.deno-version || 'v2.8.3' }} + deno-version: 'v2.8.3' - name: Restore caches uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Run tests run: | cd packages/deno @@ -566,13 +569,13 @@ jobs: name: Node (${{ matrix.node }}) Unit Tests needs: [job_get_metadata, job_build] timeout-minutes: 10 - runs-on: - namespace-profile-sentry-javascript-large;overrides.cache-tag=sentry-javascript${{ github.event_name == - 'pull_request' && '-pr' || '' }} + runs-on: ubuntu-24.04 + # Node 27 is an alpha: its ABI and APIs can change, so its failures must not fail the required checks. + continue-on-error: ${{ matrix.node == '^27.0.0-0' }} strategy: fail-fast: false matrix: - node: [20.19, 22, 24, 26] + node: [20.19, 22, 24, 26, '^27.0.0-0'] steps: - name: Check out base commit (${{ github.event.pull_request.base.sha }}) uses: actions/checkout@v7 @@ -589,6 +592,13 @@ jobs: node-version: ${{ matrix.node }} - name: Restore caches uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + # The native modules have no prebuilt binaries for the Node 27 alpha, so compile them like an install on a + # user's machine does. + - name: Compile native modules + if: matrix.node == '^27.0.0-0' + run: pnpm rebuild @sentry/node-cpu-profiler @sentry/node-native-stacktrace - name: Run affected tests run: pnpm run test:pr:node --base=${{ github.event.pull_request.base.sha }} @@ -608,9 +618,7 @@ jobs: matrix.project) || ''}}${{ matrix.shard && format(' ({0}/{1})', matrix.shard, matrix.shards) || ''}} Tests needs: [job_get_metadata, job_build, job_build_bundles] if: needs.job_build.outputs.changed_browser_integration == 'true' || github.event_name != 'pull_request' - runs-on: - namespace-profile-sentry-javascript-large;overrides.cache-tag=sentry-javascript${{ github.event_name == - 'pull_request' && '-pr' || '' }} + runs-on: ubuntu-24.04-large-js timeout-minutes: 25 strategy: fail-fast: false @@ -670,6 +678,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Restore build bundle artifacts uses: actions/download-artifact@v8 @@ -706,9 +716,7 @@ jobs: name: PW ${{ matrix.bundle }} Tests needs: [job_get_metadata, job_build, job_build_bundles] if: needs.job_build.outputs.changed_browser_integration == 'true' || github.event_name != 'pull_request' - runs-on: - namespace-profile-sentry-javascript;overrides.cache-tag=sentry-javascript${{ github.event_name == 'pull_request' - && '-pr' || '' }} + runs-on: ubuntu-24.04 timeout-minutes: 15 strategy: fail-fast: false @@ -733,6 +741,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Restore build bundle artifacts uses: actions/download-artifact@v8 @@ -766,14 +776,14 @@ jobs: name: Node (${{ matrix.node }}) Integration Tests (${{ matrix.shard }}/3) needs: [job_get_metadata, job_build] if: needs.job_build.outputs.changed_node_integration == 'true' || github.event_name != 'pull_request' - runs-on: - namespace-profile-sentry-javascript-large;overrides.cache-tag=sentry-javascript${{ github.event_name == - 'pull_request' && '-pr' || '' }} + runs-on: ubuntu-24.04 timeout-minutes: 20 + # Node 27 is an alpha: its ABI and APIs can change, so its failures must not fail the required checks. + continue-on-error: ${{ matrix.node == '^27.0.0-0' }} strategy: fail-fast: false matrix: - node: [20.19, 22, 24, 26] + node: [20.19, 22, 24, 26, '^27.0.0-0'] shard: [1, 2, 3] steps: - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) @@ -799,6 +809,13 @@ jobs: node-version: ${{ matrix.node }} - name: Restore caches uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + # The native modules have no prebuilt binaries for the Node 27 alpha, so compile them like an install on a + # user's machine does. + - name: Compile native modules + if: matrix.node == '^27.0.0-0' + run: pnpm rebuild @sentry/node-cpu-profiler @sentry/node-native-stacktrace - name: Run integration tests working-directory: dev-packages/node-integration-tests @@ -809,9 +826,7 @@ jobs: name: Cloudflare Integration Tests (${{ matrix.shard }}/2) needs: [job_get_metadata, job_build] if: needs.job_build.outputs.changed_cloudflare_integration == 'true' || github.event_name != 'pull_request' - runs-on: - namespace-profile-sentry-javascript;overrides.cache-tag=sentry-javascript${{ github.event_name == 'pull_request' - && '-pr' || '' }} + runs-on: ubuntu-24.04 timeout-minutes: 15 strategy: fail-fast: false @@ -828,11 +843,12 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} # The Vite plugin depends on wrangler and writes config for it, so both move together. In the hoisted layout, # pnpm keeps the nested `node_modules` folders of the old versions (for example an old workerd under - # `node_modules/wrangler`), so the install starts from empty `node_modules` folders. They are mount points of the - # cache volume, so only their contents are deleted. + # `node_modules/wrangler`), so the install starts from empty `node_modules` folders. - name: Install the latest wrangler if: matrix.wrangler == 'latest' run: | @@ -852,9 +868,7 @@ jobs: if: needs.job_get_metadata.outputs.is_release != 'true' && (needs.job_build.outputs.changed_cloudflare_integration == 'true' || github.event_name != 'pull_request') - runs-on: - namespace-profile-sentry-javascript;overrides.cache-tag=sentry-javascript-latest-wrangler${{ github.event_name == - 'pull_request' && '-pr' || '' }} + runs-on: ubuntu-24.04 timeout-minutes: 15 strategy: fail-fast: false @@ -867,14 +881,14 @@ jobs: name: Bundler Plugin Integration Tests (Node ${{ matrix.node }}) needs: [job_get_metadata, job_build] if: needs.job_build.outputs.changed_bundler_plugin_integration == 'true' || github.event_name != 'pull_request' - runs-on: - namespace-profile-sentry-javascript-large;overrides.cache-tag=sentry-javascript${{ github.event_name == - 'pull_request' && '-pr' || '' }} + runs-on: ubuntu-24.04 timeout-minutes: 15 + # Node 27 is an alpha: its ABI and APIs can change, so its failures must not fail the required checks. + continue-on-error: ${{ matrix.node == '^27.0.0-0' }} strategy: fail-fast: false matrix: - node: [20.19, 22, 24, 26] + node: [20.19, 22, 24, 26, '^27.0.0-0'] steps: - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v7 @@ -886,6 +900,8 @@ jobs: node-version: ${{ matrix.node }} - name: Restore caches uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Run integration tests working-directory: dev-packages/bundler-plugin-integration-tests @@ -895,9 +911,7 @@ jobs: name: Bun (${{ matrix.bun }}) Integration Tests (${{ matrix.shard }}/2) needs: [job_get_metadata, job_build] if: needs.job_build.outputs.changed_bun_integration == 'true' || github.event_name != 'pull_request' - runs-on: - namespace-profile-sentry-javascript-large;overrides.cache-tag=sentry-javascript${{ github.event_name == - 'pull_request' && '-pr' || '' }} + runs-on: ubuntu-24.04 timeout-minutes: 15 strategy: fail-fast: false @@ -920,6 +934,8 @@ jobs: bun-version: ${{ matrix.bun }} - name: Restore caches uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Run integration tests working-directory: dev-packages/bun-integration-tests @@ -929,9 +945,7 @@ jobs: name: Deno Integration Tests (${{ matrix.shard }}/2) needs: [job_get_metadata, job_build] if: needs.job_build.outputs.changed_deno_integration == 'true' || github.event_name != 'pull_request' - runs-on: - namespace-profile-sentry-javascript-large;overrides.cache-tag=sentry-javascript${{ github.event_name == - 'pull_request' && '-pr' || '' }} + runs-on: ubuntu-24.04 timeout-minutes: 15 strategy: fail-fast: false @@ -952,6 +966,8 @@ jobs: deno-version: 'v2.8.3' - name: Restore caches uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Build @sentry/deno working-directory: packages/deno run: pnpm run build @@ -974,9 +990,7 @@ jobs: always() && needs.job_build.result == 'success' needs: [job_get_metadata, job_build] - runs-on: - namespace-profile-sentry-javascript-large;overrides.cache-tag=sentry-javascript${{ github.event_name == - 'pull_request' && '-pr' || '' }} + runs-on: ubuntu-24.04 timeout-minutes: 15 steps: - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) @@ -997,9 +1011,13 @@ jobs: - name: Restore caches uses: ./.github/actions/restore-cache + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Build tarballs - run: pnpm run build:tarball + run: | + pnpm run clean:tarballs + pnpm run build:tarballs --parallel=8 - name: Upload tarball artifacts uses: actions/upload-artifact@v7 @@ -1021,20 +1039,20 @@ jobs: compression-level: 6 overwrite: true - job_e2e_tests: + # GitHub Actions allows at most 256 jobs in one matrix, so the E2E tests are split into two jobs that share `env` + # and `steps`. + job_e2e_tests_1: name: E2E ${{ matrix.label || matrix.test-application }} Test # We need to add the `always()` check here because the previous step has this as well :( # See: https://github.com/actions/runner/issues/2205 if: - always() && needs.job_build_tarballs.result == 'success' && needs.job_build.outputs.e2e-matrix !='{"include":[]}' + always() && needs.job_build_tarballs.result == 'success' && needs.job_build.outputs.e2e-matrix-1 != + '{"include":[]}' needs: [job_get_metadata, job_build, job_build_layer, job_build_tarballs] - # Most apps finish in under a minute on 4 vCPUs, so they run on 2. Apps that take longer set `sentryTest.runner`. - runs-on: - ${{ matrix.runner || 'namespace-profile-sentry-javascript-small' }};overrides.cache-tag=sentry-javascript-e2e${{ - github.event_name == 'pull_request' && '-pr' || '' }} + runs-on: ubuntu-24.04 timeout-minutes: 15 - env: - # We just use a dummy DSN here, only send to the tunnel anyhow + # We just use a dummy DSN here, only send to the tunnel anyhow + env: &e2e-env E2E_TEST_DSN: 'https://username@domain/123' # Needed because some apps expect a certain prefix NEXT_PUBLIC_E2E_TEST_DSN: 'https://username@domain/123' @@ -1048,8 +1066,8 @@ jobs: pnpm_config_verify_deps_before_run: 'false' strategy: fail-fast: false - matrix: ${{ fromJson(needs.job_build.outputs.e2e-matrix) }} - steps: + matrix: ${{ fromJson(needs.job_build.outputs.e2e-matrix-1) }} + steps: &e2e-steps - name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }}) uses: actions/checkout@v7 with: @@ -1061,10 +1079,9 @@ jobs: runtime: ${{ matrix.runtime }} deno-version: ${{ matrix.deno-version }} - # Installs Playwright while the app installs and builds. Keep it right before the install: the runner sets up a - # background composite action's steps from the job's shared `github` context, which fails with "Collection was - # modified" if a foreground step starts at that moment. Starting next to `download-artifact` also made it fail to - # read the event file. + # Installs Playwright while the app builds. Keep it right before the build: the runner sets up a background + # composite action's steps from the job's shared `github` context, which fails with "Collection was modified" if + # a foreground step starts at that moment. Starting next to `download-artifact` also made it fail to read the event file. - name: Install Playwright id: install-playwright background: true @@ -1073,23 +1090,6 @@ jobs: browsers: chromium cwd: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }} - # Every build command runs `pnpm install`, so installing first makes that a no-op. Some downloads from the npm - # registry crawl at tens of kB/s for the whole 7 minutes of the build step, and pnpm does not give up on a slow - # download. Another attempt reuses what is already in the store and opens new connections. - # The hoisted variant installs with another node linker, so installing first would only redo the install. - - name: Install E2E app dependencies - if: ${{ !contains(matrix.build-command, 'hoisted') }} - working-directory: ${{ runner.temp }}/test-application - timeout-minutes: 7 - run: | - for attempt in 1 2; do - timeout 150 pnpm install && exit 0 - echo "::warning::pnpm install attempt $attempt failed or did not finish in 150 s, retrying" - done - pnpm install - env: - SENTRY_E2E_WORKSPACE_ROOT: ${{ github.workspace }} - - name: Build E2E app working-directory: ${{ runner.temp }}/test-application timeout-minutes: 7 @@ -1115,6 +1115,20 @@ jobs: overwrite: true retention-days: 7 + job_e2e_tests_2: + name: E2E ${{ matrix.label || matrix.test-application }} Test + if: + always() && needs.job_build_tarballs.result == 'success' && needs.job_build.outputs.e2e-matrix-2 != + '{"include":[]}' + needs: [job_get_metadata, job_build, job_build_layer, job_build_tarballs] + runs-on: ubuntu-24.04 + timeout-minutes: 15 + env: *e2e-env + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.job_build.outputs.e2e-matrix-2) }} + steps: *e2e-steps + # - We skip optional tests on release branches # - The tests run in optional-e2e.yml, so their failures do not mark the PR as failed. This job only waits for # them. optional-e2e.yml reports the results in a PR comment. @@ -1128,8 +1142,7 @@ jobs: needs.job_build.outputs.e2e-matrix-optional != '{"include":[]}' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && github.actor != 'dependabot[bot]' needs: [job_get_metadata, job_build, job_build_tarballs] - # This job only waits for optional-e2e.yml, which can take most of an hour. GitHub runners are free for public - # repos and Namespace runners are paid per minute, so the wait runs here and the tests run on Namespace. + # This job only waits for optional-e2e.yml, which can take most of an hour. runs-on: ubuntu-24.04 timeout-minutes: 60 permissions: @@ -1146,7 +1159,7 @@ jobs: node-version-file: 'package.json' - name: Install pnpm uses: ./.github/actions/install-pnpm - # The Namespace cache volume is not available here, so only install what the action needs + # Only install what the action needs - name: Install action dependencies run: pnpm install --frozen-lockfile --filter @sentry-internal/optional-e2e-gh-action - name: Run optional E2E tests @@ -1175,7 +1188,8 @@ jobs: job_deno_integration_tests, job_browser_playwright_tests, job_browser_loader_tests, - job_e2e_tests, + job_e2e_tests_1, + job_e2e_tests_2, job_artifacts, job_lint, job_check_lockfile, @@ -1184,7 +1198,7 @@ jobs: ] # Always run this, even if a dependent job failed if: always() - runs-on: namespace-profile-sentry-javascript-small + runs-on: ubuntu-24.04 permissions: {} steps: - name: Check for failures diff --git a/.github/workflows/canary.yml b/.github/workflows/canary.yml index 55a6d1f10bef..422cecffa19c 100644 --- a/.github/workflows/canary.yml +++ b/.github/workflows/canary.yml @@ -12,15 +12,6 @@ on: env: HEAD_COMMIT: ${{ github.event.inputs.commit || github.sha }} - CACHED_BUILD_PATHS: | - ${{ github.workspace }}/packages/*/*.tgz - ${{ github.workspace }}/dev-packages/test-utils/*.tgz - ${{ github.workspace }}/node_modules - ${{ github.workspace }}/packages/*/node_modules - ${{ github.workspace }}/dev-packages/*/node_modules - ${{ github.workspace }}/dev-packages/*/build - ${{ github.workspace }}/packages/*/build - permissions: contents: read issues: write @@ -28,8 +19,10 @@ permissions: jobs: job_e2e_prepare: name: Prepare E2E Canary tests - runs-on: namespace-profile-sentry-javascript-large + runs-on: ubuntu-24.04 timeout-minutes: 30 + outputs: + matrix: ${{ steps.matrix.outputs.matrix }} steps: - name: Check out current commit uses: actions/checkout@v7 @@ -41,11 +34,6 @@ jobs: uses: actions/setup-node@v7 with: node-version-file: 'package.json' - - name: Check canary cache - uses: actions/cache@v6.1.0 - with: - path: ${{ env.CACHED_BUILD_PATHS }} - key: canary-${{ env.HEAD_COMMIT }} - name: Install dependencies run: pnpm install - name: Build packages @@ -54,10 +42,37 @@ jobs: - name: Build tarballs run: pnpm run build:tarball + # Same artifacts as in build.yml, so the test jobs can use the shared prepare-e2e-test action + - name: Upload tarball artifacts + uses: actions/upload-artifact@v7 + with: + name: build-tarball-output + path: packages/*/*.tgz + if-no-files-found: error + retention-days: 4 + compression-level: 6 + overwrite: true + + - name: Upload test-utils tarball artifact + uses: actions/upload-artifact@v7 + with: + name: build-test-utils-tarball-output + path: dev-packages/test-utils/*.tgz + if-no-files-found: error + retention-days: 4 + compression-level: 6 + overwrite: true + + - name: Determine canary test applications + id: matrix + run: pnpm --silent run ci:build-matrix-canary >> $GITHUB_OUTPUT + working-directory: dev-packages/e2e-tests + job_e2e_tests: - name: E2E ${{ matrix.label }} Test + name: E2E ${{ matrix.label || matrix.test-application }} Test needs: [job_e2e_prepare] - runs-on: namespace-profile-sentry-javascript + if: needs.job_e2e_prepare.outputs.matrix != '{"include":[]}' + runs-on: ubuntu-24.04 timeout-minutes: 20 env: # We just use a dummy DSN here, only send to the tunnel anyhow @@ -68,105 +83,30 @@ jobs: REACT_APP_E2E_TEST_DSN: 'https://username@domain/123' E2E_TEST_SENTRY_ORG_SLUG: 'sentry-javascript-sdks' E2E_TEST_SENTRY_PROJECT: 'sentry-javascript-e2e-tests' + # The `runtime` of a runtime variant, see dev-packages/e2e-tests/README.md + RUNTIME: ${{ matrix.runtime }} + # Without the root node_modules, `pnpm run` would first install the whole workspace. + pnpm_config_verify_deps_before_run: 'false' strategy: fail-fast: false - matrix: - include: - - test-application: 'angular-22' - build-command: 'test:build-canary' - label: 'angular-22 (next)' - - test-application: 'create-react-app' - build-command: 'test:build-canary' - label: 'create-react-app (canary)' - - test-application: 'nextjs-app-dir' - build-command: 'test:build-15' - label: 'nextjs-app-dir (next@15)' - - test-application: 'nextjs-14' - build-command: 'test:build-canary' - label: 'nextjs-14 (canary)' - - test-application: 'nextjs-14' - build-command: 'test:build-latest' - label: 'nextjs-14 (latest)' - - test-application: 'nextjs-15' - build-command: 'test:build-latest' - label: 'nextjs-15 (latest)' - - test-application: 'nextjs-16' - build-command: 'test:build-canary' - label: 'nextjs-16 (canary)' - - test-application: 'nextjs-16' - build-command: 'test:build-canary-webpack' - label: 'nextjs-16 (canary-webpack)' - - test-application: 'react-create-hash-router' - build-command: 'test:build-canary' - label: 'react-create-hash-router (canary)' - - test-application: 'react-router-6' - build-command: 'test:build-canary' - label: 'react-router-6 (canary)' - - test-application: 'nuxt-3' - build-command: 'test:build-canary' - label: 'nuxt-3 (canary)' - - test-application: 'nuxt-4' - build-command: 'test:build-canary' - label: 'nuxt-4 (canary)' - - test-application: 'tanstackstart-react' - build-command: 'test:build-latest' - label: 'tanstackstart-react (latest)' - - test-application: 'nestjs-12' - build-command: 'test:build-latest' - label: 'nestjs-12 (latest)' + matrix: ${{ fromJson(needs.job_e2e_prepare.outputs.matrix) }} steps: - name: Check out current commit uses: actions/checkout@v7 with: ref: ${{ env.HEAD_COMMIT }} - - name: Install pnpm - uses: ./.github/actions/install-pnpm - # Uses the repo's root node pin for the steps before the app is copied (`test:prepare`, `test:validate`, - # `ci:copy-to-temp`). The app's own node version is set up after the copy below. - - name: Set up Node - uses: actions/setup-node@v7 - with: - node-version-file: 'package.json' - - - name: Restore canary cache - uses: actions/cache/restore@v6.1.0 - with: - path: ${{ env.CACHED_BUILD_PATHS }} - key: canary-${{ env.HEAD_COMMIT }} - - - name: Prepare e2e tests - run: pnpm run test:prepare - working-directory: dev-packages/e2e-tests - - - name: Validate test setup - run: pnpm run test:validate - working-directory: dev-packages/e2e-tests - - - name: Copy to temp - run: - pnpm run ci:copy-to-temp ./test-applications/${{ matrix.test-application }} ${{ runner.temp - }}/test-application - working-directory: dev-packages/e2e-tests - - # Reads the node version that `ci:copy-to-temp` wrote into the copied app's `devEngines.runtime`: the app's own pin - # if it has one, otherwise the one inherited from the repo root. setup-node can't look up parent directories, so - # the temp copy has to carry the resolved version. - - name: Set up Node for the E2E app - uses: actions/setup-node@v7 + - name: Prepare E2E test application + uses: ./.github/actions/prepare-e2e-test with: - node-version-file: '${{ runner.temp }}/test-application/package.json' - - - name: Add pnpm overrides - run: - pnpm run ci:pnpm-overrides ${{ runner.temp }}/test-application ${{ github.workspace - }}/dev-packages/e2e-tests/packed - working-directory: dev-packages/e2e-tests + test-application: ${{ matrix.test-application }} + runtime: ${{ matrix.runtime }} + deno-version: ${{ matrix.deno-version }} - name: Build E2E app working-directory: ${{ runner.temp }}/test-application timeout-minutes: 7 - run: pnpm ${{ matrix.build-command }} + run: ${{ matrix.build-command || 'pnpm test:build' }} - name: Install Playwright uses: ./.github/actions/install-playwright @@ -177,7 +117,7 @@ jobs: - name: Run E2E test working-directory: ${{ runner.temp }}/test-application timeout-minutes: 15 - run: pnpm test:assert + run: ${{ matrix.assert-command || 'pnpm test:assert' }} - name: Create Issue if: failure() && github.event_name == 'schedule' @@ -185,7 +125,7 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} RUN_LINK: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - TITLE: ${{ matrix.label }} Test Failed + TITLE: ${{ matrix.label || matrix.test-application }} Test Failed with: filename: .github/CANARY_FAILURE_TEMPLATE.md update_existing: true diff --git a/.github/workflows/ci-metadata.yml b/.github/workflows/ci-metadata.yml index ed114f394a19..3b5969cc1803 100644 --- a/.github/workflows/ci-metadata.yml +++ b/.github/workflows/ci-metadata.yml @@ -11,7 +11,7 @@ on: description: 'Short SHA + commit message for display' value: ${{ jobs.get_metadata.outputs.commit_label }} is_base_branch: - description: 'Whether the ref is develop, v9, or v8' + description: 'Whether the ref is develop, v10, v9, or v8' value: ${{ jobs.get_metadata.outputs.is_base_branch }} is_release: description: 'Whether the ref is a release branch' @@ -38,7 +38,7 @@ on: jobs: get_metadata: name: Get Metadata - runs-on: namespace-profile-sentry-javascript-small + runs-on: ubuntu-24.04 permissions: pull-requests: read steps: @@ -81,7 +81,8 @@ jobs: commit_label: '${{ env.COMMIT_SHA }}: ${{ env.COMMIT_MESSAGE }}' # Note: These next three have to be checked as strings ('true'/'false')! is_base_branch: - ${{ github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/v9' || github.ref == 'refs/heads/v8'}} + ${{ github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/v10' || github.ref == 'refs/heads/v9' || + github.ref == 'refs/heads/v8'}} is_release: ${{ startsWith(github.ref, 'refs/heads/release/') }} changed_ci: ${{ steps.changed.outputs.workflow == 'true' }} changed_any_code: ${{ steps.changed.outputs.any_code == 'true' }} diff --git a/.github/workflows/cleanup-e2e-workers.yml b/.github/workflows/cleanup-e2e-workers.yml index 917de52e997d..69aae225e408 100644 --- a/.github/workflows/cleanup-e2e-workers.yml +++ b/.github/workflows/cleanup-e2e-workers.yml @@ -8,7 +8,7 @@ jobs: cleanup: # The optional E2E job deploys only for PRs from this repository, so forks never have a worker to delete. if: github.event.pull_request.head.repo.full_name == github.repository - runs-on: namespace-profile-sentry-javascript-small + runs-on: ubuntu-latest permissions: {} timeout-minutes: 5 env: diff --git a/.github/workflows/cleanup-pr-caches.yml b/.github/workflows/cleanup-pr-caches.yml index 4b7dbb4ee476..e4543e524c91 100644 --- a/.github/workflows/cleanup-pr-caches.yml +++ b/.github/workflows/cleanup-pr-caches.yml @@ -6,7 +6,7 @@ on: jobs: cleanup: - runs-on: namespace-profile-sentry-javascript-small + runs-on: ubuntu-latest permissions: # `actions:write` permission is required to delete caches # See also: https://docs.github.com/en/rest/actions/cache?apiVersion=2022-11-28#delete-a-github-actions-cache-for-a-repository-using-a-cache-id diff --git a/.github/workflows/clear-cache.yml b/.github/workflows/clear-cache.yml index 28dba37b8d7c..750b39305d1f 100644 --- a/.github/workflows/clear-cache.yml +++ b/.github/workflows/clear-cache.yml @@ -21,7 +21,7 @@ on: jobs: clear-caches: name: Delete all caches - runs-on: namespace-profile-sentry-javascript + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@v7 diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 79b9bb42a677..4e41c7f5d145 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -32,7 +32,7 @@ concurrency: jobs: analyze: name: Analyze - runs-on: namespace-profile-sentry-javascript-large + runs-on: ubuntu-latest # Skip for pushes from dependabot, which is not supported if: github.event_name == 'pull_request' || github.actor != 'dependabot[bot]' diff --git a/.github/workflows/dependabot-auto-triage.yml b/.github/workflows/dependabot-auto-triage.yml index 56ebdfe42f2f..d20178d76b9a 100644 --- a/.github/workflows/dependabot-auto-triage.yml +++ b/.github/workflows/dependabot-auto-triage.yml @@ -35,6 +35,11 @@ env: FIX_CAP_DEV: '3' # ⬇️ debatable but let's start off with this PR_REVIEWERS: 'getsentry/team-javascript-sdks-framework' + CACHED_DEPENDENCY_PATHS: | + ${{ github.workspace }}/node_modules + ${{ github.workspace }}/packages/*/node_modules + ${{ github.workspace }}/dev-packages/*/node_modules + ~/.cache/mongodb-binaries/ concurrency: group: dependabot-auto-triage @@ -43,7 +48,7 @@ concurrency: jobs: classify: name: Classify alerts & auto-dismiss noise - runs-on: namespace-profile-sentry-javascript + runs-on: ubuntu-24.04 timeout-minutes: 20 outputs: runtime_alerts: ${{ steps.select.outputs.runtime_alerts }} @@ -124,7 +129,7 @@ jobs: if: (github.event_name == 'schedule' || github.event.inputs.mode == 'full') && needs.classify.outputs.runtime_alerts != '' - runs-on: namespace-profile-sentry-javascript + runs-on: ubuntu-24.04 timeout-minutes: 40 environment: ci-triage steps: @@ -155,7 +160,7 @@ jobs: git config user.email "${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com" - name: Open batched runtime fix PR via skill - uses: anthropics/claude-code-action@8ce9314fa9a404564fa7e954cd84f25bcba2b829 # v1 + uses: anthropics/claude-code-action@d535f5172dec02b204866b393d6b20b9b2aaf98d # v1 env: ANTHROPIC_BASE_URL: https://openrouter.ai/api with: @@ -213,7 +218,7 @@ jobs: needs: classify if: (github.event_name == 'schedule' || github.event.inputs.mode == 'full') && needs.classify.outputs.dev_alerts != '' - runs-on: namespace-profile-sentry-javascript + runs-on: ubuntu-24.04 timeout-minutes: 40 environment: ci-triage steps: @@ -244,7 +249,7 @@ jobs: git config user.email "${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com" - name: Open batched dev fix PR via skill - uses: anthropics/claude-code-action@8ce9314fa9a404564fa7e954cd84f25bcba2b829 # v1 + uses: anthropics/claude-code-action@d535f5172dec02b204866b393d6b20b9b2aaf98d # v1 env: ANTHROPIC_BASE_URL: https://openrouter.ai/api with: diff --git a/.github/workflows/enforce-license-compliance.yml b/.github/workflows/enforce-license-compliance.yml index 997cfc71ff30..8d09e421768f 100644 --- a/.github/workflows/enforce-license-compliance.yml +++ b/.github/workflows/enforce-license-compliance.yml @@ -27,7 +27,7 @@ concurrency: jobs: enforce-license-compliance: - runs-on: namespace-profile-sentry-javascript + runs-on: ubuntu-24.04 steps: - name: 'Enforce License Compliance' uses: getsentry/action-enforce-license-compliance@main diff --git a/.github/workflows/external-contributors.yml b/.github/workflows/external-contributors.yml index 6e3a3c5aff8c..1b131781dc05 100644 --- a/.github/workflows/external-contributors.yml +++ b/.github/workflows/external-contributors.yml @@ -12,7 +12,7 @@ jobs: permissions: pull-requests: write contents: write - runs-on: namespace-profile-sentry-javascript + runs-on: ubuntu-24.04 if: | github.event.pull_request.merged == true && github.event.pull_request.author_association != 'COLLABORATOR' diff --git a/.github/workflows/gitflow-merge-conflict.yml b/.github/workflows/gitflow-merge-conflict.yml index 6195a73350d5..102d05b6d03b 100644 --- a/.github/workflows/gitflow-merge-conflict.yml +++ b/.github/workflows/gitflow-merge-conflict.yml @@ -9,7 +9,7 @@ on: jobs: check-merge-conflicts: name: Detect merge conflicts in gitflow PRs - runs-on: namespace-profile-sentry-javascript-small + runs-on: ubuntu-24.04 if: | ${{ contains(github.event.pull_request.labels.*.name, 'Dev: Gitflow') }} permissions: diff --git a/.github/workflows/gitflow-sync-develop.yml b/.github/workflows/gitflow-sync-develop.yml index 21d3c45dc1d0..20f8b335fd0f 100644 --- a/.github/workflows/gitflow-sync-develop.yml +++ b/.github/workflows/gitflow-sync-develop.yml @@ -17,7 +17,7 @@ env: jobs: main: name: Create PR master->develop - runs-on: namespace-profile-sentry-javascript-small + runs-on: ubuntu-24.04 permissions: pull-requests: write contents: write diff --git a/.github/workflows/issue-package-label.yml b/.github/workflows/issue-package-label.yml index c855066f318f..8ac1c7987801 100644 --- a/.github/workflows/issue-package-label.yml +++ b/.github/workflows/issue-package-label.yml @@ -7,7 +7,7 @@ on: jobs: add_labels: name: Add package label - runs-on: namespace-profile-sentry-javascript-small + runs-on: ubuntu-latest if: ${{ !github.event.issue.pull_request }} steps: - name: Get used package from issue body diff --git a/.github/workflows/label-external-prs.yml b/.github/workflows/label-external-prs.yml index 6caf6fae7387..e6a73aaae5a7 100644 --- a/.github/workflows/label-external-prs.yml +++ b/.github/workflows/label-external-prs.yml @@ -9,7 +9,7 @@ jobs: name: Add external label permissions: pull-requests: write - runs-on: namespace-profile-sentry-javascript-small + runs-on: ubuntu-24.04 if: | github.event.pull_request.author_association != 'COLLABORATOR' && github.event.pull_request.author_association != 'MEMBER' diff --git a/.github/workflows/lighthouse.yml b/.github/workflows/lighthouse.yml deleted file mode 100644 index 1073b0c17b51..000000000000 --- a/.github/workflows/lighthouse.yml +++ /dev/null @@ -1,32 +0,0 @@ -name: 'Nightly: Lighthouse' - -# Nightly: builds the Lighthouse fixture app and uploads bundles to the -# lighthouse.sentry.gg lab. Never blocks merges. - -on: - schedule: - - cron: '0 0 * * *' - workflow_dispatch: - -jobs: - build-and-upload: - name: Bundle and upload Lighthouse cells - runs-on: namespace-profile-sentry-javascript-large - timeout-minutes: 30 - env: - LIGHTHOUSE_LAB_URL: ${{ secrets.LIGHTHOUSE_LAB_URL }} - LIGHTHOUSE_UPLOAD_TOKEN: ${{ secrets.LIGHTHOUSE_UPLOAD_TOKEN }} - VITE_E2E_TEST_DSN: 'https://username@domain/123' - steps: - - uses: actions/checkout@v7 - - name: Install pnpm - uses: ./.github/actions/install-pnpm - - uses: actions/setup-node@v7 - with: - node-version-file: 'package.json' - - run: pnpm install --frozen-lockfile - - run: pnpm run build:ci - - run: pnpm run build:tarball - - run: pnpm run test:prepare - working-directory: dev-packages/e2e-tests - - run: node scripts/lighthouse-bundle-and-upload.mjs diff --git a/.github/workflows/optional-e2e.yml b/.github/workflows/optional-e2e.yml index f73eaa256fc0..0fc020c9b327 100644 --- a/.github/workflows/optional-e2e.yml +++ b/.github/workflows/optional-e2e.yml @@ -37,11 +37,7 @@ env: jobs: job_optional_e2e_tests: name: E2E ${{ matrix.label || matrix.test-application }} Test (optional) - # Same cache volume as the E2E jobs in build.yml. The event is always `workflow_dispatch`, so the PR number - # decides if the run gets the PR volume. - runs-on: - namespace-profile-sentry-javascript;overrides.cache-tag=sentry-javascript-e2e${{ inputs.pr_number != '' && '-pr' - || '' }} + runs-on: ubuntu-24.04 timeout-minutes: 15 permissions: contents: read @@ -59,8 +55,7 @@ jobs: E2E_OPENROUTER_API_KEY: ${{ secrets.E2E_OPENROUTER_API_KEY }} # Only this app talks to OpenAI directly (OpenRouter has no Decisions route), so only it gets the key. E2E_OPENAI_DECISIONS_API_KEY: - ${{ matrix.test-application == 'node-openai-decisions-send-to-sentry' && secrets.E2E_OPENAI_DECISIONS_API_KEY || - '' }} + ${{ matrix.test-application == 'node-openai-decisions' && secrets.E2E_OPENAI_DECISIONS_API_KEY || '' }} # Used by test apps that deploy a real Cloudflare Worker, e.g. cloudflare-workers-send-to-sentry CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} diff --git a/.github/workflows/pr-stale-notifier.yml b/.github/workflows/pr-stale-notifier.yml index 3c4816dafebe..13bba260a0b0 100644 --- a/.github/workflows/pr-stale-notifier.yml +++ b/.github/workflows/pr-stale-notifier.yml @@ -15,7 +15,7 @@ concurrency: jobs: stale: - runs-on: namespace-profile-sentry-javascript-small + runs-on: ubuntu-latest steps: - name: Warn and close stale PRs uses: actions/stale@v11 diff --git a/.github/workflows/release-comment-issues.yml b/.github/workflows/release-comment-issues.yml index 88055af892c8..dfb782b1b6d8 100644 --- a/.github/workflows/release-comment-issues.yml +++ b/.github/workflows/release-comment-issues.yml @@ -12,7 +12,7 @@ on: # This workflow is triggered when a release is published jobs: release-comment-issues: - runs-on: namespace-profile-sentry-javascript-small + runs-on: ubuntu-24.04 name: 'Notify issues' steps: - name: Get version diff --git a/.github/workflows/release-size-info.yml b/.github/workflows/release-size-info.yml index 591a14b6b553..a1f75303d1ff 100644 --- a/.github/workflows/release-size-info.yml +++ b/.github/workflows/release-size-info.yml @@ -13,7 +13,7 @@ on: # It fetches the size-limit info from the release branch and adds it to the release jobs: release-size-info: - runs-on: namespace-profile-sentry-javascript-small + runs-on: ubuntu-24.04 name: 'Add size-limit info to release' steps: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e579c239dcfe..0d9d491c87a4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,7 +18,7 @@ permissions: jobs: release: - runs-on: namespace-profile-sentry-javascript-small + runs-on: ubuntu-24.04 name: 'Release a new version' steps: - name: Get auth token @@ -39,7 +39,7 @@ jobs: with: node-version-file: 'package.json' - name: Prepare release - uses: getsentry/craft@25028d0646040cc0a669ae3314cbf884f4c4347a # v2.31.2 + uses: getsentry/craft@3f1f02d50b08b8c509a28d2cc2abf594e9a28011 # v3.5.0 env: GITHUB_TOKEN: ${{ steps.token.outputs.token }} with: diff --git a/.github/workflows/size-check.yml b/.github/workflows/size-check.yml index ababb321b866..3321585750bb 100644 --- a/.github/workflows/size-check.yml +++ b/.github/workflows/size-check.yml @@ -13,10 +13,17 @@ concurrency: group: size-check-label-${{ github.event.pull_request.number }}-${{ github.event.label.name }} cancel-in-progress: true +env: + CACHED_DEPENDENCY_PATHS: | + ${{ github.workspace }}/node_modules + ${{ github.workspace }}/packages/*/node_modules + ${{ github.workspace }}/dev-packages/*/node_modules + ~/.cache/mongodb-binaries/ + jobs: rerun: if: github.event.label.name == 'Accept Bundlesize Increase' - runs-on: namespace-profile-sentry-javascript;overrides.cache-tag=sentry-javascript + runs-on: ubuntu-24.04 timeout-minutes: 60 steps: - name: Check out workflow commit diff --git a/.github/workflows/track-framework-updates.yml b/.github/workflows/track-framework-updates.yml index 3e0172884e7f..cebb7ef821dc 100644 --- a/.github/workflows/track-framework-updates.yml +++ b/.github/workflows/track-framework-updates.yml @@ -18,7 +18,7 @@ concurrency: jobs: track-updates: - runs-on: namespace-profile-sentry-javascript-small + runs-on: ubuntu-latest environment: ci-triage permissions: contents: read @@ -43,7 +43,7 @@ jobs: - name: Run Claude digest id: digest - uses: anthropics/claude-code-action@8ce9314fa9a404564fa7e954cd84f25bcba2b829 # v1 + uses: anthropics/claude-code-action@d535f5172dec02b204866b393d6b20b9b2aaf98d # v1 env: ANTHROPIC_BASE_URL: https://openrouter.ai/api with: diff --git a/.github/workflows/validate-pr.yml b/.github/workflows/validate-pr.yml index f3c9f955547f..251134290f00 100644 --- a/.github/workflows/validate-pr.yml +++ b/.github/workflows/validate-pr.yml @@ -6,11 +6,11 @@ on: jobs: validate-pr: - runs-on: namespace-profile-sentry-javascript-small + runs-on: ubuntu-24.04 permissions: pull-requests: write steps: - - uses: getsentry/github-workflows/validate-pr@36c729264d2edc29ebae61950c50e1e9f043ad7e + - uses: getsentry/github-workflows/validate-pr@01bcb7fe69bb48bacd83e675840e3dc95b33c730 with: app-id: ${{ vars.SDK_MAINTAINER_BOT_APP_ID }} private-key: ${{ secrets.SDK_MAINTAINER_BOT_PRIVATE_KEY }} diff --git a/.github/workflows/weekly-flaky-tests.yml b/.github/workflows/weekly-flaky-tests.yml index 23d0f340b174..b9c02c1b0421 100644 --- a/.github/workflows/weekly-flaky-tests.yml +++ b/.github/workflows/weekly-flaky-tests.yml @@ -18,7 +18,7 @@ concurrency: jobs: report: name: Weekly flaky test report - runs-on: namespace-profile-sentry-javascript-small + runs-on: ubuntu-24.04 timeout-minutes: 20 steps: - uses: actions/checkout@v7 diff --git a/.oxlintrc.base.json b/.oxlintrc.base.json index 41be2844716d..3c42d69b4bb4 100644 --- a/.oxlintrc.base.json +++ b/.oxlintrc.base.json @@ -61,10 +61,14 @@ "typescript/no-duplicate-type-constituents": "off", // @types/node 20 still marks `channel.subscribe()` and `channel.unsubscribe()` as deprecated, // but Node revoked this deprecation (DEP0163) in v22.20.0 and v24.8.0. + // `handleTunnelRequest` has only one deprecated overload, but the rule flags every re-export of it. "typescript/no-deprecated": [ "error", { - "allow": [{ "from": "package", "package": "diagnostics_channel", "name": ["subscribe", "unsubscribe"] }] + "allow": [ + { "from": "package", "package": "diagnostics_channel", "name": ["subscribe", "unsubscribe"] }, + "handleTunnelRequest" + ] } ] }, diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a0d0150cd82..bf3624e80c8a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,208 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +## 11.11.0 + +### Important Changes + +- **ref(cloudflare): Use the shared request handler wrapper ([#25588](https://github.com/getsentry/sentry-javascript/pull/25588))** + + Cloudflare Workers now use the same request handler wrapper as `Deno.serve` and `Bun.serve`. Changes for Cloudflare users: + + - Request spans now set `client.address` from the `CF-Connecting-IP` header (or another forwarding header), as on the other runtimes. This follows `dataCollection.userInfo`, which is `true` by default. To leave it out, set `dataCollection: { userInfo: false }`. + - Request spans now have the response header attributes, and events have the `response` context with the status code. + - When the source of a streamed response fails, the SDK now captures the error and marks the span as failed. With `captureErrors: false`, it does not capture it. + - Request spans now list every `Set-Cookie` header of a response, as on Deno and Bun. + - Without `AsyncLocalStorage` (for example on Shopify Oxygen), events now also get the request URL and method. + +- **feat(nestjs): Add an option to monitor every @Cron method ([#25471](https://github.com/getsentry/sentry-javascript/pull/25471))** + + Set `monitorCronJobs: true` on `nestIntegration()` to send check-ins for every `@Cron()` method, without adding `@SentryCron` to each one. It's off by default. Each monitored job creates a monitor in Sentry, and monitors are billed. Use `excludeCronJobs` to skip jobs by slug. Methods that also have `@SentryCron` check in once, with the slug and options passed to `@SentryCron`. + +- **feat(node): Add cronJobIntegration to monitor cron jobs ([#25477](https://github.com/getsentry/sentry-javascript/pull/25477))** + + Add `Sentry.cronJobIntegration()` to `integrations` to send check-ins for every named `CronJob` of the `cron` package (4.3+), with the job name as the monitor slug. It's off by default. Each monitored job creates a monitor in Sentry, and monitors are billed. Use `excludeJobs` to skip jobs by name. `Sentry.cron.instrumentCron` is deprecated; use `cronJobIntegration` instead. + +- **fix(server-utils): Instrument @anthropic-ai/sdk 1.x and turn off its own OTel spans ([#25750](https://github.com/getsentry/sentry-javascript/pull/25750))** + + The Anthropic integration now instruments `@anthropic-ai/sdk` 1.x. Since 0.134 the SDK also records its own OpenTelemetry spans, such as `anthropic.messages.create`, which duplicated Sentry's `gen_ai` spans. Once Sentry instruments the SDK, it sets `ANTHROPIC_OPEN_TELEMETRY=false` to turn those spans off. To keep them, set `ANTHROPIC_OPEN_TELEMETRY=true` or pass the `openTelemetry` option to the Anthropic client. + +- **feat(nextjs): Mark pageloads of cached pages ([#25318](https://github.com/getsentry/sentry-javascript/pull/25318))** + + When Next.js serves a page from its cache, for example an ISR page or a `cacheComponents` shell, the pageload now has the `page.server_rendering.type` attribute. It is `static` if the whole page came from the cache, and `partially_static` if parts of it rendered for this request. Then the pageload stays connected to this request, as before. If the request that rendered the cached page was sampled, the pageload also links to it. So in Sentry, a cached page no longer looks like missing server instrumentation. + +### Other Changes + +- feat(astro): Add `@sentry/astro/integration` entry for the Astro integration ([#25726](https://github.com/getsentry/sentry-javascript/pull/25726)) +- feat(bundler-plugins): Add `sourcemaps.deleteSourcemapsAfterUpload` and staged uploads ([#25637](https://github.com/getsentry/sentry-javascript/pull/25637)) +- feat(nestjs): Derive SentryCron monitor config from @Cron ([#25013](https://github.com/getsentry/sentry-javascript/pull/25013)) +- feat(nextjs): Add `applicationKeyExcludeDependencies` option ([#25818](https://github.com/getsentry/sentry-javascript/pull/25818)) +- feat(node): Add `awsLambdaWebIntegration` for Lambda Web Functions ([#25355](https://github.com/getsentry/sentry-javascript/pull/25355)) +- feat(node): Add BullMQ integration ([#21777](https://github.com/getsentry/sentry-javascript/pull/21777)) +- feat(node): Add nodeCronIntegration to monitor node-cron tasks ([#25473](https://github.com/getsentry/sentry-javascript/pull/25473)) +- feat(node): Add nodeScheduleIntegration to monitor node-schedule jobs ([#25474](https://github.com/getsentry/sentry-javascript/pull/25474)) +- feat(node): Support postgres v4 through its native tracing channels ([#26076](https://github.com/getsentry/sentry-javascript/pull/26076)) +- feat(server-utils): Extract `getSqlSpanName` for SQL span names ([#25929](https://github.com/getsentry/sentry-javascript/pull/25929)) +- feat(server-utils): Move to new `getSqlQueryAttributes` helper for consistency ([#25927](https://github.com/getsentry/sentry-javascript/pull/25927)) +- feat(server-utils): Record AI SDK v7 runtime and tool context on Vercel AI spans ([#25774](https://github.com/getsentry/sentry-javascript/pull/25774)) +- feat(server-utils): Set `db.operation.name` on SQL spans ([#25850](https://github.com/getsentry/sentry-javascript/pull/25850)) +- feat(server-utils): Support @mistralai/mistralai v3 ([#25827](https://github.com/getsentry/sentry-javascript/pull/25827)) +- feat(solidstart): Export `withSentry` from `@sentry/solidstart/vite` ([#25725](https://github.com/getsentry/sentry-javascript/pull/25725)) +- fix(astro): Initialize the server SDK on the first endpoint request ([#25731](https://github.com/getsentry/sentry-javascript/pull/25731)) +- fix(browser): Attribute long animation frame spans to the longest script ([#25990](https://github.com/getsentry/sentry-javascript/pull/25990)) +- fix(bundler-plugins): Inject debug IDs into every esbuild rebuild ([#26099](https://github.com/getsentry/sentry-javascript/pull/26099)) +- fix(bundler-plugins): upload literal artifact paths ([#25356](https://github.com/getsentry/sentry-javascript/pull/25356)) +- fix(cloudflare): Run build-time instrumentation in vite dev ([#25777](https://github.com/getsentry/sentry-javascript/pull/25777)) +- fix(core): Call native toString without page-replaceable lookups in FunctionToString ([#25921](https://github.com/getsentry/sentry-javascript/pull/25921)) +- fix(core): Enforce linked error limits for aggregate and suppressed errors ([#25873](https://github.com/getsentry/sentry-javascript/pull/25873)) +- fix(core): Only create Supabase db spans when there is a parent span ([#25807](https://github.com/getsentry/sentry-javascript/pull/25807)) +- fix(core): preserve span links through beforeSendSpan ([#25566](https://github.com/getsentry/sentry-javascript/pull/25566)) +- fix(core): Respect header collection for session user agents ([#25871](https://github.com/getsentry/sentry-javascript/pull/25871)) +- fix(deps): Bump glob to 14 and drop the direct dependency from react-router ([#26014](https://github.com/getsentry/sentry-javascript/pull/26014)) +- fix(nextjs): Don't abort Cache Components renders on Cloudflare Workers when requests overlap ([#25315](https://github.com/getsentry/sentry-javascript/pull/25315)) +- fix(nextjs): Drop tunnel requests under the Next.request proxy root span ([#26096](https://github.com/getsentry/sentry-javascript/pull/26096)) +- fix(nextjs): Fork isolation scope for Next.request proxy root spans ([#26084](https://github.com/getsentry/sentry-javascript/pull/26084)) +- fix(nextjs): Strip source map comments before trailing debug IDs ([#25881](https://github.com/getsentry/sentry-javascript/pull/25881)) +- fix(nitro): Capture errors thrown in tasks ([#26089](https://github.com/getsentry/sentry-javascript/pull/26089)) +- fix(nuxt): Keep storage instrumentation plugin synchronous ([#25949](https://github.com/getsentry/sentry-javascript/pull/25949)) +- fix(replay): Skip buffered replays for captureMessage stacks from the attachStacktrace default ([#25846](https://github.com/getsentry/sentry-javascript/pull/25846)) +- fix(server-utils): Fix orchestrion source maps of instrumented modules ([#25772](https://github.com/getsentry/sentry-javascript/pull/25772)) +- fix(server-utils): Read the pi-durable session id in every phase outside a run ([#25838](https://github.com/getsentry/sentry-javascript/pull/25838)) +- fix(server-utils): Require a parent span for Firestore operations ([#25855](https://github.com/getsentry/sentry-javascript/pull/25855)) +- fix(server-utils): Require a parent span for Tedious queries ([#25822](https://github.com/getsentry/sentry-javascript/pull/25822)) +- fix(server-utils): Use onlyIfParent for Firestore spans ([#25877](https://github.com/getsentry/sentry-javascript/pull/25877)) +- fix(server-utils): Use onlyIfParent for Tedious spans ([#25876](https://github.com/getsentry/sentry-javascript/pull/25876)) +- fix(solidstart): Serve the managed tunnel under Vite's base on SolidStart 2 ([#25848](https://github.com/getsentry/sentry-javascript/pull/25848)) +- fix(sveltekit): Resolve Handle and ResolveOptions types on SvelteKit 3 ([#26085](https://github.com/getsentry/sentry-javascript/pull/26085)) +- fix(wasm): Skip length prefix in 16-byte build IDs ([#24857](https://github.com/getsentry/sentry-javascript/pull/24857)) +- ref(core): Deprecate the released `handleTunnelRequest` form and reword the `serveTunnelIntegration` docs ([#25844](https://github.com/getsentry/sentry-javascript/pull/25844)) + +
+ Internal Changes + +- chore(ci): Ignore Dependabot bumps for deps that are also peer deps ([#26015](https://github.com/getsentry/sentry-javascript/pull/26015)) +- chore(ci): Remove nightly sentry-lighthouse upload workflow ([#25966](https://github.com/getsentry/sentry-javascript/pull/25966)) +- chore(dependabot): Group related dependency updates ([#26059](https://github.com/getsentry/sentry-javascript/pull/26059)) +- chore(dependabot): Ignore major react-router updates ([#25892](https://github.com/getsentry/sentry-javascript/pull/25892)) +- chore(dependabot): Preserve SDK compatibility baselines ([#26013](https://github.com/getsentry/sentry-javascript/pull/26013)) +- chore(dependabot): Remove obsolete update rules ([#26007](https://github.com/getsentry/sentry-javascript/pull/26007)) +- chore(deps): Align AWS SDK clients to 3.1161.0 ([#26032](https://github.com/getsentry/sentry-javascript/pull/26032)) +- chore(deps): Bump @actions/core and @actions/github in internal GH actions ([#26109](https://github.com/getsentry/sentry-javascript/pull/26109)) +- chore(deps): Bump @nuxt/devtools from 3.4.1 to 3.4.4 ([#26097](https://github.com/getsentry/sentry-javascript/pull/26097)) +- chore(deps): Bump @sentry/conventions to 0.30.0 ([#25837](https://github.com/getsentry/sentry-javascript/pull/25837)) +- chore(deps): Bump undici from 6.24.1 to 6.29.1 ([#26108](https://github.com/getsentry/sentry-javascript/pull/26108)) +- chore(deps): Bump webpack from 5.104.1 to 5.112.2 ([#25910](https://github.com/getsentry/sentry-javascript/pull/25910)) +- chore(deps): Enable pnpm autoDedupe ([#25961](https://github.com/getsentry/sentry-javascript/pull/25961)) +- chore(deps): Group AWS SDK Dependabot updates ([#26030](https://github.com/getsentry/sentry-javascript/pull/26030)) +- chore(deps-dev): Bump @babel/parser from 7.29.9 to 8.0.10 ([#26043](https://github.com/getsentry/sentry-javascript/pull/26043)) +- chore(deps-dev): Bump @babel/plugin-transform-typescript from 7.29.7 to 8.0.6 ([#26012](https://github.com/getsentry/sentry-javascript/pull/26012)) +- chore(deps-dev): Bump @hono/node-server from 2.1.3 to 2.1.5 ([#26054](https://github.com/getsentry/sentry-javascript/pull/26054)) +- chore(deps-dev): Bump @rollup/plugin-terser from 0.4.4 to 1.0.2 ([#25890](https://github.com/getsentry/sentry-javascript/pull/25890)) +- chore(deps-dev): Bump @testing-library/user-event from 14.5.2 to 14.6.8 ([#26071](https://github.com/getsentry/sentry-javascript/pull/26071)) +- chore(deps-dev): Bump fake-indexeddb from 6.2.5 to 6.2.7 ([#26034](https://github.com/getsentry/sentry-javascript/pull/26034)) +- chore(deps-dev): Bump markdown-table from 3.0.3 to 3.0.4 ([#26074](https://github.com/getsentry/sentry-javascript/pull/26074)) +- chore(deps-dev): Bump node-schedule and @types/node-schedule ([#26058](https://github.com/getsentry/sentry-javascript/pull/26058)) +- chore(deps-dev): Bump redux from 4.0.5 to 5.0.1 ([#25959](https://github.com/getsentry/sentry-javascript/pull/25959)) +- chore(deps-dev): Bump the nestjs group with 2 updates ([#26060](https://github.com/getsentry/sentry-javascript/pull/26060)) +- chore(deps-dev): Bump the react-router group across 1 directory with 3 updates ([#25955](https://github.com/getsentry/sentry-javascript/pull/25955)) +- chore(deps-dev): Bump tsx from 4.23.0 to 4.23.15 ([#25958](https://github.com/getsentry/sentry-javascript/pull/25958)) +- chore(deps-dev): Bump wrangler from 4.61.0 to 4.156.0 ([#26009](https://github.com/getsentry/sentry-javascript/pull/26009)) +- chore(dev-packages): Move dependencies to devDependencies ([#26046](https://github.com/getsentry/sentry-javascript/pull/26046)) +- chore(node-integration-tests): Remove unused dependencies ([#25942](https://github.com/getsentry/sentry-javascript/pull/25942)) +- chore: Add Bugbot guidance for cold imports in tests ([#25976](https://github.com/getsentry/sentry-javascript/pull/25976)) +- chore: Add external contributor to CHANGELOG.md ([#25862](https://github.com/getsentry/sentry-javascript/pull/25862)) +- chore: Add external contributor to CHANGELOG.md ([#25870](https://github.com/getsentry/sentry-javascript/pull/25870)) +- chore: Add external contributor to CHANGELOG.md ([#25872](https://github.com/getsentry/sentry-javascript/pull/25872)) +- chore: Add external contributor to CHANGELOG.md ([#26017](https://github.com/getsentry/sentry-javascript/pull/26017)) +- chore: Add external contributor to CHANGELOG.md ([#26062](https://github.com/getsentry/sentry-javascript/pull/26062)) +- chore: Batch npm dependency updates daily ([#26072](https://github.com/getsentry/sentry-javascript/pull/26072)) +- chore: Exclude integration and bundler tests from Dependabot updates ([#26075](https://github.com/getsentry/sentry-javascript/pull/26075)) +- chore: Ignore major Prisma dependency updates ([#26063](https://github.com/getsentry/sentry-javascript/pull/26063)) +- chore: Prune unused regexp dependencies from lockfile ([#25948](https://github.com/getsentry/sentry-javascript/pull/25948)) +- chore: Refresh Smithy deprecation metadata in lockfile ([#25971](https://github.com/getsentry/sentry-javascript/pull/25971)) +- chore: Remove unused eslint-plugin-regexp dependencies ([#25944](https://github.com/getsentry/sentry-javascript/pull/25944)) +- chore: Remove unused test and build dependencies ([#25945](https://github.com/getsentry/sentry-javascript/pull/25945)) +- ci(canary): Build the canary test matrix from `canaryVariants` ([#25866](https://github.com/getsentry/sentry-javascript/pull/25866)) +- ci(deps): Bump anthropics/claude-code-action from 1.0.236 to 1.0.264 ([#25952](https://github.com/getsentry/sentry-javascript/pull/25952)) +- ci(deps): Bump anthropics/claude-code-action from 1.0.264 to 1.0.266 ([#26008](https://github.com/getsentry/sentry-javascript/pull/26008)) +- ci(deps): Bump getsentry/craft from 2.31.2 to 3.4.0 ([#25987](https://github.com/getsentry/sentry-javascript/pull/25987)) +- ci(deps): Bump getsentry/craft from 3.4.0 to 3.5.0 ([#26011](https://github.com/getsentry/sentry-javascript/pull/26011)) +- ci(deps): Bump getsentry/github-workflows/validate-pr from 36c729264d2edc29ebae61950c50e1e9f043ad7e to 01bcb7fe69bb48bacd83e675840e3dc95b33c730 ([#25954](https://github.com/getsentry/sentry-javascript/pull/25954)) +- ci(deps): Group langchain dependabot updates and skip majors ([#26041](https://github.com/getsentry/sentry-javascript/pull/26041)) +- ci(deps): Ignore langchain in dependabot ([#26056](https://github.com/getsentry/sentry-javascript/pull/26056)) +- ci(node): Add optional Node 27 alpha tests ([#25984](https://github.com/getsentry/sentry-javascript/pull/25984)) +- ci: Drop dead matrix reference in Deno unit tests job ([#25852](https://github.com/getsentry/sentry-javascript/pull/25852)) +- ci: Include v10 in base-branch metadata ([#25977](https://github.com/getsentry/sentry-javascript/pull/25977)) +- ci: run CI on GitHub-hosted runners again ([#26057](https://github.com/getsentry/sentry-javascript/pull/26057)) +- ci: Split the E2E test matrix into two jobs ([#25913](https://github.com/getsentry/sentry-javascript/pull/25913)) +- docs(changelog): Add monitorCronJobs to Unreleased ([#25996](https://github.com/getsentry/sentry-javascript/pull/25996)) +- docs(changelog): Restore order of unreleased section in changelog ([#26006](https://github.com/getsentry/sentry-javascript/pull/26006)) +- feat(deps): Bump @aws-sdk/client-lambda from 3.1068.0 to 3.1161.0 ([#26026](https://github.com/getsentry/sentry-javascript/pull/26026)) +- feat(deps): Bump @embroider/addon-shim from 1.10.3 to 1.11.0 ([#26048](https://github.com/getsentry/sentry-javascript/pull/26048)) +- feat(deps): Bump @flue/runtime from 2.0.3 to 2.3.0 ([#26044](https://github.com/getsentry/sentry-javascript/pull/26044)) +- feat(deps): Bump @hapi/content from 6.0.2 to 6.0.3 ([#26021](https://github.com/getsentry/sentry-javascript/pull/26021)) +- feat(deps): Bump @hono/node-server from 2.0.10 to 2.1.3 ([#26053](https://github.com/getsentry/sentry-javascript/pull/26053)) +- feat(deps): Bump @sveltejs/acorn-typescript from 1.0.9 to 1.0.13 ([#26050](https://github.com/getsentry/sentry-javascript/pull/26050)) +- feat(deps): Bump @types/aws-lambda from 8.10.161 to 8.10.164 ([#26066](https://github.com/getsentry/sentry-javascript/pull/26066)) +- feat(deps): Bump axios from 1.20.0 to 1.21.0 ([#26033](https://github.com/getsentry/sentry-javascript/pull/26033)) +- feat(deps): Bump dataloader from 2.2.2 to 2.2.3 ([#25891](https://github.com/getsentry/sentry-javascript/pull/25891)) +- feat(deps): Bump lodash.omit from 4.5.0 to 4.18.0 ([#26055](https://github.com/getsentry/sentry-javascript/pull/26055)) +- feat(deps): Bump node-forge from 1.4.0 to 1.4.2 ([#26020](https://github.com/getsentry/sentry-javascript/pull/26020)) +- feat(deps): Bump pg and @types/pg ([#26042](https://github.com/getsentry/sentry-javascript/pull/26042)) +- feat(deps): Bump proxy from 2.1.1 to 4.1.0 ([#26029](https://github.com/getsentry/sentry-javascript/pull/26029)) +- feat(deps): Bump valibot from 1.4.2 to 1.5.0 ([#25957](https://github.com/getsentry/sentry-javascript/pull/25957)) +- feat(deps): Bump vite from 8.3.1 to 8.3.5 ([#26045](https://github.com/getsentry/sentry-javascript/pull/26045)) +- feat(deps): Bump yargs from 16.2.0 to 18.2.0 ([#26031](https://github.com/getsentry/sentry-javascript/pull/26031)) +- fix(build): Handle CRLF line endings in pnpm-workspace.yaml check ([#26083](https://github.com/getsentry/sentry-javascript/pull/26083)) +- fix(deps): Bump seroval to 1.6.12 and 1.7.7 ([#26047](https://github.com/getsentry/sentry-javascript/pull/26047)) +- fix(deps): Bump tar from 7.5.11 to 7.5.22 ([#26025](https://github.com/getsentry/sentry-javascript/pull/26025)) +- perf(ci): Increase tarball packing concurrency ([#25979](https://github.com/getsentry/sentry-javascript/pull/25979)) +- perf(ci): Speed up Next.js E2E tests by sharding into dev/prod ([#25960](https://github.com/getsentry/sentry-javascript/pull/25960)) +- perf(ci): Split Next.js application-key tests by environment ([#26040](https://github.com/getsentry/sentry-javascript/pull/26040)) +- ref(astro): Use convention constants for attribute keys ([#25894](https://github.com/getsentry/sentry-javascript/pull/25894)) +- ref(browser): Use convention constants for attribute keys ([#25895](https://github.com/getsentry/sentry-javascript/pull/25895)) +- ref(browser-utils): Use convention constants for attribute keys ([#25896](https://github.com/getsentry/sentry-javascript/pull/25896)) +- ref(bun): Use convention constants for attribute keys ([#25897](https://github.com/getsentry/sentry-javascript/pull/25897)) +- ref(cloudflare): Simplify shared SDK initialization ([#25864](https://github.com/getsentry/sentry-javascript/pull/25864)) +- ref(cloudflare): Use convention constants for attribute keys ([#25898](https://github.com/getsentry/sentry-javascript/pull/25898)) +- ref(core): Use convention constants for attribute keys ([#25899](https://github.com/getsentry/sentry-javascript/pull/25899)) +- ref(deno): Use convention constants for attribute keys ([#25900](https://github.com/getsentry/sentry-javascript/pull/25900)) +- ref(google-cloud-serverless): Use convention constants for attribute keys ([#25901](https://github.com/getsentry/sentry-javascript/pull/25901)) +- ref(nextjs): Use convention constants for attribute keys ([#25902](https://github.com/getsentry/sentry-javascript/pull/25902)) +- ref(node): Use convention constants for attribute keys ([#25903](https://github.com/getsentry/sentry-javascript/pull/25903)) +- ref(nuxt): Use convention constants for attribute keys ([#25904](https://github.com/getsentry/sentry-javascript/pull/25904)) +- ref(profiling-node): Use convention constants for attribute keys ([#25905](https://github.com/getsentry/sentry-javascript/pull/25905)) +- ref(react-router): Use convention constants for attribute keys ([#25906](https://github.com/getsentry/sentry-javascript/pull/25906)) +- ref(replay-internal): Use convention constants for attribute keys ([#25907](https://github.com/getsentry/sentry-javascript/pull/25907)) +- ref(server-utils): Clarify PostgreSQL instrumentation filenames ([#25939](https://github.com/getsentry/sentry-javascript/pull/25939)) +- ref(server-utils): Remove `requiresParentSpan` in favor of `onlyIfParent` ([#25926](https://github.com/getsentry/sentry-javascript/pull/25926)) +- ref(server-utils): Share the postgres.js query settlement logic ([#26094](https://github.com/getsentry/sentry-javascript/pull/26094)) +- ref(server-utils): Use convention constants for attribute keys ([#25908](https://github.com/getsentry/sentry-javascript/pull/25908)) +- ref(sveltekit): Use convention constants for attribute keys ([#25909](https://github.com/getsentry/sentry-javascript/pull/25909)) +- ref: Pass optional span attributes directly instead of conditional spreads ([#25878](https://github.com/getsentry/sentry-javascript/pull/25878)) +- test(aws-serverless): Move Lambda extension setup outside test timeout ([#25975](https://github.com/getsentry/sentry-javascript/pull/25975)) +- test(browser): Stabilize long animation frame tests ([#25974](https://github.com/getsentry/sentry-javascript/pull/25974)) +- test(bundler-plugins): Stabilize missing-parser test setup ([#25972](https://github.com/getsentry/sentry-javascript/pull/25972)) +- test(bundler-tests): Guard server runtime entries against build-time tooling ([#25724](https://github.com/getsentry/sentry-javascript/pull/25724)) +- test(e2e): Check node-openai-decisions through the event proxy ([#25840](https://github.com/getsentry/sentry-javascript/pull/25840)) +- test(e2e): Import Type from typebox in node-pi-durable ([#26018](https://github.com/getsentry/sentry-javascript/pull/26018)) +- test(e2e): Move canary variants into `canaryVariants` ([#25865](https://github.com/getsentry/sentry-javascript/pull/25865)) +- test(e2e): Move OpenAI and Anthropic send-to-sentry cases into gen-ai-libraries ([#25841](https://github.com/getsentry/sentry-javascript/pull/25841)) +- test(e2e): Point together-ai 1.x at OpenRouter's base URL in gen-ai-libraries ([#25947](https://github.com/getsentry/sentry-javascript/pull/25947)) +- test(e2e): Remove node-flue-send-to-sentry ([#25839](https://github.com/getsentry/sentry-javascript/pull/25839)) +- test(e2e): Rename nuxt-5 middleware files to avoid Nitro's dotted suffixes ([#25923](https://github.com/getsentry/sentry-javascript/pull/25923)) +- test(effect): Assert error severities without event ordering ([#25973](https://github.com/getsentry/sentry-javascript/pull/25973)) +- test(node-integration-tests): Install Flue and pi-durable as package dependencies ([#25933](https://github.com/getsentry/sentry-javascript/pull/25933)) +- test(node-integration-tests): Install Prisma suite dependencies as package dependencies ([#25943](https://github.com/getsentry/sentry-javascript/pull/25943)) +- test(node-integration-tests): Install single-version suite dependencies as package dependencies ([#25936](https://github.com/getsentry/sentry-javascript/pull/25936)) +- test(nuxt): cover the client plugin's pages-router instrumentation ([#25348](https://github.com/getsentry/sentry-javascript/pull/25348)) +- test(nuxt): Make the `nuxt-5 (tunnel-dev)` e2e variant optional ([#26004](https://github.com/getsentry/sentry-javascript/pull/26004)) +- test(nuxt): Use Nuxt's own Nitro and add db0 in nuxt-5 E2E app ([#26095](https://github.com/getsentry/sentry-javascript/pull/26095)) + +
+ +Work in this release was contributed by @lazerg, @chiliec, @61465, @jpdefo, and @AyeshaWaseemAhmad. Thank you for your contributions! + ## 11.10.0 ### Important Changes diff --git a/dev-packages/browser-integration-tests/package.json b/dev-packages/browser-integration-tests/package.json index 32d494be4e1d..af34bd6d41c5 100644 --- a/dev-packages/browser-integration-tests/package.json +++ b/dev-packages/browser-integration-tests/package.json @@ -55,30 +55,26 @@ "test:update-snapshots": "pnpm run test:all --update-snapshots", "test:detect-flaky": "tsx scripts/detectFlakyTests.ts" }, - "dependencies": { + "devDependencies": { "@babel/core": "^7.29.6", "@babel/preset-typescript": "^7.16.7", "@playwright/test": "1.64.1", - "@sentry/rrweb": "2.45.0", "@sentry/browser": "11.10.0", - "@sentry/replay": "11.10.0", - "@sentry/opentelemetry": "11.10.0", - "@sentry/conventions": "0.29.0", - "@supabase/supabase-js": "2.109.0", - "axios": "1.20.0", - "babel-loader": "^10.1.1", - "fflate": "0.8.3", - "html-webpack-plugin": "^5.5.0", - "webpack": "^5.95.0" - }, - "devDependencies": { "@sentry/browser-utils": "11.10.0", + "@sentry/conventions": "0.30.0", "@sentry/core": "11.10.0", + "@sentry/replay": "11.10.0", "@sentry/replay-canvas": "11.10.0", + "@sentry/rrweb": "2.45.0", "@sentry/rrweb-types": "^2.45.0", "@sentry/wasm": "11.10.0", + "@supabase/supabase-js": "2.109.0", "@types/node": "^20.19.0", - "eslint-plugin-regexp": "^3.1.0", - "glob": "^13.0.6" + "axios": "1.21.0", + "babel-loader": "^10.1.1", + "fflate": "0.8.3", + "glob": "^14.0.3", + "html-webpack-plugin": "^5.5.0", + "webpack": "^5.112.2" } } diff --git a/dev-packages/browser-integration-tests/suites/replay/errors/captureMessage/test.ts b/dev-packages/browser-integration-tests/suites/replay/errors/captureMessage/test.ts new file mode 100644 index 000000000000..90b867c117e3 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/replay/errors/captureMessage/test.ts @@ -0,0 +1,33 @@ +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequest } from '../../../../utils/helpers'; +import { getReplaySnapshot, shouldSkipReplayTest, waitForReplayRunning } from '../../../../utils/replayHelpers'; + +sentryTest( + '[error-mode] should not flush for captureMessage when attachStacktrace is left at its default', + async ({ getLocalTestUrl, page, forceFlushReplay }) => { + if (shouldSkipReplayTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.goto(url); + await waitForReplayRunning(page); + + const errorRequestPromise = waitForErrorRequest(page); + await page.evaluate(() => (window as any).Sentry.captureMessage('Some message')); + const event = envelopeRequestParser(await errorRequestPromise); + await forceFlushReplay(); + + expect(event.exception?.values?.[0]?.stacktrace?.frames?.length).toBeGreaterThan(0); + expect(event.tags?.replayId).toBeUndefined(); + expect(await getReplaySnapshot(page)).toEqual( + expect.objectContaining({ + _isEnabled: true, + _isPaused: false, + recordingMode: 'buffer', + }), + ); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/replay/errors/captureMessageAttachStacktrace/init.js b/dev-packages/browser-integration-tests/suites/replay/errors/captureMessageAttachStacktrace/init.js new file mode 100644 index 000000000000..76d3c2afd889 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/replay/errors/captureMessageAttachStacktrace/init.js @@ -0,0 +1,18 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; +window.Replay = Sentry.replayIntegration({ + flushMinDelay: 200, + flushMaxDelay: 200, + minReplayDuration: 0, +}); + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + sampleRate: 1, + replaysSessionSampleRate: 0.0, + replaysOnErrorSampleRate: 1.0, + attachStacktrace: true, + + integrations: [window.Replay], +}); diff --git a/dev-packages/browser-integration-tests/suites/replay/errors/captureMessageAttachStacktrace/test.ts b/dev-packages/browser-integration-tests/suites/replay/errors/captureMessageAttachStacktrace/test.ts new file mode 100644 index 000000000000..d89371695436 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/replay/errors/captureMessageAttachStacktrace/test.ts @@ -0,0 +1,36 @@ +import { expect } from '@playwright/test'; +import { sentryTest } from '../../../../utils/fixtures'; +import { envelopeRequestParser, waitForErrorRequest } from '../../../../utils/helpers'; +import { + getReplaySnapshot, + shouldSkipReplayTest, + waitForReplayRequest, + waitForReplayRunning, +} from '../../../../utils/replayHelpers'; + +sentryTest( + '[error-mode] should flush for captureMessage when attachStacktrace is explicitly enabled', + async ({ getLocalTestUrl, page }) => { + if (shouldSkipReplayTest()) { + sentryTest.skip(); + } + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.goto(url); + await waitForReplayRunning(page); + + const errorRequestPromise = waitForErrorRequest(page); + const replayRequestPromise = waitForReplayRequest(page, 0); + await page.evaluate(() => (window as any).Sentry.captureMessage('Some message')); + const event = envelopeRequestParser(await errorRequestPromise); + await replayRequestPromise; + + expect(event.tags?.replayId).toEqual(expect.any(String)); + expect(await getReplaySnapshot(page)).toEqual( + expect.objectContaining({ + recordingMode: 'session', + }), + ); + }, +); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/test.ts index 5881bf8b3ba0..72fce94d9815 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled-static/test.ts @@ -10,7 +10,12 @@ import { import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { envelopeRequestParser, shouldSkipTracingTest, waitForTransactionRequest } from '../../../../utils/helpers'; +import { + envelopeRequestParser, + shouldSkipTracingTest, + waitForNextFrame, + waitForTransactionRequest, +} from '../../../../utils/helpers'; sentryTest( 'should capture long animation frame for top-level script.', @@ -83,8 +88,11 @@ sentryTest( await page.goto(url); - // trigger long animation frame function - await page.getByRole('button').click(); + // Separate mousedown and click into different frames so LoAF reports the click handler. + await page.getByRole('button').hover(); + await page.mouse.down(); + await waitForNextFrame(page); + await page.mouse.up(); const eventData = envelopeRequestParser(await promise); diff --git a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/test.ts b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/test.ts index 6a2638a61c45..93abdb679273 100644 --- a/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/test.ts +++ b/dev-packages/browser-integration-tests/suites/tracing/browserTracingIntegration/long-tasks-and-animation-frame-enabled/test.ts @@ -9,7 +9,7 @@ import { import type { Route } from '@playwright/test'; import { expect } from '@playwright/test'; import { sentryTest } from '../../../../utils/fixtures'; -import { shouldSkipTracingTest } from '../../../../utils/helpers'; +import { shouldSkipTracingTest, waitForNextFrame } from '../../../../utils/helpers'; import { collectStreamedSpans, getSpanOp, waitForStreamedSpan } from '../../../../utils/spanUtils'; sentryTest( @@ -83,8 +83,11 @@ sentryTest('captures long animation frame span for event listener.', async ({ br await page.goto(url); - // trigger long animation frame function - await page.getByRole('button').click(); + // Separate mousedown and click into different frames so LoAF reports the click handler. + await page.getByRole('button').hover(); + await page.mouse.down(); + await waitForNextFrame(page); + await page.mouse.up(); const pageloadSpan = await pageloadSpanPromise; await page.evaluate(() => (window as any).Sentry.flush()); diff --git a/dev-packages/browser-integration-tests/utils/helpers.ts b/dev-packages/browser-integration-tests/utils/helpers.ts index 90b3deae3cb9..60df3fd96eb2 100644 --- a/dev-packages/browser-integration-tests/utils/helpers.ts +++ b/dev-packages/browser-integration-tests/utils/helpers.ts @@ -564,6 +564,15 @@ export async function waitForLcpCandidate(page: Page, selector: string): Promise }); } +/** + * Resolves after the current frame has been presented (double rAF). + */ +export async function waitForNextFrame(page: Page): Promise { + await page.evaluate( + () => new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(() => resolve()))), + ); +} + export async function hidePage(page: Page, interactionSelector?: string): Promise { // web-vitals processes an interaction's event entries in `requestIdleCallback(..., { timeout: // 1000 })`, and Chromium only reaches idle here once that timeout elapses. Hiding before that diff --git a/dev-packages/bun-integration-tests/node-suites/excludes.ts b/dev-packages/bun-integration-tests/node-suites/excludes.ts index c62a8b2059ec..046e973354dd 100644 --- a/dev-packages/bun-integration-tests/node-suites/excludes.ts +++ b/dev-packages/bun-integration-tests/node-suites/excludes.ts @@ -7,6 +7,7 @@ // `node:sqlite`, which `flue` needs, and the Vercel keep-alive, which needs `http.server.response.finish`. const NODE_ONLY = [ 'suites/anr/test.ts', + 'suites/aws-lambda-web/**', 'suites/aws-serverless/**', 'suites/breadcrumbs/**', 'suites/child-process/test.ts', @@ -64,6 +65,9 @@ const NO_FETCH_INSTRUMENTATION = [ // instrumentation creates no spans. Apps must be built with `@sentry/bun/plugin`. // See https://github.com/getsentry/sentry-javascript/issues/23882 export const NO_AUTO_INSTRUMENTATION = [ + 'suites/cron/cron-integration/test.ts', + 'suites/cron/node-cron/integration/test.ts', + 'suites/cron/node-schedule-integration/test.ts', 'suites/express/**', 'suites/fs-instrumentation/test.ts', 'suites/hono/test.ts', @@ -71,7 +75,9 @@ export const NO_AUTO_INSTRUMENTATION = [ 'suites/pino/test.ts', 'suites/tracing/amqplib/test.ts', 'suites/tracing/anthropic/test.ts', + 'suites/tracing/anthropic/v0/test.ts', 'suites/tracing/anthropic/v0.129/test.ts', + 'suites/tracing/anthropic/v1/test.ts', 'suites/tracing/apollo-graphql/**', 'suites/tracing/dataloader/test.ts', 'suites/tracing/fastify/test.ts', @@ -92,6 +98,7 @@ export const NO_AUTO_INSTRUMENTATION = [ 'suites/tracing/mastra/test.ts', 'suites/tracing/mcp-handler-exact-once/test.ts', 'suites/tracing/mcp-server/**', + 'suites/tracing/mistral-v2/test.ts', 'suites/tracing/mistral/test.ts', 'suites/tracing/mongodb-v4/test.ts', 'suites/tracing/mongodb-v5/test.ts', @@ -211,6 +218,10 @@ const BUN_BUILD_EAGER_SUBSCRIPTION = ['suites/tracing/orchestrion-lazy-registrat // `http.server.request.start`. const BUN_BUILD_NO_HTTP_SERVER_INTEGRATION = ['suites/express/multiple-init/test.ts']; +// `@mistralai/mistralai` v3 statically imports its optional peer `@livekit/rtc-node`, so Bun cannot +// bundle the scenarios. `mistral-v2` still covers Mistral here. +const BUN_BUILD_UNBUNDLEABLE_DEPENDENCY = ['suites/tracing/mistral/test.ts']; + // Some or all tests fail with the bundled scenarios. The cause is not investigated yet. In // `express/tracing` only the request data tests fail: they set `httpIntegration` options, and with // `@sentry/bun` the request body comes from `bunHttpServerIntegration`. @@ -248,5 +259,6 @@ export const BUN_BUILD_EXCLUDE = [ '**/node_modules/**', ...BUN_BUILD_EAGER_SUBSCRIPTION, ...BUN_BUILD_NO_HTTP_SERVER_INTEGRATION, + ...BUN_BUILD_UNBUNDLEABLE_DEPENDENCY, ...BUN_BUILD_NOT_TRIAGED, ]; diff --git a/dev-packages/bun-integration-tests/package.json b/dev-packages/bun-integration-tests/package.json index 5bf90187d898..1b63d888b452 100644 --- a/dev-packages/bun-integration-tests/package.json +++ b/dev-packages/bun-integration-tests/package.json @@ -13,20 +13,18 @@ "test": "vitest run", "test:watch": "pnpm run test --watch" }, - "dependencies": { - "@sentry/bun": "11.10.0", - "@sentry/hono": "11.10.0", - "hono": "^4.13.7", - "mysql": "^2.18.1", - "pg": "8.16.0" - }, "devDependencies": { "@sentry-internal/node-integration-tests": "11.10.0", "@sentry-internal/test-utils": "11.10.0", + "@sentry/bun": "11.10.0", "@sentry/core": "11.10.0", + "@sentry/hono": "11.10.0", "@types/mysql": "^2.15.27", - "@types/pg": "^8.20.0", + "@types/pg": "^8.23.1", "bun-types": "^1.2.9", + "hono": "^4.13.7", + "mysql": "^2.18.1", + "pg": "8.23.1", "vitest": "^4.1.11" } } diff --git a/dev-packages/bundler-plugin-integration-tests/fixtures/esbuild/telemetry.test.ts b/dev-packages/bundler-plugin-integration-tests/fixtures/esbuild/telemetry.test.ts index fe5649ca57dc..2585278adc54 100644 --- a/dev-packages/bundler-plugin-integration-tests/fixtures/esbuild/telemetry.test.ts +++ b/dev-packages/bundler-plugin-integration-tests/fixtures/esbuild/telemetry.test.ts @@ -6,7 +6,7 @@ test(import.meta.url, ({ runBundler, readOutputFiles, runFileInNode }) => { expect(readOutputFiles()).toMatchInlineSnapshot(` { "sentry-telemetry.json": "[{"sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"}},[[{"type":"session"},{"sid":"UUID","init":true,"started":"TIMESTAMP","timestamp":"TIMESTAMP","status":"ok","errors":0,"duration":DURATION,"attrs":{"release":"PLUGIN_VERSION","environment":"production"}}]]], - [{"event_id":"UUID","sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"},"trace":{"environment":"production","release":"PLUGIN_VERSION","public_key":"UUID","trace_id":"UUID","org_id":"1","transaction":"Sentry Bundler Plugin execution","sampled":"true","sample_rand":"SAMPLE_RAND","sample_rate":"0.3"}},[[{"type":"transaction"},{"contexts":{"trace":{"span_id":"SHORT_UUID","trace_id":"UUID","data":{"sentry.origin":"manual","sentry.segment.name.source":"custom","sentry.sample_rate":0.3},"status":"ok","origin":"manual"},"runtime":{"name":"node","version":"NODE_VERSION"}},"spans":[],"start_timestamp":START_TIMESTAMP,"timestamp":TIMESTAMP,"transaction":"Sentry Bundler Plugin execution","type":"transaction","transaction_info":{"source":"custom"},"platform":"PLATFORM","event_id":"UUID","environment":"production","release":"PLUGIN_VERSION","tags":{"upload-legacy-sourcemaps":false,"module-metadata":false,"inject-build-information":false,"set-commits":"auto","finalize-release":true,"deploy-options":false,"custom-error-handler":false,"sourcemaps-assets":false,"delete-after-upload":false,"sourcemaps-disabled":false,"react-annotate":false,"node":"NODE_VERSION","platform":"PLATFORM","meta-framework":"none","application-key-set":false,"ci":true,"project":"undefined","bundler":"esbuild","bundler-major-version":"28"},"user":{},"sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION","integrations":[],"packages":[{"name":"npm:@sentry/node","version":"SDK_VERSION"}]}}]]], + [{"event_id":"UUID","sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"},"trace":{"environment":"production","release":"PLUGIN_VERSION","public_key":"UUID","trace_id":"UUID","org_id":"1","transaction":"Sentry Bundler Plugin execution","sampled":"true","sample_rand":"SAMPLE_RAND","sample_rate":"0.3"}},[[{"type":"transaction"},{"contexts":{"trace":{"span_id":"SHORT_UUID","trace_id":"UUID","data":{"sentry.origin":"manual","sentry.segment.name.source":"custom","sentry.sample_rate":0.3},"status":"ok","origin":"manual"},"runtime":{"name":"node","version":"NODE_VERSION"}},"spans":[],"start_timestamp":START_TIMESTAMP,"timestamp":TIMESTAMP,"transaction":"Sentry Bundler Plugin execution","type":"transaction","transaction_info":{"source":"custom"},"platform":"PLATFORM","event_id":"UUID","environment":"production","release":"PLUGIN_VERSION","tags":{"upload-legacy-sourcemaps":false,"module-metadata":false,"inject-build-information":false,"set-commits":"auto","finalize-release":true,"deploy-options":false,"custom-error-handler":false,"sourcemaps-assets":false,"delete-after-upload":false,"delete-sourcemaps-after-upload":"auto","sourcemaps-disabled":false,"react-annotate":false,"node":"NODE_VERSION","platform":"PLATFORM","meta-framework":"none","application-key-set":false,"ci":true,"project":"undefined","bundler":"esbuild","bundler-major-version":"28"},"user":{},"sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION","integrations":[],"packages":[{"name":"npm:@sentry/node","version":"SDK_VERSION"}]}}]]], [{"sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"}},[[{"type":"session"},{"sid":"UUID","init":false,"started":"TIMESTAMP","timestamp":"TIMESTAMP","status":"exited","errors":0,"duration":DURATION,"attrs":{"release":"PLUGIN_VERSION","environment":"production"}}]]], ", "telemetry.js": "(() => { diff --git a/dev-packages/bundler-plugin-integration-tests/fixtures/rolldown/telemetry.test.ts b/dev-packages/bundler-plugin-integration-tests/fixtures/rolldown/telemetry.test.ts index 77ac826d8d70..f995682ca797 100644 --- a/dev-packages/bundler-plugin-integration-tests/fixtures/rolldown/telemetry.test.ts +++ b/dev-packages/bundler-plugin-integration-tests/fixtures/rolldown/telemetry.test.ts @@ -18,7 +18,7 @@ test(import.meta.url, ({ runBundler, readOutputFiles, runFileInNode }) => { //#endregion ", "sentry-telemetry.json": "[{"sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"}},[[{"type":"session"},{"sid":"UUID","init":true,"started":"TIMESTAMP","timestamp":"TIMESTAMP","status":"ok","errors":0,"duration":DURATION,"attrs":{"release":"PLUGIN_VERSION","environment":"production"}}]]], - [{"event_id":"UUID","sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"},"trace":{"environment":"production","release":"PLUGIN_VERSION","public_key":"UUID","trace_id":"UUID","org_id":"1","transaction":"Sentry Bundler Plugin execution","sampled":"true","sample_rand":"SAMPLE_RAND","sample_rate":"0.3"}},[[{"type":"transaction"},{"contexts":{"trace":{"span_id":"SHORT_UUID","trace_id":"UUID","data":{"sentry.origin":"manual","sentry.segment.name.source":"custom","sentry.sample_rate":0.3},"status":"ok","origin":"manual"},"runtime":{"name":"node","version":"NODE_VERSION"}},"spans":[],"start_timestamp":START_TIMESTAMP,"timestamp":TIMESTAMP,"transaction":"Sentry Bundler Plugin execution","type":"transaction","transaction_info":{"source":"custom"},"platform":"PLATFORM","event_id":"UUID","environment":"production","release":"PLUGIN_VERSION","tags":{"upload-legacy-sourcemaps":false,"module-metadata":false,"inject-build-information":false,"set-commits":"auto","finalize-release":true,"deploy-options":false,"custom-error-handler":false,"sourcemaps-assets":false,"delete-after-upload":false,"sourcemaps-disabled":false,"react-annotate":false,"node":"NODE_VERSION","platform":"PLATFORM","meta-framework":"none","application-key-set":false,"ci":true,"project":"undefined","bundler":"rollup","bundler-major-version":"4"},"user":{},"sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION","integrations":[],"packages":[{"name":"npm:@sentry/node","version":"SDK_VERSION"}]}}]]], + [{"event_id":"UUID","sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"},"trace":{"environment":"production","release":"PLUGIN_VERSION","public_key":"UUID","trace_id":"UUID","org_id":"1","transaction":"Sentry Bundler Plugin execution","sampled":"true","sample_rand":"SAMPLE_RAND","sample_rate":"0.3"}},[[{"type":"transaction"},{"contexts":{"trace":{"span_id":"SHORT_UUID","trace_id":"UUID","data":{"sentry.origin":"manual","sentry.segment.name.source":"custom","sentry.sample_rate":0.3},"status":"ok","origin":"manual"},"runtime":{"name":"node","version":"NODE_VERSION"}},"spans":[],"start_timestamp":START_TIMESTAMP,"timestamp":TIMESTAMP,"transaction":"Sentry Bundler Plugin execution","type":"transaction","transaction_info":{"source":"custom"},"platform":"PLATFORM","event_id":"UUID","environment":"production","release":"PLUGIN_VERSION","tags":{"upload-legacy-sourcemaps":false,"module-metadata":false,"inject-build-information":false,"set-commits":"auto","finalize-release":true,"deploy-options":false,"custom-error-handler":false,"sourcemaps-assets":false,"delete-after-upload":false,"delete-sourcemaps-after-upload":"auto","sourcemaps-disabled":false,"react-annotate":false,"node":"NODE_VERSION","platform":"PLATFORM","meta-framework":"none","application-key-set":false,"ci":true,"project":"undefined","bundler":"rollup","bundler-major-version":"4"},"user":{},"sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION","integrations":[],"packages":[{"name":"npm:@sentry/node","version":"SDK_VERSION"}]}}]]], [{"sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"}},[[{"type":"session"},{"sid":"UUID","init":false,"started":"TIMESTAMP","timestamp":"TIMESTAMP","status":"exited","errors":0,"duration":DURATION,"attrs":{"release":"PLUGIN_VERSION","environment":"production"}}]]], ", } diff --git a/dev-packages/bundler-plugin-integration-tests/fixtures/rollup3/telemetry.test.ts b/dev-packages/bundler-plugin-integration-tests/fixtures/rollup3/telemetry.test.ts index 329f3b6969ed..72ae140b7b42 100644 --- a/dev-packages/bundler-plugin-integration-tests/fixtures/rollup3/telemetry.test.ts +++ b/dev-packages/bundler-plugin-integration-tests/fixtures/rollup3/telemetry.test.ts @@ -9,7 +9,7 @@ test(import.meta.url, ({ runBundler, readOutputFiles, runFileInNode }) => { !function(){try{var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};e.SENTRY_RELEASE={id:"CURRENT_SHA"};var n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="00000000-0000-0000-0000-000000000000",e._sentryDebugIdIdentifier="sentry-dbid-00000000-0000-0000-0000-000000000000");}catch(e){}}();console.log("hello world"); ", "sentry-telemetry.json": "[{"sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"}},[[{"type":"session"},{"sid":"UUID","init":true,"started":"TIMESTAMP","timestamp":"TIMESTAMP","status":"ok","errors":0,"duration":DURATION,"attrs":{"release":"PLUGIN_VERSION","environment":"production"}}]]], - [{"event_id":"UUID","sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"},"trace":{"environment":"production","release":"PLUGIN_VERSION","public_key":"UUID","trace_id":"UUID","org_id":"1","transaction":"Sentry Bundler Plugin execution","sampled":"true","sample_rand":"SAMPLE_RAND","sample_rate":"0.3"}},[[{"type":"transaction"},{"contexts":{"trace":{"span_id":"SHORT_UUID","trace_id":"UUID","data":{"sentry.origin":"manual","sentry.segment.name.source":"custom","sentry.sample_rate":0.3},"status":"ok","origin":"manual"},"runtime":{"name":"node","version":"NODE_VERSION"}},"spans":[],"start_timestamp":START_TIMESTAMP,"timestamp":TIMESTAMP,"transaction":"Sentry Bundler Plugin execution","type":"transaction","transaction_info":{"source":"custom"},"platform":"PLATFORM","event_id":"UUID","environment":"production","release":"PLUGIN_VERSION","tags":{"upload-legacy-sourcemaps":false,"module-metadata":false,"inject-build-information":false,"set-commits":"auto","finalize-release":true,"deploy-options":false,"custom-error-handler":false,"sourcemaps-assets":false,"delete-after-upload":false,"sourcemaps-disabled":false,"react-annotate":false,"node":"NODE_VERSION","platform":"PLATFORM","meta-framework":"none","application-key-set":false,"ci":true,"project":"undefined","bundler":"rollup","bundler-major-version":"3"},"user":{},"sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION","integrations":[],"packages":[{"name":"npm:@sentry/node","version":"SDK_VERSION"}]}}]]], + [{"event_id":"UUID","sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"},"trace":{"environment":"production","release":"PLUGIN_VERSION","public_key":"UUID","trace_id":"UUID","org_id":"1","transaction":"Sentry Bundler Plugin execution","sampled":"true","sample_rand":"SAMPLE_RAND","sample_rate":"0.3"}},[[{"type":"transaction"},{"contexts":{"trace":{"span_id":"SHORT_UUID","trace_id":"UUID","data":{"sentry.origin":"manual","sentry.segment.name.source":"custom","sentry.sample_rate":0.3},"status":"ok","origin":"manual"},"runtime":{"name":"node","version":"NODE_VERSION"}},"spans":[],"start_timestamp":START_TIMESTAMP,"timestamp":TIMESTAMP,"transaction":"Sentry Bundler Plugin execution","type":"transaction","transaction_info":{"source":"custom"},"platform":"PLATFORM","event_id":"UUID","environment":"production","release":"PLUGIN_VERSION","tags":{"upload-legacy-sourcemaps":false,"module-metadata":false,"inject-build-information":false,"set-commits":"auto","finalize-release":true,"deploy-options":false,"custom-error-handler":false,"sourcemaps-assets":false,"delete-after-upload":false,"delete-sourcemaps-after-upload":"auto","sourcemaps-disabled":false,"react-annotate":false,"node":"NODE_VERSION","platform":"PLATFORM","meta-framework":"none","application-key-set":false,"ci":true,"project":"undefined","bundler":"rollup","bundler-major-version":"3"},"user":{},"sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION","integrations":[],"packages":[{"name":"npm:@sentry/node","version":"SDK_VERSION"}]}}]]], [{"sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"}},[[{"type":"session"},{"sid":"UUID","init":false,"started":"TIMESTAMP","timestamp":"TIMESTAMP","status":"exited","errors":0,"duration":DURATION,"attrs":{"release":"PLUGIN_VERSION","environment":"production"}}]]], ", } diff --git a/dev-packages/bundler-plugin-integration-tests/fixtures/rollup4/telemetry.test.ts b/dev-packages/bundler-plugin-integration-tests/fixtures/rollup4/telemetry.test.ts index 4e0c4d969660..d4bde5c071ae 100644 --- a/dev-packages/bundler-plugin-integration-tests/fixtures/rollup4/telemetry.test.ts +++ b/dev-packages/bundler-plugin-integration-tests/fixtures/rollup4/telemetry.test.ts @@ -9,7 +9,7 @@ test(import.meta.url, ({ runBundler, readOutputFiles, runFileInNode }) => { !function(){try{var e="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};e.SENTRY_RELEASE={id:"CURRENT_SHA"};var n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="00000000-0000-0000-0000-000000000000",e._sentryDebugIdIdentifier="sentry-dbid-00000000-0000-0000-0000-000000000000");}catch(e){}}();console.log("hello world"); ", "sentry-telemetry.json": "[{"sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"}},[[{"type":"session"},{"sid":"UUID","init":true,"started":"TIMESTAMP","timestamp":"TIMESTAMP","status":"ok","errors":0,"duration":DURATION,"attrs":{"release":"PLUGIN_VERSION","environment":"production"}}]]], - [{"event_id":"UUID","sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"},"trace":{"environment":"production","release":"PLUGIN_VERSION","public_key":"UUID","trace_id":"UUID","org_id":"1","transaction":"Sentry Bundler Plugin execution","sampled":"true","sample_rand":"SAMPLE_RAND","sample_rate":"0.3"}},[[{"type":"transaction"},{"contexts":{"trace":{"span_id":"SHORT_UUID","trace_id":"UUID","data":{"sentry.origin":"manual","sentry.segment.name.source":"custom","sentry.sample_rate":0.3},"status":"ok","origin":"manual"},"runtime":{"name":"node","version":"NODE_VERSION"}},"spans":[],"start_timestamp":START_TIMESTAMP,"timestamp":TIMESTAMP,"transaction":"Sentry Bundler Plugin execution","type":"transaction","transaction_info":{"source":"custom"},"platform":"PLATFORM","event_id":"UUID","environment":"production","release":"PLUGIN_VERSION","tags":{"upload-legacy-sourcemaps":false,"module-metadata":false,"inject-build-information":false,"set-commits":"auto","finalize-release":true,"deploy-options":false,"custom-error-handler":false,"sourcemaps-assets":false,"delete-after-upload":false,"sourcemaps-disabled":false,"react-annotate":false,"node":"NODE_VERSION","platform":"PLATFORM","meta-framework":"none","application-key-set":false,"ci":true,"project":"undefined","bundler":"rollup","bundler-major-version":"4"},"user":{},"sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION","integrations":[],"packages":[{"name":"npm:@sentry/node","version":"SDK_VERSION"}]}}]]], + [{"event_id":"UUID","sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"},"trace":{"environment":"production","release":"PLUGIN_VERSION","public_key":"UUID","trace_id":"UUID","org_id":"1","transaction":"Sentry Bundler Plugin execution","sampled":"true","sample_rand":"SAMPLE_RAND","sample_rate":"0.3"}},[[{"type":"transaction"},{"contexts":{"trace":{"span_id":"SHORT_UUID","trace_id":"UUID","data":{"sentry.origin":"manual","sentry.segment.name.source":"custom","sentry.sample_rate":0.3},"status":"ok","origin":"manual"},"runtime":{"name":"node","version":"NODE_VERSION"}},"spans":[],"start_timestamp":START_TIMESTAMP,"timestamp":TIMESTAMP,"transaction":"Sentry Bundler Plugin execution","type":"transaction","transaction_info":{"source":"custom"},"platform":"PLATFORM","event_id":"UUID","environment":"production","release":"PLUGIN_VERSION","tags":{"upload-legacy-sourcemaps":false,"module-metadata":false,"inject-build-information":false,"set-commits":"auto","finalize-release":true,"deploy-options":false,"custom-error-handler":false,"sourcemaps-assets":false,"delete-after-upload":false,"delete-sourcemaps-after-upload":"auto","sourcemaps-disabled":false,"react-annotate":false,"node":"NODE_VERSION","platform":"PLATFORM","meta-framework":"none","application-key-set":false,"ci":true,"project":"undefined","bundler":"rollup","bundler-major-version":"4"},"user":{},"sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION","integrations":[],"packages":[{"name":"npm:@sentry/node","version":"SDK_VERSION"}]}}]]], [{"sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"}},[[{"type":"session"},{"sid":"UUID","init":false,"started":"TIMESTAMP","timestamp":"TIMESTAMP","status":"exited","errors":0,"duration":DURATION,"attrs":{"release":"PLUGIN_VERSION","environment":"production"}}]]], ", } diff --git a/dev-packages/bundler-plugin-integration-tests/fixtures/vite4/telemetry.test.ts b/dev-packages/bundler-plugin-integration-tests/fixtures/vite4/telemetry.test.ts index d6ea3f3a91d5..f61dde5478e3 100644 --- a/dev-packages/bundler-plugin-integration-tests/fixtures/vite4/telemetry.test.ts +++ b/dev-packages/bundler-plugin-integration-tests/fixtures/vite4/telemetry.test.ts @@ -17,7 +17,7 @@ test(import.meta.url, ({ runBundler, readOutputFiles, runFileInNode }) => { console.log("hello world"); ", "sentry-telemetry.json": "[{"sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"}},[[{"type":"session"},{"sid":"UUID","init":true,"started":"TIMESTAMP","timestamp":"TIMESTAMP","status":"ok","errors":0,"duration":DURATION,"attrs":{"release":"PLUGIN_VERSION","environment":"production"}}]]], - [{"event_id":"UUID","sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"},"trace":{"environment":"production","release":"PLUGIN_VERSION","public_key":"UUID","trace_id":"UUID","org_id":"1","transaction":"Sentry Bundler Plugin execution","sampled":"true","sample_rand":"SAMPLE_RAND","sample_rate":"0.3"}},[[{"type":"transaction"},{"contexts":{"trace":{"span_id":"SHORT_UUID","trace_id":"UUID","data":{"sentry.origin":"manual","sentry.segment.name.source":"custom","sentry.sample_rate":0.3},"status":"ok","origin":"manual"},"runtime":{"name":"node","version":"NODE_VERSION"}},"spans":[],"start_timestamp":START_TIMESTAMP,"timestamp":TIMESTAMP,"transaction":"Sentry Bundler Plugin execution","type":"transaction","transaction_info":{"source":"custom"},"platform":"PLATFORM","event_id":"UUID","environment":"production","release":"PLUGIN_VERSION","tags":{"upload-legacy-sourcemaps":false,"module-metadata":false,"inject-build-information":false,"set-commits":"auto","finalize-release":true,"deploy-options":false,"custom-error-handler":false,"sourcemaps-assets":false,"delete-after-upload":false,"sourcemaps-disabled":false,"react-annotate":false,"node":"NODE_VERSION","platform":"PLATFORM","meta-framework":"none","application-key-set":false,"ci":true,"project":"undefined","bundler":"vite","bundler-major-version":"4"},"user":{},"sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION","integrations":[],"packages":[{"name":"npm:@sentry/node","version":"SDK_VERSION"}]}}]]], + [{"event_id":"UUID","sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"},"trace":{"environment":"production","release":"PLUGIN_VERSION","public_key":"UUID","trace_id":"UUID","org_id":"1","transaction":"Sentry Bundler Plugin execution","sampled":"true","sample_rand":"SAMPLE_RAND","sample_rate":"0.3"}},[[{"type":"transaction"},{"contexts":{"trace":{"span_id":"SHORT_UUID","trace_id":"UUID","data":{"sentry.origin":"manual","sentry.segment.name.source":"custom","sentry.sample_rate":0.3},"status":"ok","origin":"manual"},"runtime":{"name":"node","version":"NODE_VERSION"}},"spans":[],"start_timestamp":START_TIMESTAMP,"timestamp":TIMESTAMP,"transaction":"Sentry Bundler Plugin execution","type":"transaction","transaction_info":{"source":"custom"},"platform":"PLATFORM","event_id":"UUID","environment":"production","release":"PLUGIN_VERSION","tags":{"upload-legacy-sourcemaps":false,"module-metadata":false,"inject-build-information":false,"set-commits":"auto","finalize-release":true,"deploy-options":false,"custom-error-handler":false,"sourcemaps-assets":false,"delete-after-upload":false,"delete-sourcemaps-after-upload":"auto","sourcemaps-disabled":false,"react-annotate":false,"node":"NODE_VERSION","platform":"PLATFORM","meta-framework":"none","application-key-set":false,"ci":true,"project":"undefined","bundler":"vite","bundler-major-version":"4"},"user":{},"sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION","integrations":[],"packages":[{"name":"npm:@sentry/node","version":"SDK_VERSION"}]}}]]], [{"sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"}},[[{"type":"session"},{"sid":"UUID","init":false,"started":"TIMESTAMP","timestamp":"TIMESTAMP","status":"exited","errors":0,"duration":DURATION,"attrs":{"release":"PLUGIN_VERSION","environment":"production"}}]]], ", } diff --git a/dev-packages/bundler-plugin-integration-tests/fixtures/vite7/telemetry.test.ts b/dev-packages/bundler-plugin-integration-tests/fixtures/vite7/telemetry.test.ts index 3b5c50c6a113..9ad711e030fc 100644 --- a/dev-packages/bundler-plugin-integration-tests/fixtures/vite7/telemetry.test.ts +++ b/dev-packages/bundler-plugin-integration-tests/fixtures/vite7/telemetry.test.ts @@ -17,7 +17,7 @@ test(import.meta.url, ({ runBundler, readOutputFiles, runFileInNode }) => { console.log("hello world"); ", "sentry-telemetry.json": "[{"sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"}},[[{"type":"session"},{"sid":"UUID","init":true,"started":"TIMESTAMP","timestamp":"TIMESTAMP","status":"ok","errors":0,"duration":DURATION,"attrs":{"release":"PLUGIN_VERSION","environment":"production"}}]]], - [{"event_id":"UUID","sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"},"trace":{"environment":"production","release":"PLUGIN_VERSION","public_key":"UUID","trace_id":"UUID","org_id":"1","transaction":"Sentry Bundler Plugin execution","sampled":"true","sample_rand":"SAMPLE_RAND","sample_rate":"0.3"}},[[{"type":"transaction"},{"contexts":{"trace":{"span_id":"SHORT_UUID","trace_id":"UUID","data":{"sentry.origin":"manual","sentry.segment.name.source":"custom","sentry.sample_rate":0.3},"status":"ok","origin":"manual"},"runtime":{"name":"node","version":"NODE_VERSION"}},"spans":[],"start_timestamp":START_TIMESTAMP,"timestamp":TIMESTAMP,"transaction":"Sentry Bundler Plugin execution","type":"transaction","transaction_info":{"source":"custom"},"platform":"PLATFORM","event_id":"UUID","environment":"production","release":"PLUGIN_VERSION","tags":{"upload-legacy-sourcemaps":false,"module-metadata":false,"inject-build-information":false,"set-commits":"auto","finalize-release":true,"deploy-options":false,"custom-error-handler":false,"sourcemaps-assets":false,"delete-after-upload":false,"sourcemaps-disabled":false,"react-annotate":false,"node":"NODE_VERSION","platform":"PLATFORM","meta-framework":"none","application-key-set":false,"ci":true,"project":"undefined","bundler":"vite","bundler-major-version":"7"},"user":{},"sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION","integrations":[],"packages":[{"name":"npm:@sentry/node","version":"SDK_VERSION"}]}}]]], + [{"event_id":"UUID","sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"},"trace":{"environment":"production","release":"PLUGIN_VERSION","public_key":"UUID","trace_id":"UUID","org_id":"1","transaction":"Sentry Bundler Plugin execution","sampled":"true","sample_rand":"SAMPLE_RAND","sample_rate":"0.3"}},[[{"type":"transaction"},{"contexts":{"trace":{"span_id":"SHORT_UUID","trace_id":"UUID","data":{"sentry.origin":"manual","sentry.segment.name.source":"custom","sentry.sample_rate":0.3},"status":"ok","origin":"manual"},"runtime":{"name":"node","version":"NODE_VERSION"}},"spans":[],"start_timestamp":START_TIMESTAMP,"timestamp":TIMESTAMP,"transaction":"Sentry Bundler Plugin execution","type":"transaction","transaction_info":{"source":"custom"},"platform":"PLATFORM","event_id":"UUID","environment":"production","release":"PLUGIN_VERSION","tags":{"upload-legacy-sourcemaps":false,"module-metadata":false,"inject-build-information":false,"set-commits":"auto","finalize-release":true,"deploy-options":false,"custom-error-handler":false,"sourcemaps-assets":false,"delete-after-upload":false,"delete-sourcemaps-after-upload":"auto","sourcemaps-disabled":false,"react-annotate":false,"node":"NODE_VERSION","platform":"PLATFORM","meta-framework":"none","application-key-set":false,"ci":true,"project":"undefined","bundler":"vite","bundler-major-version":"7"},"user":{},"sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION","integrations":[],"packages":[{"name":"npm:@sentry/node","version":"SDK_VERSION"}]}}]]], [{"sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"}},[[{"type":"session"},{"sid":"UUID","init":false,"started":"TIMESTAMP","timestamp":"TIMESTAMP","status":"exited","errors":0,"duration":DURATION,"attrs":{"release":"PLUGIN_VERSION","environment":"production"}}]]], ", } diff --git a/dev-packages/bundler-plugin-integration-tests/fixtures/vite8/telemetry.test.ts b/dev-packages/bundler-plugin-integration-tests/fixtures/vite8/telemetry.test.ts index d09ee8bc094a..6d832d2ae117 100644 --- a/dev-packages/bundler-plugin-integration-tests/fixtures/vite8/telemetry.test.ts +++ b/dev-packages/bundler-plugin-integration-tests/fixtures/vite8/telemetry.test.ts @@ -18,7 +18,7 @@ test(import.meta.url, ({ runBundler, readOutputFiles, runFileInNode }) => { //#endregion ", "sentry-telemetry.json": "[{"sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"}},[[{"type":"session"},{"sid":"UUID","init":true,"started":"TIMESTAMP","timestamp":"TIMESTAMP","status":"ok","errors":0,"duration":DURATION,"attrs":{"release":"PLUGIN_VERSION","environment":"production"}}]]], - [{"event_id":"UUID","sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"},"trace":{"environment":"production","release":"PLUGIN_VERSION","public_key":"UUID","trace_id":"UUID","org_id":"1","transaction":"Sentry Bundler Plugin execution","sampled":"true","sample_rand":"SAMPLE_RAND","sample_rate":"0.3"}},[[{"type":"transaction"},{"contexts":{"trace":{"span_id":"SHORT_UUID","trace_id":"UUID","data":{"sentry.origin":"manual","sentry.segment.name.source":"custom","sentry.sample_rate":0.3},"status":"ok","origin":"manual"},"runtime":{"name":"node","version":"NODE_VERSION"}},"spans":[],"start_timestamp":START_TIMESTAMP,"timestamp":TIMESTAMP,"transaction":"Sentry Bundler Plugin execution","type":"transaction","transaction_info":{"source":"custom"},"platform":"PLATFORM","event_id":"UUID","environment":"production","release":"PLUGIN_VERSION","tags":{"upload-legacy-sourcemaps":false,"module-metadata":false,"inject-build-information":false,"set-commits":"auto","finalize-release":true,"deploy-options":false,"custom-error-handler":false,"sourcemaps-assets":false,"delete-after-upload":false,"sourcemaps-disabled":false,"react-annotate":false,"node":"NODE_VERSION","platform":"PLATFORM","meta-framework":"none","application-key-set":false,"ci":true,"project":"undefined","bundler":"vite","bundler-major-version":"8"},"user":{},"sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION","integrations":[],"packages":[{"name":"npm:@sentry/node","version":"SDK_VERSION"}]}}]]], + [{"event_id":"UUID","sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"},"trace":{"environment":"production","release":"PLUGIN_VERSION","public_key":"UUID","trace_id":"UUID","org_id":"1","transaction":"Sentry Bundler Plugin execution","sampled":"true","sample_rand":"SAMPLE_RAND","sample_rate":"0.3"}},[[{"type":"transaction"},{"contexts":{"trace":{"span_id":"SHORT_UUID","trace_id":"UUID","data":{"sentry.origin":"manual","sentry.segment.name.source":"custom","sentry.sample_rate":0.3},"status":"ok","origin":"manual"},"runtime":{"name":"node","version":"NODE_VERSION"}},"spans":[],"start_timestamp":START_TIMESTAMP,"timestamp":TIMESTAMP,"transaction":"Sentry Bundler Plugin execution","type":"transaction","transaction_info":{"source":"custom"},"platform":"PLATFORM","event_id":"UUID","environment":"production","release":"PLUGIN_VERSION","tags":{"upload-legacy-sourcemaps":false,"module-metadata":false,"inject-build-information":false,"set-commits":"auto","finalize-release":true,"deploy-options":false,"custom-error-handler":false,"sourcemaps-assets":false,"delete-after-upload":false,"delete-sourcemaps-after-upload":"auto","sourcemaps-disabled":false,"react-annotate":false,"node":"NODE_VERSION","platform":"PLATFORM","meta-framework":"none","application-key-set":false,"ci":true,"project":"undefined","bundler":"vite","bundler-major-version":"8"},"user":{},"sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION","integrations":[],"packages":[{"name":"npm:@sentry/node","version":"SDK_VERSION"}]}}]]], [{"sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"}},[[{"type":"session"},{"sid":"UUID","init":false,"started":"TIMESTAMP","timestamp":"TIMESTAMP","status":"exited","errors":0,"duration":DURATION,"attrs":{"release":"PLUGIN_VERSION","environment":"production"}}]]], ", } diff --git a/dev-packages/bundler-plugin-integration-tests/fixtures/webpack5/telemetry.test.ts b/dev-packages/bundler-plugin-integration-tests/fixtures/webpack5/telemetry.test.ts index 0854328dd4a8..22d582168680 100644 --- a/dev-packages/bundler-plugin-integration-tests/fixtures/webpack5/telemetry.test.ts +++ b/dev-packages/bundler-plugin-integration-tests/fixtures/webpack5/telemetry.test.ts @@ -13,7 +13,7 @@ test(import.meta.url, ({ runBundler, readOutputFiles, runFileInNode }) => { /******/ })() ;", "sentry-telemetry.json": "[{"sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"}},[[{"type":"session"},{"sid":"UUID","init":true,"started":"TIMESTAMP","timestamp":"TIMESTAMP","status":"ok","errors":0,"duration":DURATION,"attrs":{"release":"PLUGIN_VERSION","environment":"production"}}]]], - [{"event_id":"UUID","sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"},"trace":{"environment":"production","release":"PLUGIN_VERSION","public_key":"UUID","trace_id":"UUID","org_id":"1","transaction":"Sentry Bundler Plugin execution","sampled":"true","sample_rand":"SAMPLE_RAND","sample_rate":"0.3"}},[[{"type":"transaction"},{"contexts":{"trace":{"span_id":"SHORT_UUID","trace_id":"UUID","data":{"sentry.origin":"manual","sentry.segment.name.source":"custom","sentry.sample_rate":0.3},"status":"ok","origin":"manual"},"runtime":{"name":"node","version":"NODE_VERSION"}},"spans":[],"start_timestamp":START_TIMESTAMP,"timestamp":TIMESTAMP,"transaction":"Sentry Bundler Plugin execution","type":"transaction","transaction_info":{"source":"custom"},"platform":"PLATFORM","event_id":"UUID","environment":"production","release":"PLUGIN_VERSION","tags":{"upload-legacy-sourcemaps":false,"module-metadata":false,"inject-build-information":false,"set-commits":"auto","finalize-release":true,"deploy-options":false,"custom-error-handler":false,"sourcemaps-assets":false,"delete-after-upload":false,"sourcemaps-disabled":false,"react-annotate":false,"node":"NODE_VERSION","platform":"PLATFORM","meta-framework":"none","application-key-set":false,"ci":true,"project":"undefined","bundler":"webpack","bundler-major-version":"5"},"user":{},"sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION","integrations":[],"packages":[{"name":"npm:@sentry/node","version":"SDK_VERSION"}]}}]]], + [{"event_id":"UUID","sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"},"trace":{"environment":"production","release":"PLUGIN_VERSION","public_key":"UUID","trace_id":"UUID","org_id":"1","transaction":"Sentry Bundler Plugin execution","sampled":"true","sample_rand":"SAMPLE_RAND","sample_rate":"0.3"}},[[{"type":"transaction"},{"contexts":{"trace":{"span_id":"SHORT_UUID","trace_id":"UUID","data":{"sentry.origin":"manual","sentry.segment.name.source":"custom","sentry.sample_rate":0.3},"status":"ok","origin":"manual"},"runtime":{"name":"node","version":"NODE_VERSION"}},"spans":[],"start_timestamp":START_TIMESTAMP,"timestamp":TIMESTAMP,"transaction":"Sentry Bundler Plugin execution","type":"transaction","transaction_info":{"source":"custom"},"platform":"PLATFORM","event_id":"UUID","environment":"production","release":"PLUGIN_VERSION","tags":{"upload-legacy-sourcemaps":false,"module-metadata":false,"inject-build-information":false,"set-commits":"auto","finalize-release":true,"deploy-options":false,"custom-error-handler":false,"sourcemaps-assets":false,"delete-after-upload":false,"delete-sourcemaps-after-upload":"auto","sourcemaps-disabled":false,"react-annotate":false,"node":"NODE_VERSION","platform":"PLATFORM","meta-framework":"none","application-key-set":false,"ci":true,"project":"undefined","bundler":"webpack","bundler-major-version":"5"},"user":{},"sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION","integrations":[],"packages":[{"name":"npm:@sentry/node","version":"SDK_VERSION"}]}}]]], [{"sent_at":"TIMESTAMP","sdk":{"name":"sentry.javascript.node","version":"SDK_VERSION"}},[[{"type":"session"},{"sid":"UUID","init":false,"started":"TIMESTAMP","timestamp":"TIMESTAMP","status":"exited","errors":0,"duration":DURATION,"attrs":{"release":"PLUGIN_VERSION","environment":"production"}}]]], ", } diff --git a/dev-packages/bundler-plugin-integration-tests/package.json b/dev-packages/bundler-plugin-integration-tests/package.json index df87dbd53c3a..b4f44b5923e7 100644 --- a/dev-packages/bundler-plugin-integration-tests/package.json +++ b/dev-packages/bundler-plugin-integration-tests/package.json @@ -11,10 +11,8 @@ "clean:build": "premove ./fixtures/*/out", "clean:deps": "premove node_modules" }, - "dependencies": { - "@sentry/bundler-plugins": "11.10.0" - }, "devDependencies": { + "@sentry/bundler-plugins": "11.10.0", "premove": "^4.0.0", "sentry": "^0.45.0", "typescript": "~6.0.3", diff --git a/dev-packages/bundler-tests/package.json b/dev-packages/bundler-tests/package.json index 9f119ab94f65..7baa238d34a6 100644 --- a/dev-packages/bundler-tests/package.json +++ b/dev-packages/bundler-tests/package.json @@ -11,13 +11,27 @@ "scripts": { "test": "vitest run" }, - "dependencies": { + "devDependencies": { "@rollup/plugin-node-resolve": "^16.0.3", + "@sentry/astro": "11.10.0", "@sentry/browser": "11.10.0", + "@sentry/bun": "11.10.0", + "@sentry/cloudflare": "11.10.0", + "@sentry/deno": "11.10.0", + "@sentry/nextjs": "11.10.0", + "@sentry/node": "11.10.0", + "@sentry/nuxt": "11.10.0", + "@sentry/react-router": "11.10.0", + "@sentry/remix": "11.10.0", + "@sentry/solidstart": "11.10.0", + "@sentry/sveltekit": "11.10.0", + "@sentry/tanstackstart-react": "11.10.0", + "@sentry/vercel-edge": "11.10.0", + "@vercel/nft": "^1.11.0", "rollup": "^4.60.3", - "vite": "^8.3.1", + "vite": "^8.3.5", "vitest": "^4.1.11", - "webpack": "^5.0.0" + "webpack": "^5.112.2" }, "type": "module" } diff --git a/dev-packages/bundler-tests/tests/runtime-entries.test.ts b/dev-packages/bundler-tests/tests/runtime-entries.test.ts new file mode 100644 index 000000000000..156102cc6765 --- /dev/null +++ b/dev-packages/bundler-tests/tests/runtime-entries.test.ts @@ -0,0 +1,279 @@ +import { describe, expect, test } from 'vitest'; +import * as path from 'node:path'; +import * as fs from 'node:fs'; +import { fileURLToPath } from 'node:url'; + +import { nodeFileTrace, resolve } from '@vercel/nft'; + +/** + * Server runtime entries must not reach build-time tooling. When they do, + * the tooling ends up in server bundles and traced outputs (Next standalone, + * Nitro, workerd, ...), where it breaks in a different way for each + * consumer. `@vercel/nft` is the tracer that Next standalone and the Vercel + * adapters use, so it reports what those consumers would ship. + */ + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT_DIR = path.join(__dirname, '../../..'); +const FIXTURE_DIR = path.join(__dirname, '..', 'dist', 'runtime-entries'); + +const BUILD_TIME_PATTERNS: RegExp[] = [ + /(^|\/)packages\/bundler-plugins\//, + /(^|\/)node_modules\/@sentry\/bundler-plugins(\/|$)/, + /(^|\/)packages\/server-utils\/build\/[^/]+\/orchestrion\/bundler\//, + /(^|\/)node_modules\/(vite|webpack|rollup|esbuild|oxc-parser)\//, + /(^|\/)node_modules\/@(babel|rollup|oxc-parser)\//, + /\.wasm$/, +]; + +const KNOWN_VIOLATIONS = new Set([ + // These root entries also re-export config-time code (`sentryAstro`, + // `withSentry`). They must keep it until v12 for backwards compatibility. + '@sentry/astro', + '@sentry/solidstart', + // Remix 3 has no bundler. Its asset server runs the orchestrion + // transform on browser modules at request time. + '@sentry/remix/v3', + '@sentry/remix/v3/node', +]); + +type Format = 'esm' | 'cjs'; + +interface Case { + specifier: string; + conditions: string[]; + formats: Format[]; +} + +const NODE = ['node']; +const WORKERD = ['workerd', 'worker']; +const BOTH: Format[] = ['esm', 'cjs']; +const ESM: Format[] = ['esm']; + +const CASES: Case[] = [ + { specifier: '@sentry/node', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/node/import', conditions: NODE, formats: ESM }, + { specifier: '@sentry/bun', conditions: ['bun', 'node'], formats: BOTH }, + { specifier: '@sentry/deno', conditions: ['deno', 'node'], formats: ESM }, + { specifier: '@sentry/vercel-edge', conditions: ['edge-light'], formats: BOTH }, + { specifier: '@sentry/cloudflare', conditions: WORKERD, formats: BOTH }, + { specifier: '@sentry/cloudflare', conditions: [...WORKERD, 'development'], formats: BOTH }, + { specifier: '@sentry/cloudflare/request', conditions: WORKERD, formats: BOTH }, + { specifier: '@sentry/cloudflare/orchestrion-diagnostics-channel', conditions: WORKERD, formats: BOTH }, + { specifier: '@sentry/deno/import', conditions: ['deno', 'node'], formats: ESM }, + + { specifier: '@sentry/nextjs', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/nextjs', conditions: ['edge-light'], formats: BOTH }, + { specifier: '@sentry/nextjs', conditions: WORKERD, formats: BOTH }, + { specifier: '@sentry/nextjs/cloudflare', conditions: WORKERD, formats: BOTH }, + { specifier: '@sentry/nextjs/import', conditions: NODE, formats: ESM }, + { specifier: '@sentry/nextjs/tunnel', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/nextjs/tunnel', conditions: ['edge-light'], formats: BOTH }, + { specifier: '@sentry/nextjs/async-storage-shim', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/nextjs/orchestrion-runtime/index', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/nextjs/orchestrion-runtime/register', conditions: NODE, formats: BOTH }, + + { specifier: '@sentry/nuxt', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/nuxt/module/plugins', conditions: NODE, formats: ESM }, + + { specifier: '@sentry/sveltekit', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/sveltekit', conditions: WORKERD, formats: BOTH }, + { specifier: '@sentry/sveltekit', conditions: ['worker'], formats: BOTH }, + { specifier: '@sentry/sveltekit/opentelemetry-api', conditions: NODE, formats: BOTH }, + + { specifier: '@sentry/astro', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/astro/middleware', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/astro/import', conditions: NODE, formats: ESM }, + + { specifier: '@sentry/react-router', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/react-router', conditions: WORKERD, formats: BOTH }, + { specifier: '@sentry/react-router/cloudflare', conditions: WORKERD, formats: BOTH }, + + { specifier: '@sentry/remix', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/remix', conditions: ['worker'], formats: BOTH }, + { specifier: '@sentry/remix/cloudflare', conditions: WORKERD, formats: BOTH }, + { specifier: '@sentry/remix/import', conditions: NODE, formats: ESM }, + { specifier: '@sentry/remix/v3', conditions: NODE, formats: ESM }, + { specifier: '@sentry/remix/v3/node', conditions: NODE, formats: ESM }, + + { specifier: '@sentry/solidstart', conditions: NODE, formats: BOTH }, + + { specifier: '@sentry/tanstackstart-react', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/tanstackstart-react/import', conditions: NODE, formats: ESM }, +]; + +/** + * Build-time entries must reach build-time tooling. If one does not, then + * `BUILD_TIME_PATTERNS` no longer matches the tooling that we use. + */ +const BUILD_TIME_CASES: Case[] = [ + { specifier: '@sentry/node/vite', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/node/rollup', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/node/webpack', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/node/esbuild', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/bun/plugin', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/cloudflare/vite', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/nextjs/config', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/nuxt/module', conditions: NODE, formats: ESM }, + { specifier: '@sentry/sveltekit/vite', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/astro/integration', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/react-router/vite', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/remix/vite', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/solidstart/vite', conditions: NODE, formats: BOTH }, + { specifier: '@sentry/tanstackstart-react/vite', conditions: NODE, formats: BOTH }, +]; + +/** Browser entries of the packages above. This test does not check them. */ +const CLIENT_ENTRIES = new Set([ + '@sentry/sveltekit/browser-tracing-variant', + '@sentry/remix/v3/client', + '@sentry/solidstart/solidrouter', +]); + +function writeFixture(specifier: string, conditions: string[], format: Format): string { + fs.mkdirSync(FIXTURE_DIR, { recursive: true }); + const name = `${specifier.replace(/[@/]/g, '_')}-${conditions.join('_')}.${format === 'esm' ? 'mjs' : 'cjs'}`; + const file = path.join(FIXTURE_DIR, name); + fs.writeFileSync(file, format === 'esm' ? `import '${specifier}';\n` : `require('${specifier}');\n`); + return file; +} + +function isBuildTime(file: string): boolean { + return BUILD_TIME_PATTERNS.some(pattern => pattern.test(file)); +} + +type Reasons = Awaited>['reasons']; + +// Most entries share `@sentry/node` and its dependencies, so sharing the +// file and analysis cache saves most of the work. +const traceCache = {}; + +/** The shortest import chain from `entry` to `file`, both relative to `ROOT_DIR`. */ +function importChain(reasons: Reasons, entry: string, file: string): string[] { + const importedBy = new Map([[file, file]]); + const queue = [file]; + while (queue.length) { + const current = queue.shift() as string; + if (current === entry) { + const chain = [current]; + for (let link = current; link !== file;) { + link = importedBy.get(link) as string; + chain.push(link); + } + return chain; + } + for (const parent of reasons.get(current)?.parents ?? []) { + if (!importedBy.has(parent)) { + importedBy.set(parent, current); + queue.push(parent); + } + } + } + return [file]; +} + +/** + * Each place where a runtime module imports a build-time module, as the + * import chain from the entry. + */ +async function findBuildTimeImports(fixture: string, conditions: string[]): Promise { + const imports: [importer: string, file: string][] = []; + + const { fileList, reasons } = await nodeFileTrace([fixture], { + base: ROOT_DIR, + conditions, + cache: traceCache, + // A dynamic `fs` path in config code makes nft glob the whole repo. + // That is slow and finds user files, not package code. + analysis: { emitGlobs: false }, + // Stop at the first build-time module, instead of tracing all of Vite + // or Babel. `.wasm` files are assets, so they stay in the trace with + // their importer. + ignore: file => isBuildTime(file) && !file.endsWith('.wasm'), + async resolve(id, parent, job, cjsResolve) { + const resolved = await resolve(id, parent, job, cjsResolve); + for (const file of [resolved].flat()) { + if (isBuildTime(file)) { + imports.push([path.relative(ROOT_DIR, parent), path.relative(ROOT_DIR, file)]); + } + } + return resolved; + }, + }); + + for (const file of fileList) { + if (file.endsWith('.wasm')) { + for (const parent of reasons.get(file)?.parents ?? []) { + imports.push([parent, file]); + } + } + } + + const entry = path.relative(ROOT_DIR, fixture); + const violations = imports.map(([importer, file]) => + importChain(reasons, entry, importer).concat(file).join('\n -> '), + ); + return [...new Set(violations)].sort(); +} + +describe('server runtime entries do not reach build-time tooling', () => { + for (const { specifier, conditions, formats } of CASES) { + for (const format of formats) { + const name = `${specifier} (${format}, conditions: ${conditions.join(', ')})`; + + if (KNOWN_VIOLATIONS.has(specifier)) { + test(`${name} is a known violation`, async () => { + const violations = await findBuildTimeImports(writeFixture(specifier, conditions, format), conditions); + // Remove the entry from KNOWN_VIOLATIONS when this fails. + expect(violations).not.toEqual([]); + }); + } else { + test(name, async () => { + const violations = await findBuildTimeImports(writeFixture(specifier, conditions, format), conditions); + expect(violations).toEqual([]); + }); + } + } + } +}); + +describe('build-time entries reach build-time tooling', () => { + for (const { specifier, conditions, formats } of BUILD_TIME_CASES) { + for (const format of formats) { + test(`${specifier} (${format}, conditions: ${conditions.join(', ')})`, async () => { + const violations = await findBuildTimeImports(writeFixture(specifier, conditions, format), conditions); + expect(violations).not.toEqual([]); + }); + } + } +}); + +test('every export of a tested package is classified', () => { + const classified = new Set([...CASES, ...BUILD_TIME_CASES].map(({ specifier }) => specifier)); + for (const specifier of CLIENT_ENTRIES) { + classified.add(specifier); + } + const packages = new Set([...classified].map(specifier => specifier.split('/').slice(0, 2).join('/'))); + + const unclassified: string[] = []; + for (const pkg of packages) { + const manifest = path.join(ROOT_DIR, 'packages', pkg.replace('@sentry/', ''), 'package.json'); + const { exports } = JSON.parse(fs.readFileSync(manifest, 'utf8')) as { exports: Record }; + for (const subpath of Object.keys(exports)) { + if (subpath === './package.json') { + continue; + } + const specifier = pkg + subpath.slice(1); + const [prefix, suffix] = specifier.split('*'); + const found = + suffix === undefined + ? classified.has(specifier) + : [...classified].some(s => s.startsWith(prefix as string) && s.endsWith(suffix)); + if (!found) { + unclassified.push(specifier); + } + } + } + // Add each one to CASES, BUILD_TIME_CASES, or CLIENT_ENTRIES. + expect(unclassified).toEqual([]); +}); diff --git a/dev-packages/clear-cache-gh-action/package.json b/dev-packages/clear-cache-gh-action/package.json index a6ffee8b7ba8..c5fd1253c0c0 100644 --- a/dev-packages/clear-cache-gh-action/package.json +++ b/dev-packages/clear-cache-gh-action/package.json @@ -13,11 +13,8 @@ "lint": "oxlint . --type-aware", "lint:fix": "oxlint . --fix --type-aware" }, - "dependencies": { - "@actions/core": "1.10.1", - "@actions/github": "^5.0.0" - }, "devDependencies": { - "eslint-plugin-regexp": "^3.1.0" + "@actions/core": "3.0.1", + "@actions/github": "^9.1.1" } } diff --git a/dev-packages/cloudflare-integration-tests/package.json b/dev-packages/cloudflare-integration-tests/package.json index 9fe7f72dda29..1e81fd479ed6 100644 --- a/dev-packages/cloudflare-integration-tests/package.json +++ b/dev-packages/cloudflare-integration-tests/package.json @@ -13,34 +13,31 @@ "test": "vitest run", "test:watch": "pnpm run test --watch" }, - "dependencies": { - "ai": "^6.0.0", + "devDependencies": { "@anthropic-ai/sdk": "0.133.0", + "@cloudflare/vite-plugin": "1.34.0", + "@cloudflare/workers-types": "^4.20260426.0", + "@cloudflare/workers-types-v5": "npm:@cloudflare/workers-types@5.20260710.1", "@google/genai": "^1.20.0", "@langchain/core": "^1.2.17", "@langchain/langgraph": "^1.0.1", "@langchain/openai": "^1.6.2", "@langchain/typesafe": "^0.0.2", + "@opentelemetry/api": "^1.9.1", "@prisma/adapter-d1": "6.15.0", "@prisma/client": "6.15.0", + "@sentry-internal/test-utils": "11.10.0", "@sentry/cloudflare": "11.10.0", + "@sentry/conventions": "0.30.0", "@sentry/core": "11.10.0", "@sentry/hono": "11.10.0", + "ai": "^6.0.0", "hono": "^4.13.7", "langchain": "^1.5.16", - "openai": "5.18.1" - }, - "devDependencies": { - "@cloudflare/vite-plugin": "1.34.0", - "@cloudflare/workers-types": "^4.20260426.0", - "@cloudflare/workers-types-v5": "npm:@cloudflare/workers-types@5.20260710.1", - "@opentelemetry/api": "^1.9.1", - "@sentry-internal/test-utils": "11.10.0", - "@sentry/conventions": "0.29.0", - "eslint-plugin-regexp": "^3.1.0", + "openai": "5.18.1", "prisma": "6.15.0", - "vite": "^8.3.1", + "vite": "^8.3.5", "vitest": "^4.1.11", - "wrangler": "4.86.0" + "wrangler": "4.156.0" } } diff --git a/dev-packages/cloudflare-integration-tests/suites/public-api/startSpan/test.ts b/dev-packages/cloudflare-integration-tests/suites/public-api/startSpan/test.ts index 0fa5d410368b..adf52419022f 100644 --- a/dev-packages/cloudflare-integration-tests/suites/public-api/startSpan/test.ts +++ b/dev-packages/cloudflare-integration-tests/suites/public-api/startSpan/test.ts @@ -1,5 +1,6 @@ import { SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE } from '@sentry/core'; import { + CLIENT_ADDRESS, SENTRY_SEGMENT_NAME_SOURCE, SENTRY_SDK_NAME, SENTRY_SDK_VERSION, @@ -205,6 +206,15 @@ it('sends a streamed span envelope with correct spans for a manually started spa type: 'string', value: 'localhost', }, + // From the `CF-Connecting-IP` header that `wrangler dev` sets. + [CLIENT_ADDRESS]: { + type: 'string', + value: '127.0.0.1', + }, + 'http.response.header.content-type': { + type: 'array', + value: ['text/plain;charset=UTF-8'], + }, 'url.full': { type: 'string', value: expect.stringMatching(/^http:\/\/localhost:.+$/), diff --git a/dev-packages/cloudflare-integration-tests/suites/tracing/opentelemetry-tracer/subpath-request-handler/test.ts b/dev-packages/cloudflare-integration-tests/suites/tracing/opentelemetry-tracer/subpath-request-handler/test.ts index 596ae6c6ef6d..27e35a07270f 100644 --- a/dev-packages/cloudflare-integration-tests/suites/tracing/opentelemetry-tracer/subpath-request-handler/test.ts +++ b/dev-packages/cloudflare-integration-tests/suites/tracing/opentelemetry-tracer/subpath-request-handler/test.ts @@ -1,16 +1,19 @@ -import type { Event } from '@sentry/core'; +import { HTTP_SERVER } from '@sentry/conventions/op'; import { expect, it } from 'vitest'; import { createRunner } from '../../../../runner'; +import { getSpanOp, getSpansFromEnvelope } from '../../../../spanUtils'; it('does not capture spans emitted through @opentelemetry/api inside wrapRequestHandler from the /request subpath', async ({ signal, }) => { const runner = createRunner(__dirname) .expect(envelope => { - const event = envelope[1]?.[0]?.[1] as Event; - expect(event.transaction).toBe('GET /'); - expect(event.contexts?.trace?.op).toBe('http.server'); - expect(event.spans).toEqual([]); + // Only the request's segment span. The OpenTelemetry span is not captured. + const spans = getSpansFromEnvelope(envelope); + expect(spans).toHaveLength(1); + expect(spans[0]?.is_segment).toBe(true); + expect(spans[0]?.name).toBe('GET /'); + expect(getSpanOp(spans[0]!)).toBe(HTTP_SERVER); }) .start(signal); diff --git a/dev-packages/deno-integration-tests/node-suites/excludes.ts b/dev-packages/deno-integration-tests/node-suites/excludes.ts index b93a8ff63a65..e6d5906bc2c8 100644 --- a/dev-packages/deno-integration-tests/node-suites/excludes.ts +++ b/dev-packages/deno-integration-tests/node-suites/excludes.ts @@ -2,8 +2,13 @@ // fails on Deno runs only on the other runtimes, with `test.runIf(supports({ runtimes }))` in the // Node suite, and is not listed here. -// Node-only features: ANR and native thread watchdogs, child process and worker thread breadcrumbs. -const NODE_ONLY = ['suites/anr/test.ts', 'suites/breadcrumbs/**', 'suites/thread-blocked-native/test.ts']; +// Node-only features: ANR and native thread watchdogs, child process and worker thread breadcrumbs, and the AWS Lambda Node runtime. +const NODE_ONLY = [ + 'suites/anr/test.ts', + 'suites/aws-lambda-web/**', + 'suites/breadcrumbs/**', + 'suites/thread-blocked-native/test.ts', +]; // `@sentry/node` instruments `fetch` through undici's diagnostics channels, which Deno's `fetch` // does not publish. `@sentry/deno` has its own `fetchIntegration` for this. These suites check @@ -62,6 +67,7 @@ const NOT_TRIAGED = [ 'suites/tracing/koa/test.ts', 'suites/tracing/langchain/test.ts', 'suites/tracing/langgraph/test.ts', + 'suites/tracing/mistral-v2/test.ts', 'suites/tracing/mistral/test.ts', 'suites/tracing/mongodb/test.ts', 'suites/tracing/mongoose-v5/test.ts', diff --git a/dev-packages/deno-integration-tests/package.json b/dev-packages/deno-integration-tests/package.json index 67f24f8c0f8f..19b9c21d536a 100644 --- a/dev-packages/deno-integration-tests/package.json +++ b/dev-packages/deno-integration-tests/package.json @@ -15,15 +15,13 @@ "test:unit": "deno test --allow-net --allow-read --allow-run --allow-env --no-check suites", "test:node-suites": "vitest run" }, - "dependencies": { + "devDependencies": { + "@sentry-internal/node-integration-tests": "11.10.0", + "@sentry/conventions": "^0.30.0", "@sentry/core": "11.10.0", "@sentry/deno": "11.10.0", "mysql": "^2.18.1", - "pg": "^8.22.0" - }, - "devDependencies": { - "@sentry-internal/node-integration-tests": "11.10.0", - "@sentry/conventions": "^0.29.0", + "pg": "^8.23.1", "vitest": "^4.1.11" } } diff --git a/dev-packages/e2e-tests/.env.example b/dev-packages/e2e-tests/.env.example index 3815cae556f0..17326f227680 100644 --- a/dev-packages/e2e-tests/.env.example +++ b/dev-packages/e2e-tests/.env.example @@ -17,7 +17,7 @@ E2E_TEST_SENTRY_PROJECT= E2E_OPENROUTER_API_KEY= # An OpenAI API key for the Decisions API (OpenRouter has no Decisions route). -# Only needed to run node-openai-decisions-send-to-sentry locally. +# Only needed to run node-openai-decisions locally. E2E_OPENAI_DECISIONS_API_KEY= # Cloudflare credentials for E2E tests that deploy a real Worker (e.g. cloudflare-workers-send-to-sentry). diff --git a/dev-packages/e2e-tests/README.md b/dev-packages/e2e-tests/README.md index ce769d3c87f8..be2b94586ece 100644 --- a/dev-packages/e2e-tests/README.md +++ b/dev-packages/e2e-tests/README.md @@ -193,6 +193,12 @@ is the reference setup. because workerd does not allow it. `nextjs-16` has no Vite build: its Cloudflare variant builds with `opennextjs-cloudflare build --config wrangler.opennext.jsonc` and wraps the generated Worker in `worker.ts` with `withSentry` from `@sentry/nextjs/cloudflare`. +- **Cloudflare in `vite dev`**: to run the Worker in `vite dev` (for example the dev tests of the `hono-4` Cloudflare + variant), start it with `CLOUDFLARE_INCLUDE_PROCESS_ENV=true vite dev`. The runner gives `E2E_TEST_DSN` to the app + only as a process env var. `wrangler dev` adds it to the Worker `env` with `--var "E2E_TEST_DSN:$E2E_TEST_DSN"`, but + `@cloudflare/vite-plugin` has no such option. It fills the Worker `env` from the `vars` of the wrangler config and + from `.dev.vars` or `.env`, and adds process env vars only when `CLOUDFLARE_INCLUDE_PROCESS_ENV` is `true`. Without + it, `env.E2E_TEST_DSN` is `undefined`, the SDK sends no events, and the tests fail. - **Build flavors**: a variant can also build the same app with another tool, for example `nextjs-16 (vinext)` builds `nextjs-16` with vinext. A build flavor is not a runtime: its assert script sets its own env var (`VINEXT=1`, read by `tests/isVinext.ts`). It sets `runtime` only to run on another runtime, for example `nextjs-16 (vinext, bun)`. @@ -286,9 +292,21 @@ For example, if something is changed in the browser package, only E2E test apps You can add additional information about the test (e.g. canary versions, optional in CI) by adding `sentryTest` in the `package.json` of a test application. -The required E2E jobs run on 2 vCPUs (`namespace-profile-sentry-javascript-small`). If an app's job takes longer than -about 90 seconds, set `"sentryTest": { "runner": "namespace-profile-sentry-javascript" }` to run it and its variants on -4 vCPUs. +`sentryTest.baseVariant` overrides the default matrix entry using the same fields as an entry in `variants`. For example, +set its `assert-command` to run production tests and add a development variant to run both environments in parallel. +The local `test:assert` script can still run both; select the base variant locally with `--variant