Skip to content

Commit a8038ee

Browse files
authored
Merge branch 'develop' into cg/JS-1873/sentry-browsertracing-causes-googlebot-rendering-failures
2 parents 201152d + f83f288 commit a8038ee

31 files changed

Lines changed: 774 additions & 324 deletions

File tree

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
---
2+
name: bump-size-limit
3+
description: Bump size limits in .size-limit.js when the size-limit CI check is failing. Use when the user mentions size limit failures, bundle size checks failing, CI size check errors, or needs to update size-limit thresholds. Also use when the user says "bumpSizeLimit", "fix size limit", "size check failing", or "update bundle size limits".
4+
---
5+
6+
# Bump Size Limit
7+
8+
When the size-limit GitHub Action fails, it means one or more bundle scenarios exceed their configured byte thresholds in `.size-limit.js`. This skill walks through building, measuring, and bumping only the limits that need it.
9+
10+
## Workflow
11+
12+
### Step 1: Build all packages (including CDN bundles)
13+
14+
A full build is required because size-limit measures the actual compiled artifacts.
15+
16+
```bash
17+
yarn build
18+
```
19+
20+
This takes a few minutes. CDN bundles in `packages/browser/build/bundles/` must be up to date — a dev build is not sufficient.
21+
22+
### Step 2: Run the size check in JSON mode
23+
24+
```bash
25+
yarn test:size-limit
26+
```
27+
28+
The JSON output is an array of objects. Each object has:
29+
30+
- `name` — the scenario label
31+
- `passed` — whether it's within the limit
32+
- `size` — actual size in bytes
33+
- `sizeLimit` — configured limit in bytes
34+
35+
### Step 3: Identify failed scenarios
36+
37+
Filter for entries where `"passed": false`. These are the only ones that need bumping.
38+
39+
### Step 4: Calculate new limits
40+
41+
For each failed scenario, round the actual size **up to the next full KB** (1 KB = 1000 bytes in this context, matching how size-limit interprets the limits in `.size-limit.js`).
42+
43+
**Example:** If actual size is `129,127` bytes, the new limit is `130 KB` (i.e. 130,000 bytes).
44+
45+
The heuristic is intentionally conservative — it gives just enough headroom without inflating limits unnecessarily.
46+
47+
### Step 5: Update `.size-limit.js`
48+
49+
Open `.size-limit.js` at the repository root and update the `limit` field for each failed scenario. Limits are strings like `'130 KB'`.
50+
51+
Only change limits for scenarios that actually failed. Do not touch passing scenarios.
52+
53+
### Step 6: Verify the fix
54+
55+
Re-run size-limit to confirm everything passes:
56+
57+
```bash
58+
yarn test:size-limit
59+
```
60+
61+
If any scenario still fails (e.g., due to rounding edge cases), bump that specific limit by another 1 KB and re-run.

.github/workflows/canary.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ jobs:
6969
fail-fast: false
7070
matrix:
7171
include:
72-
- test-application: 'angular-20'
72+
- test-application: 'angular-21'
7373
build-command: 'test:build-canary'
74-
label: 'angular-20 (next)'
74+
label: 'angular-21 (next)'
7575
- test-application: 'create-react-app'
7676
build-command: 'test:build-canary'
7777
label: 'create-react-app (canary)'
@@ -130,7 +130,6 @@ jobs:
130130
with:
131131
version: 9.15.9
132132
- name: Set up Node
133-
if: matrix.test-application != 'angular-20'
134133
uses: actions/setup-node@v6
135134
with:
136135
node-version-file: 'dev-packages/e2e-tests/test-applications/${{ matrix.test-application }}/package.json'

agents.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ source = "path:.agents/skills/triage-issue"
4242
name = "upgrade-dep"
4343
source = "path:.agents/skills/upgrade-dep"
4444

45+
[[skills]]
46+
name = "bump-size-limit"
47+
source = "path:.agents/skills/bump-size-limit"
48+
4549
[[skills]]
4650
name = "upgrade-otel"
4751
source = "path:.agents/skills/upgrade-otel"

dev-packages/e2e-tests/test-applications/angular-20/package.json

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
"watch": "ng build --watch --configuration development",
1111
"test": "playwright test",
1212
"test:build": "pnpm install && pnpm build",
13-
"test:build-canary": "pnpm install && pnpm add @angular/animations@next @angular/common@next @angular/compiler@next @angular/core@next @angular/forms@next @angular/platform-browser@next @angular/platform-browser-dynamic@next @angular/router@next && pnpm add -D @angular-devkit/build-angular@next @angular/cli@next @angular/compiler-cli@next && pnpm build",
1413
"test:assert": "playwright test",
1514
"clean": "npx rimraf .angular node_modules pnpm-lock.yaml dist"
1615
},
@@ -48,13 +47,5 @@
4847
},
4948
"volta": {
5049
"extends": "../../package.json"
51-
},
52-
"sentryTest": {
53-
"optionalVariants": [
54-
{
55-
"build-command": "pnpm test:build-canary",
56-
"label": "angular (canary)"
57-
}
58-
]
5950
}
6051
}

dev-packages/e2e-tests/test-applications/angular-21/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
"typescript": "~5.9.0"
4848
},
4949
"volta": {
50+
"node": "22.22.0",
5051
"extends": "../../package.json"
5152
},
5253
"sentryTest": {
Lines changed: 55 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,74 @@
1+
import * as assert from 'assert/strict';
12
import {
2-
loadSourcemapUploadRecords,
3-
assertSourcemapUploadRequests,
4-
getArtifactBundleManifests,
5-
assertDebugIdPairs,
6-
assertSourcemapMappings,
7-
assertSourcemapSources,
8-
assertArtifactBundleAssembly,
9-
getSourcemapUploadSummary,
3+
loadMockServerResults,
4+
getArtifactBundles,
5+
getDebugIdPairs,
6+
getSourcemaps,
7+
getChunkUploadPosts,
8+
getAssembleRequests,
109
} from '@sentry-internal/test-utils';
1110

12-
const requests = loadSourcemapUploadRecords();
11+
const requests = loadMockServerResults();
1312

1413
console.log(`Captured ${requests.length} requests to mock Sentry server:\n`);
1514
for (const req of requests) {
1615
console.log(` ${req.method} ${req.url} (${req.bodySize} bytes)`);
1716
}
1817
console.log('');
1918

20-
assertSourcemapUploadRequests(requests, 'fake-auth-token');
19+
// Auth token is present
20+
const authenticated = requests.filter(r => r.authorization.includes('fake-auth-token'));
21+
assert.ok(authenticated.length > 0, 'Expected requests with the configured auth token');
2122

22-
const manifests = getArtifactBundleManifests(requests);
23-
console.log(`Found ${manifests.length} artifact bundle manifest(s):\n`);
23+
// Chunk uploads happened
24+
const chunkPosts = getChunkUploadPosts(requests);
25+
assert.ok(chunkPosts.length > 0, 'Expected at least one chunk upload POST');
26+
assert.ok(
27+
chunkPosts.some(r => r.bodySize > 0),
28+
'Expected at least one chunk upload with a non-empty body',
29+
);
2430

25-
const debugIdPairs = assertDebugIdPairs(manifests);
26-
console.log(`Found ${debugIdPairs.length} JS/sourcemap pairs with debug IDs:`);
31+
// Release endpoint was called
32+
assert.ok(
33+
requests.some(r => r.url?.includes('/releases/')),
34+
'Expected at least one request to releases endpoint',
35+
);
36+
37+
// Artifact bundles have manifests
38+
const bundles = getArtifactBundles(requests);
39+
assert.ok(bundles.length > 0, 'Expected at least one artifact bundle with a manifest');
40+
console.log(`Found ${bundles.length} artifact bundle(s)\n`);
41+
42+
// Debug ID pairs exist and are valid UUIDs
43+
const debugIdPairs = getDebugIdPairs(bundles);
44+
assert.ok(debugIdPairs.length > 0, 'Expected at least one JS/sourcemap pair with matching debug IDs');
45+
46+
const uuidRegex = /^[\da-f]{8}-[\da-f]{4}-[\da-f]{4}-[\da-f]{4}-[\da-f]{12}$/i;
2747
for (const pair of debugIdPairs) {
48+
assert.match(pair.debugId, uuidRegex, `Invalid debug ID: ${pair.debugId}`);
2849
console.log(` ${pair.debugId} ${pair.jsUrl}`);
2950
}
3051
console.log('');
3152

32-
assertSourcemapMappings(manifests);
33-
assertSourcemapSources(manifests, /client-page|page\.tsx/);
34-
assertArtifactBundleAssembly(requests, 'test-project');
53+
// Sourcemaps have real content
54+
const sourcemaps = getSourcemaps(bundles);
55+
assert.ok(
56+
sourcemaps.some(s => s.sourcemap.mappings && s.sourcemap.mappings.length > 0),
57+
'Expected at least one sourcemap with non-empty mappings',
58+
);
3559

36-
const summary = getSourcemapUploadSummary(requests, manifests, debugIdPairs);
60+
// At least one sourcemap references app source files
61+
assert.ok(
62+
sourcemaps.some(s => s.sourcemap.sources?.some(src => /client-page|page\.tsx/.test(src))),
63+
'Expected at least one sourcemap referencing app source files',
64+
);
65+
66+
// Assemble requests reference the correct project
67+
const assembleReqs = getAssembleRequests(requests);
68+
assert.ok(assembleReqs.length > 0, 'Expected at least one assemble request');
69+
for (const req of assembleReqs) {
70+
assert.ok(req.assembleBody?.projects?.includes('test-project'), 'Expected assemble request to include test-project');
71+
assert.ok((req.assembleBody?.chunks?.length ?? 0) > 0, 'Expected assemble request to have chunk checksums');
72+
}
3773

38-
console.log('\nAll sourcemap upload assertions passed!');
39-
console.log(` - ${summary.totalRequests} total requests captured`);
40-
console.log(` - ${summary.chunkUploadPosts} chunk upload POST requests`);
41-
console.log(` - ${summary.artifactBundles} artifact bundles with manifests`);
42-
console.log(` - ${summary.debugIdPairs} JS/sourcemap pairs with debug IDs`);
43-
console.log(` - ${summary.assembleRequests} artifact bundle assemble requests`);
74+
console.log('All sourcemap upload assertions passed!');
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as Sentry from '@sentry/node-core';
2+
import { loggingTransport } from '@sentry-internal/node-integration-tests';
3+
import { setupOtel } from '../../../../utils/setupOtel.js';
4+
5+
const client = Sentry.init({
6+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
7+
release: '1.0',
8+
integrations: [Sentry.nativeNodeFetchIntegration({ tracePropagation: false })],
9+
transport: loggingTransport,
10+
});
11+
12+
setupOtel(client);
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as Sentry from '@sentry/node-core';
2+
3+
async function run() {
4+
Sentry.addBreadcrumb({ message: 'manual breadcrumb' });
5+
6+
await fetch(`${process.env.SERVER_URL}/api/v0`).then(res => res.text());
7+
await fetch(`${process.env.SERVER_URL}/api/v1`).then(res => res.text());
8+
9+
Sentry.captureException(new Error('foo'));
10+
}
11+
12+
run();
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import { createTestServer } from '@sentry-internal/test-utils';
2+
import { describe, expect } from 'vitest';
3+
import { createEsmAndCjsTests } from '../../../../utils/runner';
4+
5+
describe('outgoing fetch with tracePropagation disabled', () => {
6+
createEsmAndCjsTests(__dirname, 'scenario.mjs', 'instrument.mjs', (createRunner, test) => {
7+
test('does not inject trace headers but still creates breadcrumbs', async () => {
8+
expect.assertions(5);
9+
10+
const [SERVER_URL, closeTestServer] = await createTestServer()
11+
.get('/api/v0', headers => {
12+
expect(headers['sentry-trace']).toBeUndefined();
13+
expect(headers['baggage']).toBeUndefined();
14+
})
15+
.get('/api/v1', headers => {
16+
expect(headers['sentry-trace']).toBeUndefined();
17+
expect(headers['baggage']).toBeUndefined();
18+
})
19+
.start();
20+
21+
await createRunner()
22+
.withEnv({ SERVER_URL })
23+
.expect({
24+
event: {
25+
breadcrumbs: [
26+
{
27+
message: 'manual breadcrumb',
28+
timestamp: expect.any(Number),
29+
},
30+
{
31+
category: 'http',
32+
data: {
33+
'http.method': 'GET',
34+
url: `${SERVER_URL}/api/v0`,
35+
status_code: 200,
36+
},
37+
timestamp: expect.any(Number),
38+
type: 'http',
39+
},
40+
{
41+
category: 'http',
42+
data: {
43+
'http.method': 'GET',
44+
url: `${SERVER_URL}/api/v1`,
45+
status_code: 200,
46+
},
47+
timestamp: expect.any(Number),
48+
type: 'http',
49+
},
50+
],
51+
exception: {
52+
values: [
53+
{
54+
type: 'Error',
55+
value: 'foo',
56+
},
57+
],
58+
},
59+
},
60+
})
61+
.start()
62+
.completed();
63+
closeTestServer();
64+
});
65+
});
66+
});
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as Sentry from '@sentry/node-core';
2+
import { loggingTransport } from '@sentry-internal/node-integration-tests';
3+
import { setupOtel } from '../../../../utils/setupOtel.js';
4+
5+
const client = Sentry.init({
6+
dsn: 'https://public@dsn.ingest.sentry.io/1337',
7+
release: '1.0',
8+
integrations: [Sentry.httpIntegration({ tracePropagation: false, spans: false })],
9+
transport: loggingTransport,
10+
});
11+
12+
setupOtel(client);

0 commit comments

Comments
 (0)