diff --git a/.github/actions/install-playwright/action.yml b/.github/actions/install-playwright/action.yml index 9de6e1a2b104..8fc0aeba7330 100644 --- a/.github/actions/install-playwright/action.yml +++ b/.github/actions/install-playwright/action.yml @@ -21,15 +21,6 @@ runs: ~/.cache/ms-playwright key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }} - # Only store cache on develop branch - - name: Store cached playwright binaries - uses: actions/cache/save@v4 - if: github.event_name == 'push' && github.ref == 'refs/heads/develop' - with: - path: | - ~/.cache/ms-playwright - key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }} - # We always install all browsers, if uncached - name: Install Playwright dependencies (uncached) run: npx playwright install chromium webkit firefox --with-deps @@ -40,3 +31,12 @@ runs: run: npx playwright install-deps ${{ inputs.browsers || 'chromium webkit firefox' }} if: steps.playwright-cache.outputs.cache-hit == 'true' shell: bash + + # Only store cache on develop branch + - name: Store cached playwright binaries + uses: actions/cache/save@v4 + if: github.event_name == 'push' && github.ref == 'refs/heads/develop' + with: + path: | + ~/.cache/ms-playwright + key: playwright-${{ runner.os }}-${{ steps.playwright-version.outputs.version }} diff --git a/.github/actions/restore-cache/action.yml b/.github/actions/restore-cache/action.yml index 848983376840..6cd63a6550e4 100644 --- a/.github/actions/restore-cache/action.yml +++ b/.github/actions/restore-cache/action.yml @@ -1,6 +1,14 @@ name: "Restore dependency & build cache" description: "Restore the dependency & build cache." +inputs: + dependency_cache_key: + description: "The dependency cache key" + required: true + node_version: + description: "If set, temporarily set node version to default one before installing, then revert to this version after." + required: false + runs: using: "composite" steps: @@ -9,17 +17,26 @@ runs: uses: actions/cache/restore@v4 with: path: ${{ env.CACHED_DEPENDENCY_PATHS }} - key: ${{ env.DEPENDENCY_CACHE_KEY }} + key: ${{ inputs.dependency_cache_key }} - - name: Check build cache - uses: actions/cache/restore@v4 - id: build-cache + - name: Restore build artifacts + uses: actions/download-artifact@v4 with: - path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }} + name: build-output + + - name: Use default node version for install + if: inputs.node_version && steps.dep-cache.outputs.cache-hit != 'true' + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + + - name: Install dependencies + if: steps.dep-cache.outputs.cache-hit != 'true' + run: yarn install --ignore-engines --frozen-lockfile + shell: bash - - name: Check if caches are restored - uses: actions/github-script@v6 - if: steps.dep-cache.outputs.cache-hit != 'true' || steps.build-cache.outputs.cache-hit != 'true' + - name: Revert node version to ${{ inputs.node_version }} + if: inputs.node_version && steps.dep-cache.outputs.cache-hit != 'true' + uses: actions/setup-node@v4 with: - script: core.setFailed('Dependency or build cache could not be restored - please re-run ALL jobs.') + node-version: ${{ inputs.node_version }} diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 33d8c9ed27c0..652d9daa2e39 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -15,6 +15,9 @@ updates: allow: - dependency-name: "@sentry/cli" - dependency-name: "@sentry/vite-plugin" + - dependency-name: "@sentry/webpack-plugin" + - dependency-name: "@sentry/rollup-plugin" + - dependency-name: "@sentry/esbuild-plugin" - dependency-name: "@opentelemetry/*" - dependency-name: "@prisma/instrumentation" - dependency-name: "opentelemetry-instrumentation-fetch-node" diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 39b024bd77dd..77ddae4705b6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -48,7 +48,6 @@ env: ${{ github.workspace }}/packages/utils/cjs ${{ github.workspace }}/packages/utils/esm - BUILD_CACHE_KEY: build-cache-${{ github.event.inputs.commit || github.sha }} BUILD_CACHE_TARBALL_KEY: tarball-${{ github.event.inputs.commit || github.sha }} # GH will use the first restore-key it finds that matches @@ -160,13 +159,6 @@ jobs: base: ${{ github.event.pull_request.base.sha }} head: ${{ env.HEAD_COMMIT }} - - name: Check build cache - uses: actions/cache@v4 - id: cache_built_packages - with: - path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ env.BUILD_CACHE_KEY }} - - name: NX cache uses: actions/cache@v4 # Disable cache when: @@ -188,6 +180,15 @@ jobs: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} run: yarn build + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: build-output + path: ${{ env.CACHED_BUILD_PATHS }} + retention-days: 4 + compression-level: 6 + overwrite: true + 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') }} @@ -232,8 +233,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check bundle sizes uses: ./dev-packages/size-limit-gh-action with: @@ -259,8 +260,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Lint source files run: yarn lint:lerna - name: Lint C++ files @@ -305,8 +306,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Run madge run: yarn circularDepCheck @@ -327,8 +328,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Extract Profiling Node Prebuilt Binaries uses: actions/download-artifact@v4 @@ -344,6 +345,7 @@ jobs: uses: actions/upload-artifact@v4 with: name: ${{ github.sha }} + retention-days: 90 path: | ${{ github.workspace }}/packages/browser/build/bundles/** ${{ github.workspace }}/packages/replay-internal/build/bundles/** @@ -374,8 +376,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Run affected tests run: yarn test:pr:browser --base=${{ github.event.pull_request.base.sha }} @@ -411,8 +413,8 @@ jobs: uses: oven-sh/setup-bun@v2 - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Run tests run: | yarn test:ci:bun @@ -435,13 +437,13 @@ jobs: with: node-version-file: 'package.json' - name: Set up Deno - uses: denoland/setup-deno@v1.1.4 + uses: denoland/setup-deno@1.4.0 with: deno-version: v1.38.5 - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Run tests run: | cd packages/deno @@ -456,7 +458,8 @@ jobs: strategy: fail-fast: false matrix: - node: [14, 16, 18, 20, 22] + # TODO(lforst): Unpin Node.js version 22 when https://github.com/protobufjs/protobuf.js/issues/2025 is resolved which broke the nodejs tests + node: [14, 16, 18, 20, '22.6.0'] steps: - name: Check out base commit (${{ github.event.pull_request.base.sha }}) uses: actions/checkout@v4 @@ -473,8 +476,9 @@ jobs: node-version: ${{ matrix.node }} - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + node_version: ${{ matrix.node == 14 && '14' || '' }} - name: Run affected tests run: yarn test:pr:node --base=${{ github.event.pull_request.base.sha }} @@ -496,7 +500,10 @@ jobs: job_profiling_node_unit_tests: name: Node Profiling Unit Tests needs: [job_get_metadata, job_build] - if: needs.job_build.outputs.changed_node == 'true' || needs.job_get_metadata.outputs.changed_profiling_node == 'true' || github.event_name != 'pull_request' + if: | + needs.job_build.outputs.changed_node == 'true' || + needs.job_get_metadata.outputs.changed_profiling_node == 'true' || + github.event_name != 'pull_request' runs-on: ubuntu-latest timeout-minutes: 10 steps: @@ -512,8 +519,8 @@ jobs: python-version: '3.11.7' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Build Configure node-gyp run: yarn lerna run build:bindings:configure --scope @sentry/profiling-node - name: Build Bindings for Current Environment @@ -580,8 +587,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Install Playwright uses: ./.github/actions/install-playwright @@ -595,11 +602,13 @@ jobs: run: yarn test:ci${{ matrix.project && format(' --project={0}', matrix.project) || '' }}${{ matrix.shard && format(' --shard={0}/{1}', matrix.shard, matrix.shards) || '' }} - name: Upload Playwright Traces - uses: actions/upload-artifact@v3 - if: always() + uses: actions/upload-artifact@v4 + if: failure() with: - name: playwright-traces + name: playwright-traces-job_browser_playwright_tests-${{ matrix.bundle}}-${{matrix.project}}-${{matrix.shard || '0'}} path: dev-packages/browser-integration-tests/test-results + overwrite: true + retention-days: 7 job_browser_loader_tests: name: PW ${{ matrix.bundle }} Tests @@ -630,8 +639,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Install Playwright uses: ./.github/actions/install-playwright @@ -645,11 +654,13 @@ jobs: cd dev-packages/browser-integration-tests yarn test:loader - name: Upload Playwright Traces - uses: actions/upload-artifact@v3 - if: always() + uses: actions/upload-artifact@v4 + if: failure() with: - name: playwright-traces + name: playwright-traces-job_browser_loader_tests-${{ matrix.bundle}} path: dev-packages/browser-integration-tests/test-results + overwrite: true + retention-days: 7 job_check_for_faulty_dts: name: Check for faulty .d.ts files @@ -667,8 +678,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Check for dts files that reference stuff in the temporary build folder run: | if grep -r --include "*.d.ts" --exclude-dir ".nxcache" 'import("@sentry(-internal)?/[^/]*/build' .; then @@ -705,8 +716,9 @@ jobs: node-version: ${{ matrix.node }} - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} + node_version: ${{ matrix.node == 14 && '14' || '' }} - name: Overwrite typescript version if: matrix.typescript @@ -746,8 +758,8 @@ jobs: node-version: ${{ matrix.node }} - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Install Playwright uses: ./.github/actions/install-playwright @@ -784,8 +796,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: NX cache uses: actions/cache/restore@v4 with: @@ -896,6 +908,8 @@ jobs: 'nestjs-basic', 'nestjs-distributed-tracing', 'nestjs-with-submodules', + 'nestjs-with-submodules-decorator', + 'nestjs-graphql', 'node-exports-test-app', 'node-koa', 'node-connect', @@ -946,15 +960,19 @@ jobs: uses: oven-sh/setup-bun@v2 - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Restore tarball cache uses: actions/cache/restore@v4 + id: restore-tarball-cache with: path: ${{ github.workspace }}/packages/*/*.tgz key: ${{ env.BUILD_CACHE_TARBALL_KEY }} - fail-on-cache-miss: true + + - name: Build tarballs if not cached + if: steps.restore-tarball-cache.outputs.cache-hit != 'true' + run: yarn build:tarball - name: Install Playwright uses: ./.github/actions/install-playwright @@ -985,6 +1003,15 @@ jobs: working-directory: dev-packages/e2e-tests/test-applications/${{ matrix.test-application }} timeout-minutes: 5 run: pnpm test:assert + + - name: Upload Playwright Traces + uses: actions/upload-artifact@v4 + if: failure() + with: + name: playwright-traces-job_e2e_playwright_tests-${{ matrix.test-application}} + path: dev-packages/e2e-tests/test-applications/${{ matrix.test-application}}/test-results + overwrite: true + retention-days: 7 job_optional_e2e_tests: name: E2E ${{ matrix.label || matrix.test-application }} Test @@ -1046,15 +1073,19 @@ jobs: node-version-file: 'dev-packages/e2e-tests/package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Restore tarball cache uses: actions/cache/restore@v4 + id: restore-tarball-cache with: path: ${{ github.workspace }}/packages/*/*.tgz key: ${{ env.BUILD_CACHE_TARBALL_KEY }} - fail-on-cache-miss: true + + - name: Build tarballs if not cached + if: steps.restore-tarball-cache.outputs.cache-hit != 'true' + run: yarn build:tarball - name: Install Playwright uses: ./.github/actions/install-playwright @@ -1108,8 +1139,7 @@ jobs: (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository) && ( (needs.job_get_metadata.outputs.changed_profiling_node == 'true') || - (needs.job_get_metadata.outputs.is_release == 'true') || - (github.event_name != 'pull_request') + (needs.job_get_metadata.outputs.is_release == 'true') ) needs: [job_get_metadata, job_build, job_e2e_prepare] runs-on: ubuntu-20.04 @@ -1141,8 +1171,8 @@ jobs: node-version-file: 'dev-packages/e2e-tests/package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Build Profiling Node run: yarn lerna run build:lib --scope @sentry/profiling-node - name: Extract Profiling Node Prebuilt Binaries @@ -1151,12 +1181,17 @@ jobs: pattern: profiling-node-binaries-${{ github.sha }}-* path: ${{ github.workspace }}/packages/profiling-node/lib/ merge-multiple: true + - name: Restore tarball cache uses: actions/cache/restore@v4 + id: restore-tarball-cache with: path: ${{ github.workspace }}/packages/*/*.tgz key: ${{ env.BUILD_CACHE_TARBALL_KEY }} - fail-on-cache-miss : true + + - name: Build tarballs if not cached + if: steps.restore-tarball-cache.outputs.cache-hit != 'true' + run: yarn build:tarball - name: Install Playwright uses: ./.github/actions/install-playwright @@ -1237,8 +1272,8 @@ jobs: node-version-file: 'package.json' - name: Restore caches uses: ./.github/actions/restore-cache - env: - DEPENDENCY_CACHE_KEY: ${{ needs.job_build.outputs.dependency_cache_key }} + with: + dependency_cache_key: ${{ needs.job_build.outputs.dependency_cache_key }} - name: Collect run: yarn ci:collect @@ -1259,6 +1294,7 @@ jobs: with: name: ${{ steps.process.outputs.artifactName }} path: ${{ steps.process.outputs.artifactPath }} + retention-days: 7 job_compile_bindings_profiling_node: name: Compile & Test Profiling Bindings (v${{ matrix.node }}) ${{ matrix.target_platform || matrix.os }}, ${{ matrix.node || matrix.container }}, ${{ matrix.arch || matrix.container }}, ${{ contains(matrix.container, 'alpine') && 'musl' || 'glibc' }} @@ -1267,8 +1303,7 @@ jobs: # Skip precompile unless we are on a release branch as precompile slows down CI times. if: | (needs.job_get_metadata.outputs.changed_profiling_node == 'true') || - (needs.job_get_metadata.outputs.is_release == 'true') || - (github.event_name != 'pull_request') + (needs.job_get_metadata.outputs.is_release == 'true') runs-on: ${{ matrix.os }} container: ${{ matrix.container }} timeout-minutes: 30 diff --git a/.github/workflows/clear-cache.yml b/.github/workflows/clear-cache.yml index 2946723fe6b8..5c327553e3b8 100644 --- a/.github/workflows/clear-cache.yml +++ b/.github/workflows/clear-cache.yml @@ -1,11 +1,43 @@ name: "Action: Clear all GHA caches" on: workflow_dispatch: + inputs: + clear_pending_prs: + description: Delete caches of pending PR workflows + type: boolean + default: false + clear_develop: + description: Delete caches on develop branch + type: boolean + default: false + clear_branches: + description: Delete caches on non-develop branches + type: boolean + default: true + schedule: + # Run every day at midnight + - cron: '0 0 * * *' jobs: clear-caches: name: Delete all caches runs-on: ubuntu-20.04 steps: - - name: Clear caches - uses: easimon/wipe-cache@v2 + - uses: actions/checkout@v4 + + - name: Set up Node + uses: actions/setup-node@v4 + with: + node-version-file: 'package.json' + + # TODO: Use cached version if possible (but never store cache) + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Delete GHA caches + uses: ./dev-packages/clear-cache-gh-action + with: + clear_pending_prs: ${{ inputs.clear_pending_prs }} + clear_develop: ${{ inputs.clear_develop }} + clear_branches: ${{ inputs.clear_branches }} + github_token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/external-contributors.yml b/.github/workflows/external-contributors.yml index 50acb2be8e73..e01a1a66a589 100644 --- a/.github/workflows/external-contributors.yml +++ b/.github/workflows/external-contributors.yml @@ -25,7 +25,6 @@ jobs: uses: actions/setup-node@v4 with: node-version-file: 'package.json' - cache: 'yarn' - name: Install dependencies run: yarn install --frozen-lockfile diff --git a/.size-limit.js b/.size-limit.js index 859ce741cc3d..5da293511976 100644 --- a/.size-limit.js +++ b/.size-limit.js @@ -10,6 +10,31 @@ module.exports = [ gzip: true, limit: '24 KB', }, + { + name: '@sentry/browser - with treeshaking flags', + path: 'packages/browser/build/npm/esm/index.js', + import: createImport('init'), + gzip: true, + limit: '24 KB', + modifyWebpackConfig: function (config) { + const webpack = require('webpack'); + const TerserPlugin = require('terser-webpack-plugin'); + + config.plugins.push( + new webpack.DefinePlugin({ + __SENTRY_DEBUG__: false, + __RRWEB_EXCLUDE_SHADOW_DOM__: true, + __RRWEB_EXCLUDE_IFRAME__: true, + __SENTRY_EXCLUDE_REPLAY_WORKER__: true, + }), + ); + + config.optimization.minimize = true; + config.optimization.minimizer = [new TerserPlugin()]; + + return config; + }, + }, { name: '@sentry/browser (incl. Tracing)', path: 'packages/browser/build/npm/esm/index.js', @@ -22,16 +47,18 @@ module.exports = [ path: 'packages/browser/build/npm/esm/index.js', import: createImport('init', 'browserTracingIntegration', 'replayIntegration'), gzip: true, - limit: '73 KB', + limit: '75 KB', }, { name: '@sentry/browser (incl. Tracing, Replay) - with treeshaking flags', path: 'packages/browser/build/npm/esm/index.js', import: createImport('init', 'browserTracingIntegration', 'replayIntegration'), gzip: true, - limit: '66 KB', + limit: '68 KB', modifyWebpackConfig: function (config) { const webpack = require('webpack'); + const TerserPlugin = require('terser-webpack-plugin'); + config.plugins.push( new webpack.DefinePlugin({ __SENTRY_DEBUG__: false, @@ -40,6 +67,10 @@ module.exports = [ __SENTRY_EXCLUDE_REPLAY_WORKER__: true, }), ); + + config.optimization.minimize = true; + config.optimization.minimizer = [new TerserPlugin()]; + return config; }, }, @@ -170,7 +201,7 @@ module.exports = [ path: createCDNPath('bundle.tracing.min.js'), gzip: false, brotli: false, - limit: '111 KB', + limit: '113 KB', }, { name: 'CDN Bundle (incl. Tracing, Replay) - uncompressed', @@ -222,11 +253,17 @@ module.exports = [ ignore: [...builtinModules, ...nodePrefixedBuiltinModules], modifyWebpackConfig: function (config) { const webpack = require('webpack'); + const TerserPlugin = require('terser-webpack-plugin'); + config.plugins.push( new webpack.DefinePlugin({ __SENTRY_TRACING__: false, }), ); + + config.optimization.minimize = true; + config.optimization.minimizer = [new TerserPlugin()]; + return config; }, }, diff --git a/CHANGELOG.md b/CHANGELOG.md index 7864efac6871..234c9e5704a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,69 @@ - "You miss 100 percent of the chances you don't take. — Wayne Gretzky" — Michael Scott +Work in this release was contributed by @Zen-cronic. Thank you for your contribution! + +## 8.28.0 + +### Important Changes + +- **Beta release of official NestJS SDK** + +This release contains the beta version of `@sentry/nestjs`! For details on how to use it, check out the +[README](https://github.com/getsentry/sentry-javascript/blob/master/packages/nestjs/README.md). Any feedback/bug reports +are greatly appreciated, please reach out on GitHub. + +- **fix(browser): Remove faulty LCP, FCP and FP normalization logic (#13502)** + +This release fixes a bug in the `@sentry/browser` package and all SDKs depending on this package (e.g. `@sentry/react` +or `@sentry/nextjs`) that caused the SDK to send incorrect web vital values for the LCP, FCP and FP vitals. The SDK +previously incorrectly processed the original values as they were reported from the browser. When updating your SDK to +this version, you might experience an increase in LCP, FCP and FP values, which potentially leads to a decrease in your +performance score in the Web Vitals Insights module in Sentry. This is because the previously reported values were +smaller than the actually measured values. We apologize for the inconvenience! + +### Other Changes + +- feat(nestjs): Add `SentryGlobalGraphQLFilter` (#13545) +- feat(nestjs): Automatic instrumentation of nestjs interceptors after route execution (#13264) +- feat(nextjs): Add `bundleSizeOptimizations` to build options (#13323) +- feat(nextjs): Stabilize `captureRequestError` (#13550) +- feat(nuxt): Wrap config in nuxt context (#13457) +- feat(profiling): Expose profiler as top level primitive (#13512) +- feat(replay): Add layout shift to CLS replay data (#13386) +- feat(replay): Upgrade rrweb packages to 2.26.0 (#13483) +- fix(cdn): Do not mangle \_metadata (#13426) +- fix(cdn): Fix SDK source for CDN bundles (#13475) +- fix(nestjs): Check arguments before instrumenting with `@Injectable` (#13544) +- fix(nestjs): Ensure exception and host are correctly passed on when using @WithSentry (#13564) +- fix(node): Suppress tracing for transport request execution rather than transport creation (#13491) +- fix(replay): Consider more things as DOM mutations for dead clicks (#13518) +- fix(vue): Correctly obtain component name (#13484) + +Work in this release was contributed by @leopoldkristjansson, @mhuggins and @filips123. Thank you for your +contributions! + +## 8.27.0 + +### Important Changes + +- **fix(nestjs): Exception filters in main app module are not being executed (#13278)** + + With this release nestjs error monitoring is no longer automatically set up after adding the `SentryModule` to your + application, which led to issues in certain scenarios. You will now have to either add the `SentryGlobalFilter` to + your main module providers or decorate the `catch()` method in your existing global exception filters with the newly + released `@WithSentry()` decorator. See the [docs](https://docs.sentry.io/platforms/javascript/guides/nestjs/) for + more details. + +### Other Changes + +- feat: Add options for passing nonces to feedback integration (#13347) +- feat: Add support for SENTRY_SPOTLIGHT env var in Node (#13325) +- feat(deps): bump @prisma/instrumentation from 5.17.0 to 5.18.0 (#13327) +- feat(feedback): Improve error message for 403 errors (#13441) +- fix(deno): Don't rely on `Deno.permissions.querySync` (#13378) +- fix(replay): Ensure we publish replay CDN bundles (#13437) + Work in this release was contributed by @charpeni. Thank you for your contribution! ## 8.26.0 diff --git a/dev-packages/browser-integration-tests/loader-suites/loader/noOnLoad/captureException/test.ts b/dev-packages/browser-integration-tests/loader-suites/loader/noOnLoad/captureException/test.ts index 09a10464c22e..4404dac91364 100644 --- a/dev-packages/browser-integration-tests/loader-suites/loader/noOnLoad/captureException/test.ts +++ b/dev-packages/browser-integration-tests/loader-suites/loader/noOnLoad/captureException/test.ts @@ -1,4 +1,5 @@ import { expect } from '@playwright/test'; +import { SDK_VERSION } from '@sentry/browser'; import { sentryTest } from '../../../../utils/fixtures'; import { envelopeRequestParser, waitForErrorRequestOnUrl } from '../../../../utils/helpers'; @@ -11,3 +12,22 @@ sentryTest('captureException works', async ({ getLocalTestUrl, page }) => { expect(eventData.message).toBe('Test exception'); }); + +sentryTest('should capture correct SDK metadata', async ({ getLocalTestUrl, page }) => { + const url = await getLocalTestUrl({ testDir: __dirname }); + const req = await waitForErrorRequestOnUrl(page, url); + + const eventData = envelopeRequestParser(req); + + expect(eventData.sdk).toMatchObject({ + name: 'sentry.javascript.browser', + version: SDK_VERSION, + integrations: expect.any(Object), + packages: [ + { + name: 'loader:@sentry/browser', + version: SDK_VERSION, + }, + ], + }); +}); diff --git a/dev-packages/browser-integration-tests/package.json b/dev-packages/browser-integration-tests/package.json index dfd3b78c2c14..d012484a4e0b 100644 --- a/dev-packages/browser-integration-tests/package.json +++ b/dev-packages/browser-integration-tests/package.json @@ -1,6 +1,6 @@ { "name": "@sentry-internal/browser-integration-tests", - "version": "8.26.0", + "version": "8.28.0", "main": "index.js", "license": "MIT", "engines": { @@ -9,7 +9,7 @@ "private": true, "scripts": { "clean": "rimraf -g suites/**/dist loader-suites/**/dist tmp", - "install-browsers": "[[ -z \"$SKIP_PLAYWRIGHT_BROWSER_INSTALL\" ]] && yarn install-browsers || echo 'Skipping browser installation'", + "install-browsers": "[[ -z \"$SKIP_PLAYWRIGHT_BROWSER_INSTALL\" ]] && yarn npx playwright install --with-deps || echo 'Skipping browser installation'", "lint": "eslint . --format stylish", "fix": "eslint . --format stylish --fix", "type-check": "tsc", @@ -43,12 +43,12 @@ "@babel/preset-typescript": "^7.16.7", "@playwright/test": "^1.44.1", "@sentry-internal/rrweb": "2.11.0", - "@sentry/browser": "8.26.0", + "@sentry/browser": "8.28.0", "axios": "1.6.7", "babel-loader": "^8.2.2", "html-webpack-plugin": "^5.5.0", "pako": "^2.1.0", - "webpack": "^5.90.3" + "webpack": "^5.94.0" }, "devDependencies": { "@types/glob": "8.0.0", diff --git a/dev-packages/browser-integration-tests/suites/feedback/attachTo/init.js b/dev-packages/browser-integration-tests/suites/feedback/attachTo/init.js new file mode 100644 index 000000000000..5eb27143fdc7 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/attachTo/init.js @@ -0,0 +1,17 @@ +import * as Sentry from '@sentry/browser'; +// Import this separately so that generatePlugin can handle it for CDN scenarios +import { feedbackIntegration } from '@sentry/browser'; + +const feedback = feedbackIntegration({ + autoInject: false, +}); + +window.Sentry = Sentry; +window.feedback = feedback; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + integrations: [feedback], +}); + +feedback.attachTo('#custom-feedback-buttom'); diff --git a/dev-packages/browser-integration-tests/suites/feedback/attachTo/template.html b/dev-packages/browser-integration-tests/suites/feedback/attachTo/template.html new file mode 100644 index 000000000000..ae36b0c69c7b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/attachTo/template.html @@ -0,0 +1,9 @@ + + +
+ + + + + + diff --git a/dev-packages/browser-integration-tests/suites/feedback/attachTo/test.ts b/dev-packages/browser-integration-tests/suites/feedback/attachTo/test.ts new file mode 100644 index 000000000000..507b08685092 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/attachTo/test.ts @@ -0,0 +1,82 @@ +import { expect } from '@playwright/test'; + +import { TEST_HOST, sentryTest } from '../../../utils/fixtures'; +import { envelopeRequestParser, getEnvelopeType, shouldSkipFeedbackTest } from '../../../utils/helpers'; + +sentryTest('should capture feedback with custom button', async ({ getLocalTestUrl, page }) => { + if (shouldSkipFeedbackTest()) { + sentryTest.skip(); + } + + const feedbackRequestPromise = page.waitForResponse(res => { + const req = res.request(); + + const postData = req.postData(); + if (!postData) { + return false; + } + + try { + return getEnvelopeType(req) === 'feedback'; + } catch (err) { + return false; + } + }); + + await page.route('https://dsn.ingest.sentry.io/**/*', route => { + return route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ id: 'test-id' }), + }); + }); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.goto(url); + await page.locator('#custom-feedback-buttom').click(); + await page.waitForSelector(':visible:text-is("Report a Bug")'); + + expect(await page.locator(':visible:text-is("Report a Bug")').count()).toEqual(1); + await page.locator('[name="name"]').fill('Jane Doe'); + await page.locator('[name="email"]').fill('janedoe@example.org'); + await page.locator('[name="message"]').fill('my example feedback'); + await page.locator('[data-sentry-feedback] .btn--primary').click(); + + const feedbackEvent = envelopeRequestParser((await feedbackRequestPromise).request()); + expect(feedbackEvent).toEqual({ + type: 'feedback', + breadcrumbs: expect.any(Array), + contexts: { + feedback: { + contact_email: 'janedoe@example.org', + message: 'my example feedback', + name: 'Jane Doe', + source: 'widget', + url: `${TEST_HOST}/index.html`, + }, + trace: { + trace_id: expect.stringMatching(/\w{32}/), + span_id: expect.stringMatching(/\w{16}/), + }, + }, + level: 'info', + timestamp: expect.any(Number), + event_id: expect.stringMatching(/\w{32}/), + environment: 'production', + tags: {}, + sdk: { + integrations: expect.arrayContaining(['Feedback']), + version: expect.any(String), + name: 'sentry.javascript.browser', + packages: expect.anything(), + }, + request: { + url: `${TEST_HOST}/index.html`, + headers: { + 'User-Agent': expect.stringContaining(''), + }, + }, + platform: 'javascript', + }); +}); diff --git a/dev-packages/browser-integration-tests/suites/feedback/logger/init.js b/dev-packages/browser-integration-tests/suites/feedback/logger/init.js new file mode 100644 index 000000000000..3251bd6c7a4c --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/logger/init.js @@ -0,0 +1,19 @@ +import * as Sentry from '@sentry/browser'; +// Import this separately so that generatePlugin can handle it for CDN scenarios +import { feedbackIntegration } from '@sentry/browser'; + +const feedback = feedbackIntegration({ + autoInject: false, +}); + +window.Sentry = Sentry; +window.feedback = feedback; + +Sentry.init({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + debug: true, + integrations: [feedback], +}); + +// This should log an error! +feedback.attachTo('#does-not-exist'); diff --git a/dev-packages/browser-integration-tests/suites/feedback/logger/test.ts b/dev-packages/browser-integration-tests/suites/feedback/logger/test.ts new file mode 100644 index 000000000000..34fadfc2503b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/logger/test.ts @@ -0,0 +1,36 @@ +import { expect } from '@playwright/test'; + +import { sentryTest } from '../../../utils/fixtures'; +import { shouldSkipFeedbackTest } from '../../../utils/helpers'; + +/** + * This test is mostly relevant for ensuring that the logger works in all combinations of CDN bundles. + * Even if feedback is included via the CDN, this test ensures that the logger is working correctly. + */ +sentryTest('should log error correctly', async ({ getLocalTestUrl, page }) => { + // In minified bundles we do not have logger messages, so we skip the test + if (shouldSkipFeedbackTest() || (process.env.PW_BUNDLE || '').includes('_min')) { + sentryTest.skip(); + } + + const messages: string[] = []; + + page.on('console', message => { + messages.push(message.text()); + }); + + await page.route('https://dsn.ingest.sentry.io/**/*', route => { + return route.fulfill({ + status: 200, + contentType: 'application/json', + body: JSON.stringify({ id: 'test-id' }), + }); + }); + + const url = await getLocalTestUrl({ testDir: __dirname }); + + await page.goto(url); + + expect(messages).toContain('Sentry Logger [log]: Integration installed: Feedback'); + expect(messages).toContain('Sentry Logger [error]: [Feedback] Unable to attach to target element'); +}); diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpContext/init.js b/dev-packages/browser-integration-tests/suites/integrations/httpContext/init.js new file mode 100644 index 000000000000..984534454ac7 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/httpContext/init.js @@ -0,0 +1,20 @@ +import * as Sentry from '@sentry/browser'; + +window.Sentry = Sentry; + +const integrations = Sentry.getDefaultIntegrations({}).filter( + defaultIntegration => defaultIntegration.name === 'HttpContext', +); + +const client = new Sentry.BrowserClient({ + dsn: 'https://public@dsn.ingest.sentry.io/1337', + transport: Sentry.makeFetchTransport, + stackParser: Sentry.defaultStackParser, + integrations: integrations, +}); + +const scope = new Sentry.Scope(); +scope.setClient(client); +client.init(); + +window._sentryScope = scope; diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpContext/subject.js b/dev-packages/browser-integration-tests/suites/integrations/httpContext/subject.js new file mode 100644 index 000000000000..62ce205e2ffc --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/httpContext/subject.js @@ -0,0 +1 @@ +window._sentryScope.captureException(new Error('client init')); diff --git a/dev-packages/browser-integration-tests/suites/integrations/httpContext/test.ts b/dev-packages/browser-integration-tests/suites/integrations/httpContext/test.ts new file mode 100644 index 000000000000..02a62142e02b --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/integrations/httpContext/test.ts @@ -0,0 +1,26 @@ +import { expect } from '@playwright/test'; +import type { Event } from '@sentry/types'; + +import { sentryTest } from '../../../utils/fixtures'; +import { getFirstSentryEnvelopeRequest } from '../../../utils/helpers'; + +sentryTest('httpContextIntegration captures user-agent and referrer', async ({ getLocalTestPath, page }) => { + const url = await getLocalTestPath({ testDir: __dirname }); + + const errorEventPromise = getFirstSentryEnvelopeRequest