Skip to content

Commit 7f65c39

Browse files
authored
Merge branch 'develop' into ab/deno-reader
2 parents dccdf31 + f1932c9 commit 7f65c39

File tree

83 files changed

+2564
-107
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

+2564
-107
lines changed

.cursor/BUGBOT.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ Do not flag the issues below if they appear in tests.
5858
- Flag usage of `expect.objectContaining` and other relaxed assertions, when a test expects something NOT to be included in a payload but there's no respective assertion.
5959
- Flag usage of conditionals in one test and recommend splitting up the test for the different paths.
6060
- Flag usage of loops testing multiple scenarios in one test and recommend using `(it)|(test).each` instead.
61+
- Flag tests that are likely to introduce flakes. In our case this usually means we wait for some telemetry requests sent from an SDK. Patterns to look out for:
62+
- Only waiting for a request, after an action is performed. Instead, start waiting, perform action, await request promise.
63+
- 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.
64+
- Timeouts or sleeps in tests. Instead suggest concrete events or other signals to wait on.
65+
- Flag usage of `getFirstEnvelope*`, `getMultipleEnvelope*` or related test helpers. These are NOT reliable anymore. Instead suggest helpers like `waitForTransaction`, `waitForError`, `waitForSpans`, etc.
6166

6267
## Platform-safe code
6368

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
title: '[Flaky CI]: {{ env.JOB_NAME }}'
3+
labels: Tests
4+
---
5+
6+
### Flakiness Type
7+
8+
Other / Unknown
9+
10+
### Name of Job
11+
12+
{{ env.JOB_NAME }}
13+
14+
### Name of Test
15+
16+
_Not available - check the run link for details_
17+
18+
### Link to Test Run
19+
20+
{{ env.RUN_LINK }}
21+
22+
---
23+
24+
_This issue was automatically created._

.github/workflows/build.yml

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1192,7 +1192,85 @@ jobs:
11921192
# Always run this, even if a dependent job failed
11931193
if: always()
11941194
runs-on: ubuntu-24.04
1195+
permissions:
1196+
issues: write
11951197
steps:
1198+
- name: Check out current commit
1199+
if: github.ref == 'refs/heads/develop' && contains(needs.*.result, 'failure')
1200+
uses: actions/checkout@v6
1201+
with:
1202+
sparse-checkout: .github
1203+
1204+
- name: Create issues for failed jobs
1205+
if: github.ref == 'refs/heads/develop' && contains(needs.*.result, 'failure')
1206+
uses: actions/github-script@v7
1207+
with:
1208+
script: |
1209+
const fs = require('fs');
1210+
1211+
// Fetch actual job details from the API to get descriptive names
1212+
const jobs = await github.paginate(github.rest.actions.listJobsForWorkflowRun, {
1213+
owner: context.repo.owner,
1214+
repo: context.repo.repo,
1215+
run_id: context.runId,
1216+
per_page: 100
1217+
});
1218+
1219+
const failedJobs = jobs.filter(job => job.conclusion === 'failure');
1220+
1221+
if (failedJobs.length === 0) {
1222+
console.log('No failed jobs found');
1223+
return;
1224+
}
1225+
1226+
// Read and parse template
1227+
const template = fs.readFileSync('.github/FLAKY_CI_FAILURE_TEMPLATE.md', 'utf8');
1228+
const [, frontmatter, bodyTemplate] = template.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
1229+
1230+
// Get existing open issues with Tests label
1231+
const existing = await github.paginate(github.rest.issues.listForRepo, {
1232+
owner: context.repo.owner,
1233+
repo: context.repo.repo,
1234+
state: 'open',
1235+
labels: 'Tests',
1236+
per_page: 100
1237+
});
1238+
1239+
for (const job of failedJobs) {
1240+
const jobName = job.name;
1241+
const jobUrl = job.html_url;
1242+
1243+
// Replace template variables
1244+
const vars = {
1245+
'JOB_NAME': jobName,
1246+
'RUN_LINK': jobUrl
1247+
};
1248+
1249+
let title = frontmatter.match(/title:\s*'(.*)'/)[1];
1250+
let issueBody = bodyTemplate;
1251+
for (const [key, value] of Object.entries(vars)) {
1252+
const pattern = new RegExp(`\\{\\{\\s*env\\.${key}\\s*\\}\\}`, 'g');
1253+
title = title.replace(pattern, value);
1254+
issueBody = issueBody.replace(pattern, value);
1255+
}
1256+
1257+
const existingIssue = existing.find(i => i.title === title);
1258+
1259+
if (existingIssue) {
1260+
console.log(`Issue already exists for ${jobName}: #${existingIssue.number}`);
1261+
continue;
1262+
}
1263+
1264+
const newIssue = await github.rest.issues.create({
1265+
owner: context.repo.owner,
1266+
repo: context.repo.repo,
1267+
title: title,
1268+
body: issueBody.trim(),
1269+
labels: ['Tests']
1270+
});
1271+
console.log(`Created issue #${newIssue.data.number} for ${jobName}`);
1272+
}
1273+
11961274
- name: Check for failures
11971275
if: cancelled() || contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled')
11981276
run: |

.github/workflows/ci-metadata.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,11 @@ jobs:
5151
id: get_metadata
5252
# We need to try a number of different options for finding the head commit, because each kind of trigger event
5353
# stores it in a different location
54+
env:
55+
COMMIT_SHA_EXPR:
56+
${{ github.event.pull_request.head.sha || github.event.head_commit.id || inputs.head_commit }}
5457
run: |
55-
COMMIT_SHA=$(git rev-parse --short ${{ github.event.pull_request.head.sha || github.event.head_commit.id || inputs.head_commit }})
58+
COMMIT_SHA=$(git rev-parse --short "$COMMIT_SHA_EXPR")
5659
echo "COMMIT_SHA=$COMMIT_SHA" >> $GITHUB_ENV
5760
echo "COMMIT_MESSAGE=$(git log -n 1 --pretty=format:%s $COMMIT_SHA)" >> $GITHUB_ENV
5861

.size-limit.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ module.exports = [
276276
path: createCDNPath('bundle.tracing.replay.logs.metrics.min.js'),
277277
gzip: false,
278278
brotli: false,
279-
limit: '250 KB',
279+
limit: '251 KB',
280280
},
281281
{
282282
name: 'CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed',

dev-packages/browser-integration-tests/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
"@sentry-internal/rrweb": "2.34.0",
6363
"@sentry/browser": "10.48.0",
6464
"@supabase/supabase-js": "2.49.3",
65-
"axios": "1.13.5",
65+
"axios": "1.15.0",
6666
"babel-loader": "^10.1.1",
6767
"fflate": "0.8.2",
6868
"html-webpack-plugin": "^5.5.0",

dev-packages/cloudflare-integration-tests/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"@langchain/langgraph": "^1.0.1",
1717
"@sentry/cloudflare": "10.48.0",
1818
"@sentry/hono": "10.48.0",
19-
"hono": "^4.12.7"
19+
"hono": "^4.12.12"
2020
},
2121
"devDependencies": {
2222
"@cloudflare/workers-types": "^4.20250922.0",

dev-packages/cloudflare-integration-tests/runner.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,17 @@ export function createRunner(...paths: string[]) {
6868
// By default, we ignore session & sessions
6969
const ignored: Set<EnvelopeItemType> = new Set(['session', 'sessions', 'client_report']);
7070
let serverUrl: string | undefined;
71+
const extraWranglerArgs: string[] = [];
7172

7273
return {
7374
withServerUrl: function (url: string) {
7475
serverUrl = url;
7576
return this;
7677
},
78+
withWranglerArgs: function (...args: string[]) {
79+
extraWranglerArgs.push(...args);
80+
return this;
81+
},
7782
expect: function (expected: Expected) {
7883
expectedEnvelopes.push(expected);
7984
return this;
@@ -237,6 +242,7 @@ export function createRunner(...paths: string[]) {
237242
`SENTRY_DSN:http://public@localhost:${mockServerPort}/1337`,
238243
'--var',
239244
`SERVER_URL:${serverUrl}`,
245+
...extraWranglerArgs,
240246
],
241247
{ stdio, signal },
242248
);
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { instrumentDurableObjectWithSentry, withSentry } from '@sentry/cloudflare';
2+
import { DurableObject } from 'cloudflare:workers';
3+
4+
interface Env {
5+
SENTRY_DSN: string;
6+
ECHO_HEADERS_DO: DurableObjectNamespace;
7+
}
8+
9+
class EchoHeadersDurableObjectBase extends DurableObject<Env> {
10+
async fetch(incoming: Request): Promise<Response> {
11+
return Response.json({
12+
sentryTrace: incoming.headers.get('sentry-trace'),
13+
baggage: incoming.headers.get('baggage'),
14+
authorization: incoming.headers.get('authorization'),
15+
xFromInit: incoming.headers.get('x-from-init'),
16+
xExtra: incoming.headers.get('x-extra'),
17+
xMergeProbe: incoming.headers.get('x-merge-probe'),
18+
});
19+
}
20+
}
21+
22+
export const EchoHeadersDurableObject = instrumentDurableObjectWithSentry(
23+
(env: Env) => ({
24+
dsn: env.SENTRY_DSN,
25+
tracesSampleRate: 1.0,
26+
}),
27+
EchoHeadersDurableObjectBase,
28+
);
29+
30+
export default withSentry(
31+
(env: Env) => ({
32+
dsn: env.SENTRY_DSN,
33+
tracesSampleRate: 1.0,
34+
}),
35+
{
36+
async fetch(request, env) {
37+
const url = new URL(request.url);
38+
const id = env.ECHO_HEADERS_DO.idFromName('instrument-fetcher-echo');
39+
const stub = env.ECHO_HEADERS_DO.get(id);
40+
const doUrl = new URL(request.url);
41+
42+
let subResponse: Response;
43+
44+
if (url.pathname === '/via-init') {
45+
subResponse = await stub.fetch(doUrl, {
46+
headers: {
47+
Authorization: 'Bearer from-init',
48+
'X-Extra': 'init-extra',
49+
'X-Merge-Probe': 'via-init-probe',
50+
},
51+
});
52+
} else if (url.pathname === '/via-request') {
53+
subResponse = await stub.fetch(
54+
new Request(doUrl, {
55+
headers: {
56+
Authorization: 'Bearer from-request',
57+
'X-Extra': 'request-extra',
58+
'X-Merge-Probe': 'via-request-probe',
59+
},
60+
}),
61+
);
62+
} else if (url.pathname === '/via-request-and-init') {
63+
subResponse = await stub.fetch(
64+
new Request(doUrl, {
65+
headers: {
66+
Authorization: 'Bearer from-request',
67+
'X-Extra': 'request-extra',
68+
'X-Merge-Probe': 'dropped-from-request',
69+
},
70+
}),
71+
{
72+
headers: {
73+
'X-From-Init': '1',
74+
'X-Merge-Probe': 'via-init-wins',
75+
},
76+
},
77+
);
78+
} else if (url.pathname === '/with-preset-sentry-baggage') {
79+
subResponse = await stub.fetch(
80+
new Request(doUrl, {
81+
headers: {
82+
baggage: 'sentry-environment=preset,acme=vendor',
83+
},
84+
}),
85+
);
86+
} else {
87+
return new Response('not found', { status: 404 });
88+
}
89+
90+
const payload: unknown = await subResponse.json();
91+
return Response.json(payload);
92+
},
93+
} satisfies ExportedHandler<Env>,
94+
);

0 commit comments

Comments
 (0)