Skip to content

Commit e09a904

Browse files
authored
Merge branch 'develop' into cg/csp-nonce
2 parents 3122bec + ecc299d commit e09a904

File tree

83 files changed

+1859
-269
lines changed

Some content is hidden

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

83 files changed

+1859
-269
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: "Install yarn dependencies"
2+
description: "Installs yarn dependencies and caches them."
3+
4+
outputs:
5+
cache_key:
6+
description: "The dependency cache key"
7+
value: ${{ steps.compute_lockfile_hash.outputs.hash }}
8+
9+
runs:
10+
using: "composite"
11+
steps:
12+
# we use a hash of yarn.lock as our cache key, because if it hasn't changed, our dependencies haven't changed,
13+
# so no need to reinstall them
14+
- name: Compute dependency cache key
15+
id: compute_lockfile_hash
16+
run: echo "hash=dependencies-${{ hashFiles('yarn.lock', 'packages/*/package.json', 'dev-packages/*/package.json') }}" >> "$GITHUB_OUTPUT"
17+
shell: bash
18+
19+
- name: Check dependency cache
20+
uses: actions/cache@v4
21+
id: cache_dependencies
22+
with:
23+
path: ${{ env.CACHED_DEPENDENCY_PATHS }}
24+
key: ${{ steps.compute_lockfile_hash.outputs.hash }}
25+
26+
- name: Install dependencies
27+
if: steps.cache_dependencies.outputs.cache-hit != 'true'
28+
run: yarn install --ignore-engines --frozen-lockfile
29+
shell: bash

.github/workflows/build.yml

Lines changed: 20 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ jobs:
9696
profiling_node:
9797
- 'packages/profiling-node/**'
9898
- 'dev-packages/e2e-tests/test-applications/node-profiling/**'
99+
any_code:
100+
- '!**/*.md'
99101
100102
101103
- name: Get PR labels
@@ -109,6 +111,8 @@ jobs:
109111
is_release: ${{ startsWith(github.ref, 'refs/heads/release/') }}
110112
changed_profiling_node: ${{ steps.changed.outputs.profiling_node == 'true' }}
111113
changed_ci: ${{ steps.changed.outputs.workflow == 'true' }}
114+
changed_any_code: ${{ steps.changed.outputs.any_code == 'true' }}
115+
112116
# When merging into master, or from master
113117
is_gitflow_sync: ${{ github.head_ref == 'master' || github.ref == 'refs/heads/master' }}
114118
has_gitflow_label:
@@ -123,6 +127,7 @@ jobs:
123127
runs-on: ubuntu-20.04
124128
timeout-minutes: 15
125129
if: |
130+
needs.job_get_metadata.outputs.changed_any_code == 'true' &&
126131
(needs.job_get_metadata.outputs.is_gitflow_sync == 'false' && needs.job_get_metadata.outputs.has_gitflow_label == 'false')
127132
steps:
128133
- name: Check out base commit (${{ github.event.pull_request.base.sha }})
@@ -141,22 +146,9 @@ jobs:
141146
with:
142147
node-version-file: 'package.json'
143148

144-
# we use a hash of yarn.lock as our cache key, because if it hasn't changed, our dependencies haven't changed,
145-
# so no need to reinstall them
146-
- name: Compute dependency cache key
147-
id: compute_lockfile_hash
148-
run: echo "hash=${{ hashFiles('yarn.lock', '**/package.json') }}" >> "$GITHUB_OUTPUT"
149-
150-
- name: Check dependency cache
151-
uses: actions/cache@v4
152-
id: cache_dependencies
153-
with:
154-
path: ${{ env.CACHED_DEPENDENCY_PATHS }}
155-
key: ${{ steps.compute_lockfile_hash.outputs.hash }}
156-
157-
- name: Install dependencies
158-
if: steps.cache_dependencies.outputs.cache-hit != 'true'
159-
run: yarn install --ignore-engines --frozen-lockfile
149+
- name: Install Dependencies
150+
uses: ./.github/actions/install-dependencies
151+
id: install_dependencies
160152

161153
- name: Check for Affected Nx Projects
162154
uses: dkhunt27/[email protected]
@@ -195,7 +187,7 @@ jobs:
195187
run: yarn build
196188

197189
outputs:
198-
dependency_cache_key: ${{ steps.compute_lockfile_hash.outputs.hash }}
190+
dependency_cache_key: ${{ steps.install_dependencies.outputs.cache_key }}
199191
changed_node_integration: ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, '@sentry-internal/node-integration-tests') }}
200192
changed_remix: ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, '@sentry/remix') }}
201193
changed_node: ${{ needs.job_get_metadata.outputs.changed_ci == 'true' || contains(steps.checkForAffected.outputs.affected, '@sentry/node') }}
@@ -274,24 +266,24 @@ jobs:
274266

275267
job_check_format:
276268
name: Check file formatting
277-
needs: [job_get_metadata, job_build]
269+
needs: [job_get_metadata]
278270
timeout-minutes: 10
279271
runs-on: ubuntu-20.04
280272
steps:
281273
- name: Check out current commit (${{ needs.job_get_metadata.outputs.commit_label }})
282274
uses: actions/checkout@v4
283275
with:
284276
ref: ${{ env.HEAD_COMMIT }}
277+
285278
- name: Set up Node
286279
uses: actions/setup-node@v4
287280
with:
288281
node-version-file: 'package.json'
289-
- name: Check dependency cache
290-
uses: actions/cache/restore@v4
291-
with:
292-
path: ${{ env.CACHED_DEPENDENCY_PATHS }}
293-
key: ${{ needs.job_build.outputs.dependency_cache_key }}
294-
fail-on-cache-miss: true
282+
283+
- name: Install Dependencies
284+
uses: ./.github/actions/install-dependencies
285+
id: install_dependencies
286+
295287
- name: Check file formatting
296288
run: yarn lint:prettier && yarn lint:biome
297289

@@ -959,6 +951,7 @@ jobs:
959951
with:
960952
path: ${{ github.workspace }}/packages/*/*.tgz
961953
key: ${{ env.BUILD_CACHE_TARBALL_KEY }}
954+
fail-on-cache-miss: true
962955

963956
- name: Install Playwright
964957
uses: ./.github/actions/install-playwright
@@ -1058,6 +1051,7 @@ jobs:
10581051
with:
10591052
path: ${{ github.workspace }}/packages/*/*.tgz
10601053
key: ${{ env.BUILD_CACHE_TARBALL_KEY }}
1054+
fail-on-cache-miss: true
10611055

10621056
- name: Install Playwright
10631057
uses: ./.github/actions/install-playwright
@@ -1428,6 +1422,7 @@ jobs:
14281422
path: ${{ env.CACHED_DEPENDENCY_PATHS }}
14291423
key: ${{ needs.job_build.outputs.dependency_cache_key }}
14301424
enableCrossOsArchive: true
1425+
fail-on-cache-miss: true
14311426

14321427
- name: Restore build cache
14331428
uses: actions/cache/restore@v4
@@ -1436,6 +1431,7 @@ jobs:
14361431
path: ${{ env.CACHED_BUILD_PATHS }}
14371432
key: ${{ needs.job_build.outputs.dependency_cache_key }}
14381433
enableCrossOsArchive: true
1434+
fail-on-cache-miss: true
14391435

14401436
- name: Configure safe directory
14411437
run: |
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
name: "Automation: Cleanup PR caches"
2+
on:
3+
pull_request:
4+
types:
5+
- closed
6+
7+
jobs:
8+
cleanup:
9+
runs-on: ubuntu-latest
10+
permissions:
11+
# `actions:write` permission is required to delete caches
12+
# 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
13+
actions: write
14+
contents: read
15+
steps:
16+
- name: Check out code
17+
uses: actions/checkout@v4
18+
19+
- name: Cleanup
20+
run: |
21+
gh extension install actions/gh-actions-cache
22+
23+
REPO=${{ github.repository }}
24+
BRANCH=refs/pull/${{ github.event.pull_request.number }}/merge
25+
26+
echo "Fetching list of cache key"
27+
cacheKeysForPR=$(gh actions-cache list -R $REPO -B $BRANCH | cut -f 1 )
28+
29+
## Setting this to not fail the workflow while deleting cache keys.
30+
set +e
31+
echo "Deleting caches..."
32+
for cacheKey in $cacheKeysForPR
33+
do
34+
gh actions-cache delete $cacheKey -R $REPO -B $BRANCH --confirm
35+
done
36+
echo "Done"
37+
env:
38+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,5 @@
4242
"[typescript]": {
4343
"editor.defaultFormatter": "biomejs.biome"
4444
},
45-
"cSpell.words": ["arrayify"]
45+
"cSpell.words": ["arrayify", "OTEL"]
4646
}

dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.controller.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1-
import { Controller, Get, Param, ParseIntPipe, UseGuards, UseInterceptors } from '@nestjs/common';
1+
import { Controller, Get, Param, ParseIntPipe, UseFilters, UseGuards, UseInterceptors } from '@nestjs/common';
22
import { flush } from '@sentry/nestjs';
33
import { AppService } from './app.service';
4+
import { ExampleExceptionGlobalFilter } from './example-global-filter.exception';
5+
import { ExampleExceptionLocalFilter } from './example-local-filter.exception';
6+
import { ExampleLocalFilter } from './example-local.filter';
47
import { ExampleGuard } from './example.guard';
58
import { ExampleInterceptor } from './example.interceptor';
69

710
@Controller()
11+
@UseFilters(ExampleLocalFilter)
812
export class AppController {
913
constructor(private readonly appService: AppService) {}
1014

@@ -74,4 +78,14 @@ export class AppController {
7478
async flush() {
7579
await flush();
7680
}
81+
82+
@Get('example-exception-global-filter')
83+
async exampleExceptionGlobalFilter() {
84+
throw new ExampleExceptionGlobalFilter();
85+
}
86+
87+
@Get('example-exception-local-filter')
88+
async exampleExceptionLocalFilter() {
89+
throw new ExampleExceptionLocalFilter();
90+
}
7791
}

dev-packages/e2e-tests/test-applications/nestjs-basic/src/app.module.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
11
import { MiddlewareConsumer, Module } from '@nestjs/common';
2+
import { APP_FILTER } from '@nestjs/core';
23
import { ScheduleModule } from '@nestjs/schedule';
3-
import { SentryModule } from '@sentry/nestjs/setup';
4+
import { SentryGlobalFilter, SentryModule } from '@sentry/nestjs/setup';
45
import { AppController } from './app.controller';
56
import { AppService } from './app.service';
7+
import { ExampleGlobalFilter } from './example-global.filter';
68
import { ExampleMiddleware } from './example.middleware';
79

810
@Module({
911
imports: [SentryModule.forRoot(), ScheduleModule.forRoot()],
1012
controllers: [AppController],
11-
providers: [AppService],
13+
providers: [
14+
AppService,
15+
{
16+
provide: APP_FILTER,
17+
useClass: SentryGlobalFilter,
18+
},
19+
{
20+
provide: APP_FILTER,
21+
useClass: ExampleGlobalFilter,
22+
},
23+
],
1224
})
1325
export class AppModule {
1426
configure(consumer: MiddlewareConsumer): void {
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export class ExampleExceptionGlobalFilter extends Error {
2+
constructor() {
3+
super('Original global example exception!');
4+
}
5+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { ArgumentsHost, BadRequestException, Catch, ExceptionFilter } from '@nestjs/common';
2+
import { Request, Response } from 'express';
3+
import { ExampleExceptionGlobalFilter } from './example-global-filter.exception';
4+
5+
@Catch(ExampleExceptionGlobalFilter)
6+
export class ExampleGlobalFilter implements ExceptionFilter {
7+
catch(exception: BadRequestException, host: ArgumentsHost): void {
8+
const ctx = host.switchToHttp();
9+
const response = ctx.getResponse<Response>();
10+
const request = ctx.getRequest<Request>();
11+
12+
response.status(400).json({
13+
statusCode: 400,
14+
timestamp: new Date().toISOString(),
15+
path: request.url,
16+
message: 'Example exception was handled by global filter!',
17+
});
18+
}
19+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export class ExampleExceptionLocalFilter extends Error {
2+
constructor() {
3+
super('Original local example exception!');
4+
}
5+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { ArgumentsHost, BadRequestException, Catch, ExceptionFilter } from '@nestjs/common';
2+
import { Request, Response } from 'express';
3+
import { ExampleExceptionLocalFilter } from './example-local-filter.exception';
4+
5+
@Catch(ExampleExceptionLocalFilter)
6+
export class ExampleLocalFilter implements ExceptionFilter {
7+
catch(exception: BadRequestException, host: ArgumentsHost): void {
8+
const ctx = host.switchToHttp();
9+
const response = ctx.getResponse<Response>();
10+
const request = ctx.getRequest<Request>();
11+
12+
response.status(400).json({
13+
statusCode: 400,
14+
timestamp: new Date().toISOString(),
15+
path: request.url,
16+
message: 'Example exception was handled by local filter!',
17+
});
18+
}
19+
}

dev-packages/e2e-tests/test-applications/nestjs-basic/tests/errors.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,73 @@ test('Does not send RpcExceptions to Sentry', async ({ baseURL }) => {
9494

9595
expect(errorEventOccurred).toBe(false);
9696
});
97+
98+
test('Global exception filter registered in main module is applied and exception is not sent to Sentry', async ({
99+
baseURL,
100+
}) => {
101+
let errorEventOccurred = false;
102+
103+
waitForError('nestjs-basic', event => {
104+
if (!event.type && event.exception?.values?.[0]?.value === 'Example exception was handled by global filter!') {
105+
errorEventOccurred = true;
106+
}
107+
108+
return event?.transaction === 'GET /example-exception-global-filter';
109+
});
110+
111+
const transactionEventPromise = waitForTransaction('nestjs-basic', transactionEvent => {
112+
return transactionEvent?.transaction === 'GET /example-exception-global-filter';
113+
});
114+
115+
const response = await fetch(`${baseURL}/example-exception-global-filter`);
116+
const responseBody = await response.json();
117+
118+
expect(response.status).toBe(400);
119+
expect(responseBody).toEqual({
120+
statusCode: 400,
121+
timestamp: expect.any(String),
122+
path: '/example-exception-global-filter',
123+
message: 'Example exception was handled by global filter!',
124+
});
125+
126+
await transactionEventPromise;
127+
128+
(await fetch(`${baseURL}/flush`)).text();
129+
130+
expect(errorEventOccurred).toBe(false);
131+
});
132+
133+
test('Local exception filter registered in main module is applied and exception is not sent to Sentry', async ({
134+
baseURL,
135+
}) => {
136+
let errorEventOccurred = false;
137+
138+
waitForError('nestjs-basic', event => {
139+
if (!event.type && event.exception?.values?.[0]?.value === 'Example exception was handled by local filter!') {
140+
errorEventOccurred = true;
141+
}
142+
143+
return event?.transaction === 'GET /example-exception-local-filter';
144+
});
145+
146+
const transactionEventPromise = waitForTransaction('nestjs-basic', transactionEvent => {
147+
return transactionEvent?.transaction === 'GET /example-exception-local-filter';
148+
});
149+
150+
const response = await fetch(`${baseURL}/example-exception-local-filter`);
151+
const responseBody = await response.json();
152+
153+
expect(response.status).toBe(400);
154+
expect(responseBody).toEqual({
155+
statusCode: 400,
156+
timestamp: expect.any(String),
157+
path: '/example-exception-local-filter',
158+
message: 'Example exception was handled by local filter!',
159+
});
160+
161+
await transactionEventPromise;
162+
163+
(await fetch(`${baseURL}/flush`)).text();
164+
165+
expect(errorEventOccurred).toBe(false);
166+
});

0 commit comments

Comments
 (0)