diff --git a/.github/actions/install-dependencies/action.yml b/.github/actions/install-dependencies/action.yml index 8cb80ac7440e..5fbf87c67d79 100644 --- a/.github/actions/install-dependencies/action.yml +++ b/.github/actions/install-dependencies/action.yml @@ -9,11 +9,9 @@ outputs: runs: using: "composite" steps: - # we use a hash of yarn.lock as our cache key, because if it hasn't changed, our dependencies haven't changed, - # so no need to reinstall them - name: Compute dependency cache key id: compute_lockfile_hash - run: echo "hash=dependencies-${{ hashFiles('yarn.lock', 'packages/*/package.json', 'dev-packages/*/package.json') }}" >> "$GITHUB_OUTPUT" + run: node ./scripts/dependency-hash-key.js >> "$GITHUB_OUTPUT" shell: bash - name: Check dependency cache diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c66b6e64c8e8..5fc43c3e08d5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -127,7 +127,9 @@ jobs: runs-on: ubuntu-20.04 timeout-minutes: 15 if: | - needs.job_get_metadata.outputs.changed_any_code == 'true' && + needs.job_get_metadata.outputs.changed_any_code == 'true' || + needs.job_get_metadata.outputs.is_develop == 'true' || + needs.job_get_metadata.outputs.is_release == 'true' || (needs.job_get_metadata.outputs.is_gitflow_sync == 'false' && needs.job_get_metadata.outputs.has_gitflow_label == 'false') steps: - name: Check out base commit (${{ github.event.pull_request.base.sha }}) @@ -344,7 +346,7 @@ jobs: name: ${{ github.sha }} path: | ${{ github.workspace }}/packages/browser/build/bundles/** - ${{ github.workspace }}/packages/replay/build/bundles/** + ${{ github.workspace }}/packages/replay-internal/build/bundles/** ${{ github.workspace }}/packages/replay-canvas/build/bundles/** ${{ github.workspace }}/packages/feedback/build/bundles/** ${{ github.workspace }}/packages/**/*.tgz @@ -1417,6 +1419,11 @@ jobs: with: ref: ${{ env.HEAD_COMMIT }} + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + - name: Restore dependency cache uses: actions/cache/restore@v4 id: restore-dependencies @@ -1424,24 +1431,17 @@ jobs: path: ${{ env.CACHED_DEPENDENCY_PATHS }} key: ${{ needs.job_build.outputs.dependency_cache_key }} enableCrossOsArchive: true - fail-on-cache-miss: true - - name: Restore build cache - uses: actions/cache/restore@v4 - id: restore-build - with: - path: ${{ env.CACHED_BUILD_PATHS }} - key: ${{ needs.job_build.outputs.dependency_cache_key }} - enableCrossOsArchive: true - fail-on-cache-miss: true + - name: Install dependencies + env: + SKIP_PLAYWRIGHT_BROWSER_INSTALL: "1" + if: steps.restore-dependencies.outputs.cache-hit != 'true' + run: yarn install --ignore-engines --frozen-lockfile - name: Configure safe directory run: | git config --global --add safe.directory "*" - - name: Install yarn - run: npm i -g yarn@1.22.19 --force - - name: Increase yarn network timeout on Windows if: contains(matrix.os, 'windows') run: yarn config set network-timeout 600000 -g @@ -1453,15 +1453,6 @@ jobs: with: python-version: '3.8.10' - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node }} - - - name: Install Dependencies - if: steps.restore-dependencies.outputs.cache-hit != 'true' - run: yarn install --frozen-lockfile --ignore-engines --ignore-scripts - - name: Setup (arm64| ${{ contains(matrix.container, 'alpine') && 'musl' || 'glibc' }}) if: matrix.arch == 'arm64' && !contains(matrix.container, 'alpine') && matrix.target_platform != 'darwin' run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c187aebffe3..6fa005af46c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,9 @@ - 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! diff --git a/dev-packages/browser-integration-tests/package.json b/dev-packages/browser-integration-tests/package.json index 2e2e6cab12ab..dfd3b78c2c14 100644 --- a/dev-packages/browser-integration-tests/package.json +++ b/dev-packages/browser-integration-tests/package.json @@ -9,7 +9,7 @@ "private": true, "scripts": { "clean": "rimraf -g suites/**/dist loader-suites/**/dist tmp", - "install-browsers": "npx playwright install --with-deps", + "install-browsers": "[[ -z \"$SKIP_PLAYWRIGHT_BROWSER_INSTALL\" ]] && yarn install-browsers || echo 'Skipping browser installation'", "lint": "eslint . --format stylish", "fix": "eslint . --format stylish --fix", "type-check": "tsc", diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/init.js b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/init.js index 067dbec23fd4..27e5495f66a8 100644 --- a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/init.js +++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/init.js @@ -10,10 +10,3 @@ Sentry.init({ feedbackIntegration({ tags: { from: 'integration init' }, styleNonce: 'foo1234', scriptNonce: 'foo1234' }), ], }); - -document.addEventListener('securitypolicyviolation', () => { - const container = document.querySelector('#csp-violation'); - if (container) { - container.innerText = 'CSP Violation'; - } -}); diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/subject.js b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/subject.js new file mode 100644 index 000000000000..66adfd0f87d4 --- /dev/null +++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/subject.js @@ -0,0 +1,4 @@ +window.__CSPVIOLATION__ = false; +document.addEventListener('securitypolicyviolation', () => { + window.__CSPVIOLATION__ = true; +}); diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/template.html b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/template.html index 919f372ef468..8039192f5787 100644 --- a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/template.html +++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/template.html @@ -7,7 +7,5 @@ content="style-src 'nonce-foo1234'; script-src sentry-test.io 'nonce-foo1234';" /> - -
- + diff --git a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/test.ts b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/test.ts index 95a8a2eacee8..bca9b498fed0 100644 --- a/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/test.ts +++ b/dev-packages/browser-integration-tests/suites/feedback/captureFeedbackCsp/test.ts @@ -79,6 +79,6 @@ sentryTest('should capture feedback', async ({ getLocalTestUrl, page }) => { }, platform: 'javascript', }); - const cspContainer = await page.locator('#csp-violation'); - expect(cspContainer).not.toContainText('CSP Violation'); + const cspViolation = await page.evaluate('window.__CSPVIOLATION__'); + expect(cspViolation).toBe(false); }); diff --git a/packages/cloudflare/README.md b/packages/cloudflare/README.md index f7de52a56e88..398153563f1c 100644 --- a/packages/cloudflare/README.md +++ b/packages/cloudflare/README.md @@ -114,16 +114,16 @@ Currently only ESM handlers are supported. import * as Sentry from '@sentry/cloudflare'; export default withSentry( - (env) => ({ - dsn: env.SENTRY_DSN, + env => ({ + dsn: env.SENTRY_DSN, // Set tracesSampleRate to 1.0 to capture 100% of spans for tracing. - tracesSampleRate: 1.0, - }), - { - async fetch(request, env, ctx) { - return new Response('Hello World!'); - }, - } satisfies ExportedHandler + tracesSampleRate: 1.0, + }), + { + async fetch(request, env, ctx) { + return new Response('Hello World!'); + }, + } satisfies ExportedHandler, ); ``` diff --git a/packages/feedback/src/core/sendFeedback.ts b/packages/feedback/src/core/sendFeedback.ts index ca9875284c6e..c0b8ccaa2704 100644 --- a/packages/feedback/src/core/sendFeedback.ts +++ b/packages/feedback/src/core/sendFeedback.ts @@ -64,6 +64,12 @@ export const sendFeedback: SendFeedback = ( ); } + if (response && typeof response.statusCode === 'number' && response.statusCode === 403) { + return reject( + 'Unable to send Feedback. This could be because this domain is not in your list of allowed domains.', + ); + } + return reject( 'Unable to send Feedback. This could be because of network issues, or because you are using an ad-blocker', ); diff --git a/scripts/dependency-hash-key.js b/scripts/dependency-hash-key.js new file mode 100644 index 000000000000..55e38e4a385e --- /dev/null +++ b/scripts/dependency-hash-key.js @@ -0,0 +1,73 @@ +const crypto = require('crypto'); +const fs = require('fs'); +const path = require('path'); + +/** + * Build a cache key for the dependencies of the monorepo. + * In addition to the content of the yarn.lock file, we also include + * dependencies of all workspace packages in the cache key. + * This ensures that we get a consistent cache key even if a dependency change does not affect + * the yarn.lock file. + */ +function outputDependencyCacheKey() { + const lockfileContent = fs.readFileSync(path.join(process.cwd(), 'yarn.lock'), 'utf8'); + + const hashParts = [lockfileContent]; + + const packageJson = require(path.join(process.cwd(), 'package.json')); + + const workspacePackages = packageJson.workspaces || []; + + // Get the package name (e.g. @sentry/browser) of all workspace packages + // we want to ignore their version numbers later + const workspacePackageNames = getWorkspacePackageNames(workspacePackages); + + // Add the dependencies of the workspace itself + hashParts.push(getNormalizedDependencies(packageJson, workspacePackageNames)); + + // Now for each workspace package, add the dependencies + workspacePackages.forEach(workspace => { + const packageJsonPath = path.join(process.cwd(), workspace, 'package.json'); + const packageJson = require(packageJsonPath); + hashParts.push(getNormalizedDependencies(packageJson, workspacePackageNames)); + }); + + const hash = crypto.createHash('md5').update(hashParts.join('\n')).digest('hex'); + // We log the output in a way that the GitHub Actions can append it to the output + // We prefix it with `dependencies-` so it is easier to identify in the logs + // eslint-disable-next-line no-console + console.log(`hash=dependencies-${hash}`); +} + +function getNormalizedDependencies(packageJson, workspacePackageNames) { + const { dependencies, devDependencies } = packageJson; + + const mergedDependencies = { + ...devDependencies, + ...dependencies, + }; + + const normalizedDependencies = {}; + + // Sort the keys to ensure a consistent order + Object.keys(mergedDependencies) + .sort() + .forEach(key => { + // If the dependency is a workspace package, ignore the version + // No need to invalidate a cache after every release + const version = workspacePackageNames.includes(key) ? '**workspace**' : mergedDependencies[key]; + normalizedDependencies[key] = version; + }); + + return JSON.stringify(normalizedDependencies); +} + +function getWorkspacePackageNames(workspacePackages) { + return workspacePackages.map(workspace => { + const packageJsonPath = path.join(process.cwd(), workspace, 'package.json'); + const packageJson = require(packageJsonPath); + return packageJson.name; + }); +} + +outputDependencyCacheKey();